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