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 fmt.line(";;;; `Opcode` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;");
147 fmt.empty_line();
148 fmt.line("(type Opcode extern");
149 fmt.indent(|fmt| {
150 fmt.line("(enum");
151 fmt.indent(|fmt| {
152 for inst in instructions {
153 fmtln!(fmt, "{}", inst.camel_name);
154 }
155 });
156 fmt.line(")");
157 });
158 fmt.line(")");
159 fmt.empty_line();
160
161 fmtln!(
163 fmt,
164 ";;;; `InstructionData` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;",
165 );
166 fmt.empty_line();
167 fmtln!(fmt, "(type InstructionData extern");
168 fmt.indent(|fmt| {
169 fmt.line("(enum");
170 fmt.indent(|fmt| {
171 for format in formats {
172 let mut s = format!("({} (opcode Opcode)", format.name);
173 if format.has_value_list {
174 s.push_str(" (args ValueList)");
175 } else if format.num_value_operands == 1 {
176 s.push_str(" (arg Value)");
177 } else if format.num_value_operands > 1 {
178 write!(&mut s, " (args ValueArray{})", format.num_value_operands).unwrap();
179 }
180
181 match format.num_block_operands {
182 0 => (),
183 1 => write!(&mut s, " (destination BlockCall)").unwrap(),
184 n => write!(&mut s, " (blocks BlockArray{n})").unwrap(),
185 }
186
187 for field in &format.imm_fields {
188 write!(
189 &mut s,
190 " ({} {})",
191 field.member,
192 field.kind.rust_type.rsplit("::").next().unwrap()
193 )
194 .unwrap();
195 }
196 s.push(')');
197 fmt.line(&s);
198 }
199 });
200 fmt.line(")");
201 });
202 fmt.line(")");
203 fmt.empty_line();
204
205 fmtln!(
207 fmt,
208 ";;;; Extracting Opcode, Operands, and Immediates from `InstructionData` ;;;;;;;;",
209 );
210 fmt.empty_line();
211 for inst in instructions {
212 let results_len = inst.value_results.len();
213 let is_var_args = inst.format.has_value_list;
214 let has_side_effects = inst.can_trap || inst.other_side_effects;
215
216 let (ret_ty, ty_in_decl, make_inst_ctor, inst_data_etor) =
217 match (isle_target, is_var_args, results_len, has_side_effects) {
218 (IsleTarget::Opt, true, _, _) => continue,
220
221 (IsleTarget::Opt, _, 1, false) => ("Value", true, "make_inst", "inst_data_value"),
222 (IsleTarget::Opt, _, _, _) => ("Inst", false, "make_skeleton_inst", "inst_data"),
223 (IsleTarget::Lower, _, _, _) => ("Inst", false, "make_inst", "inst_data_value"),
224 };
225
226 fmtln!(
227 fmt,
228 "(decl {} ({}{}) {})",
229 inst.name,
230 if ty_in_decl { "Type " } else { "" },
231 inst.operands_in
232 .iter()
233 .map(|o| {
234 let ty = o.kind.rust_type;
235 if ty == "&[Value]" {
236 "ValueSlice"
237 } else {
238 ty.rsplit("::").next().unwrap()
239 }
240 })
241 .collect::<Vec<_>>()
242 .join(" "),
243 ret_ty
244 );
245 fmtln!(fmt, "(extractor");
246 fmt.indent(|fmt| {
247 fmtln!(
248 fmt,
249 "({} {}{})",
250 inst.name,
251 if ty_in_decl { "ty " } else { "" },
252 inst.operands_in
253 .iter()
254 .map(|o| { o.name })
255 .collect::<Vec<_>>()
256 .join(" ")
257 );
258
259 let mut s = format!(
260 "({inst_data_etor} {}(InstructionData.{} (Opcode.{})",
261 if ty_in_decl { "ty " } else { "" },
262 inst.format.name,
263 inst.camel_name
264 );
265
266 if inst.format.has_value_list {
268 let values: Vec<_> = inst
274 .operands_in
275 .iter()
276 .filter(|o| o.is_value())
277 .map(|o| o.name)
278 .collect();
279 let varargs = inst
280 .operands_in
281 .iter()
282 .find(|o| o.is_varargs())
283 .unwrap()
284 .name;
285 if values.is_empty() {
286 write!(&mut s, " (value_list_slice {varargs})").unwrap();
287 } else {
288 write!(
289 &mut s,
290 " (unwrap_head_value_list_{} {} {})",
291 values.len(),
292 values.join(" "),
293 varargs
294 )
295 .unwrap();
296 }
297 } else if inst.format.num_value_operands == 1 {
298 write!(
299 &mut s,
300 " {}",
301 inst.operands_in.iter().find(|o| o.is_value()).unwrap().name
302 )
303 .unwrap();
304 } else if inst.format.num_value_operands > 1 {
305 let values = inst
306 .operands_in
307 .iter()
308 .filter(|o| o.is_value())
309 .map(|o| o.name)
310 .collect::<Vec<_>>();
311 assert_eq!(values.len(), inst.format.num_value_operands);
312 let values = values.join(" ");
313 write!(
314 &mut s,
315 " (value_array_{} {})",
316 inst.format.num_value_operands, values,
317 )
318 .unwrap();
319 }
320
321 let imm_operands: Vec<_> = inst
323 .operands_in
324 .iter()
325 .filter(|o| !o.is_value() && !o.is_varargs() && !o.kind.is_block())
326 .collect();
327 assert_eq!(imm_operands.len(), inst.format.imm_fields.len(),);
328 for op in imm_operands {
329 write!(&mut s, " {}", op.name).unwrap();
330 }
331
332 let block_operands: Vec<_> = inst
334 .operands_in
335 .iter()
336 .filter(|o| o.kind.is_block())
337 .collect();
338 assert_eq!(block_operands.len(), inst.format.num_block_operands);
339 assert!(block_operands.len() <= 2);
340
341 if !block_operands.is_empty() {
342 if block_operands.len() == 1 {
343 write!(&mut s, " {}", block_operands[0].name).unwrap();
344 } else {
345 let blocks: Vec<_> = block_operands.iter().map(|o| o.name).collect();
346 let blocks = blocks.join(" ");
347 write!(
348 &mut s,
349 " (block_array_{} {})",
350 inst.format.num_block_operands, blocks,
351 )
352 .unwrap();
353 }
354 }
355
356 s.push_str("))");
357 fmt.line(&s);
358 });
359 fmt.line(")");
360
361 if isle_target == IsleTarget::Opt {
363 fmtln!(
364 fmt,
365 "(rule ({}{} {})",
366 inst.name,
367 if ty_in_decl { " ty" } else { "" },
368 inst.operands_in
369 .iter()
370 .map(|o| o.name)
371 .collect::<Vec<_>>()
372 .join(" ")
373 );
374 fmt.indent(|fmt| {
375 let mut s = format!(
376 "({make_inst_ctor}{} (InstructionData.{} (Opcode.{})",
377 if ty_in_decl { " ty" } else { "" },
378 inst.format.name,
379 inst.camel_name
380 );
381
382 assert!(!inst.format.has_value_list);
397 if inst.format.num_value_operands == 1 {
398 write!(
399 &mut s,
400 " {}",
401 inst.operands_in.iter().find(|o| o.is_value()).unwrap().name
402 )
403 .unwrap();
404 } else if inst.format.num_value_operands > 1 {
405 let values = inst
409 .operands_in
410 .iter()
411 .filter(|o| o.is_value())
412 .map(|o| o.name)
413 .collect::<Vec<_>>();
414 assert_eq!(values.len(), inst.format.num_value_operands);
415 let values = values.join(" ");
416 write!(
417 &mut s,
418 " (value_array_{}_ctor {})",
419 inst.format.num_value_operands, values
420 )
421 .unwrap();
422 }
423
424 if inst.format.num_block_operands > 0 {
425 let blocks: Vec<_> = inst
426 .operands_in
427 .iter()
428 .filter(|o| o.kind.is_block())
429 .map(|o| o.name)
430 .collect();
431 if inst.format.num_block_operands == 1 {
432 write!(&mut s, " {}", blocks.first().unwrap(),).unwrap();
433 } else {
434 write!(
435 &mut s,
436 " (block_array_{} {})",
437 inst.format.num_block_operands,
438 blocks.join(" ")
439 )
440 .unwrap();
441 }
442 }
443
444 for o in inst
446 .operands_in
447 .iter()
448 .filter(|o| !o.is_value() && !o.is_varargs() && !o.kind.is_block())
449 {
450 write!(&mut s, " {}", o.name).unwrap();
451 }
452 s.push_str("))");
453 fmt.line(&s);
454 });
455 fmt.line(")");
456 }
457
458 fmt.empty_line();
459 }
460}
461
462fn gen_opt_isle(
463 formats: &[Rc<InstructionFormat>],
464 instructions: &AllInstructions,
465 fmt: &mut Formatter,
466) {
467 gen_common_isle(formats, instructions, fmt, IsleTarget::Opt);
468}
469
470fn gen_lower_isle(
471 formats: &[Rc<InstructionFormat>],
472 instructions: &AllInstructions,
473 fmt: &mut Formatter,
474) {
475 gen_common_isle(formats, instructions, fmt, IsleTarget::Lower);
476}
477
478fn gen_isle_enum(name: &str, mut variants: Vec<&str>, fmt: &mut Formatter) {
480 variants.sort();
481 let prefix = format!(";;;; Enumerated Immediate: {name} ");
482 fmtln!(fmt, "{:;<80}", prefix);
483 fmt.empty_line();
484 fmtln!(fmt, "(type {} extern", name);
485 fmt.indent(|fmt| {
486 fmt.line("(enum");
487 fmt.indent(|fmt| {
488 for variant in variants {
489 fmtln!(fmt, "{}", variant);
490 }
491 });
492 fmt.line(")");
493 });
494 fmt.line(")");
495 fmt.empty_line();
496}
497
498#[derive(Clone, Copy, PartialEq, Eq)]
499struct NumericType {
500 signed: bool,
501 byte_width: u8,
502}
503
504impl NumericType {
505 fn all() -> impl Iterator<Item = NumericType> {
506 [1, 2, 4, 8, 16].into_iter().flat_map(|byte_width| {
507 [true, false]
508 .into_iter()
509 .map(move |signed| NumericType { signed, byte_width })
510 })
511 }
512
513 fn name(&self) -> &'static str {
514 let idx = self.byte_width.ilog2();
515 let idx = usize::try_from(idx).unwrap();
516 if self.signed {
517 ["i8", "i16", "i32", "i64", "i128"][idx]
518 } else {
519 ["u8", "u16", "u32", "u64", "u128"][idx]
520 }
521 }
522}
523
524#[derive(Clone, Default, PartialEq, Eq)]
525struct NumericOp<'a> {
526 name: &'a str,
528 ret: &'a str,
530 partial: bool,
532 args: Rc<[(&'a str, &'a str)]>,
534 body: &'a str,
536 etors: bool,
540}
541
542impl NumericOp<'_> {
543 fn ops_for_type(ty: &NumericType) -> impl Iterator<Item = NumericOp<'_>> {
544 let arity1 = NumericOp {
545 args: [("a", ty.name())].into(),
546 ..NumericOp::default()
547 };
548
549 let arity2 = NumericOp {
550 args: [("a", ty.name()), ("b", ty.name())].into(),
551 ..NumericOp::default()
552 };
553
554 let comparison = NumericOp {
555 ret: "bool",
556 ..arity2.clone()
557 };
558
559 let predicate = NumericOp {
560 ret: "bool",
561 etors: true,
562 ..arity1.clone()
563 };
564
565 let binop = NumericOp {
566 ret: ty.name(),
567 ..arity2.clone()
568 };
569
570 let partial_binop = NumericOp {
571 ret: ty.name(),
572 partial: true,
573 ..binop.clone()
574 };
575
576 let unop = NumericOp {
577 ret: ty.name(),
578 ..arity1.clone()
579 };
580
581 let partial_unop = NumericOp {
582 ret: ty.name(),
583 partial: true,
584 ..unop.clone()
585 };
586
587 let shift = NumericOp {
588 args: [("a", ty.name()), ("b", "u32")].into(),
589 ..binop.clone()
590 };
591
592 let partial_shift = NumericOp {
593 args: [("a", ty.name()), ("b", "u32")].into(),
594 ..partial_binop.clone()
595 };
596
597 let ops = [
599 NumericOp {
601 name: "eq",
602 body: "a == b",
603 ..comparison.clone()
604 },
605 NumericOp {
606 name: "ne",
607 body: "a != b",
608 ..comparison.clone()
609 },
610 NumericOp {
611 name: "lt",
612 body: "a < b",
613 ..comparison.clone()
614 },
615 NumericOp {
616 name: "lt_eq",
617 body: "a <= b",
618 ..comparison.clone()
619 },
620 NumericOp {
621 name: "gt",
622 body: "a > b",
623 ..comparison.clone()
624 },
625 NumericOp {
626 name: "gt_eq",
627 body: "a >= b",
628 ..comparison.clone()
629 },
630 NumericOp {
638 name: "checked_add",
639 body: "a.checked_add(b)",
640 ..partial_binop.clone()
641 },
642 NumericOp {
643 name: "wrapping_add",
644 body: "a.wrapping_add(b)",
645 ..binop.clone()
646 },
647 NumericOp {
648 name: "add",
649 body: r#"a.checked_add(b).unwrap_or_else(|| panic!("addition overflow: {a} + {b}"))"#,
650 ..binop.clone()
651 },
652 NumericOp {
653 name: "checked_sub",
654 body: "a.checked_sub(b)",
655 ..partial_binop.clone()
656 },
657 NumericOp {
658 name: "wrapping_sub",
659 body: "a.wrapping_sub(b)",
660 ..binop.clone()
661 },
662 NumericOp {
663 name: "sub",
664 body: r#"a.checked_sub(b).unwrap_or_else(|| panic!("subtraction overflow: {a} - {b}"))"#,
665 ..binop.clone()
666 },
667 NumericOp {
668 name: "checked_mul",
669 body: "a.checked_mul(b)",
670 ..partial_binop.clone()
671 },
672 NumericOp {
673 name: "wrapping_mul",
674 body: "a.wrapping_mul(b)",
675 ..binop.clone()
676 },
677 NumericOp {
678 name: "mul",
679 body: r#"a.checked_mul(b).unwrap_or_else(|| panic!("multiplication overflow: {a} * {b}"))"#,
680 ..binop.clone()
681 },
682 NumericOp {
683 name: "checked_div",
684 body: "a.checked_div(b)",
685 ..partial_binop.clone()
686 },
687 NumericOp {
688 name: "wrapping_div",
689 body: "a.wrapping_div(b)",
690 ..binop.clone()
691 },
692 NumericOp {
693 name: "div",
694 body: r#"a.checked_div(b).unwrap_or_else(|| panic!("div failure: {a} / {b}"))"#,
695 ..binop.clone()
696 },
697 NumericOp {
698 name: "checked_rem",
699 body: "a.checked_rem(b)",
700 ..partial_binop.clone()
701 },
702 NumericOp {
703 name: "rem",
704 body: r#"a.checked_rem(b).unwrap_or_else(|| panic!("rem failure: {a} % {b}"))"#,
705 ..binop.clone()
706 },
707 NumericOp {
712 name: "and",
713 body: "a & b",
714 ..binop.clone()
715 },
716 NumericOp {
717 name: "or",
718 body: "a | b",
719 ..binop.clone()
720 },
721 NumericOp {
722 name: "xor",
723 body: "a ^ b",
724 ..binop.clone()
725 },
726 NumericOp {
727 name: "not",
728 body: "!a",
729 ..unop.clone()
730 },
731 NumericOp {
732 name: "checked_shl",
733 body: "a.checked_shl(b)",
734 ..partial_shift.clone()
735 },
736 NumericOp {
737 name: "wrapping_shl",
738 body: "a.wrapping_shl(b)",
739 ..shift.clone()
740 },
741 NumericOp {
742 name: "shl",
743 body: r#"a.checked_shl(b).unwrap_or_else(|| panic!("shl overflow: {a} << {b}"))"#,
744 ..shift.clone()
745 },
746 NumericOp {
747 name: "checked_shr",
748 body: "a.checked_shr(b)",
749 ..partial_shift.clone()
750 },
751 NumericOp {
752 name: "wrapping_shr",
753 body: "a.wrapping_shr(b)",
754 ..shift.clone()
755 },
756 NumericOp {
757 name: "shr",
758 body: r#"a.checked_shr(b).unwrap_or_else(|| panic!("shr overflow: {a} >> {b}"))"#,
759 ..shift.clone()
760 },
761 NumericOp {
767 name: "is_zero",
768 body: "a == 0",
769 ..predicate.clone()
770 },
771 NumericOp {
772 name: "is_non_zero",
773 body: "a != 0",
774 ..predicate.clone()
775 },
776 NumericOp {
777 name: "is_odd",
778 body: "a & 1 == 1",
779 ..predicate.clone()
780 },
781 NumericOp {
782 name: "is_even",
783 body: "a & 1 == 0",
784 ..predicate.clone()
785 },
786 NumericOp {
788 name: "checked_ilog2",
789 body: "a.checked_ilog2()",
790 ret: "u32",
791 ..partial_unop.clone()
792 },
793 NumericOp {
794 name: "ilog2",
795 body: r#"a.checked_ilog2().unwrap_or_else(|| panic!("ilog2 overflow: {a}"))"#,
796 ret: "u32",
797 ..unop.clone()
798 },
799 NumericOp {
800 name: "trailing_zeros",
801 body: "a.trailing_zeros()",
802 ret: "u32",
803 ..unop.clone()
804 },
805 NumericOp {
806 name: "trailing_ones",
807 body: "a.trailing_ones()",
808 ret: "u32",
809 ..unop.clone()
810 },
811 NumericOp {
812 name: "leading_zeros",
813 body: "a.leading_zeros()",
814 ret: "u32",
815 ..unop.clone()
816 },
817 NumericOp {
818 name: "leading_ones",
819 body: "a.leading_ones()",
820 ret: "u32",
821 ..unop.clone()
822 },
823 ];
824
825 let signed_ops = [
827 NumericOp {
828 name: "checked_neg",
829 body: "a.checked_neg()",
830 ..partial_unop.clone()
831 },
832 NumericOp {
833 name: "wrapping_neg",
834 body: "a.wrapping_neg()",
835 ..unop.clone()
836 },
837 NumericOp {
838 name: "neg",
839 body: r#"a.checked_neg().unwrap_or_else(|| panic!("negation overflow: {a}"))"#,
840 ..unop.clone()
841 },
842 ];
843
844 let unsigned_ops = [NumericOp {
846 name: "is_power_of_two",
847 body: "a.is_power_of_two()",
848 ..predicate.clone()
849 }];
850
851 struct IterIf<I> {
852 condition: bool,
853 iter: I,
854 }
855
856 impl<I: Iterator> Iterator for IterIf<I> {
857 type Item = I::Item;
858
859 fn next(&mut self) -> Option<Self::Item> {
860 if self.condition {
861 self.iter.next()
862 } else {
863 None
864 }
865 }
866 }
867
868 ops.into_iter()
869 .chain(IterIf {
870 condition: ty.signed,
871 iter: signed_ops.into_iter(),
872 })
873 .chain(IterIf {
874 condition: !ty.signed,
875 iter: unsigned_ops.into_iter(),
876 })
877 }
878}
879
880fn gen_numerics_isle(isle: &mut Formatter, rust: &mut Formatter) {
881 fmtln!(rust, "#[macro_export]");
882 fmtln!(rust, "#[doc(hidden)]");
883 fmtln!(rust, "macro_rules! isle_numerics_methods {{");
884 rust.indent_push();
885 fmtln!(rust, "() => {{");
886 rust.indent_push();
887
888 for ty in NumericType::all() {
889 for op in NumericOp::ops_for_type(&ty) {
890 let ty = ty.name();
891 let op_name = format!("{ty}_{}", op.name);
892 let partial = if op.partial { " partial" } else { "" };
893 let ret = op.ret;
894 fmtln!(isle, "(decl pure{partial} {op_name} (");
895 isle.indent(|isle| {
896 for (_arg_name, arg_ty) in op.args.iter() {
897 fmtln!(isle, "{arg_ty}");
898 }
899 });
900 fmtln!(isle, ") {ret})");
901 fmtln!(isle, "(extern constructor {op_name} {op_name})");
902
903 let ret = if op.partial {
904 Cow::from(format!("Option<{ret}>"))
905 } else {
906 Cow::from(ret)
907 };
908 let body = op.body;
909 fmtln!(rust, "#[inline]");
910 fmtln!(rust, "fn {op_name}(");
911 rust.indent(|rust| {
912 fmtln!(rust, "&mut self,");
913 for (arg_name, arg_ty) in op.args.iter() {
914 fmtln!(rust, "{arg_name}: {arg_ty},");
915 }
916 });
917 fmtln!(rust, ") -> {ret} {{");
918 rust.indent(|rust| {
919 fmtln!(rust, "{body}");
920 });
921 fmtln!(rust, "}}");
922
923 if op.etors {
935 debug_assert_eq!(op.args.len(), 1);
936 debug_assert_eq!(op.args[0].1, ty);
937 debug_assert_eq!(op.ret, "bool");
938 debug_assert!(op.name.starts_with("is_"));
939
940 let base_name = &op.name[3..];
942 debug_assert!(base_name.len() > 0);
943
944 fmtln!(isle, "(decl pure {ty}_matches_{base_name} (bool) {ty})");
945 fmtln!(
946 isle,
947 "(extern extractor {ty}_matches_{base_name} {ty}_matches_{base_name})"
948 );
949 fmtln!(rust, "#[inline]");
950 fmtln!(
951 rust,
952 "fn {ty}_matches_{base_name}(&mut self, a: {ty}) -> Option<bool> {{"
953 );
954 rust.indent(|rust| {
955 fmtln!(rust, "Some({body})");
956 });
957 fmtln!(rust, "}}");
958
959 fmtln!(isle, "(decl pure {ty}_extract_{base_name} ({ty}) {ty})");
960 fmtln!(
961 isle,
962 "(extractor ({ty}_extract_{base_name} x) (and ({ty}_matches_{base_name} true) x))"
963 );
964
965 fmtln!(isle, "(decl pure {ty}_when_{base_name} () {ty})");
966 fmtln!(
967 isle,
968 "(extractor ({ty}_when_{base_name}) ({ty}_matches_{base_name} true))"
969 );
970
971 fmtln!(isle, "(decl pure {ty}_when_not_{base_name} () {ty})");
972 fmtln!(
973 isle,
974 "(extractor ({ty}_when_not_{base_name}) ({ty}_matches_{base_name} false))"
975 );
976 }
977
978 isle.empty_line();
979 rust.empty_line();
980 }
981 }
982
983 for from in NumericType::all() {
1003 for to in NumericType::all() {
1004 if from == to {
1005 continue;
1006 }
1007
1008 let from_name = from.name();
1009 let to_name = to.name();
1010
1011 let lossy = match (from.byte_width.cmp(&to.byte_width), from.signed, to.signed) {
1012 (Ordering::Less, true, true) | (Ordering::Less, false, false) => false,
1014 (Ordering::Less, false, true) => false,
1016 (Ordering::Less, true, false) => true,
1018 (Ordering::Equal, _, _) => {
1021 debug_assert_ne!(from.signed, to.signed);
1022 true
1023 }
1024 (Ordering::Greater, _, _) => true,
1026 };
1027
1028 let (ctor, partial, rust_ret) = if lossy {
1029 (
1030 "try_into",
1031 " partial",
1032 Cow::from(format!("Option<{to_name}>")),
1033 )
1034 } else {
1035 ("into", "", Cow::from(to_name))
1036 };
1037
1038 fmtln!(
1040 isle,
1041 "(decl pure{partial} {from_name}_{ctor}_{to_name} ({from_name}) {to_name})"
1042 );
1043 fmtln!(
1044 isle,
1045 "(extern constructor {from_name}_{ctor}_{to_name} {from_name}_{ctor}_{to_name})"
1046 );
1047 if !lossy {
1048 fmtln!(
1049 isle,
1050 "(convert {from_name} {to_name} {from_name}_{ctor}_{to_name})"
1051 );
1052 }
1053 fmtln!(rust, "#[inline]");
1054 fmtln!(
1055 rust,
1056 "fn {from_name}_{ctor}_{to_name}(&mut self, x: {from_name}) -> {rust_ret} {{"
1057 );
1058 rust.indent(|rust| {
1059 if lossy {
1060 fmtln!(rust, "{to_name}::try_from(x).ok()");
1061 } else {
1062 fmtln!(rust, "{to_name}::from(x)");
1063 }
1064 });
1065 fmtln!(rust, "}}");
1066
1067 if lossy {
1069 fmtln!(
1070 isle,
1071 "(decl pure {from_name}_unwrap_into_{to_name} ({from_name}) {to_name})"
1072 );
1073 fmtln!(
1074 isle,
1075 "(extern constructor {from_name}_unwrap_into_{to_name} {from_name}_unwrap_into_{to_name})"
1076 );
1077 fmtln!(rust, "#[inline]");
1078 fmtln!(
1079 rust,
1080 "fn {from_name}_unwrap_into_{to_name}(&mut self, x: {from_name}) -> {to_name} {{"
1081 );
1082 rust.indent(|rust| {
1083 fmtln!(rust, "{to_name}::try_from(x).unwrap()");
1084 });
1085 fmtln!(rust, "}}");
1086 }
1087
1088 if lossy && from.signed == to.signed {
1090 fmtln!(
1091 isle,
1092 "(decl pure {from_name}_truncate_into_{to_name} ({from_name}) {to_name})"
1093 );
1094 fmtln!(
1095 isle,
1096 "(extern constructor {from_name}_truncate_into_{to_name} {from_name}_truncate_into_{to_name})"
1097 );
1098 fmtln!(rust, "#[inline]");
1099 fmtln!(
1100 rust,
1101 "fn {from_name}_truncate_into_{to_name}(&mut self, x: {from_name}) -> {to_name} {{"
1102 );
1103 rust.indent(|rust| {
1104 fmtln!(rust, "x as {to_name}");
1105 });
1106 fmtln!(rust, "}}");
1107 }
1108
1109 if from.byte_width == to.byte_width {
1111 debug_assert_ne!(from.signed, to.signed);
1112 let cast_name = if to.signed {
1113 "cast_signed"
1114 } else {
1115 "cast_unsigned"
1116 };
1117 fmtln!(
1118 isle,
1119 "(decl pure {from_name}_{cast_name} ({from_name}) {to_name})"
1120 );
1121 fmtln!(
1122 isle,
1123 "(extern constructor {from_name}_{cast_name} {from_name}_{cast_name})"
1124 );
1125 fmtln!(rust, "#[inline]");
1126 fmtln!(
1127 rust,
1128 "fn {from_name}_{cast_name}(&mut self, x: {from_name}) -> {to_name} {{"
1129 );
1130 rust.indent(|rust| {
1131 fmtln!(rust, "x as {to_name}");
1134 });
1135 fmtln!(rust, "}}");
1136 }
1137
1138 fmtln!(
1140 isle,
1141 "(decl pure {to_name}_from_{from_name} ({to_name}) {from_name})"
1142 );
1143 fmtln!(
1144 isle,
1145 "(extern extractor {to_name}_from_{from_name} {from_name}_from_{to_name})"
1146 );
1147 fmtln!(rust, "#[inline]");
1148 fmtln!(
1149 rust,
1150 "fn {from_name}_from_{to_name}(&mut self, x: {from_name}) -> Option<{to_name}> {{"
1151 );
1152 rust.indent(|rust| {
1153 if lossy {
1154 fmtln!(rust, "x.try_into().ok()");
1155 } else {
1156 fmtln!(rust, "Some(x.into())");
1157 }
1158 });
1159 fmtln!(rust, "}}");
1160
1161 isle.empty_line();
1162 rust.empty_line();
1163 }
1164 }
1165
1166 rust.indent_pop();
1167 fmtln!(rust, "}}");
1168 rust.indent_pop();
1169 fmtln!(rust, "}}");
1170}
1171
1172pub(crate) fn generate(
1173 formats: &[Rc<InstructionFormat>],
1174 all_inst: &AllInstructions,
1175 isle_numerics_filename: &str,
1176 rust_numerics_filename: &str,
1177 isle_opt_filename: &str,
1178 isle_lower_filename: &str,
1179 isle_dir: &std::path::Path,
1180) -> Result<(), error::Error> {
1181 let mut isle_fmt = Formatter::new(Language::Isle);
1183 let mut rust_fmt = Formatter::new(Language::Rust);
1184 gen_numerics_isle(&mut isle_fmt, &mut rust_fmt);
1185 isle_fmt.write(isle_numerics_filename, isle_dir)?;
1186 rust_fmt.write(rust_numerics_filename, isle_dir)?;
1187
1188 let mut fmt = Formatter::new(Language::Isle);
1190 gen_opt_isle(&formats, all_inst, &mut fmt);
1191 fmt.write(isle_opt_filename, isle_dir)?;
1192
1193 let mut fmt = Formatter::new(Language::Isle);
1195 gen_lower_isle(&formats, all_inst, &mut fmt);
1196 fmt.write(isle_lower_filename, isle_dir)?;
1197
1198 Ok(())
1199}