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 block_operands: Vec<_> = inst
346 .operands_in
347 .iter()
348 .filter(|o| o.kind.is_block())
349 .collect();
350 assert_eq!(block_operands.len(), inst.format.num_block_operands);
351 assert!(block_operands.len() <= 2);
352
353 if !block_operands.is_empty() {
354 if block_operands.len() == 1 {
355 write!(&mut s, " {}", block_operands[0].name).unwrap();
356 } else {
357 let blocks: Vec<_> = block_operands.iter().map(|o| o.name).collect();
358 let blocks = blocks.join(" ");
359 write!(
360 &mut s,
361 " (block_array_{} {})",
362 inst.format.num_block_operands, blocks,
363 )
364 .unwrap();
365 }
366 }
367
368 match inst.format.num_raw_block_operands {
370 0 => {}
371 1 => {
372 write!(&mut s, " block").unwrap();
373 }
374 _ => panic!("Too many raw block arguments"),
375 }
376
377 let imm_operands: Vec<_> = inst
379 .operands_in
380 .iter()
381 .filter(|o| {
382 !o.is_value() && !o.is_varargs() && !o.kind.is_block() && !o.kind.is_raw_block()
383 })
384 .collect();
385 assert_eq!(imm_operands.len(), inst.format.imm_fields.len(),);
386 for op in imm_operands {
387 write!(&mut s, " {}", op.name).unwrap();
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 mut ops = Vec::new();
639
640 ops.push(NumericOp {
642 name: "eq",
643 body: "a == b",
644 ..comparison.clone()
645 });
646 ops.push(NumericOp {
647 name: "ne",
648 body: "a != b",
649 ..comparison.clone()
650 });
651 ops.push(NumericOp {
652 name: "lt",
653 body: "a < b",
654 ..comparison.clone()
655 });
656 ops.push(NumericOp {
657 name: "lt_eq",
658 body: "a <= b",
659 ..comparison.clone()
660 });
661 ops.push(NumericOp {
662 name: "gt",
663 body: "a > b",
664 ..comparison.clone()
665 });
666 ops.push(NumericOp {
667 name: "gt_eq",
668 body: "a >= b",
669 ..comparison.clone()
670 });
671
672 ops.push(NumericOp {
680 name: "checked_add",
681 body: "a.checked_add(b)",
682 ..partial_binop.clone()
683 });
684
685 ops.push(NumericOp {
686 name: "wrapping_add",
687 body: "a.wrapping_add(b)",
688 ..binop.clone()
689 });
690 ops.push(NumericOp {
691 name: "add",
692 body: r#"a.checked_add(b).unwrap_or_else(|| panic!("addition overflow: {a} + {b}"))"#,
693 ..binop.clone()
694 });
695 ops.push(NumericOp {
696 name: "checked_sub",
697 body: "a.checked_sub(b)",
698 ..partial_binop.clone()
699 });
700 ops.push(NumericOp {
701 name: "wrapping_sub",
702 body: "a.wrapping_sub(b)",
703 ..binop.clone()
704 });
705 ops.push(NumericOp {
706 name: "sub",
707 body: r#"a.checked_sub(b).unwrap_or_else(|| panic!("subtraction overflow: {a} - {b}"))"#,
708 ..binop.clone()
709 });
710 ops.push(NumericOp {
711 name: "checked_mul",
712 body: "a.checked_mul(b)",
713 ..partial_binop.clone()
714 });
715
716 ops.push(NumericOp {
717 name: "wrapping_mul",
718 body: "a.wrapping_mul(b)",
719 ..binop.clone()
720 });
721 ops.push(NumericOp {
722 name: "mul",
723 body: r#"a.checked_mul(b).unwrap_or_else(|| panic!("multiplication overflow: {a} * {b}"))"#,
724 ..binop.clone()
725 });
726 ops.push(NumericOp {
727 name: "checked_div",
728 body: "a.checked_div(b)",
729 ..partial_binop.clone()
730 });
731 ops.push(NumericOp {
732 name: "wrapping_div",
733 body: "a.wrapping_div(b)",
734 ..binop.clone()
735 });
736 ops.push(NumericOp {
737 name: "div",
738 body: r#"a.checked_div(b).unwrap_or_else(|| panic!("div failure: {a} / {b}"))"#,
739 ..binop.clone()
740 });
741 ops.push(NumericOp {
742 name: "checked_rem",
743 body: "a.checked_rem(b)",
744 ..partial_binop.clone()
745 });
746 ops.push(NumericOp {
747 name: "rem",
748 body: r#"a.checked_rem(b).unwrap_or_else(|| panic!("rem failure: {a} % {b}"))"#,
749 ..binop.clone()
750 });
751 ops.push(NumericOp {
756 name: "and",
757 body: "a & b",
758 ..binop.clone()
759 });
760 ops.push(NumericOp {
761 name: "or",
762 body: "a | b",
763 ..binop.clone()
764 });
765 ops.push(NumericOp {
766 name: "xor",
767 body: "a ^ b",
768 ..binop.clone()
769 });
770 ops.push(NumericOp {
771 name: "not",
772 body: "!a",
773 ..unop.clone()
774 });
775 ops.push(NumericOp {
776 name: "checked_shl",
777 body: "a.checked_shl(b)",
778 ..partial_shift.clone()
779 });
780 ops.push(NumericOp {
781 name: "wrapping_shl",
782 body: "a.wrapping_shl(b)",
783 ..shift.clone()
784 });
785 ops.push(NumericOp {
786 name: "shl",
787 body: r#"a.checked_shl(b).unwrap_or_else(|| panic!("shl overflow: {a} << {b}"))"#,
788 ..shift.clone()
789 });
790 ops.push(NumericOp {
791 name: "checked_shr",
792 body: "a.checked_shr(b)",
793 ..partial_shift.clone()
794 });
795 ops.push(NumericOp {
796 name: "wrapping_shr",
797 body: "a.wrapping_shr(b)",
798 ..shift.clone()
799 });
800 ops.push(NumericOp {
801 name: "shr",
802 body: r#"a.checked_shr(b).unwrap_or_else(|| panic!("shr overflow: {a} >> {b}"))"#,
803 ..shift.clone()
804 });
805 ops.push(NumericOp {
806 name: "rotl",
807 body: "a.rotate_left(b)",
808 ..shift.clone()
809 });
810 ops.push(NumericOp {
811 name: "rotr",
812 body: "a.rotate_right(b)",
813 ..shift.clone()
814 });
815
816 ops.push(NumericOp {
822 name: "is_zero",
823 body: "a == 0",
824 ..predicate.clone()
825 });
826 ops.push(NumericOp {
827 name: "is_non_zero",
828 body: "a != 0",
829 ..predicate.clone()
830 });
831 ops.push(NumericOp {
832 name: "is_odd",
833 body: "a & 1 == 1",
834 ..predicate.clone()
835 });
836 ops.push(NumericOp {
837 name: "is_even",
838 body: "a & 1 == 0",
839 ..predicate.clone()
840 });
841 ops.push(NumericOp {
843 name: "checked_ilog2",
844 body: "a.checked_ilog2()",
845 ret: "u32",
846 ..partial_unop.clone()
847 });
848 ops.push(NumericOp {
849 name: "ilog2",
850 body: r#"a.checked_ilog2().unwrap_or_else(|| panic!("ilog2 overflow: {a}"))"#,
851 ret: "u32",
852 ..unop.clone()
853 });
854 ops.push(NumericOp {
855 name: "trailing_zeros",
856 body: "a.trailing_zeros()",
857 ret: "u32",
858 ..unop.clone()
859 });
860 ops.push(NumericOp {
861 name: "trailing_ones",
862 body: "a.trailing_ones()",
863 ret: "u32",
864 ..unop.clone()
865 });
866 ops.push(NumericOp {
867 name: "leading_zeros",
868 body: "a.leading_zeros()",
869 ret: "u32",
870 ..unop.clone()
871 });
872 ops.push(NumericOp {
873 name: "leading_ones",
874 body: "a.leading_ones()",
875 ret: "u32",
876 ..unop.clone()
877 });
878
879 if ty.signed {
880 ops.push(NumericOp {
882 name: "checked_neg",
883 body: "a.checked_neg()",
884 ..partial_unop.clone()
885 });
886 ops.push(NumericOp {
887 name: "wrapping_neg",
888 body: "a.wrapping_neg()",
889 ..unop.clone()
890 });
891 ops.push(NumericOp {
892 name: "neg",
893 body: r#"a.checked_neg().unwrap_or_else(|| panic!("negation overflow: {a}"))"#,
894 ..unop.clone()
895 });
896 } else {
897 ops.push(NumericOp {
899 name: "is_power_of_two",
900 body: "a.is_power_of_two()",
901 ..predicate.clone()
902 });
903 }
904
905 ops.into_iter()
906 }
907}
908
909fn gen_numerics_isle(isle: &mut Formatter, rust: &mut Formatter) {
910 fmtln!(rust, "#[macro_export]");
911 fmtln!(rust, "#[doc(hidden)]");
912 fmtln!(rust, "macro_rules! isle_numerics_methods {{");
913 rust.indent_push();
914 fmtln!(rust, "() => {{");
915 rust.indent_push();
916
917 for ty in NumericType::all() {
918 for op in NumericOp::ops_for_type(&ty) {
919 let ty = ty.name();
920 let op_name = format!("{ty}_{}", op.name);
921 let partial = if op.partial { " partial" } else { "" };
922 let ret = op.ret;
923 fmtln!(isle, "(decl pure{partial} {op_name} (");
924 isle.indent(|isle| {
925 for (_arg_name, arg_ty) in op.args.iter() {
926 fmtln!(isle, "{arg_ty}");
927 }
928 });
929 fmtln!(isle, ") {ret})");
930 fmtln!(isle, "(extern constructor {op_name} {op_name})");
931
932 let ret = if op.partial {
933 Cow::from(format!("Option<{ret}>"))
934 } else {
935 Cow::from(ret)
936 };
937 let body = op.body;
938 fmtln!(rust, "#[inline]");
939 fmtln!(rust, "fn {op_name}(");
940 rust.indent(|rust| {
941 fmtln!(rust, "&mut self,");
942 for (arg_name, arg_ty) in op.args.iter() {
943 fmtln!(rust, "{arg_name}: {arg_ty},");
944 }
945 });
946 fmtln!(rust, ") -> {ret} {{");
947 rust.indent(|rust| {
948 fmtln!(rust, "{body}");
949 });
950 fmtln!(rust, "}}");
951
952 if op.etors {
964 debug_assert_eq!(op.args.len(), 1);
965 debug_assert_eq!(op.args[0].1, ty);
966 debug_assert_eq!(op.ret, "bool");
967 debug_assert!(op.name.starts_with("is_"));
968
969 let base_name = &op.name[3..];
971 debug_assert!(base_name.len() > 0);
972
973 fmtln!(isle, "(decl pure {ty}_matches_{base_name} (bool) {ty})");
974 fmtln!(
975 isle,
976 "(extern extractor {ty}_matches_{base_name} {ty}_matches_{base_name})"
977 );
978 fmtln!(rust, "#[inline]");
979 fmtln!(
980 rust,
981 "fn {ty}_matches_{base_name}(&mut self, a: {ty}) -> Option<bool> {{"
982 );
983 rust.indent(|rust| {
984 fmtln!(rust, "Some({body})");
985 });
986 fmtln!(rust, "}}");
987
988 fmtln!(isle, "(decl pure {ty}_extract_{base_name} ({ty}) {ty})");
989 fmtln!(
990 isle,
991 "(extractor ({ty}_extract_{base_name} x) (and ({ty}_matches_{base_name} true) x))"
992 );
993
994 fmtln!(isle, "(decl pure {ty}_when_{base_name} () {ty})");
995 fmtln!(
996 isle,
997 "(extractor ({ty}_when_{base_name}) ({ty}_matches_{base_name} true))"
998 );
999
1000 fmtln!(isle, "(decl pure {ty}_when_not_{base_name} () {ty})");
1001 fmtln!(
1002 isle,
1003 "(extractor ({ty}_when_not_{base_name}) ({ty}_matches_{base_name} false))"
1004 );
1005 }
1006
1007 isle.empty_line();
1008 rust.empty_line();
1009 }
1010 }
1011
1012 for from in NumericType::all() {
1032 for to in NumericType::all() {
1033 if from == to {
1034 continue;
1035 }
1036
1037 let from_name = from.name();
1038 let to_name = to.name();
1039
1040 let lossy = match (from.byte_width.cmp(&to.byte_width), from.signed, to.signed) {
1041 (Ordering::Less, true, true) | (Ordering::Less, false, false) => false,
1043 (Ordering::Less, false, true) => false,
1045 (Ordering::Less, true, false) => true,
1047 (Ordering::Equal, _, _) => {
1050 debug_assert_ne!(from.signed, to.signed);
1051 true
1052 }
1053 (Ordering::Greater, _, _) => true,
1055 };
1056
1057 let (ctor, partial, rust_ret) = if lossy {
1058 (
1059 "try_into",
1060 " partial",
1061 Cow::from(format!("Option<{to_name}>")),
1062 )
1063 } else {
1064 ("into", "", Cow::from(to_name))
1065 };
1066
1067 fmtln!(
1069 isle,
1070 "(decl pure{partial} {from_name}_{ctor}_{to_name} ({from_name}) {to_name})"
1071 );
1072 fmtln!(
1073 isle,
1074 "(extern constructor {from_name}_{ctor}_{to_name} {from_name}_{ctor}_{to_name})"
1075 );
1076 if !lossy {
1077 fmtln!(
1078 isle,
1079 "(convert {from_name} {to_name} {from_name}_{ctor}_{to_name})"
1080 );
1081 }
1082 fmtln!(rust, "#[inline]");
1083 fmtln!(
1084 rust,
1085 "fn {from_name}_{ctor}_{to_name}(&mut self, x: {from_name}) -> {rust_ret} {{"
1086 );
1087 rust.indent(|rust| {
1088 if lossy {
1089 fmtln!(rust, "{to_name}::try_from(x).ok()");
1090 } else {
1091 fmtln!(rust, "{to_name}::from(x)");
1092 }
1093 });
1094 fmtln!(rust, "}}");
1095
1096 if lossy {
1098 fmtln!(
1099 isle,
1100 "(decl pure {from_name}_unwrap_into_{to_name} ({from_name}) {to_name})"
1101 );
1102 fmtln!(
1103 isle,
1104 "(extern constructor {from_name}_unwrap_into_{to_name} {from_name}_unwrap_into_{to_name})"
1105 );
1106 fmtln!(rust, "#[inline]");
1107 fmtln!(
1108 rust,
1109 "fn {from_name}_unwrap_into_{to_name}(&mut self, x: {from_name}) -> {to_name} {{"
1110 );
1111 rust.indent(|rust| {
1112 fmtln!(rust, "{to_name}::try_from(x).unwrap()");
1113 });
1114 fmtln!(rust, "}}");
1115 }
1116
1117 if lossy && from.signed == to.signed {
1119 fmtln!(
1120 isle,
1121 "(decl pure {from_name}_truncate_into_{to_name} ({from_name}) {to_name})"
1122 );
1123 fmtln!(
1124 isle,
1125 "(extern constructor {from_name}_truncate_into_{to_name} {from_name}_truncate_into_{to_name})"
1126 );
1127 fmtln!(rust, "#[inline]");
1128 fmtln!(
1129 rust,
1130 "fn {from_name}_truncate_into_{to_name}(&mut self, x: {from_name}) -> {to_name} {{"
1131 );
1132 rust.indent(|rust| {
1133 fmtln!(rust, "x as {to_name}");
1134 });
1135 fmtln!(rust, "}}");
1136 }
1137
1138 if from.byte_width == to.byte_width {
1140 debug_assert_ne!(from.signed, to.signed);
1141 let cast_name = if to.signed {
1142 "cast_signed"
1143 } else {
1144 "cast_unsigned"
1145 };
1146 fmtln!(
1147 isle,
1148 "(decl pure {from_name}_{cast_name} ({from_name}) {to_name})"
1149 );
1150 fmtln!(
1151 isle,
1152 "(extern constructor {from_name}_{cast_name} {from_name}_{cast_name})"
1153 );
1154 fmtln!(rust, "#[inline]");
1155 fmtln!(
1156 rust,
1157 "fn {from_name}_{cast_name}(&mut self, x: {from_name}) -> {to_name} {{"
1158 );
1159 rust.indent(|rust| {
1160 fmtln!(rust, "x as {to_name}");
1163 });
1164 fmtln!(rust, "}}");
1165 }
1166
1167 fmtln!(
1169 isle,
1170 "(decl pure {to_name}_from_{from_name} ({to_name}) {from_name})"
1171 );
1172 fmtln!(
1173 isle,
1174 "(extern extractor {to_name}_from_{from_name} {from_name}_from_{to_name})"
1175 );
1176 fmtln!(rust, "#[inline]");
1177 fmtln!(
1178 rust,
1179 "fn {from_name}_from_{to_name}(&mut self, x: {from_name}) -> Option<{to_name}> {{"
1180 );
1181 rust.indent(|rust| {
1182 if lossy {
1183 fmtln!(rust, "x.try_into().ok()");
1184 } else {
1185 fmtln!(rust, "Some(x.into())");
1186 }
1187 });
1188 fmtln!(rust, "}}");
1189
1190 isle.empty_line();
1191 rust.empty_line();
1192 }
1193 }
1194
1195 rust.indent_pop();
1196 fmtln!(rust, "}}");
1197 rust.indent_pop();
1198 fmtln!(rust, "}}");
1199}
1200
1201pub(crate) fn generate(
1202 formats: &[Rc<InstructionFormat>],
1203 all_inst: &AllInstructions,
1204 isle_numerics_filename: &str,
1205 rust_numerics_filename: &str,
1206 isle_opt_filename: &str,
1207 isle_lower_filename: &str,
1208 isle_dir: &std::path::Path,
1209) -> Result<(), error::Error> {
1210 let mut isle_fmt = Formatter::new(Language::Isle);
1212 let mut rust_fmt = Formatter::new(Language::Rust);
1213 gen_numerics_isle(&mut isle_fmt, &mut rust_fmt);
1214 isle_fmt.write(isle_numerics_filename, isle_dir)?;
1215 rust_fmt.write(rust_numerics_filename, isle_dir)?;
1216
1217 let mut fmt = Formatter::new(Language::Isle);
1219 gen_opt_isle(&formats, all_inst, &mut fmt);
1220 fmt.write(isle_opt_filename, isle_dir)?;
1221
1222 let mut fmt = Formatter::new(Language::Isle);
1224 gen_lower_isle(&formats, all_inst, &mut fmt);
1225 fmt.write(isle_lower_filename, isle_dir)?;
1226
1227 Ok(())
1228}