1 use crate::cdsl::formats::InstructionFormat;
2 use crate::cdsl::instructions::AllInstructions;
3 use crate::error;
4 use cranelift_srcgen::{Formatter, Language, fmtln};
5 use std::{borrow::Cow, cmp::Ordering, rc::Rc};
6 
7 /// Which ISLE target are we generating code for?
8 #[derive(Clone, Copy, PartialEq, Eq)]
9 enum IsleTarget {
10     /// Generating code for instruction selection and lowering.
11     Lower,
12     /// Generating code for CLIF to CLIF optimizations.
13     Opt,
14 }
15 
16 fn 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     // Collect and deduplicate the immediate types from the instruction fields.
39     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     // Separate the `enum` immediates (e.g., `FloatCC`) from other kinds of
52     // immediates.
53     let (enums, others): (BTreeMap<_, _>, BTreeMap<_, _>) = immediate_types
54         .iter()
55         .partition(|(_, field)| field.enum_values().is_some());
56 
57     // Generate all the extern type declarations we need for the non-`enum`
58     // immediates.
59     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     // Generate the `enum` immediates, expanding all of the available variants
67     // into ISLE.
68     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     // Generate all of the value arrays we need for `InstructionData` as well as
75     // the constructors and extractors for them.
76     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     // Generate all of the block arrays we need for `InstructionData` as well as
111     // the constructors and extractors for them.
112     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     // Generate the extern type declaration for `Opcode`.
146     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     // Generate the extern type declaration for `InstructionData`.
162     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     // Generate the helper extractors for each opcode's full instruction.
206     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                 // The mid-end does not deal with instructions that have var-args right now.
219                 (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             // Value and varargs operands.
267             if inst.format.has_value_list {
268                 // The instruction format uses a value list, but the
269                 // instruction itself might have not only a `&[Value]`
270                 // varargs operand, but also one or more `Value` operands as
271                 // well. If this is the case, then we need to read them off
272                 // the front of the `ValueList`.
273                 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             // Immediates.
322             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             // Blocks.
333             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         // Generate a constructor if this is the mid-end prelude.
362         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                 // Handle values. Note that we skip generating
383                 // constructors for any instructions with variadic
384                 // value lists. This is fine for the mid-end because
385                 // in practice only calls and branches (for branch
386                 // args) use this functionality, and neither can
387                 // really be optimized or rewritten in the mid-end
388                 // (currently).
389                 //
390                 // As a consequence, we only have to handle the
391                 // one-`Value` case, in which the `Value` is directly
392                 // in the `InstructionData`, and the multiple-`Value`
393                 // case, in which the `Value`s are in a
394                 // statically-sized array (e.g. `[Value; 2]` for a
395                 // binary op).
396                 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                     // As above, get all bindings together, and pass
406                     // to a sub-term; here we use a constructor to
407                     // build the value array.
408                     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                 // Immediates (non-value args).
445                 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 
462 fn 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 
470 fn 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 
478 /// Generate an `enum` immediate in ISLE.
479 fn 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)]
499 struct NumericType {
500     signed: bool,
501     byte_width: u8,
502 }
503 
504 impl 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)]
525 struct NumericOp<'a> {
526     /// The name of this operation.
527     name: &'a str,
528     /// The return type of this operation.
529     ret: &'a str,
530     /// Whether this operation is partial.
531     partial: bool,
532     /// (name, type) pairs of arguments.
533     args: Rc<[(&'a str, &'a str)]>,
534     /// The source text for the constructor's body.
535     body: &'a str,
536     /// Whether extractors should be generated for this op.
537     ///
538     /// Must have `arity == 1`, `ret == bool`, and `name.starts_with("is_")`.
539     etors: bool,
540 }
541 
542 impl 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         // Operations that apply to both signed and unsigned numbers.
598         let ops = [
599             // Comparisons.
600             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             // Arithmetic operations.
631             //
632             // For each operation (e.g. addition) we have three variants:
633             //
634             // * partial ctor `checked_add`: no return value on overflow
635             // * ctor `wrapping_add`: wraps on overflow
636             // * ctor `add`: non-partial but panics at runtime on overflow
637             NumericOp {
638                 name: "checked_add",
639                 body: "a.checked_add(b)",
640                 ..partial_binop.clone()
641             },
642             NumericOp {
643                 name: "wrapping_add",
644                 body: "a.wrapping_add(b)",
645                 ..binop.clone()
646             },
647             NumericOp {
648                 name: "add",
649                 body: r#"a.checked_add(b).unwrap_or_else(|| panic!("addition overflow: {a} + {b}"))"#,
650                 ..binop.clone()
651             },
652             NumericOp {
653                 name: "checked_sub",
654                 body: "a.checked_sub(b)",
655                 ..partial_binop.clone()
656             },
657             NumericOp {
658                 name: "wrapping_sub",
659                 body: "a.wrapping_sub(b)",
660                 ..binop.clone()
661             },
662             NumericOp {
663                 name: "sub",
664                 body: r#"a.checked_sub(b).unwrap_or_else(|| panic!("subtraction overflow: {a} - {b}"))"#,
665                 ..binop.clone()
666             },
667             NumericOp {
668                 name: "checked_mul",
669                 body: "a.checked_mul(b)",
670                 ..partial_binop.clone()
671             },
672             NumericOp {
673                 name: "wrapping_mul",
674                 body: "a.wrapping_mul(b)",
675                 ..binop.clone()
676             },
677             NumericOp {
678                 name: "mul",
679                 body: r#"a.checked_mul(b).unwrap_or_else(|| panic!("multiplication overflow: {a} * {b}"))"#,
680                 ..binop.clone()
681             },
682             NumericOp {
683                 name: "checked_div",
684                 body: "a.checked_div(b)",
685                 ..partial_binop.clone()
686             },
687             NumericOp {
688                 name: "wrapping_div",
689                 body: "a.wrapping_div(b)",
690                 ..binop.clone()
691             },
692             NumericOp {
693                 name: "div",
694                 body: r#"a.checked_div(b).unwrap_or_else(|| panic!("div failure: {a} / {b}"))"#,
695                 ..binop.clone()
696             },
697             NumericOp {
698                 name: "checked_rem",
699                 body: "a.checked_rem(b)",
700                 ..partial_binop.clone()
701             },
702             NumericOp {
703                 name: "rem",
704                 body: r#"a.checked_rem(b).unwrap_or_else(|| panic!("rem failure: {a} % {b}"))"#,
705                 ..binop.clone()
706             },
707             // Bitwise operations.
708             //
709             // When applicable (e.g. shifts) we have checked, wrapping, and
710             // unwrapping variants, similar to arithmetic operations.
711             NumericOp {
712                 name: "and",
713                 body: "a & b",
714                 ..binop.clone()
715             },
716             NumericOp {
717                 name: "or",
718                 body: "a | b",
719                 ..binop.clone()
720             },
721             NumericOp {
722                 name: "xor",
723                 body: "a ^ b",
724                 ..binop.clone()
725             },
726             NumericOp {
727                 name: "not",
728                 body: "!a",
729                 ..unop.clone()
730             },
731             NumericOp {
732                 name: "checked_shl",
733                 body: "a.checked_shl(b)",
734                 ..partial_shift.clone()
735             },
736             NumericOp {
737                 name: "wrapping_shl",
738                 body: "a.wrapping_shl(b)",
739                 ..shift.clone()
740             },
741             NumericOp {
742                 name: "shl",
743                 body: r#"a.checked_shl(b).unwrap_or_else(|| panic!("shl overflow: {a} << {b}"))"#,
744                 ..shift.clone()
745             },
746             NumericOp {
747                 name: "checked_shr",
748                 body: "a.checked_shr(b)",
749                 ..partial_shift.clone()
750             },
751             NumericOp {
752                 name: "wrapping_shr",
753                 body: "a.wrapping_shr(b)",
754                 ..shift.clone()
755             },
756             NumericOp {
757                 name: "shr",
758                 body: r#"a.checked_shr(b).unwrap_or_else(|| panic!("shr overflow: {a} >> {b}"))"#,
759                 ..shift.clone()
760             },
761             // Predicates.
762             //
763             // We generate both pure constructors and a variety of extractors
764             // for these. See the relevant comments in `gen_numerics_isle` about
765             // the extractors.
766             NumericOp {
767                 name: "is_zero",
768                 body: "a == 0",
769                 ..predicate.clone()
770             },
771             NumericOp {
772                 name: "is_non_zero",
773                 body: "a != 0",
774                 ..predicate.clone()
775             },
776             NumericOp {
777                 name: "is_odd",
778                 body: "a & 1 == 1",
779                 ..predicate.clone()
780             },
781             NumericOp {
782                 name: "is_even",
783                 body: "a & 1 == 0",
784                 ..predicate.clone()
785             },
786             // Miscellaneous unary operations.
787             NumericOp {
788                 name: "checked_ilog2",
789                 body: "a.checked_ilog2()",
790                 ret: "u32",
791                 ..partial_unop.clone()
792             },
793             NumericOp {
794                 name: "ilog2",
795                 body: r#"a.checked_ilog2().unwrap_or_else(|| panic!("ilog2 overflow: {a}"))"#,
796                 ret: "u32",
797                 ..unop.clone()
798             },
799             NumericOp {
800                 name: "trailing_zeros",
801                 body: "a.trailing_zeros()",
802                 ret: "u32",
803                 ..unop.clone()
804             },
805             NumericOp {
806                 name: "trailing_ones",
807                 body: "a.trailing_ones()",
808                 ret: "u32",
809                 ..unop.clone()
810             },
811             NumericOp {
812                 name: "leading_zeros",
813                 body: "a.leading_zeros()",
814                 ret: "u32",
815                 ..unop.clone()
816             },
817             NumericOp {
818                 name: "leading_ones",
819                 body: "a.leading_ones()",
820                 ret: "u32",
821                 ..unop.clone()
822             },
823         ];
824 
825         // Operations that apply only to signed numbers.
826         let signed_ops = [
827             NumericOp {
828                 name: "checked_neg",
829                 body: "a.checked_neg()",
830                 ..partial_unop.clone()
831             },
832             NumericOp {
833                 name: "wrapping_neg",
834                 body: "a.wrapping_neg()",
835                 ..unop.clone()
836             },
837             NumericOp {
838                 name: "neg",
839                 body: r#"a.checked_neg().unwrap_or_else(|| panic!("negation overflow: {a}"))"#,
840                 ..unop.clone()
841             },
842         ];
843 
844         // Operations that apply only to unsigned numbers.
845         let unsigned_ops = [NumericOp {
846             name: "is_power_of_two",
847             body: "a.is_power_of_two()",
848             ..predicate.clone()
849         }];
850 
851         struct IterIf<I> {
852             condition: bool,
853             iter: I,
854         }
855 
856         impl<I: Iterator> Iterator for IterIf<I> {
857             type Item = I::Item;
858 
859             fn next(&mut self) -> Option<Self::Item> {
860                 if self.condition {
861                     self.iter.next()
862                 } else {
863                     None
864                 }
865             }
866         }
867 
868         ops.into_iter()
869             .chain(IterIf {
870                 condition: ty.signed,
871                 iter: signed_ops.into_iter(),
872             })
873             .chain(IterIf {
874                 condition: !ty.signed,
875                 iter: unsigned_ops.into_iter(),
876             })
877     }
878 }
879 
880 fn gen_numerics_isle(isle: &mut Formatter, rust: &mut Formatter) {
881     fmtln!(rust, "#[macro_export]");
882     fmtln!(rust, "#[doc(hidden)]");
883     fmtln!(rust, "macro_rules! isle_numerics_methods {{");
884     rust.indent_push();
885     fmtln!(rust, "() => {{");
886     rust.indent_push();
887 
888     for ty in NumericType::all() {
889         for op in NumericOp::ops_for_type(&ty) {
890             let ty = ty.name();
891             let op_name = format!("{ty}_{}", op.name);
892             let partial = if op.partial { " partial" } else { "" };
893             let ret = op.ret;
894             fmtln!(isle, "(decl pure{partial} {op_name} (");
895             isle.indent(|isle| {
896                 for (_arg_name, arg_ty) in op.args.iter() {
897                     fmtln!(isle, "{arg_ty}");
898                 }
899             });
900             fmtln!(isle, ") {ret})");
901             fmtln!(isle, "(extern constructor {op_name} {op_name})");
902 
903             let ret = if op.partial {
904                 Cow::from(format!("Option<{ret}>"))
905             } else {
906                 Cow::from(ret)
907             };
908             let body = op.body;
909             fmtln!(rust, "#[inline]");
910             fmtln!(rust, "fn {op_name}(");
911             rust.indent(|rust| {
912                 fmtln!(rust, "&mut self,");
913                 for (arg_name, arg_ty) in op.args.iter() {
914                     fmtln!(rust, "{arg_name}: {arg_ty},");
915                 }
916             });
917             fmtln!(rust, ") -> {ret} {{");
918             rust.indent(|rust| {
919                 fmtln!(rust, "{body}");
920             });
921             fmtln!(rust, "}}");
922 
923             // When generating extractors for a `{ty}_is_foo` predicate,
924             // we generate the following:
925             //
926             // * bool <- ty etor: `{ty}_matches_foo`
927             // * ty <- ty etor: `{ty}_extract_foo`
928             // * () <- ty etor: `{ty}_when_foo`
929             // * () <- ty etor: `{ty}_when_not_foo`
930             //
931             // The last three are defined as local extractors that are
932             // implemented in terms of the first. This gives the ISLE compiler
933             // visibility into the extractors' overlapping-ness.
934             if op.etors {
935                 debug_assert_eq!(op.args.len(), 1);
936                 debug_assert_eq!(op.args[0].1, ty);
937                 debug_assert_eq!(op.ret, "bool");
938                 debug_assert!(op.name.starts_with("is_"));
939 
940                 // Cut of the `is_` prefix.
941                 let base_name = &op.name[3..];
942                 debug_assert!(base_name.len() > 0);
943 
944                 fmtln!(isle, "(decl pure {ty}_matches_{base_name} (bool) {ty})");
945                 fmtln!(
946                     isle,
947                     "(extern extractor {ty}_matches_{base_name} {ty}_matches_{base_name})"
948                 );
949                 fmtln!(rust, "#[inline]");
950                 fmtln!(
951                     rust,
952                     "fn {ty}_matches_{base_name}(&mut self, a: {ty}) -> Option<bool> {{"
953                 );
954                 rust.indent(|rust| {
955                     fmtln!(rust, "Some({body})");
956                 });
957                 fmtln!(rust, "}}");
958 
959                 fmtln!(isle, "(decl pure {ty}_extract_{base_name} ({ty}) {ty})");
960                 fmtln!(
961                     isle,
962                     "(extractor ({ty}_extract_{base_name} x) (and ({ty}_matches_{base_name} true) x))"
963                 );
964 
965                 fmtln!(isle, "(decl pure {ty}_when_{base_name} () {ty})");
966                 fmtln!(
967                     isle,
968                     "(extractor ({ty}_when_{base_name}) ({ty}_matches_{base_name} true))"
969                 );
970 
971                 fmtln!(isle, "(decl pure {ty}_when_not_{base_name} () {ty})");
972                 fmtln!(
973                     isle,
974                     "(extractor ({ty}_when_not_{base_name}) ({ty}_matches_{base_name} false))"
975                 );
976             }
977 
978             isle.empty_line();
979             rust.empty_line();
980         }
981     }
982 
983     // Numeric type conversions.
984     //
985     // Naming and conventions:
986     //
987     // * Constructors:
988     //   * "<from>_into_<to>" for lossless, infallible conversion
989     //   * "<from>_try_into_<to>" for lossless, fallible conversions (exposed as
990     //     partial constructors)
991     //   * "<from>_unwrap_into_<to>" for lossless, fallible conversions that will
992     //     panic at runtime if the conversion would be lossy
993     //   * "<from>_truncate_into_<to>" for lossy, infallible conversions that
994     //     ignore upper bits
995     //   * "<from>_cast_[un]signed" for signed-to-unsigned (and vice versa)
996     //     reinterpretation
997     // * Extractors:
998     //   * "<to>_from_<from>" for both fallible and infallible extractors
999     //   * No unwrapping extractors
1000     //   * No truncating extractors
1001     //   * No signed-to-unsigned reinterpreting extractors
1002     for from in NumericType::all() {
1003         for to in NumericType::all() {
1004             if from == to {
1005                 continue;
1006             }
1007 
1008             let from_name = from.name();
1009             let to_name = to.name();
1010 
1011             let lossy = match (from.byte_width.cmp(&to.byte_width), from.signed, to.signed) {
1012                 // Widening with the same signedness is lossless.
1013                 (Ordering::Less, true, true) | (Ordering::Less, false, false) => false,
1014                 // Widening from unsigned to signed is lossless.
1015                 (Ordering::Less, false, true) => false,
1016                 // Widening from signed to unsigned is lossy.
1017                 (Ordering::Less, true, false) => true,
1018                 // Same width means we must be changing sign, since we skip
1019                 // `from == to`, and this is lossy.
1020                 (Ordering::Equal, _, _) => {
1021                     debug_assert_ne!(from.signed, to.signed);
1022                     true
1023                 }
1024                 // Narrowing is always lossy.
1025                 (Ordering::Greater, _, _) => true,
1026             };
1027 
1028             let (ctor, partial, rust_ret) = if lossy {
1029                 (
1030                     "try_into",
1031                     " partial",
1032                     Cow::from(format!("Option<{to_name}>")),
1033                 )
1034             } else {
1035                 ("into", "", Cow::from(to_name))
1036             };
1037 
1038             // Constructor.
1039             fmtln!(
1040                 isle,
1041                 "(decl pure{partial} {from_name}_{ctor}_{to_name} ({from_name}) {to_name})"
1042             );
1043             fmtln!(
1044                 isle,
1045                 "(extern constructor {from_name}_{ctor}_{to_name} {from_name}_{ctor}_{to_name})"
1046             );
1047             if !lossy {
1048                 fmtln!(
1049                     isle,
1050                     "(convert {from_name} {to_name} {from_name}_{ctor}_{to_name})"
1051                 );
1052             }
1053             fmtln!(rust, "#[inline]");
1054             fmtln!(
1055                 rust,
1056                 "fn {from_name}_{ctor}_{to_name}(&mut self, x: {from_name}) -> {rust_ret} {{"
1057             );
1058             rust.indent(|rust| {
1059                 if lossy {
1060                     fmtln!(rust, "{to_name}::try_from(x).ok()");
1061                 } else {
1062                     fmtln!(rust, "{to_name}::from(x)");
1063                 }
1064             });
1065             fmtln!(rust, "}}");
1066 
1067             // Unwrapping constructor.
1068             if lossy {
1069                 fmtln!(
1070                     isle,
1071                     "(decl pure {from_name}_unwrap_into_{to_name} ({from_name}) {to_name})"
1072                 );
1073                 fmtln!(
1074                     isle,
1075                     "(extern constructor {from_name}_unwrap_into_{to_name} {from_name}_unwrap_into_{to_name})"
1076                 );
1077                 fmtln!(rust, "#[inline]");
1078                 fmtln!(
1079                     rust,
1080                     "fn {from_name}_unwrap_into_{to_name}(&mut self, x: {from_name}) -> {to_name} {{"
1081                 );
1082                 rust.indent(|rust| {
1083                     fmtln!(rust, "{to_name}::try_from(x).unwrap()");
1084                 });
1085                 fmtln!(rust, "}}");
1086             }
1087 
1088             // Truncating constructor.
1089             if lossy && from.signed == to.signed {
1090                 fmtln!(
1091                     isle,
1092                     "(decl pure {from_name}_truncate_into_{to_name} ({from_name}) {to_name})"
1093                 );
1094                 fmtln!(
1095                     isle,
1096                     "(extern constructor {from_name}_truncate_into_{to_name} {from_name}_truncate_into_{to_name})"
1097                 );
1098                 fmtln!(rust, "#[inline]");
1099                 fmtln!(
1100                     rust,
1101                     "fn {from_name}_truncate_into_{to_name}(&mut self, x: {from_name}) -> {to_name} {{"
1102                 );
1103                 rust.indent(|rust| {
1104                     fmtln!(rust, "x as {to_name}");
1105                 });
1106                 fmtln!(rust, "}}");
1107             }
1108 
1109             // Signed-to-unsigned reinterpreting constructor.
1110             if from.byte_width == to.byte_width {
1111                 debug_assert_ne!(from.signed, to.signed);
1112                 let cast_name = if to.signed {
1113                     "cast_signed"
1114                 } else {
1115                     "cast_unsigned"
1116                 };
1117                 fmtln!(
1118                     isle,
1119                     "(decl pure {from_name}_{cast_name} ({from_name}) {to_name})"
1120                 );
1121                 fmtln!(
1122                     isle,
1123                     "(extern constructor {from_name}_{cast_name} {from_name}_{cast_name})"
1124                 );
1125                 fmtln!(rust, "#[inline]");
1126                 fmtln!(
1127                     rust,
1128                     "fn {from_name}_{cast_name}(&mut self, x: {from_name}) -> {to_name} {{"
1129                 );
1130                 rust.indent(|rust| {
1131                     // TODO: Once our MSRV is >= 1.87, we should use
1132                     // `x.cast_[un]signed()` here.
1133                     fmtln!(rust, "x as {to_name}");
1134                 });
1135                 fmtln!(rust, "}}");
1136             }
1137 
1138             // Extractor.
1139             fmtln!(
1140                 isle,
1141                 "(decl pure {to_name}_from_{from_name} ({to_name}) {from_name})"
1142             );
1143             fmtln!(
1144                 isle,
1145                 "(extern extractor {to_name}_from_{from_name} {from_name}_from_{to_name})"
1146             );
1147             fmtln!(rust, "#[inline]");
1148             fmtln!(
1149                 rust,
1150                 "fn {from_name}_from_{to_name}(&mut self, x: {from_name}) -> Option<{to_name}> {{"
1151             );
1152             rust.indent(|rust| {
1153                 if lossy {
1154                     fmtln!(rust, "x.try_into().ok()");
1155                 } else {
1156                     fmtln!(rust, "Some(x.into())");
1157                 }
1158             });
1159             fmtln!(rust, "}}");
1160 
1161             isle.empty_line();
1162             rust.empty_line();
1163         }
1164     }
1165 
1166     rust.indent_pop();
1167     fmtln!(rust, "}}");
1168     rust.indent_pop();
1169     fmtln!(rust, "}}");
1170 }
1171 
1172 pub(crate) fn generate(
1173     formats: &[Rc<InstructionFormat>],
1174     all_inst: &AllInstructions,
1175     isle_numerics_filename: &str,
1176     rust_numerics_filename: &str,
1177     isle_opt_filename: &str,
1178     isle_lower_filename: &str,
1179     isle_dir: &std::path::Path,
1180 ) -> Result<(), error::Error> {
1181     // Numerics
1182     let mut isle_fmt = Formatter::new(Language::Isle);
1183     let mut rust_fmt = Formatter::new(Language::Rust);
1184     gen_numerics_isle(&mut isle_fmt, &mut rust_fmt);
1185     isle_fmt.write(isle_numerics_filename, isle_dir)?;
1186     rust_fmt.write(rust_numerics_filename, isle_dir)?;
1187 
1188     // ISLE DSL: mid-end ("opt") generated bindings.
1189     let mut fmt = Formatter::new(Language::Isle);
1190     gen_opt_isle(&formats, all_inst, &mut fmt);
1191     fmt.write(isle_opt_filename, isle_dir)?;
1192 
1193     // ISLE DSL: lowering generated bindings.
1194     let mut fmt = Formatter::new(Language::Isle);
1195     gen_lower_isle(&formats, all_inst, &mut fmt);
1196     fmt.write(isle_lower_filename, isle_dir)?;
1197 
1198     Ok(())
1199 }
1200