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 {
702 name: "and",
703 body: "a & b",
704 ..binop.clone()
705 },
706 NumericOp {
707 name: "or",
708 body: "a | b",
709 ..binop.clone()
710 },
711 NumericOp {
712 name: "xor",
713 body: "a ^ b",
714 ..binop.clone()
715 },
716 NumericOp {
717 name: "not",
718 body: "!a",
719 ..unop.clone()
720 },
721 NumericOp {
722 name: "checked_shl",
723 body: "a.checked_shl(b)",
724 ..partial_shift.clone()
725 },
726 NumericOp {
727 name: "wrapping_shl",
728 body: "a.wrapping_shl(b)",
729 ..shift.clone()
730 },
731 NumericOp {
732 name: "shl",
733 body: r#"a.checked_shl(b).unwrap_or_else(|| panic!("shl overflow: {a} << {b}"))"#,
734 ..shift.clone()
735 },
736 NumericOp {
737 name: "checked_shr",
738 body: "a.checked_shr(b)",
739 ..partial_shift.clone()
740 },
741 NumericOp {
742 name: "wrapping_shr",
743 body: "a.wrapping_shr(b)",
744 ..shift.clone()
745 },
746 NumericOp {
747 name: "shr",
748 body: r#"a.checked_shr(b).unwrap_or_else(|| panic!("shr overflow: {a} >> {b}"))"#,
749 ..shift.clone()
750 },
751 NumericOp {
757 name: "is_zero",
758 body: "a == 0",
759 ..predicate.clone()
760 },
761 NumericOp {
762 name: "is_non_zero",
763 body: "a != 0",
764 ..predicate.clone()
765 },
766 NumericOp {
767 name: "is_odd",
768 body: "a & 1 == 1",
769 ..predicate.clone()
770 },
771 NumericOp {
772 name: "is_even",
773 body: "a & 1 == 0",
774 ..predicate.clone()
775 },
776 NumericOp {
778 name: "checked_ilog2",
779 body: "a.checked_ilog2()",
780 ret: "u32",
781 ..partial_unop.clone()
782 },
783 NumericOp {
784 name: "ilog2",
785 body: r#"a.checked_ilog2().unwrap_or_else(|| panic!("ilog2 overflow: {a}"))"#,
786 ret: "u32",
787 ..unop.clone()
788 },
789 NumericOp {
790 name: "trailing_zeros",
791 body: "a.trailing_zeros()",
792 ret: "u32",
793 ..unop.clone()
794 },
795 NumericOp {
796 name: "trailing_ones",
797 body: "a.trailing_ones()",
798 ret: "u32",
799 ..unop.clone()
800 },
801 NumericOp {
802 name: "leading_zeros",
803 body: "a.leading_zeros()",
804 ret: "u32",
805 ..unop.clone()
806 },
807 NumericOp {
808 name: "leading_ones",
809 body: "a.leading_ones()",
810 ret: "u32",
811 ..unop.clone()
812 },
813 ];
814
815 let signed_ops = [
817 NumericOp {
818 name: "checked_neg",
819 body: "a.checked_neg()",
820 ..partial_unop.clone()
821 },
822 NumericOp {
823 name: "wrapping_neg",
824 body: "a.wrapping_neg()",
825 ..unop.clone()
826 },
827 NumericOp {
828 name: "neg",
829 body: r#"a.checked_neg().unwrap_or_else(|| panic!("negation overflow: {a}"))"#,
830 ..unop.clone()
831 },
832 ];
833
834 let unsigned_ops = [NumericOp {
836 name: "is_power_of_two",
837 body: "a.is_power_of_two()",
838 ..predicate.clone()
839 }];
840
841 struct IterIf<I> {
842 condition: bool,
843 iter: I,
844 }
845
846 impl<I: Iterator> Iterator for IterIf<I> {
847 type Item = I::Item;
848
849 fn next(&mut self) -> Option<Self::Item> {
850 if self.condition {
851 self.iter.next()
852 } else {
853 None
854 }
855 }
856 }
857
858 ops.into_iter()
859 .chain(IterIf {
860 condition: ty.signed,
861 iter: signed_ops.into_iter(),
862 })
863 .chain(IterIf {
864 condition: !ty.signed,
865 iter: unsigned_ops.into_iter(),
866 })
867 }
868}
869
870fn gen_numerics_isle(isle: &mut Formatter, rust: &mut Formatter) {
871 fmtln!(rust, "#[macro_export]");
872 fmtln!(rust, "#[doc(hidden)]");
873 fmtln!(rust, "macro_rules! isle_numerics_methods {{");
874 rust.indent_push();
875 fmtln!(rust, "() => {{");
876 rust.indent_push();
877
878 for ty in NumericType::all() {
879 for op in NumericOp::ops_for_type(&ty) {
880 let ty = ty.name();
881 let op_name = format!("{ty}_{}", op.name);
882 let partial = if op.partial { " partial" } else { "" };
883 let ret = op.ret;
884 fmtln!(isle, "(decl pure{partial} {op_name} (");
885 isle.indent(|isle| {
886 for (_arg_name, arg_ty) in op.args.iter() {
887 fmtln!(isle, "{arg_ty}");
888 }
889 });
890 fmtln!(isle, ") {ret})");
891 fmtln!(isle, "(extern constructor {op_name} {op_name})");
892
893 let ret = if op.partial {
894 Cow::from(format!("Option<{ret}>"))
895 } else {
896 Cow::from(ret)
897 };
898 let body = op.body;
899 fmtln!(rust, "#[inline]");
900 fmtln!(rust, "fn {op_name}(");
901 rust.indent(|rust| {
902 fmtln!(rust, "&mut self,");
903 for (arg_name, arg_ty) in op.args.iter() {
904 fmtln!(rust, "{arg_name}: {arg_ty},");
905 }
906 });
907 fmtln!(rust, ") -> {ret} {{");
908 rust.indent(|rust| {
909 fmtln!(rust, "{body}");
910 });
911 fmtln!(rust, "}}");
912
913 if op.etors {
925 debug_assert_eq!(op.args.len(), 1);
926 debug_assert_eq!(op.args[0].1, ty);
927 debug_assert_eq!(op.ret, "bool");
928 debug_assert!(op.name.starts_with("is_"));
929
930 let base_name = &op.name[3..];
932 debug_assert!(base_name.len() > 0);
933
934 fmtln!(isle, "(decl pure {ty}_matches_{base_name} (bool) {ty})");
935 fmtln!(
936 isle,
937 "(extern extractor {ty}_matches_{base_name} {ty}_matches_{base_name})"
938 );
939 fmtln!(rust, "#[inline]");
940 fmtln!(
941 rust,
942 "fn {ty}_matches_{base_name}(&mut self, a: {ty}) -> Option<bool> {{"
943 );
944 rust.indent(|rust| {
945 fmtln!(rust, "Some({body})");
946 });
947 fmtln!(rust, "}}");
948
949 fmtln!(isle, "(decl pure {ty}_extract_{base_name} ({ty}) {ty})");
950 fmtln!(
951 isle,
952 "(extractor ({ty}_extract_{base_name} x) (and ({ty}_matches_{base_name} true) x))"
953 );
954
955 fmtln!(isle, "(decl pure {ty}_when_{base_name} () {ty})");
956 fmtln!(
957 isle,
958 "(extractor ({ty}_when_{base_name}) ({ty}_matches_{base_name} true))"
959 );
960
961 fmtln!(isle, "(decl pure {ty}_when_not_{base_name} () {ty})");
962 fmtln!(
963 isle,
964 "(extractor ({ty}_when_not_{base_name}) ({ty}_matches_{base_name} false))"
965 );
966 }
967
968 isle.empty_line();
969 rust.empty_line();
970 }
971 }
972
973 for from in NumericType::all() {
993 for to in NumericType::all() {
994 if from == to {
995 continue;
996 }
997
998 let from_name = from.name();
999 let to_name = to.name();
1000
1001 let lossy = match (from.byte_width.cmp(&to.byte_width), from.signed, to.signed) {
1002 (Ordering::Less, true, true) | (Ordering::Less, false, false) => false,
1004 (Ordering::Less, false, true) => false,
1006 (Ordering::Less, true, false) => true,
1008 (Ordering::Equal, _, _) => {
1011 debug_assert_ne!(from.signed, to.signed);
1012 true
1013 }
1014 (Ordering::Greater, _, _) => true,
1016 };
1017
1018 let (ctor, partial, rust_ret) = if lossy {
1019 (
1020 "try_into",
1021 " partial",
1022 Cow::from(format!("Option<{to_name}>")),
1023 )
1024 } else {
1025 ("into", "", Cow::from(to_name))
1026 };
1027
1028 fmtln!(
1030 isle,
1031 "(decl pure{partial} {from_name}_{ctor}_{to_name} ({from_name}) {to_name})"
1032 );
1033 fmtln!(
1034 isle,
1035 "(extern constructor {from_name}_{ctor}_{to_name} {from_name}_{ctor}_{to_name})"
1036 );
1037 if !lossy {
1038 fmtln!(
1039 isle,
1040 "(convert {from_name} {to_name} {from_name}_{ctor}_{to_name})"
1041 );
1042 }
1043 fmtln!(rust, "#[inline]");
1044 fmtln!(
1045 rust,
1046 "fn {from_name}_{ctor}_{to_name}(&mut self, x: {from_name}) -> {rust_ret} {{"
1047 );
1048 rust.indent(|rust| {
1049 if lossy {
1050 fmtln!(rust, "{to_name}::try_from(x).ok()");
1051 } else {
1052 fmtln!(rust, "{to_name}::from(x)");
1053 }
1054 });
1055 fmtln!(rust, "}}");
1056
1057 if lossy {
1059 fmtln!(
1060 isle,
1061 "(decl pure {from_name}_unwrap_into_{to_name} ({from_name}) {to_name})"
1062 );
1063 fmtln!(
1064 isle,
1065 "(extern constructor {from_name}_unwrap_into_{to_name} {from_name}_unwrap_into_{to_name})"
1066 );
1067 fmtln!(rust, "#[inline]");
1068 fmtln!(
1069 rust,
1070 "fn {from_name}_unwrap_into_{to_name}(&mut self, x: {from_name}) -> {to_name} {{"
1071 );
1072 rust.indent(|rust| {
1073 fmtln!(rust, "{to_name}::try_from(x).unwrap()");
1074 });
1075 fmtln!(rust, "}}");
1076 }
1077
1078 if lossy && from.signed == to.signed {
1080 fmtln!(
1081 isle,
1082 "(decl pure {from_name}_truncate_into_{to_name} ({from_name}) {to_name})"
1083 );
1084 fmtln!(
1085 isle,
1086 "(extern constructor {from_name}_truncate_into_{to_name} {from_name}_truncate_into_{to_name})"
1087 );
1088 fmtln!(rust, "#[inline]");
1089 fmtln!(
1090 rust,
1091 "fn {from_name}_truncate_into_{to_name}(&mut self, x: {from_name}) -> {to_name} {{"
1092 );
1093 rust.indent(|rust| {
1094 fmtln!(rust, "x as {to_name}");
1095 });
1096 fmtln!(rust, "}}");
1097 }
1098
1099 if from.byte_width == to.byte_width {
1101 debug_assert_ne!(from.signed, to.signed);
1102 let cast_name = if to.signed {
1103 "cast_signed"
1104 } else {
1105 "cast_unsigned"
1106 };
1107 fmtln!(
1108 isle,
1109 "(decl pure {from_name}_{cast_name} ({from_name}) {to_name})"
1110 );
1111 fmtln!(
1112 isle,
1113 "(extern constructor {from_name}_{cast_name} {from_name}_{cast_name})"
1114 );
1115 fmtln!(rust, "#[inline]");
1116 fmtln!(
1117 rust,
1118 "fn {from_name}_{cast_name}(&mut self, x: {from_name}) -> {to_name} {{"
1119 );
1120 rust.indent(|rust| {
1121 fmtln!(rust, "x as {to_name}");
1124 });
1125 fmtln!(rust, "}}");
1126 }
1127
1128 fmtln!(
1130 isle,
1131 "(decl pure {to_name}_from_{from_name} ({to_name}) {from_name})"
1132 );
1133 fmtln!(
1134 isle,
1135 "(extern extractor {to_name}_from_{from_name} {from_name}_from_{to_name})"
1136 );
1137 fmtln!(rust, "#[inline]");
1138 fmtln!(
1139 rust,
1140 "fn {from_name}_from_{to_name}(&mut self, x: {from_name}) -> Option<{to_name}> {{"
1141 );
1142 rust.indent(|rust| {
1143 if lossy {
1144 fmtln!(rust, "x.try_into().ok()");
1145 } else {
1146 fmtln!(rust, "Some(x.into())");
1147 }
1148 });
1149 fmtln!(rust, "}}");
1150
1151 isle.empty_line();
1152 rust.empty_line();
1153 }
1154 }
1155
1156 rust.indent_pop();
1157 fmtln!(rust, "}}");
1158 rust.indent_pop();
1159 fmtln!(rust, "}}");
1160}
1161
1162pub(crate) fn generate(
1163 formats: &[Rc<InstructionFormat>],
1164 all_inst: &AllInstructions,
1165 isle_numerics_filename: &str,
1166 rust_numerics_filename: &str,
1167 isle_opt_filename: &str,
1168 isle_lower_filename: &str,
1169 isle_dir: &std::path::Path,
1170) -> Result<(), error::Error> {
1171 let mut isle_fmt = Formatter::new(Language::Isle);
1173 let mut rust_fmt = Formatter::new(Language::Rust);
1174 gen_numerics_isle(&mut isle_fmt, &mut rust_fmt);
1175 isle_fmt.write(isle_numerics_filename, isle_dir)?;
1176 rust_fmt.write(rust_numerics_filename, isle_dir)?;
1177
1178 let mut fmt = Formatter::new(Language::Isle);
1180 gen_opt_isle(&formats, all_inst, &mut fmt);
1181 fmt.write(isle_opt_filename, isle_dir)?;
1182
1183 let mut fmt = Formatter::new(Language::Isle);
1185 gen_lower_isle(&formats, all_inst, &mut fmt);
1186 fmt.write(isle_lower_filename, isle_dir)?;
1187
1188 Ok(())
1189}