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 if ty_in_decl && isle_target == IsleTarget::Opt {
244 fmt.line(";; The `Type` argument is the instruction's *controlling* type, both");
245 fmt.line(";; when matching and when constructing. For most instructions this is");
246 fmt.line(";; the result type, but not for e.g. comparisons (`icmp`'s controlling");
247 fmt.line(";; type is the operand type; its result is `i8` or a lane-wise mask).");
248 fmt.line(";; Use `(has_type ty ...)` to bind the result type instead.");
249 }
250 fmtln!(
251 fmt,
252 "(decl {} ({}{}) {})",
253 inst.name,
254 if ty_in_decl { "Type " } else { "" },
255 inst.operands_in
256 .iter()
257 .map(|o| {
258 let ty = o.kind.rust_type;
259 if ty == "&[Value]" {
260 "ValueSlice"
261 } else {
262 ty.rsplit("::").next().unwrap()
263 }
264 })
265 .collect::<Vec<_>>()
266 .join(" "),
267 ret_ty
268 );
269 fmtln!(fmt, "(extractor");
270 fmt.indent(|fmt| {
271 fmtln!(
272 fmt,
273 "({} {}{})",
274 inst.name,
275 if ty_in_decl { "ty " } else { "" },
276 inst.operands_in
277 .iter()
278 .map(|o| { o.name })
279 .collect::<Vec<_>>()
280 .join(" ")
281 );
282
283 let mut s = format!(
284 "({inst_data_etor} {}(InstructionData.{} (Opcode.{})",
285 if ty_in_decl {
286 "ty "
287 } else if isle_target == IsleTarget::Lower {
288 "_ "
289 } else {
290 ""
291 },
292 inst.format.name,
293 inst.camel_name
294 );
295
296 if inst.format.has_value_list {
298 let values: Vec<_> = inst
304 .operands_in
305 .iter()
306 .filter(|o| o.is_value())
307 .map(|o| o.name)
308 .collect();
309 let varargs = inst
310 .operands_in
311 .iter()
312 .find(|o| o.is_varargs())
313 .unwrap()
314 .name;
315 if values.is_empty() {
316 write!(&mut s, " (value_list_slice {varargs})").unwrap();
317 } else {
318 write!(
319 &mut s,
320 " (unwrap_head_value_list_{} {} {})",
321 values.len(),
322 values.join(" "),
323 varargs
324 )
325 .unwrap();
326 }
327 } else if inst.format.num_value_operands == 1 {
328 write!(
329 &mut s,
330 " {}",
331 inst.operands_in.iter().find(|o| o.is_value()).unwrap().name
332 )
333 .unwrap();
334 } else if inst.format.num_value_operands > 1 {
335 let values = inst
336 .operands_in
337 .iter()
338 .filter(|o| o.is_value())
339 .map(|o| o.name)
340 .collect::<Vec<_>>();
341 assert_eq!(values.len(), inst.format.num_value_operands);
342 let values = values.join(" ");
343 write!(
344 &mut s,
345 " (value_array_{} {})",
346 inst.format.num_value_operands, values,
347 )
348 .unwrap();
349 }
350
351 let block_operands: Vec<_> = inst
353 .operands_in
354 .iter()
355 .filter(|o| o.kind.is_block())
356 .collect();
357 assert_eq!(block_operands.len(), inst.format.num_block_operands);
358 assert!(block_operands.len() <= 2);
359
360 if !block_operands.is_empty() {
361 if block_operands.len() == 1 {
362 write!(&mut s, " {}", block_operands[0].name).unwrap();
363 } else {
364 let blocks: Vec<_> = block_operands.iter().map(|o| o.name).collect();
365 let blocks = blocks.join(" ");
366 write!(
367 &mut s,
368 " (block_array_{} {})",
369 inst.format.num_block_operands, blocks,
370 )
371 .unwrap();
372 }
373 }
374
375 match inst.format.num_raw_block_operands {
377 0 => {}
378 1 => {
379 write!(&mut s, " block").unwrap();
380 }
381 _ => panic!("Too many raw block arguments"),
382 }
383
384 let imm_operands: Vec<_> = inst
386 .operands_in
387 .iter()
388 .filter(|o| {
389 !o.is_value() && !o.is_varargs() && !o.kind.is_block() && !o.kind.is_raw_block()
390 })
391 .collect();
392 assert_eq!(imm_operands.len(), inst.format.imm_fields.len(),);
393 for op in imm_operands {
394 write!(&mut s, " {}", op.name).unwrap();
395 }
396
397 s.push_str("))");
398 fmt.line(&s);
399 });
400 fmt.line(")");
401
402 if isle_target == IsleTarget::Opt {
404 fmtln!(
405 fmt,
406 "(rule ({}{} {})",
407 inst.name,
408 if ty_in_decl { " ty" } else { "" },
409 inst.operands_in
410 .iter()
411 .map(|o| o.name)
412 .collect::<Vec<_>>()
413 .join(" ")
414 );
415 fmt.indent(|fmt| {
416 let mut s = format!(
417 "({make_inst_ctor}{} (InstructionData.{} (Opcode.{})",
418 if ty_in_decl { " ty" } else { "" },
419 inst.format.name,
420 inst.camel_name
421 );
422
423 assert!(!inst.format.has_value_list);
438 if inst.format.num_value_operands == 1 {
439 write!(
440 &mut s,
441 " {}",
442 inst.operands_in.iter().find(|o| o.is_value()).unwrap().name
443 )
444 .unwrap();
445 } else if inst.format.num_value_operands > 1 {
446 let values = inst
450 .operands_in
451 .iter()
452 .filter(|o| o.is_value())
453 .map(|o| o.name)
454 .collect::<Vec<_>>();
455 assert_eq!(values.len(), inst.format.num_value_operands);
456 let values = values.join(" ");
457 write!(
458 &mut s,
459 " (value_array_{}_ctor {})",
460 inst.format.num_value_operands, values
461 )
462 .unwrap();
463 }
464
465 if inst.format.num_block_operands > 0 {
466 let blocks: Vec<_> = inst
467 .operands_in
468 .iter()
469 .filter(|o| o.kind.is_block())
470 .map(|o| o.name)
471 .collect();
472 if inst.format.num_block_operands == 1 {
473 write!(&mut s, " {}", blocks.first().unwrap(),).unwrap();
474 } else {
475 write!(
476 &mut s,
477 " (block_array_{} {})",
478 inst.format.num_block_operands,
479 blocks.join(" ")
480 )
481 .unwrap();
482 }
483 }
484
485 match inst.format.num_raw_block_operands {
486 0 => {}
487 1 => {
488 write!(&mut s, " block").unwrap();
489 }
490 _ => panic!("Too many raw block arguments"),
491 }
492
493 for o in inst.operands_in.iter().filter(|o| {
495 !o.is_value() && !o.is_varargs() && !o.kind.is_block() && !o.kind.is_raw_block()
496 }) {
497 write!(&mut s, " {}", o.name).unwrap();
498 }
499 s.push_str("))");
500 fmt.line(&s);
501 });
502 fmt.line(")");
503 }
504
505 fmt.empty_line();
506 }
507}
508
509fn gen_opt_isle(
510 formats: &[Rc<InstructionFormat>],
511 instructions: &AllInstructions,
512 fmt: &mut Formatter,
513) {
514 gen_common_isle(formats, instructions, fmt, IsleTarget::Opt);
515}
516
517fn gen_lower_isle(
518 formats: &[Rc<InstructionFormat>],
519 instructions: &AllInstructions,
520 fmt: &mut Formatter,
521) {
522 gen_common_isle(formats, instructions, fmt, IsleTarget::Lower);
523}
524
525fn gen_isle_enum(name: &str, mut variants: Vec<&str>, fmt: &mut Formatter) {
527 variants.sort();
528 let prefix = format!(";;;; Enumerated Immediate: {name} ");
529 fmtln!(fmt, "{:;<80}", prefix);
530 fmt.empty_line();
531 fmtln!(fmt, "(type {} extern", name);
532 fmt.indent(|fmt| {
533 fmt.line("(enum");
534 fmt.indent(|fmt| {
535 for variant in variants {
536 fmtln!(fmt, "{}", variant);
537 }
538 });
539 fmt.line(")");
540 });
541 fmt.line(")");
542 fmt.empty_line();
543}
544
545#[derive(Clone, Copy, PartialEq, Eq)]
546struct NumericType {
547 signed: bool,
548 byte_width: u8,
549}
550
551impl NumericType {
552 fn all() -> impl Iterator<Item = NumericType> {
553 [1, 2, 4, 8, 16].into_iter().flat_map(|byte_width| {
554 [true, false]
555 .into_iter()
556 .map(move |signed| NumericType { signed, byte_width })
557 })
558 }
559
560 fn name(&self) -> &'static str {
561 let idx = self.byte_width.ilog2();
562 let idx = usize::try_from(idx).unwrap();
563 if self.signed {
564 ["i8", "i16", "i32", "i64", "i128"][idx]
565 } else {
566 ["u8", "u16", "u32", "u64", "u128"][idx]
567 }
568 }
569}
570
571#[derive(Clone, Default, PartialEq, Eq)]
572struct NumericOp<'a> {
573 name: &'a str,
575 ret: &'a str,
577 partial: bool,
579 args: Rc<[(&'a str, &'a str)]>,
581 body: &'a str,
583 etors: bool,
587}
588
589impl NumericOp<'_> {
590 fn ops_for_type(ty: &NumericType) -> impl Iterator<Item = NumericOp<'_>> {
591 let arity1 = NumericOp {
592 args: [("a", ty.name())].into(),
593 ..NumericOp::default()
594 };
595
596 let arity2 = NumericOp {
597 args: [("a", ty.name()), ("b", ty.name())].into(),
598 ..NumericOp::default()
599 };
600
601 let comparison = NumericOp {
602 ret: "bool",
603 ..arity2.clone()
604 };
605
606 let predicate = NumericOp {
607 ret: "bool",
608 etors: true,
609 ..arity1.clone()
610 };
611
612 let binop = NumericOp {
613 ret: ty.name(),
614 ..arity2.clone()
615 };
616
617 let partial_binop = NumericOp {
618 ret: ty.name(),
619 partial: true,
620 ..binop.clone()
621 };
622
623 let unop = NumericOp {
624 ret: ty.name(),
625 ..arity1.clone()
626 };
627
628 let partial_unop = NumericOp {
629 ret: ty.name(),
630 partial: true,
631 ..unop.clone()
632 };
633
634 let shift = NumericOp {
635 args: [("a", ty.name()), ("b", "u32")].into(),
636 ..binop.clone()
637 };
638
639 let partial_shift = NumericOp {
640 args: [("a", ty.name()), ("b", "u32")].into(),
641 ..partial_binop.clone()
642 };
643
644 let mut ops = Vec::new();
646
647 ops.push(NumericOp {
649 name: "eq",
650 body: "a == b",
651 ..comparison.clone()
652 });
653 ops.push(NumericOp {
654 name: "ne",
655 body: "a != b",
656 ..comparison.clone()
657 });
658 ops.push(NumericOp {
659 name: "lt",
660 body: "a < b",
661 ..comparison.clone()
662 });
663 ops.push(NumericOp {
664 name: "lt_eq",
665 body: "a <= b",
666 ..comparison.clone()
667 });
668 ops.push(NumericOp {
669 name: "gt",
670 body: "a > b",
671 ..comparison.clone()
672 });
673 ops.push(NumericOp {
674 name: "gt_eq",
675 body: "a >= b",
676 ..comparison.clone()
677 });
678
679 ops.push(NumericOp {
687 name: "checked_add",
688 body: "a.checked_add(b)",
689 ..partial_binop.clone()
690 });
691
692 ops.push(NumericOp {
693 name: "wrapping_add",
694 body: "a.wrapping_add(b)",
695 ..binop.clone()
696 });
697 ops.push(NumericOp {
698 name: "add",
699 body: r#"a.checked_add(b).unwrap_or_else(|| panic!("addition overflow: {a} + {b}"))"#,
700 ..binop.clone()
701 });
702 ops.push(NumericOp {
703 name: "checked_sub",
704 body: "a.checked_sub(b)",
705 ..partial_binop.clone()
706 });
707 ops.push(NumericOp {
708 name: "wrapping_sub",
709 body: "a.wrapping_sub(b)",
710 ..binop.clone()
711 });
712 ops.push(NumericOp {
713 name: "sub",
714 body: r#"a.checked_sub(b).unwrap_or_else(|| panic!("subtraction overflow: {a} - {b}"))"#,
715 ..binop.clone()
716 });
717 ops.push(NumericOp {
718 name: "checked_mul",
719 body: "a.checked_mul(b)",
720 ..partial_binop.clone()
721 });
722
723 ops.push(NumericOp {
724 name: "wrapping_mul",
725 body: "a.wrapping_mul(b)",
726 ..binop.clone()
727 });
728 ops.push(NumericOp {
729 name: "mul",
730 body: r#"a.checked_mul(b).unwrap_or_else(|| panic!("multiplication overflow: {a} * {b}"))"#,
731 ..binop.clone()
732 });
733 ops.push(NumericOp {
734 name: "checked_div",
735 body: "a.checked_div(b)",
736 ..partial_binop.clone()
737 });
738 ops.push(NumericOp {
739 name: "wrapping_div",
740 body: "a.wrapping_div(b)",
741 ..binop.clone()
742 });
743 ops.push(NumericOp {
744 name: "div",
745 body: r#"a.checked_div(b).unwrap_or_else(|| panic!("div failure: {a} / {b}"))"#,
746 ..binop.clone()
747 });
748 ops.push(NumericOp {
749 name: "checked_rem",
750 body: "a.checked_rem(b)",
751 ..partial_binop.clone()
752 });
753 ops.push(NumericOp {
754 name: "rem",
755 body: r#"a.checked_rem(b).unwrap_or_else(|| panic!("rem failure: {a} % {b}"))"#,
756 ..binop.clone()
757 });
758 ops.push(NumericOp {
763 name: "and",
764 body: "a & b",
765 ..binop.clone()
766 });
767 ops.push(NumericOp {
768 name: "or",
769 body: "a | b",
770 ..binop.clone()
771 });
772 ops.push(NumericOp {
773 name: "xor",
774 body: "a ^ b",
775 ..binop.clone()
776 });
777 ops.push(NumericOp {
778 name: "not",
779 body: "!a",
780 ..unop.clone()
781 });
782 ops.push(NumericOp {
783 name: "checked_shl",
784 body: "a.checked_shl(b)",
785 ..partial_shift.clone()
786 });
787 ops.push(NumericOp {
788 name: "wrapping_shl",
789 body: "a.wrapping_shl(b)",
790 ..shift.clone()
791 });
792 ops.push(NumericOp {
793 name: "shl",
794 body: r#"a.checked_shl(b).unwrap_or_else(|| panic!("shl overflow: {a} << {b}"))"#,
795 ..shift.clone()
796 });
797 ops.push(NumericOp {
798 name: "checked_shr",
799 body: "a.checked_shr(b)",
800 ..partial_shift.clone()
801 });
802 ops.push(NumericOp {
803 name: "wrapping_shr",
804 body: "a.wrapping_shr(b)",
805 ..shift.clone()
806 });
807 ops.push(NumericOp {
808 name: "shr",
809 body: r#"a.checked_shr(b).unwrap_or_else(|| panic!("shr overflow: {a} >> {b}"))"#,
810 ..shift.clone()
811 });
812 ops.push(NumericOp {
813 name: "rotl",
814 body: "a.rotate_left(b)",
815 ..shift.clone()
816 });
817 ops.push(NumericOp {
818 name: "rotr",
819 body: "a.rotate_right(b)",
820 ..shift.clone()
821 });
822
823 ops.push(NumericOp {
829 name: "is_zero",
830 body: "a == 0",
831 ..predicate.clone()
832 });
833 ops.push(NumericOp {
834 name: "is_non_zero",
835 body: "a != 0",
836 ..predicate.clone()
837 });
838 ops.push(NumericOp {
839 name: "is_odd",
840 body: "a & 1 == 1",
841 ..predicate.clone()
842 });
843 ops.push(NumericOp {
844 name: "is_even",
845 body: "a & 1 == 0",
846 ..predicate.clone()
847 });
848 ops.push(NumericOp {
850 name: "checked_ilog2",
851 body: "a.checked_ilog2()",
852 ret: "u32",
853 ..partial_unop.clone()
854 });
855 ops.push(NumericOp {
856 name: "ilog2",
857 body: r#"a.checked_ilog2().unwrap_or_else(|| panic!("ilog2 overflow: {a}"))"#,
858 ret: "u32",
859 ..unop.clone()
860 });
861 ops.push(NumericOp {
862 name: "trailing_zeros",
863 body: "a.trailing_zeros()",
864 ret: "u32",
865 ..unop.clone()
866 });
867 ops.push(NumericOp {
868 name: "trailing_ones",
869 body: "a.trailing_ones()",
870 ret: "u32",
871 ..unop.clone()
872 });
873 ops.push(NumericOp {
874 name: "leading_zeros",
875 body: "a.leading_zeros()",
876 ret: "u32",
877 ..unop.clone()
878 });
879 ops.push(NumericOp {
880 name: "leading_ones",
881 body: "a.leading_ones()",
882 ret: "u32",
883 ..unop.clone()
884 });
885
886 if ty.signed {
887 ops.push(NumericOp {
889 name: "checked_neg",
890 body: "a.checked_neg()",
891 ..partial_unop.clone()
892 });
893 ops.push(NumericOp {
894 name: "wrapping_neg",
895 body: "a.wrapping_neg()",
896 ..unop.clone()
897 });
898 ops.push(NumericOp {
899 name: "neg",
900 body: r#"a.checked_neg().unwrap_or_else(|| panic!("negation overflow: {a}"))"#,
901 ..unop.clone()
902 });
903 } else {
904 ops.push(NumericOp {
906 name: "is_power_of_two",
907 body: "a.is_power_of_two()",
908 ..predicate.clone()
909 });
910 }
911
912 ops.into_iter()
913 }
914}
915
916fn gen_numerics_isle(isle: &mut Formatter, rust: &mut Formatter) {
917 fmtln!(rust, "#[macro_export]");
918 fmtln!(rust, "#[doc(hidden)]");
919 fmtln!(rust, "macro_rules! isle_numerics_methods {{");
920 rust.indent_push();
921 fmtln!(rust, "() => {{");
922 rust.indent_push();
923
924 for ty in NumericType::all() {
925 for op in NumericOp::ops_for_type(&ty) {
926 let ty = ty.name();
927 let op_name = format!("{ty}_{}", op.name);
928 let partial = if op.partial { " partial" } else { "" };
929 let ret = op.ret;
930 fmtln!(isle, "(decl pure{partial} {op_name} (");
931 isle.indent(|isle| {
932 for (_arg_name, arg_ty) in op.args.iter() {
933 fmtln!(isle, "{arg_ty}");
934 }
935 });
936 fmtln!(isle, ") {ret})");
937 fmtln!(isle, "(extern constructor {op_name} {op_name})");
938
939 let ret = if op.partial {
940 Cow::from(format!("Option<{ret}>"))
941 } else {
942 Cow::from(ret)
943 };
944 let body = op.body;
945 fmtln!(rust, "#[inline]");
946 fmtln!(rust, "fn {op_name}(");
947 rust.indent(|rust| {
948 fmtln!(rust, "&mut self,");
949 for (arg_name, arg_ty) in op.args.iter() {
950 fmtln!(rust, "{arg_name}: {arg_ty},");
951 }
952 });
953 fmtln!(rust, ") -> {ret} {{");
954 rust.indent(|rust| {
955 fmtln!(rust, "{body}");
956 });
957 fmtln!(rust, "}}");
958
959 if op.etors {
971 debug_assert_eq!(op.args.len(), 1);
972 debug_assert_eq!(op.args[0].1, ty);
973 debug_assert_eq!(op.ret, "bool");
974 debug_assert!(op.name.starts_with("is_"));
975
976 let base_name = &op.name[3..];
978 debug_assert!(base_name.len() > 0);
979
980 fmtln!(isle, "(decl pure {ty}_matches_{base_name} (bool) {ty})");
981 fmtln!(
982 isle,
983 "(extern extractor {ty}_matches_{base_name} {ty}_matches_{base_name})"
984 );
985 fmtln!(rust, "#[inline]");
986 fmtln!(
987 rust,
988 "fn {ty}_matches_{base_name}(&mut self, a: {ty}) -> Option<bool> {{"
989 );
990 rust.indent(|rust| {
991 fmtln!(rust, "Some({body})");
992 });
993 fmtln!(rust, "}}");
994
995 fmtln!(isle, "(decl pure {ty}_extract_{base_name} ({ty}) {ty})");
996 fmtln!(
997 isle,
998 "(extractor ({ty}_extract_{base_name} x) (and ({ty}_matches_{base_name} true) x))"
999 );
1000
1001 fmtln!(isle, "(decl pure {ty}_when_{base_name} () {ty})");
1002 fmtln!(
1003 isle,
1004 "(extractor ({ty}_when_{base_name}) ({ty}_matches_{base_name} true))"
1005 );
1006
1007 fmtln!(isle, "(decl pure {ty}_when_not_{base_name} () {ty})");
1008 fmtln!(
1009 isle,
1010 "(extractor ({ty}_when_not_{base_name}) ({ty}_matches_{base_name} false))"
1011 );
1012 }
1013
1014 isle.empty_line();
1015 rust.empty_line();
1016 }
1017 }
1018
1019 for from in NumericType::all() {
1039 for to in NumericType::all() {
1040 if from == to {
1041 continue;
1042 }
1043
1044 let from_name = from.name();
1045 let to_name = to.name();
1046
1047 let lossy = match (from.byte_width.cmp(&to.byte_width), from.signed, to.signed) {
1048 (Ordering::Less, true, true) | (Ordering::Less, false, false) => false,
1050 (Ordering::Less, false, true) => false,
1052 (Ordering::Less, true, false) => true,
1054 (Ordering::Equal, _, _) => {
1057 debug_assert_ne!(from.signed, to.signed);
1058 true
1059 }
1060 (Ordering::Greater, _, _) => true,
1062 };
1063
1064 let (ctor, partial, rust_ret) = if lossy {
1065 (
1066 "try_into",
1067 " partial",
1068 Cow::from(format!("Option<{to_name}>")),
1069 )
1070 } else {
1071 ("into", "", Cow::from(to_name))
1072 };
1073
1074 fmtln!(
1076 isle,
1077 "(decl pure{partial} {from_name}_{ctor}_{to_name} ({from_name}) {to_name})"
1078 );
1079 fmtln!(
1080 isle,
1081 "(extern constructor {from_name}_{ctor}_{to_name} {from_name}_{ctor}_{to_name})"
1082 );
1083 if !lossy {
1084 fmtln!(
1085 isle,
1086 "(convert {from_name} {to_name} {from_name}_{ctor}_{to_name})"
1087 );
1088 }
1089 fmtln!(rust, "#[inline]");
1090 fmtln!(
1091 rust,
1092 "fn {from_name}_{ctor}_{to_name}(&mut self, x: {from_name}) -> {rust_ret} {{"
1093 );
1094 rust.indent(|rust| {
1095 if lossy {
1096 fmtln!(rust, "{to_name}::try_from(x).ok()");
1097 } else {
1098 fmtln!(rust, "{to_name}::from(x)");
1099 }
1100 });
1101 fmtln!(rust, "}}");
1102
1103 if lossy {
1105 fmtln!(
1106 isle,
1107 "(decl pure {from_name}_unwrap_into_{to_name} ({from_name}) {to_name})"
1108 );
1109 fmtln!(
1110 isle,
1111 "(extern constructor {from_name}_unwrap_into_{to_name} {from_name}_unwrap_into_{to_name})"
1112 );
1113 fmtln!(rust, "#[inline]");
1114 fmtln!(
1115 rust,
1116 "fn {from_name}_unwrap_into_{to_name}(&mut self, x: {from_name}) -> {to_name} {{"
1117 );
1118 rust.indent(|rust| {
1119 fmtln!(rust, "{to_name}::try_from(x).unwrap()");
1120 });
1121 fmtln!(rust, "}}");
1122 }
1123
1124 if lossy && from.signed == to.signed {
1126 fmtln!(
1127 isle,
1128 "(decl pure {from_name}_truncate_into_{to_name} ({from_name}) {to_name})"
1129 );
1130 fmtln!(
1131 isle,
1132 "(extern constructor {from_name}_truncate_into_{to_name} {from_name}_truncate_into_{to_name})"
1133 );
1134 fmtln!(rust, "#[inline]");
1135 fmtln!(
1136 rust,
1137 "fn {from_name}_truncate_into_{to_name}(&mut self, x: {from_name}) -> {to_name} {{"
1138 );
1139 rust.indent(|rust| {
1140 fmtln!(rust, "x as {to_name}");
1141 });
1142 fmtln!(rust, "}}");
1143 }
1144
1145 if from.byte_width == to.byte_width {
1147 debug_assert_ne!(from.signed, to.signed);
1148 let cast_name = if to.signed {
1149 "cast_signed"
1150 } else {
1151 "cast_unsigned"
1152 };
1153 fmtln!(
1154 isle,
1155 "(decl pure {from_name}_{cast_name} ({from_name}) {to_name})"
1156 );
1157 fmtln!(
1158 isle,
1159 "(extern constructor {from_name}_{cast_name} {from_name}_{cast_name})"
1160 );
1161 fmtln!(rust, "#[inline]");
1162 fmtln!(
1163 rust,
1164 "fn {from_name}_{cast_name}(&mut self, x: {from_name}) -> {to_name} {{"
1165 );
1166 rust.indent(|rust| {
1167 fmtln!(rust, "x as {to_name}");
1170 });
1171 fmtln!(rust, "}}");
1172 }
1173
1174 fmtln!(
1176 isle,
1177 "(decl pure {to_name}_from_{from_name} ({to_name}) {from_name})"
1178 );
1179 fmtln!(
1180 isle,
1181 "(extern extractor {to_name}_from_{from_name} {from_name}_from_{to_name})"
1182 );
1183 fmtln!(rust, "#[inline]");
1184 fmtln!(
1185 rust,
1186 "fn {from_name}_from_{to_name}(&mut self, x: {from_name}) -> Option<{to_name}> {{"
1187 );
1188 rust.indent(|rust| {
1189 if lossy {
1190 fmtln!(rust, "x.try_into().ok()");
1191 } else {
1192 fmtln!(rust, "Some(x.into())");
1193 }
1194 });
1195 fmtln!(rust, "}}");
1196
1197 isle.empty_line();
1198 rust.empty_line();
1199 }
1200 }
1201
1202 rust.indent_pop();
1203 fmtln!(rust, "}}");
1204 rust.indent_pop();
1205 fmtln!(rust, "}}");
1206}
1207
1208pub(crate) fn generate(
1209 formats: &[Rc<InstructionFormat>],
1210 all_inst: &AllInstructions,
1211 isle_numerics_filename: &str,
1212 rust_numerics_filename: &str,
1213 isle_opt_filename: &str,
1214 isle_lower_filename: &str,
1215 isle_dir: &std::path::Path,
1216) -> Result<(), error::Error> {
1217 let mut isle_fmt = Formatter::new(Language::Isle);
1219 let mut rust_fmt = Formatter::new(Language::Rust);
1220 gen_numerics_isle(&mut isle_fmt, &mut rust_fmt);
1221 isle_fmt.write(isle_numerics_filename, isle_dir)?;
1222 rust_fmt.write(rust_numerics_filename, isle_dir)?;
1223
1224 let mut fmt = Formatter::new(Language::Isle);
1226 gen_opt_isle(&formats, all_inst, &mut fmt);
1227 fmt.write(isle_opt_filename, isle_dir)?;
1228
1229 let mut fmt = Formatter::new(Language::Isle);
1231 gen_lower_isle(&formats, all_inst, &mut fmt);
1232 fmt.write(isle_lower_filename, isle_dir)?;
1233
1234 Ok(())
1235}