1use crate::cdsl::formats::InstructionFormat;
2use crate::cdsl::instructions::AllInstructions;
3use crate::error;
4use cranelift_srcgen::{Formatter, Language, fmtln};
5use std::{borrow::Cow, cmp::Ordering, rc::Rc};
6
7#[derive(Clone, Copy, PartialEq, Eq)]
9enum IsleTarget {
10 Lower,
12 Opt,
14}
15
16fn gen_common_isle(
17 formats: &[Rc<InstructionFormat>],
18 instructions: &AllInstructions,
19 fmt: &mut Formatter,
20 isle_target: IsleTarget,
21) {
22 use std::collections::{BTreeMap, BTreeSet};
23 use std::fmt::Write;
24
25 use crate::cdsl::formats::FormatField;
26
27 fmt.multi_line(
28 r#"
29;; GENERATED BY `gen_isle`. DO NOT EDIT!!!
30;;
31;; This ISLE file defines all the external type declarations for Cranelift's
32;; data structures that ISLE will process, such as `InstructionData` and
33;; `Opcode`.
34 "#,
35 );
36 fmt.empty_line();
37
38 let rust_name = |f: &FormatField| f.kind.rust_type.rsplit("::").next().unwrap();
40 let fields = |f: &FormatField| f.kind.fields.clone();
41 let immediate_types: BTreeMap<_, _> = formats
42 .iter()
43 .flat_map(|f| {
44 f.imm_fields
45 .iter()
46 .map(|i| (rust_name(i), fields(i)))
47 .collect::<Vec<_>>()
48 })
49 .collect();
50
51 let (enums, others): (BTreeMap<_, _>, BTreeMap<_, _>) = immediate_types
54 .iter()
55 .partition(|(_, field)| field.enum_values().is_some());
56
57 fmt.line(";;;; Extern type declarations for immediates ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;");
60 fmt.empty_line();
61 for ty in others.keys() {
62 fmtln!(fmt, "(type {} (primitive {}))", ty, ty);
63 }
64 fmt.empty_line();
65
66 for (name, field) in enums {
69 let field = field.enum_values().expect("only enums considered here");
70 let variants = field.values().cloned().collect();
71 gen_isle_enum(name, variants, fmt)
72 }
73
74 fmt.line(";;;; Value Arrays ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;");
77 fmt.empty_line();
78 let value_array_arities: BTreeSet<_> = formats
79 .iter()
80 .filter(|f| f.typevar_operand.is_some() && !f.has_value_list && f.num_value_operands != 1)
81 .map(|f| f.num_value_operands)
82 .collect();
83 for n in value_array_arities {
84 fmtln!(fmt, ";; ISLE representation of `[Value; {}]`.", n);
85 fmtln!(fmt, "(type ValueArray{} extern (enum))", n);
86 fmt.empty_line();
87
88 fmtln!(
89 fmt,
90 "(decl value_array_{} ({}) ValueArray{})",
91 n,
92 (0..n).map(|_| "Value").collect::<Vec<_>>().join(" "),
93 n
94 );
95 fmtln!(
96 fmt,
97 "(extern constructor value_array_{} pack_value_array_{})",
98 n,
99 n
100 );
101 fmtln!(
102 fmt,
103 "(extern extractor infallible value_array_{} unpack_value_array_{})",
104 n,
105 n
106 );
107 fmt.empty_line();
108 }
109
110 fmt.line(";;;; Block Arrays ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;");
113 fmt.empty_line();
114 let block_array_arities: BTreeSet<_> = formats
115 .iter()
116 .filter(|f| f.num_block_operands > 1)
117 .map(|f| f.num_block_operands)
118 .collect();
119 for n in block_array_arities {
120 fmtln!(fmt, ";; ISLE representation of `[BlockCall; {}]`.", n);
121 fmtln!(fmt, "(type BlockArray{} extern (enum))", n);
122 fmt.empty_line();
123
124 fmtln!(
125 fmt,
126 "(decl block_array_{0} ({1}) BlockArray{0})",
127 n,
128 (0..n).map(|_| "BlockCall").collect::<Vec<_>>().join(" ")
129 );
130
131 fmtln!(
132 fmt,
133 "(extern constructor block_array_{0} pack_block_array_{0})",
134 n
135 );
136
137 fmtln!(
138 fmt,
139 "(extern extractor infallible block_array_{0} unpack_block_array_{0})",
140 n
141 );
142 fmt.empty_line();
143 }
144
145 fmtln!(fmt, "(type Block extern (enum))");
147 fmt.empty_line();
148
149 fmt.line(";;;; `Opcode` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;");
151 fmt.empty_line();
152 fmt.line("(type Opcode extern");
153 fmt.indent(|fmt| {
154 fmt.line("(enum");
155 fmt.indent(|fmt| {
156 for inst in instructions {
157 fmtln!(fmt, "{}", inst.camel_name);
158 }
159 });
160 fmt.line(")");
161 });
162 fmt.line(")");
163 fmt.empty_line();
164
165 fmtln!(
167 fmt,
168 ";;;; `InstructionData` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;",
169 );
170 fmt.empty_line();
171 fmtln!(fmt, "(type InstructionData extern");
172 fmt.indent(|fmt| {
173 fmt.line("(enum");
174 fmt.indent(|fmt| {
175 for format in formats {
176 let mut s = format!("({} (opcode Opcode)", format.name);
177 if format.has_value_list {
178 s.push_str(" (args ValueList)");
179 } else if format.num_value_operands == 1 {
180 s.push_str(" (arg Value)");
181 } else if format.num_value_operands > 1 {
182 write!(&mut s, " (args ValueArray{})", format.num_value_operands).unwrap();
183 }
184
185 match format.num_block_operands {
186 0 => (),
187 1 => write!(&mut s, " (destination BlockCall)").unwrap(),
188 n => write!(&mut s, " (blocks BlockArray{n})").unwrap(),
189 }
190
191 match format.num_raw_block_operands {
192 0 => (),
193 1 => write!(&mut s, "(block Block)").unwrap(),
194 _ => panic!("Too many raw block arguments"),
195 }
196
197 for field in &format.imm_fields {
198 write!(
199 &mut s,
200 " ({} {})",
201 field.member,
202 field.kind.rust_type.rsplit("::").next().unwrap()
203 )
204 .unwrap();
205 }
206 s.push(')');
207 fmt.line(&s);
208 }
209 });
210 fmt.line(")");
211 });
212 fmt.line(")");
213 fmt.empty_line();
214
215 fmtln!(
217 fmt,
218 ";;;; Extracting Opcode, Operands, and Immediates from `InstructionData` ;;;;;;;;",
219 );
220 fmt.empty_line();
221 for inst in instructions {
222 let results_len = inst.value_results.len();
223 let is_var_args = inst.format.has_value_list;
224 let has_side_effects = inst.can_trap || inst.other_side_effects;
225
226 let (ret_ty, ty_in_decl, make_inst_ctor, inst_data_etor) =
227 match (isle_target, is_var_args, results_len, has_side_effects) {
228 (IsleTarget::Opt, true, _, _) => continue,
230
231 (IsleTarget::Opt, _, 1, false) => ("Value", true, "make_inst", "inst_data_value"),
232 (IsleTarget::Opt, _, _, _) => ("Inst", false, "make_skeleton_inst", "inst_data"),
233 (IsleTarget::Lower, _, _, _) => ("Inst", false, "make_inst", "inst_data_value"),
234 };
235
236 fmtln!(
237 fmt,
238 "(decl {} ({}{}) {})",
239 inst.name,
240 if ty_in_decl { "Type " } else { "" },
241 inst.operands_in
242 .iter()
243 .map(|o| {
244 let ty = o.kind.rust_type;
245 if ty == "&[Value]" {
246 "ValueSlice"
247 } else {
248 ty.rsplit("::").next().unwrap()
249 }
250 })
251 .collect::<Vec<_>>()
252 .join(" "),
253 ret_ty
254 );
255 fmtln!(fmt, "(extractor");
256 fmt.indent(|fmt| {
257 fmtln!(
258 fmt,
259 "({} {}{})",
260 inst.name,
261 if ty_in_decl { "ty " } else { "" },
262 inst.operands_in
263 .iter()
264 .map(|o| { o.name })
265 .collect::<Vec<_>>()
266 .join(" ")
267 );
268
269 let mut s = format!(
270 "({inst_data_etor} {}(InstructionData.{} (Opcode.{})",
271 if ty_in_decl { "ty " } else { "" },
272 inst.format.name,
273 inst.camel_name
274 );
275
276 if inst.format.has_value_list {
278 let values: Vec<_> = inst
284 .operands_in
285 .iter()
286 .filter(|o| o.is_value())
287 .map(|o| o.name)
288 .collect();
289 let varargs = inst
290 .operands_in
291 .iter()
292 .find(|o| o.is_varargs())
293 .unwrap()
294 .name;
295 if values.is_empty() {
296 write!(&mut s, " (value_list_slice {varargs})").unwrap();
297 } else {
298 write!(
299 &mut s,
300 " (unwrap_head_value_list_{} {} {})",
301 values.len(),
302 values.join(" "),
303 varargs
304 )
305 .unwrap();
306 }
307 } else if inst.format.num_value_operands == 1 {
308 write!(
309 &mut s,
310 " {}",
311 inst.operands_in.iter().find(|o| o.is_value()).unwrap().name
312 )
313 .unwrap();
314 } else if inst.format.num_value_operands > 1 {
315 let values = inst
316 .operands_in
317 .iter()
318 .filter(|o| o.is_value())
319 .map(|o| o.name)
320 .collect::<Vec<_>>();
321 assert_eq!(values.len(), inst.format.num_value_operands);
322 let values = values.join(" ");
323 write!(
324 &mut s,
325 " (value_array_{} {})",
326 inst.format.num_value_operands, values,
327 )
328 .unwrap();
329 }
330
331 let imm_operands: Vec<_> = inst
333 .operands_in
334 .iter()
335 .filter(|o| {
336 !o.is_value() && !o.is_varargs() && !o.kind.is_block() && !o.kind.is_raw_block()
337 })
338 .collect();
339 assert_eq!(imm_operands.len(), inst.format.imm_fields.len(),);
340 for op in imm_operands {
341 write!(&mut s, " {}", op.name).unwrap();
342 }
343
344 let block_operands: Vec<_> = inst
346 .operands_in
347 .iter()
348 .filter(|o| o.kind.is_block())
349 .collect();
350 assert_eq!(block_operands.len(), inst.format.num_block_operands);
351 assert!(block_operands.len() <= 2);
352
353 if !block_operands.is_empty() {
354 if block_operands.len() == 1 {
355 write!(&mut s, " {}", block_operands[0].name).unwrap();
356 } else {
357 let blocks: Vec<_> = block_operands.iter().map(|o| o.name).collect();
358 let blocks = blocks.join(" ");
359 write!(
360 &mut s,
361 " (block_array_{} {})",
362 inst.format.num_block_operands, blocks,
363 )
364 .unwrap();
365 }
366 }
367
368 match inst.format.num_raw_block_operands {
370 0 => {}
371 1 => {
372 write!(&mut s, " block").unwrap();
373 }
374 _ => panic!("Too many raw block arguments"),
375 }
376
377 s.push_str("))");
378 fmt.line(&s);
379 });
380 fmt.line(")");
381
382 if isle_target == IsleTarget::Opt {
384 fmtln!(
385 fmt,
386 "(rule ({}{} {})",
387 inst.name,
388 if ty_in_decl { " ty" } else { "" },
389 inst.operands_in
390 .iter()
391 .map(|o| o.name)
392 .collect::<Vec<_>>()
393 .join(" ")
394 );
395 fmt.indent(|fmt| {
396 let mut s = format!(
397 "({make_inst_ctor}{} (InstructionData.{} (Opcode.{})",
398 if ty_in_decl { " ty" } else { "" },
399 inst.format.name,
400 inst.camel_name
401 );
402
403 assert!(!inst.format.has_value_list);
418 if inst.format.num_value_operands == 1 {
419 write!(
420 &mut s,
421 " {}",
422 inst.operands_in.iter().find(|o| o.is_value()).unwrap().name
423 )
424 .unwrap();
425 } else if inst.format.num_value_operands > 1 {
426 let values = inst
430 .operands_in
431 .iter()
432 .filter(|o| o.is_value())
433 .map(|o| o.name)
434 .collect::<Vec<_>>();
435 assert_eq!(values.len(), inst.format.num_value_operands);
436 let values = values.join(" ");
437 write!(
438 &mut s,
439 " (value_array_{}_ctor {})",
440 inst.format.num_value_operands, values
441 )
442 .unwrap();
443 }
444
445 if inst.format.num_block_operands > 0 {
446 let blocks: Vec<_> = inst
447 .operands_in
448 .iter()
449 .filter(|o| o.kind.is_block())
450 .map(|o| o.name)
451 .collect();
452 if inst.format.num_block_operands == 1 {
453 write!(&mut s, " {}", blocks.first().unwrap(),).unwrap();
454 } else {
455 write!(
456 &mut s,
457 " (block_array_{} {})",
458 inst.format.num_block_operands,
459 blocks.join(" ")
460 )
461 .unwrap();
462 }
463 }
464
465 match inst.format.num_raw_block_operands {
466 0 => {}
467 1 => {
468 write!(&mut s, " block").unwrap();
469 }
470 _ => panic!("Too many raw block arguments"),
471 }
472
473 for o in inst.operands_in.iter().filter(|o| {
475 !o.is_value() && !o.is_varargs() && !o.kind.is_block() && !o.kind.is_raw_block()
476 }) {
477 write!(&mut s, " {}", o.name).unwrap();
478 }
479 s.push_str("))");
480 fmt.line(&s);
481 });
482 fmt.line(")");
483 }
484
485 fmt.empty_line();
486 }
487}
488
489fn gen_opt_isle(
490 formats: &[Rc<InstructionFormat>],
491 instructions: &AllInstructions,
492 fmt: &mut Formatter,
493) {
494 gen_common_isle(formats, instructions, fmt, IsleTarget::Opt);
495}
496
497fn gen_lower_isle(
498 formats: &[Rc<InstructionFormat>],
499 instructions: &AllInstructions,
500 fmt: &mut Formatter,
501) {
502 gen_common_isle(formats, instructions, fmt, IsleTarget::Lower);
503}
504
505fn gen_isle_enum(name: &str, mut variants: Vec<&str>, fmt: &mut Formatter) {
507 variants.sort();
508 let prefix = format!(";;;; Enumerated Immediate: {name} ");
509 fmtln!(fmt, "{:;<80}", prefix);
510 fmt.empty_line();
511 fmtln!(fmt, "(type {} extern", name);
512 fmt.indent(|fmt| {
513 fmt.line("(enum");
514 fmt.indent(|fmt| {
515 for variant in variants {
516 fmtln!(fmt, "{}", variant);
517 }
518 });
519 fmt.line(")");
520 });
521 fmt.line(")");
522 fmt.empty_line();
523}
524
525#[derive(Clone, Copy, PartialEq, Eq)]
526struct NumericType {
527 signed: bool,
528 byte_width: u8,
529}
530
531impl NumericType {
532 fn all() -> impl Iterator<Item = NumericType> {
533 [1, 2, 4, 8, 16].into_iter().flat_map(|byte_width| {
534 [true, false]
535 .into_iter()
536 .map(move |signed| NumericType { signed, byte_width })
537 })
538 }
539
540 fn name(&self) -> &'static str {
541 let idx = self.byte_width.ilog2();
542 let idx = usize::try_from(idx).unwrap();
543 if self.signed {
544 ["i8", "i16", "i32", "i64", "i128"][idx]
545 } else {
546 ["u8", "u16", "u32", "u64", "u128"][idx]
547 }
548 }
549}
550
551#[derive(Clone, Default, PartialEq, Eq)]
552struct NumericOp<'a> {
553 name: &'a str,
555 ret: &'a str,
557 partial: bool,
559 args: Rc<[(&'a str, &'a str)]>,
561 body: &'a str,
563 etors: bool,
567}
568
569impl NumericOp<'_> {
570 fn ops_for_type(ty: &NumericType) -> impl Iterator<Item = NumericOp<'_>> {
571 let arity1 = NumericOp {
572 args: [("a", ty.name())].into(),
573 ..NumericOp::default()
574 };
575
576 let arity2 = NumericOp {
577 args: [("a", ty.name()), ("b", ty.name())].into(),
578 ..NumericOp::default()
579 };
580
581 let comparison = NumericOp {
582 ret: "bool",
583 ..arity2.clone()
584 };
585
586 let predicate = NumericOp {
587 ret: "bool",
588 etors: true,
589 ..arity1.clone()
590 };
591
592 let binop = NumericOp {
593 ret: ty.name(),
594 ..arity2.clone()
595 };
596
597 let partial_binop = NumericOp {
598 ret: ty.name(),
599 partial: true,
600 ..binop.clone()
601 };
602
603 let unop = NumericOp {
604 ret: ty.name(),
605 ..arity1.clone()
606 };
607
608 let partial_unop = NumericOp {
609 ret: ty.name(),
610 partial: true,
611 ..unop.clone()
612 };
613
614 let shift = NumericOp {
615 args: [("a", ty.name()), ("b", "u32")].into(),
616 ..binop.clone()
617 };
618
619 let partial_shift = NumericOp {
620 args: [("a", ty.name()), ("b", "u32")].into(),
621 ..partial_binop.clone()
622 };
623
624 let ops = [
626 NumericOp {
628 name: "eq",
629 body: "a == b",
630 ..comparison.clone()
631 },
632 NumericOp {
633 name: "ne",
634 body: "a != b",
635 ..comparison.clone()
636 },
637 NumericOp {
638 name: "lt",
639 body: "a < b",
640 ..comparison.clone()
641 },
642 NumericOp {
643 name: "lt_eq",
644 body: "a <= b",
645 ..comparison.clone()
646 },
647 NumericOp {
648 name: "gt",
649 body: "a > b",
650 ..comparison.clone()
651 },
652 NumericOp {
653 name: "gt_eq",
654 body: "a >= b",
655 ..comparison.clone()
656 },
657 NumericOp {
665 name: "checked_add",
666 body: "a.checked_add(b)",
667 ..partial_binop.clone()
668 },
669 NumericOp {
670 name: "wrapping_add",
671 body: "a.wrapping_add(b)",
672 ..binop.clone()
673 },
674 NumericOp {
675 name: "add",
676 body: r#"a.checked_add(b).unwrap_or_else(|| panic!("addition overflow: {a} + {b}"))"#,
677 ..binop.clone()
678 },
679 NumericOp {
680 name: "checked_sub",
681 body: "a.checked_sub(b)",
682 ..partial_binop.clone()
683 },
684 NumericOp {
685 name: "wrapping_sub",
686 body: "a.wrapping_sub(b)",
687 ..binop.clone()
688 },
689 NumericOp {
690 name: "sub",
691 body: r#"a.checked_sub(b).unwrap_or_else(|| panic!("subtraction overflow: {a} - {b}"))"#,
692 ..binop.clone()
693 },
694 NumericOp {
695 name: "checked_mul",
696 body: "a.checked_mul(b)",
697 ..partial_binop.clone()
698 },
699 NumericOp {
700 name: "wrapping_mul",
701 body: "a.wrapping_mul(b)",
702 ..binop.clone()
703 },
704 NumericOp {
705 name: "mul",
706 body: r#"a.checked_mul(b).unwrap_or_else(|| panic!("multiplication overflow: {a} * {b}"))"#,
707 ..binop.clone()
708 },
709 NumericOp {
710 name: "checked_div",
711 body: "a.checked_div(b)",
712 ..partial_binop.clone()
713 },
714 NumericOp {
715 name: "wrapping_div",
716 body: "a.wrapping_div(b)",
717 ..binop.clone()
718 },
719 NumericOp {
720 name: "div",
721 body: r#"a.checked_div(b).unwrap_or_else(|| panic!("div failure: {a} / {b}"))"#,
722 ..binop.clone()
723 },
724 NumericOp {
725 name: "checked_rem",
726 body: "a.checked_rem(b)",
727 ..partial_binop.clone()
728 },
729 NumericOp {
730 name: "rem",
731 body: r#"a.checked_rem(b).unwrap_or_else(|| panic!("rem failure: {a} % {b}"))"#,
732 ..binop.clone()
733 },
734 NumericOp {
739 name: "and",
740 body: "a & b",
741 ..binop.clone()
742 },
743 NumericOp {
744 name: "or",
745 body: "a | b",
746 ..binop.clone()
747 },
748 NumericOp {
749 name: "xor",
750 body: "a ^ b",
751 ..binop.clone()
752 },
753 NumericOp {
754 name: "not",
755 body: "!a",
756 ..unop.clone()
757 },
758 NumericOp {
759 name: "checked_shl",
760 body: "a.checked_shl(b)",
761 ..partial_shift.clone()
762 },
763 NumericOp {
764 name: "wrapping_shl",
765 body: "a.wrapping_shl(b)",
766 ..shift.clone()
767 },
768 NumericOp {
769 name: "shl",
770 body: r#"a.checked_shl(b).unwrap_or_else(|| panic!("shl overflow: {a} << {b}"))"#,
771 ..shift.clone()
772 },
773 NumericOp {
774 name: "checked_shr",
775 body: "a.checked_shr(b)",
776 ..partial_shift.clone()
777 },
778 NumericOp {
779 name: "wrapping_shr",
780 body: "a.wrapping_shr(b)",
781 ..shift.clone()
782 },
783 NumericOp {
784 name: "shr",
785 body: r#"a.checked_shr(b).unwrap_or_else(|| panic!("shr overflow: {a} >> {b}"))"#,
786 ..shift.clone()
787 },
788 NumericOp {
794 name: "is_zero",
795 body: "a == 0",
796 ..predicate.clone()
797 },
798 NumericOp {
799 name: "is_non_zero",
800 body: "a != 0",
801 ..predicate.clone()
802 },
803 NumericOp {
804 name: "is_odd",
805 body: "a & 1 == 1",
806 ..predicate.clone()
807 },
808 NumericOp {
809 name: "is_even",
810 body: "a & 1 == 0",
811 ..predicate.clone()
812 },
813 NumericOp {
815 name: "checked_ilog2",
816 body: "a.checked_ilog2()",
817 ret: "u32",
818 ..partial_unop.clone()
819 },
820 NumericOp {
821 name: "ilog2",
822 body: r#"a.checked_ilog2().unwrap_or_else(|| panic!("ilog2 overflow: {a}"))"#,
823 ret: "u32",
824 ..unop.clone()
825 },
826 NumericOp {
827 name: "trailing_zeros",
828 body: "a.trailing_zeros()",
829 ret: "u32",
830 ..unop.clone()
831 },
832 NumericOp {
833 name: "trailing_ones",
834 body: "a.trailing_ones()",
835 ret: "u32",
836 ..unop.clone()
837 },
838 NumericOp {
839 name: "leading_zeros",
840 body: "a.leading_zeros()",
841 ret: "u32",
842 ..unop.clone()
843 },
844 NumericOp {
845 name: "leading_ones",
846 body: "a.leading_ones()",
847 ret: "u32",
848 ..unop.clone()
849 },
850 ];
851
852 let signed_ops = [
854 NumericOp {
855 name: "checked_neg",
856 body: "a.checked_neg()",
857 ..partial_unop.clone()
858 },
859 NumericOp {
860 name: "wrapping_neg",
861 body: "a.wrapping_neg()",
862 ..unop.clone()
863 },
864 NumericOp {
865 name: "neg",
866 body: r#"a.checked_neg().unwrap_or_else(|| panic!("negation overflow: {a}"))"#,
867 ..unop.clone()
868 },
869 ];
870
871 let unsigned_ops = [NumericOp {
873 name: "is_power_of_two",
874 body: "a.is_power_of_two()",
875 ..predicate.clone()
876 }];
877
878 struct IterIf<I> {
879 condition: bool,
880 iter: I,
881 }
882
883 impl<I: Iterator> Iterator for IterIf<I> {
884 type Item = I::Item;
885
886 fn next(&mut self) -> Option<Self::Item> {
887 if self.condition {
888 self.iter.next()
889 } else {
890 None
891 }
892 }
893 }
894
895 ops.into_iter()
896 .chain(IterIf {
897 condition: ty.signed,
898 iter: signed_ops.into_iter(),
899 })
900 .chain(IterIf {
901 condition: !ty.signed,
902 iter: unsigned_ops.into_iter(),
903 })
904 }
905}
906
907fn gen_numerics_isle(isle: &mut Formatter, rust: &mut Formatter) {
908 fmtln!(rust, "#[macro_export]");
909 fmtln!(rust, "#[doc(hidden)]");
910 fmtln!(rust, "macro_rules! isle_numerics_methods {{");
911 rust.indent_push();
912 fmtln!(rust, "() => {{");
913 rust.indent_push();
914
915 for ty in NumericType::all() {
916 for op in NumericOp::ops_for_type(&ty) {
917 let ty = ty.name();
918 let op_name = format!("{ty}_{}", op.name);
919 let partial = if op.partial { " partial" } else { "" };
920 let ret = op.ret;
921 fmtln!(isle, "(decl pure{partial} {op_name} (");
922 isle.indent(|isle| {
923 for (_arg_name, arg_ty) in op.args.iter() {
924 fmtln!(isle, "{arg_ty}");
925 }
926 });
927 fmtln!(isle, ") {ret})");
928 fmtln!(isle, "(extern constructor {op_name} {op_name})");
929
930 let ret = if op.partial {
931 Cow::from(format!("Option<{ret}>"))
932 } else {
933 Cow::from(ret)
934 };
935 let body = op.body;
936 fmtln!(rust, "#[inline]");
937 fmtln!(rust, "fn {op_name}(");
938 rust.indent(|rust| {
939 fmtln!(rust, "&mut self,");
940 for (arg_name, arg_ty) in op.args.iter() {
941 fmtln!(rust, "{arg_name}: {arg_ty},");
942 }
943 });
944 fmtln!(rust, ") -> {ret} {{");
945 rust.indent(|rust| {
946 fmtln!(rust, "{body}");
947 });
948 fmtln!(rust, "}}");
949
950 if op.etors {
962 debug_assert_eq!(op.args.len(), 1);
963 debug_assert_eq!(op.args[0].1, ty);
964 debug_assert_eq!(op.ret, "bool");
965 debug_assert!(op.name.starts_with("is_"));
966
967 let base_name = &op.name[3..];
969 debug_assert!(base_name.len() > 0);
970
971 fmtln!(isle, "(decl pure {ty}_matches_{base_name} (bool) {ty})");
972 fmtln!(
973 isle,
974 "(extern extractor {ty}_matches_{base_name} {ty}_matches_{base_name})"
975 );
976 fmtln!(rust, "#[inline]");
977 fmtln!(
978 rust,
979 "fn {ty}_matches_{base_name}(&mut self, a: {ty}) -> Option<bool> {{"
980 );
981 rust.indent(|rust| {
982 fmtln!(rust, "Some({body})");
983 });
984 fmtln!(rust, "}}");
985
986 fmtln!(isle, "(decl pure {ty}_extract_{base_name} ({ty}) {ty})");
987 fmtln!(
988 isle,
989 "(extractor ({ty}_extract_{base_name} x) (and ({ty}_matches_{base_name} true) x))"
990 );
991
992 fmtln!(isle, "(decl pure {ty}_when_{base_name} () {ty})");
993 fmtln!(
994 isle,
995 "(extractor ({ty}_when_{base_name}) ({ty}_matches_{base_name} true))"
996 );
997
998 fmtln!(isle, "(decl pure {ty}_when_not_{base_name} () {ty})");
999 fmtln!(
1000 isle,
1001 "(extractor ({ty}_when_not_{base_name}) ({ty}_matches_{base_name} false))"
1002 );
1003 }
1004
1005 isle.empty_line();
1006 rust.empty_line();
1007 }
1008 }
1009
1010 for from in NumericType::all() {
1030 for to in NumericType::all() {
1031 if from == to {
1032 continue;
1033 }
1034
1035 let from_name = from.name();
1036 let to_name = to.name();
1037
1038 let lossy = match (from.byte_width.cmp(&to.byte_width), from.signed, to.signed) {
1039 (Ordering::Less, true, true) | (Ordering::Less, false, false) => false,
1041 (Ordering::Less, false, true) => false,
1043 (Ordering::Less, true, false) => true,
1045 (Ordering::Equal, _, _) => {
1048 debug_assert_ne!(from.signed, to.signed);
1049 true
1050 }
1051 (Ordering::Greater, _, _) => true,
1053 };
1054
1055 let (ctor, partial, rust_ret) = if lossy {
1056 (
1057 "try_into",
1058 " partial",
1059 Cow::from(format!("Option<{to_name}>")),
1060 )
1061 } else {
1062 ("into", "", Cow::from(to_name))
1063 };
1064
1065 fmtln!(
1067 isle,
1068 "(decl pure{partial} {from_name}_{ctor}_{to_name} ({from_name}) {to_name})"
1069 );
1070 fmtln!(
1071 isle,
1072 "(extern constructor {from_name}_{ctor}_{to_name} {from_name}_{ctor}_{to_name})"
1073 );
1074 if !lossy {
1075 fmtln!(
1076 isle,
1077 "(convert {from_name} {to_name} {from_name}_{ctor}_{to_name})"
1078 );
1079 }
1080 fmtln!(rust, "#[inline]");
1081 fmtln!(
1082 rust,
1083 "fn {from_name}_{ctor}_{to_name}(&mut self, x: {from_name}) -> {rust_ret} {{"
1084 );
1085 rust.indent(|rust| {
1086 if lossy {
1087 fmtln!(rust, "{to_name}::try_from(x).ok()");
1088 } else {
1089 fmtln!(rust, "{to_name}::from(x)");
1090 }
1091 });
1092 fmtln!(rust, "}}");
1093
1094 if lossy {
1096 fmtln!(
1097 isle,
1098 "(decl pure {from_name}_unwrap_into_{to_name} ({from_name}) {to_name})"
1099 );
1100 fmtln!(
1101 isle,
1102 "(extern constructor {from_name}_unwrap_into_{to_name} {from_name}_unwrap_into_{to_name})"
1103 );
1104 fmtln!(rust, "#[inline]");
1105 fmtln!(
1106 rust,
1107 "fn {from_name}_unwrap_into_{to_name}(&mut self, x: {from_name}) -> {to_name} {{"
1108 );
1109 rust.indent(|rust| {
1110 fmtln!(rust, "{to_name}::try_from(x).unwrap()");
1111 });
1112 fmtln!(rust, "}}");
1113 }
1114
1115 if lossy && from.signed == to.signed {
1117 fmtln!(
1118 isle,
1119 "(decl pure {from_name}_truncate_into_{to_name} ({from_name}) {to_name})"
1120 );
1121 fmtln!(
1122 isle,
1123 "(extern constructor {from_name}_truncate_into_{to_name} {from_name}_truncate_into_{to_name})"
1124 );
1125 fmtln!(rust, "#[inline]");
1126 fmtln!(
1127 rust,
1128 "fn {from_name}_truncate_into_{to_name}(&mut self, x: {from_name}) -> {to_name} {{"
1129 );
1130 rust.indent(|rust| {
1131 fmtln!(rust, "x as {to_name}");
1132 });
1133 fmtln!(rust, "}}");
1134 }
1135
1136 if from.byte_width == to.byte_width {
1138 debug_assert_ne!(from.signed, to.signed);
1139 let cast_name = if to.signed {
1140 "cast_signed"
1141 } else {
1142 "cast_unsigned"
1143 };
1144 fmtln!(
1145 isle,
1146 "(decl pure {from_name}_{cast_name} ({from_name}) {to_name})"
1147 );
1148 fmtln!(
1149 isle,
1150 "(extern constructor {from_name}_{cast_name} {from_name}_{cast_name})"
1151 );
1152 fmtln!(rust, "#[inline]");
1153 fmtln!(
1154 rust,
1155 "fn {from_name}_{cast_name}(&mut self, x: {from_name}) -> {to_name} {{"
1156 );
1157 rust.indent(|rust| {
1158 fmtln!(rust, "x as {to_name}");
1161 });
1162 fmtln!(rust, "}}");
1163 }
1164
1165 fmtln!(
1167 isle,
1168 "(decl pure {to_name}_from_{from_name} ({to_name}) {from_name})"
1169 );
1170 fmtln!(
1171 isle,
1172 "(extern extractor {to_name}_from_{from_name} {from_name}_from_{to_name})"
1173 );
1174 fmtln!(rust, "#[inline]");
1175 fmtln!(
1176 rust,
1177 "fn {from_name}_from_{to_name}(&mut self, x: {from_name}) -> Option<{to_name}> {{"
1178 );
1179 rust.indent(|rust| {
1180 if lossy {
1181 fmtln!(rust, "x.try_into().ok()");
1182 } else {
1183 fmtln!(rust, "Some(x.into())");
1184 }
1185 });
1186 fmtln!(rust, "}}");
1187
1188 isle.empty_line();
1189 rust.empty_line();
1190 }
1191 }
1192
1193 rust.indent_pop();
1194 fmtln!(rust, "}}");
1195 rust.indent_pop();
1196 fmtln!(rust, "}}");
1197}
1198
1199pub(crate) fn generate(
1200 formats: &[Rc<InstructionFormat>],
1201 all_inst: &AllInstructions,
1202 isle_numerics_filename: &str,
1203 rust_numerics_filename: &str,
1204 isle_opt_filename: &str,
1205 isle_lower_filename: &str,
1206 isle_dir: &std::path::Path,
1207) -> Result<(), error::Error> {
1208 let mut isle_fmt = Formatter::new(Language::Isle);
1210 let mut rust_fmt = Formatter::new(Language::Rust);
1211 gen_numerics_isle(&mut isle_fmt, &mut rust_fmt);
1212 isle_fmt.write(isle_numerics_filename, isle_dir)?;
1213 rust_fmt.write(rust_numerics_filename, isle_dir)?;
1214
1215 let mut fmt = Formatter::new(Language::Isle);
1217 gen_opt_isle(&formats, all_inst, &mut fmt);
1218 fmt.write(isle_opt_filename, isle_dir)?;
1219
1220 let mut fmt = Formatter::new(Language::Isle);
1222 gen_lower_isle(&formats, all_inst, &mut fmt);
1223 fmt.write(isle_lower_filename, isle_dir)?;
1224
1225 Ok(())
1226}