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 
gen_common_isle( formats: &[Rc<InstructionFormat>], instructions: &AllInstructions, fmt: &mut Formatter, isle_target: IsleTarget, )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     // Raw block entities.
146     fmtln!(fmt, "(type Block extern (enum))");
147     fmt.empty_line();
148 
149     // Generate the extern type declaration for `Opcode`.
150     fmt.line(";;;; `Opcode` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;");
151     fmt.empty_line();
152     fmt.line("(type Opcode extern");
153     fmt.indent(|fmt| {
154         fmt.line("(enum");
155         fmt.indent(|fmt| {
156             for inst in instructions {
157                 fmtln!(fmt, "{}", inst.camel_name);
158             }
159         });
160         fmt.line(")");
161     });
162     fmt.line(")");
163     fmt.empty_line();
164 
165     // Generate the extern type declaration for `InstructionData`.
166     fmtln!(
167         fmt,
168         ";;;; `InstructionData` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;",
169     );
170     fmt.empty_line();
171     fmtln!(fmt, "(type InstructionData extern");
172     fmt.indent(|fmt| {
173         fmt.line("(enum");
174         fmt.indent(|fmt| {
175             for format in formats {
176                 let mut s = format!("({} (opcode Opcode)", format.name);
177                 if format.has_value_list {
178                     s.push_str(" (args ValueList)");
179                 } else if format.num_value_operands == 1 {
180                     s.push_str(" (arg Value)");
181                 } else if format.num_value_operands > 1 {
182                     write!(&mut s, " (args ValueArray{})", format.num_value_operands).unwrap();
183                 }
184 
185                 match format.num_block_operands {
186                     0 => (),
187                     1 => write!(&mut s, " (destination BlockCall)").unwrap(),
188                     n => write!(&mut s, " (blocks BlockArray{n})").unwrap(),
189                 }
190 
191                 match format.num_raw_block_operands {
192                     0 => (),
193                     1 => write!(&mut s, "(block Block)").unwrap(),
194                     _ => panic!("Too many raw block arguments"),
195                 }
196 
197                 for field in &format.imm_fields {
198                     write!(
199                         &mut s,
200                         " ({} {})",
201                         field.member,
202                         field.kind.rust_type.rsplit("::").next().unwrap()
203                     )
204                     .unwrap();
205                 }
206                 s.push(')');
207                 fmt.line(&s);
208             }
209         });
210         fmt.line(")");
211     });
212     fmt.line(")");
213     fmt.empty_line();
214 
215     // Generate the helper extractors for each opcode's full instruction.
216     fmtln!(
217         fmt,
218         ";;;; Extracting Opcode, Operands, and Immediates from `InstructionData` ;;;;;;;;",
219     );
220     fmt.empty_line();
221     for inst in instructions {
222         let results_len = inst.value_results.len();
223         let is_var_args = inst.format.has_value_list;
224         let has_side_effects = inst.can_trap || inst.other_side_effects;
225 
226         let (ret_ty, ty_in_decl, make_inst_ctor, inst_data_etor) =
227             match (isle_target, is_var_args, results_len, has_side_effects) {
228                 // The mid-end does not deal with instructions that have var-args right now.
229                 (IsleTarget::Opt, true, _, _) => continue,
230 
231                 (IsleTarget::Opt, _, 1, false) => ("Value", true, "make_inst", "inst_data_value"),
232                 (IsleTarget::Opt, _, _, _) => ("Inst", false, "make_skeleton_inst", "inst_data"),
233                 (IsleTarget::Lower, false, r, _) if r >= 1 => {
234                     ("Inst", true, "make_inst", "inst_data_value")
235                 }
236                 (IsleTarget::Lower, _, _, _) => ("Inst", false, "make_inst", "inst_data_value"),
237             };
238 
239         fmtln!(
240             fmt,
241             "(decl {} ({}{}) {})",
242             inst.name,
243             if ty_in_decl { "Type " } else { "" },
244             inst.operands_in
245                 .iter()
246                 .map(|o| {
247                     let ty = o.kind.rust_type;
248                     if ty == "&[Value]" {
249                         "ValueSlice"
250                     } else {
251                         ty.rsplit("::").next().unwrap()
252                     }
253                 })
254                 .collect::<Vec<_>>()
255                 .join(" "),
256             ret_ty
257         );
258         fmtln!(fmt, "(extractor");
259         fmt.indent(|fmt| {
260             fmtln!(
261                 fmt,
262                 "({} {}{})",
263                 inst.name,
264                 if ty_in_decl { "ty " } else { "" },
265                 inst.operands_in
266                     .iter()
267                     .map(|o| { o.name })
268                     .collect::<Vec<_>>()
269                     .join(" ")
270             );
271 
272             let mut s = format!(
273                 "({inst_data_etor} {}(InstructionData.{} (Opcode.{})",
274                 if ty_in_decl {
275                     "ty "
276                 } else if isle_target == IsleTarget::Lower {
277                     "_ "
278                 } else {
279                     ""
280                 },
281                 inst.format.name,
282                 inst.camel_name
283             );
284 
285             // Value and varargs operands.
286             if inst.format.has_value_list {
287                 // The instruction format uses a value list, but the
288                 // instruction itself might have not only a `&[Value]`
289                 // varargs operand, but also one or more `Value` operands as
290                 // well. If this is the case, then we need to read them off
291                 // the front of the `ValueList`.
292                 let values: Vec<_> = inst
293                     .operands_in
294                     .iter()
295                     .filter(|o| o.is_value())
296                     .map(|o| o.name)
297                     .collect();
298                 let varargs = inst
299                     .operands_in
300                     .iter()
301                     .find(|o| o.is_varargs())
302                     .unwrap()
303                     .name;
304                 if values.is_empty() {
305                     write!(&mut s, " (value_list_slice {varargs})").unwrap();
306                 } else {
307                     write!(
308                         &mut s,
309                         " (unwrap_head_value_list_{} {} {})",
310                         values.len(),
311                         values.join(" "),
312                         varargs
313                     )
314                     .unwrap();
315                 }
316             } else if inst.format.num_value_operands == 1 {
317                 write!(
318                     &mut s,
319                     " {}",
320                     inst.operands_in.iter().find(|o| o.is_value()).unwrap().name
321                 )
322                 .unwrap();
323             } else if inst.format.num_value_operands > 1 {
324                 let values = inst
325                     .operands_in
326                     .iter()
327                     .filter(|o| o.is_value())
328                     .map(|o| o.name)
329                     .collect::<Vec<_>>();
330                 assert_eq!(values.len(), inst.format.num_value_operands);
331                 let values = values.join(" ");
332                 write!(
333                     &mut s,
334                     " (value_array_{} {})",
335                     inst.format.num_value_operands, values,
336                 )
337                 .unwrap();
338             }
339 
340             // Immediates.
341             let imm_operands: Vec<_> = inst
342                 .operands_in
343                 .iter()
344                 .filter(|o| {
345                     !o.is_value() && !o.is_varargs() && !o.kind.is_block() && !o.kind.is_raw_block()
346                 })
347                 .collect();
348             assert_eq!(imm_operands.len(), inst.format.imm_fields.len(),);
349             for op in imm_operands {
350                 write!(&mut s, " {}", op.name).unwrap();
351             }
352 
353             // Blocks.
354             let block_operands: Vec<_> = inst
355                 .operands_in
356                 .iter()
357                 .filter(|o| o.kind.is_block())
358                 .collect();
359             assert_eq!(block_operands.len(), inst.format.num_block_operands);
360             assert!(block_operands.len() <= 2);
361 
362             if !block_operands.is_empty() {
363                 if block_operands.len() == 1 {
364                     write!(&mut s, " {}", block_operands[0].name).unwrap();
365                 } else {
366                     let blocks: Vec<_> = block_operands.iter().map(|o| o.name).collect();
367                     let blocks = blocks.join(" ");
368                     write!(
369                         &mut s,
370                         " (block_array_{} {})",
371                         inst.format.num_block_operands, blocks,
372                     )
373                     .unwrap();
374                 }
375             }
376 
377             // Raw blocks.
378             match inst.format.num_raw_block_operands {
379                 0 => {}
380                 1 => {
381                     write!(&mut s, " block").unwrap();
382                 }
383                 _ => panic!("Too many raw block arguments"),
384             }
385 
386             s.push_str("))");
387             fmt.line(&s);
388         });
389         fmt.line(")");
390 
391         // Generate a constructor if this is the mid-end prelude.
392         if isle_target == IsleTarget::Opt {
393             fmtln!(
394                 fmt,
395                 "(rule ({}{} {})",
396                 inst.name,
397                 if ty_in_decl { " ty" } else { "" },
398                 inst.operands_in
399                     .iter()
400                     .map(|o| o.name)
401                     .collect::<Vec<_>>()
402                     .join(" ")
403             );
404             fmt.indent(|fmt| {
405                 let mut s = format!(
406                     "({make_inst_ctor}{} (InstructionData.{} (Opcode.{})",
407                     if ty_in_decl { " ty" } else { "" },
408                     inst.format.name,
409                     inst.camel_name
410                 );
411 
412                 // Handle values. Note that we skip generating
413                 // constructors for any instructions with variadic
414                 // value lists. This is fine for the mid-end because
415                 // in practice only calls and branches (for branch
416                 // args) use this functionality, and neither can
417                 // really be optimized or rewritten in the mid-end
418                 // (currently).
419                 //
420                 // As a consequence, we only have to handle the
421                 // one-`Value` case, in which the `Value` is directly
422                 // in the `InstructionData`, and the multiple-`Value`
423                 // case, in which the `Value`s are in a
424                 // statically-sized array (e.g. `[Value; 2]` for a
425                 // binary op).
426                 assert!(!inst.format.has_value_list);
427                 if inst.format.num_value_operands == 1 {
428                     write!(
429                         &mut s,
430                         " {}",
431                         inst.operands_in.iter().find(|o| o.is_value()).unwrap().name
432                     )
433                     .unwrap();
434                 } else if inst.format.num_value_operands > 1 {
435                     // As above, get all bindings together, and pass
436                     // to a sub-term; here we use a constructor to
437                     // build the value array.
438                     let values = inst
439                         .operands_in
440                         .iter()
441                         .filter(|o| o.is_value())
442                         .map(|o| o.name)
443                         .collect::<Vec<_>>();
444                     assert_eq!(values.len(), inst.format.num_value_operands);
445                     let values = values.join(" ");
446                     write!(
447                         &mut s,
448                         " (value_array_{}_ctor {})",
449                         inst.format.num_value_operands, values
450                     )
451                     .unwrap();
452                 }
453 
454                 if inst.format.num_block_operands > 0 {
455                     let blocks: Vec<_> = inst
456                         .operands_in
457                         .iter()
458                         .filter(|o| o.kind.is_block())
459                         .map(|o| o.name)
460                         .collect();
461                     if inst.format.num_block_operands == 1 {
462                         write!(&mut s, " {}", blocks.first().unwrap(),).unwrap();
463                     } else {
464                         write!(
465                             &mut s,
466                             " (block_array_{} {})",
467                             inst.format.num_block_operands,
468                             blocks.join(" ")
469                         )
470                         .unwrap();
471                     }
472                 }
473 
474                 match inst.format.num_raw_block_operands {
475                     0 => {}
476                     1 => {
477                         write!(&mut s, " block").unwrap();
478                     }
479                     _ => panic!("Too many raw block arguments"),
480                 }
481 
482                 // Immediates (non-value args).
483                 for o in inst.operands_in.iter().filter(|o| {
484                     !o.is_value() && !o.is_varargs() && !o.kind.is_block() && !o.kind.is_raw_block()
485                 }) {
486                     write!(&mut s, " {}", o.name).unwrap();
487                 }
488                 s.push_str("))");
489                 fmt.line(&s);
490             });
491             fmt.line(")");
492         }
493 
494         fmt.empty_line();
495     }
496 }
497 
gen_opt_isle( formats: &[Rc<InstructionFormat>], instructions: &AllInstructions, fmt: &mut Formatter, )498 fn gen_opt_isle(
499     formats: &[Rc<InstructionFormat>],
500     instructions: &AllInstructions,
501     fmt: &mut Formatter,
502 ) {
503     gen_common_isle(formats, instructions, fmt, IsleTarget::Opt);
504 }
505 
gen_lower_isle( formats: &[Rc<InstructionFormat>], instructions: &AllInstructions, fmt: &mut Formatter, )506 fn gen_lower_isle(
507     formats: &[Rc<InstructionFormat>],
508     instructions: &AllInstructions,
509     fmt: &mut Formatter,
510 ) {
511     gen_common_isle(formats, instructions, fmt, IsleTarget::Lower);
512 }
513 
514 /// Generate an `enum` immediate in ISLE.
gen_isle_enum(name: &str, mut variants: Vec<&str>, fmt: &mut Formatter)515 fn gen_isle_enum(name: &str, mut variants: Vec<&str>, fmt: &mut Formatter) {
516     variants.sort();
517     let prefix = format!(";;;; Enumerated Immediate: {name} ");
518     fmtln!(fmt, "{:;<80}", prefix);
519     fmt.empty_line();
520     fmtln!(fmt, "(type {} extern", name);
521     fmt.indent(|fmt| {
522         fmt.line("(enum");
523         fmt.indent(|fmt| {
524             for variant in variants {
525                 fmtln!(fmt, "{}", variant);
526             }
527         });
528         fmt.line(")");
529     });
530     fmt.line(")");
531     fmt.empty_line();
532 }
533 
534 #[derive(Clone, Copy, PartialEq, Eq)]
535 struct NumericType {
536     signed: bool,
537     byte_width: u8,
538 }
539 
540 impl NumericType {
all() -> impl Iterator<Item = NumericType>541     fn all() -> impl Iterator<Item = NumericType> {
542         [1, 2, 4, 8, 16].into_iter().flat_map(|byte_width| {
543             [true, false]
544                 .into_iter()
545                 .map(move |signed| NumericType { signed, byte_width })
546         })
547     }
548 
name(&self) -> &'static str549     fn name(&self) -> &'static str {
550         let idx = self.byte_width.ilog2();
551         let idx = usize::try_from(idx).unwrap();
552         if self.signed {
553             ["i8", "i16", "i32", "i64", "i128"][idx]
554         } else {
555             ["u8", "u16", "u32", "u64", "u128"][idx]
556         }
557     }
558 }
559 
560 #[derive(Clone, Default, PartialEq, Eq)]
561 struct NumericOp<'a> {
562     /// The name of this operation.
563     name: &'a str,
564     /// The return type of this operation.
565     ret: &'a str,
566     /// Whether this operation is partial.
567     partial: bool,
568     /// (name, type) pairs of arguments.
569     args: Rc<[(&'a str, &'a str)]>,
570     /// The source text for the constructor's body.
571     body: &'a str,
572     /// Whether extractors should be generated for this op.
573     ///
574     /// Must have `arity == 1`, `ret == bool`, and `name.starts_with("is_")`.
575     etors: bool,
576 }
577 
578 impl NumericOp<'_> {
ops_for_type(ty: &NumericType) -> impl Iterator<Item = NumericOp<'_>>579     fn ops_for_type(ty: &NumericType) -> impl Iterator<Item = NumericOp<'_>> {
580         let arity1 = NumericOp {
581             args: [("a", ty.name())].into(),
582             ..NumericOp::default()
583         };
584 
585         let arity2 = NumericOp {
586             args: [("a", ty.name()), ("b", ty.name())].into(),
587             ..NumericOp::default()
588         };
589 
590         let comparison = NumericOp {
591             ret: "bool",
592             ..arity2.clone()
593         };
594 
595         let predicate = NumericOp {
596             ret: "bool",
597             etors: true,
598             ..arity1.clone()
599         };
600 
601         let binop = NumericOp {
602             ret: ty.name(),
603             ..arity2.clone()
604         };
605 
606         let partial_binop = NumericOp {
607             ret: ty.name(),
608             partial: true,
609             ..binop.clone()
610         };
611 
612         let unop = NumericOp {
613             ret: ty.name(),
614             ..arity1.clone()
615         };
616 
617         let partial_unop = NumericOp {
618             ret: ty.name(),
619             partial: true,
620             ..unop.clone()
621         };
622 
623         let shift = NumericOp {
624             args: [("a", ty.name()), ("b", "u32")].into(),
625             ..binop.clone()
626         };
627 
628         let partial_shift = NumericOp {
629             args: [("a", ty.name()), ("b", "u32")].into(),
630             ..partial_binop.clone()
631         };
632 
633         // Operations that apply to both signed and unsigned numbers.
634         let ops = [
635             // Comparisons.
636             NumericOp {
637                 name: "eq",
638                 body: "a == b",
639                 ..comparison.clone()
640             },
641             NumericOp {
642                 name: "ne",
643                 body: "a != b",
644                 ..comparison.clone()
645             },
646             NumericOp {
647                 name: "lt",
648                 body: "a < b",
649                 ..comparison.clone()
650             },
651             NumericOp {
652                 name: "lt_eq",
653                 body: "a <= b",
654                 ..comparison.clone()
655             },
656             NumericOp {
657                 name: "gt",
658                 body: "a > b",
659                 ..comparison.clone()
660             },
661             NumericOp {
662                 name: "gt_eq",
663                 body: "a >= b",
664                 ..comparison.clone()
665             },
666             // Arithmetic operations.
667             //
668             // For each operation (e.g. addition) we have three variants:
669             //
670             // * partial ctor `checked_add`: no return value on overflow
671             // * ctor `wrapping_add`: wraps on overflow
672             // * ctor `add`: non-partial but panics at runtime on overflow
673             NumericOp {
674                 name: "checked_add",
675                 body: "a.checked_add(b)",
676                 ..partial_binop.clone()
677             },
678             NumericOp {
679                 name: "wrapping_add",
680                 body: "a.wrapping_add(b)",
681                 ..binop.clone()
682             },
683             NumericOp {
684                 name: "add",
685                 body: r#"a.checked_add(b).unwrap_or_else(|| panic!("addition overflow: {a} + {b}"))"#,
686                 ..binop.clone()
687             },
688             NumericOp {
689                 name: "checked_sub",
690                 body: "a.checked_sub(b)",
691                 ..partial_binop.clone()
692             },
693             NumericOp {
694                 name: "wrapping_sub",
695                 body: "a.wrapping_sub(b)",
696                 ..binop.clone()
697             },
698             NumericOp {
699                 name: "sub",
700                 body: r#"a.checked_sub(b).unwrap_or_else(|| panic!("subtraction overflow: {a} - {b}"))"#,
701                 ..binop.clone()
702             },
703             NumericOp {
704                 name: "checked_mul",
705                 body: "a.checked_mul(b)",
706                 ..partial_binop.clone()
707             },
708             NumericOp {
709                 name: "wrapping_mul",
710                 body: "a.wrapping_mul(b)",
711                 ..binop.clone()
712             },
713             NumericOp {
714                 name: "mul",
715                 body: r#"a.checked_mul(b).unwrap_or_else(|| panic!("multiplication overflow: {a} * {b}"))"#,
716                 ..binop.clone()
717             },
718             NumericOp {
719                 name: "checked_div",
720                 body: "a.checked_div(b)",
721                 ..partial_binop.clone()
722             },
723             NumericOp {
724                 name: "wrapping_div",
725                 body: "a.wrapping_div(b)",
726                 ..binop.clone()
727             },
728             NumericOp {
729                 name: "div",
730                 body: r#"a.checked_div(b).unwrap_or_else(|| panic!("div failure: {a} / {b}"))"#,
731                 ..binop.clone()
732             },
733             NumericOp {
734                 name: "checked_rem",
735                 body: "a.checked_rem(b)",
736                 ..partial_binop.clone()
737             },
738             NumericOp {
739                 name: "rem",
740                 body: r#"a.checked_rem(b).unwrap_or_else(|| panic!("rem failure: {a} % {b}"))"#,
741                 ..binop.clone()
742             },
743             // Bitwise operations.
744             //
745             // When applicable (e.g. shifts) we have checked, wrapping, and
746             // unwrapping variants, similar to arithmetic operations.
747             NumericOp {
748                 name: "and",
749                 body: "a & b",
750                 ..binop.clone()
751             },
752             NumericOp {
753                 name: "or",
754                 body: "a | b",
755                 ..binop.clone()
756             },
757             NumericOp {
758                 name: "xor",
759                 body: "a ^ b",
760                 ..binop.clone()
761             },
762             NumericOp {
763                 name: "not",
764                 body: "!a",
765                 ..unop.clone()
766             },
767             NumericOp {
768                 name: "checked_shl",
769                 body: "a.checked_shl(b)",
770                 ..partial_shift.clone()
771             },
772             NumericOp {
773                 name: "wrapping_shl",
774                 body: "a.wrapping_shl(b)",
775                 ..shift.clone()
776             },
777             NumericOp {
778                 name: "shl",
779                 body: r#"a.checked_shl(b).unwrap_or_else(|| panic!("shl overflow: {a} << {b}"))"#,
780                 ..shift.clone()
781             },
782             NumericOp {
783                 name: "checked_shr",
784                 body: "a.checked_shr(b)",
785                 ..partial_shift.clone()
786             },
787             NumericOp {
788                 name: "wrapping_shr",
789                 body: "a.wrapping_shr(b)",
790                 ..shift.clone()
791             },
792             NumericOp {
793                 name: "shr",
794                 body: r#"a.checked_shr(b).unwrap_or_else(|| panic!("shr overflow: {a} >> {b}"))"#,
795                 ..shift.clone()
796             },
797             NumericOp {
798                 name: "rotl",
799                 body: "a.rotate_left(b)",
800                 ..shift.clone()
801             },
802             NumericOp {
803                 name: "rotr",
804                 body: "a.rotate_right(b)",
805                 ..shift.clone()
806             },
807             // Predicates.
808             //
809             // We generate both pure constructors and a variety of extractors
810             // for these. See the relevant comments in `gen_numerics_isle` about
811             // the extractors.
812             NumericOp {
813                 name: "is_zero",
814                 body: "a == 0",
815                 ..predicate.clone()
816             },
817             NumericOp {
818                 name: "is_non_zero",
819                 body: "a != 0",
820                 ..predicate.clone()
821             },
822             NumericOp {
823                 name: "is_odd",
824                 body: "a & 1 == 1",
825                 ..predicate.clone()
826             },
827             NumericOp {
828                 name: "is_even",
829                 body: "a & 1 == 0",
830                 ..predicate.clone()
831             },
832             // Miscellaneous unary operations.
833             NumericOp {
834                 name: "checked_ilog2",
835                 body: "a.checked_ilog2()",
836                 ret: "u32",
837                 ..partial_unop.clone()
838             },
839             NumericOp {
840                 name: "ilog2",
841                 body: r#"a.checked_ilog2().unwrap_or_else(|| panic!("ilog2 overflow: {a}"))"#,
842                 ret: "u32",
843                 ..unop.clone()
844             },
845             NumericOp {
846                 name: "trailing_zeros",
847                 body: "a.trailing_zeros()",
848                 ret: "u32",
849                 ..unop.clone()
850             },
851             NumericOp {
852                 name: "trailing_ones",
853                 body: "a.trailing_ones()",
854                 ret: "u32",
855                 ..unop.clone()
856             },
857             NumericOp {
858                 name: "leading_zeros",
859                 body: "a.leading_zeros()",
860                 ret: "u32",
861                 ..unop.clone()
862             },
863             NumericOp {
864                 name: "leading_ones",
865                 body: "a.leading_ones()",
866                 ret: "u32",
867                 ..unop.clone()
868             },
869         ];
870 
871         // Operations that apply only to signed numbers.
872         let signed_ops = [
873             NumericOp {
874                 name: "checked_neg",
875                 body: "a.checked_neg()",
876                 ..partial_unop.clone()
877             },
878             NumericOp {
879                 name: "wrapping_neg",
880                 body: "a.wrapping_neg()",
881                 ..unop.clone()
882             },
883             NumericOp {
884                 name: "neg",
885                 body: r#"a.checked_neg().unwrap_or_else(|| panic!("negation overflow: {a}"))"#,
886                 ..unop.clone()
887             },
888         ];
889 
890         // Operations that apply only to unsigned numbers.
891         let unsigned_ops = [NumericOp {
892             name: "is_power_of_two",
893             body: "a.is_power_of_two()",
894             ..predicate.clone()
895         }];
896 
897         struct IterIf<I> {
898             condition: bool,
899             iter: I,
900         }
901 
902         impl<I: Iterator> Iterator for IterIf<I> {
903             type Item = I::Item;
904 
905             fn next(&mut self) -> Option<Self::Item> {
906                 if self.condition {
907                     self.iter.next()
908                 } else {
909                     None
910                 }
911             }
912         }
913 
914         ops.into_iter()
915             .chain(IterIf {
916                 condition: ty.signed,
917                 iter: signed_ops.into_iter(),
918             })
919             .chain(IterIf {
920                 condition: !ty.signed,
921                 iter: unsigned_ops.into_iter(),
922             })
923     }
924 }
925 
gen_numerics_isle(isle: &mut Formatter, rust: &mut Formatter)926 fn gen_numerics_isle(isle: &mut Formatter, rust: &mut Formatter) {
927     fmtln!(rust, "#[macro_export]");
928     fmtln!(rust, "#[doc(hidden)]");
929     fmtln!(rust, "macro_rules! isle_numerics_methods {{");
930     rust.indent_push();
931     fmtln!(rust, "() => {{");
932     rust.indent_push();
933 
934     for ty in NumericType::all() {
935         for op in NumericOp::ops_for_type(&ty) {
936             let ty = ty.name();
937             let op_name = format!("{ty}_{}", op.name);
938             let partial = if op.partial { " partial" } else { "" };
939             let ret = op.ret;
940             fmtln!(isle, "(decl pure{partial} {op_name} (");
941             isle.indent(|isle| {
942                 for (_arg_name, arg_ty) in op.args.iter() {
943                     fmtln!(isle, "{arg_ty}");
944                 }
945             });
946             fmtln!(isle, ") {ret})");
947             fmtln!(isle, "(extern constructor {op_name} {op_name})");
948 
949             let ret = if op.partial {
950                 Cow::from(format!("Option<{ret}>"))
951             } else {
952                 Cow::from(ret)
953             };
954             let body = op.body;
955             fmtln!(rust, "#[inline]");
956             fmtln!(rust, "fn {op_name}(");
957             rust.indent(|rust| {
958                 fmtln!(rust, "&mut self,");
959                 for (arg_name, arg_ty) in op.args.iter() {
960                     fmtln!(rust, "{arg_name}: {arg_ty},");
961                 }
962             });
963             fmtln!(rust, ") -> {ret} {{");
964             rust.indent(|rust| {
965                 fmtln!(rust, "{body}");
966             });
967             fmtln!(rust, "}}");
968 
969             // When generating extractors for a `{ty}_is_foo` predicate,
970             // we generate the following:
971             //
972             // * bool <- ty etor: `{ty}_matches_foo`
973             // * ty <- ty etor: `{ty}_extract_foo`
974             // * () <- ty etor: `{ty}_when_foo`
975             // * () <- ty etor: `{ty}_when_not_foo`
976             //
977             // The last three are defined as local extractors that are
978             // implemented in terms of the first. This gives the ISLE compiler
979             // visibility into the extractors' overlapping-ness.
980             if op.etors {
981                 debug_assert_eq!(op.args.len(), 1);
982                 debug_assert_eq!(op.args[0].1, ty);
983                 debug_assert_eq!(op.ret, "bool");
984                 debug_assert!(op.name.starts_with("is_"));
985 
986                 // Cut of the `is_` prefix.
987                 let base_name = &op.name[3..];
988                 debug_assert!(base_name.len() > 0);
989 
990                 fmtln!(isle, "(decl pure {ty}_matches_{base_name} (bool) {ty})");
991                 fmtln!(
992                     isle,
993                     "(extern extractor {ty}_matches_{base_name} {ty}_matches_{base_name})"
994                 );
995                 fmtln!(rust, "#[inline]");
996                 fmtln!(
997                     rust,
998                     "fn {ty}_matches_{base_name}(&mut self, a: {ty}) -> Option<bool> {{"
999                 );
1000                 rust.indent(|rust| {
1001                     fmtln!(rust, "Some({body})");
1002                 });
1003                 fmtln!(rust, "}}");
1004 
1005                 fmtln!(isle, "(decl pure {ty}_extract_{base_name} ({ty}) {ty})");
1006                 fmtln!(
1007                     isle,
1008                     "(extractor ({ty}_extract_{base_name} x) (and ({ty}_matches_{base_name} true) x))"
1009                 );
1010 
1011                 fmtln!(isle, "(decl pure {ty}_when_{base_name} () {ty})");
1012                 fmtln!(
1013                     isle,
1014                     "(extractor ({ty}_when_{base_name}) ({ty}_matches_{base_name} true))"
1015                 );
1016 
1017                 fmtln!(isle, "(decl pure {ty}_when_not_{base_name} () {ty})");
1018                 fmtln!(
1019                     isle,
1020                     "(extractor ({ty}_when_not_{base_name}) ({ty}_matches_{base_name} false))"
1021                 );
1022             }
1023 
1024             isle.empty_line();
1025             rust.empty_line();
1026         }
1027     }
1028 
1029     // Numeric type conversions.
1030     //
1031     // Naming and conventions:
1032     //
1033     // * Constructors:
1034     //   * "<from>_into_<to>" for lossless, infallible conversion
1035     //   * "<from>_try_into_<to>" for lossless, fallible conversions (exposed as
1036     //     partial constructors)
1037     //   * "<from>_unwrap_into_<to>" for lossless, fallible conversions that will
1038     //     panic at runtime if the conversion would be lossy
1039     //   * "<from>_truncate_into_<to>" for lossy, infallible conversions that
1040     //     ignore upper bits
1041     //   * "<from>_cast_[un]signed" for signed-to-unsigned (and vice versa)
1042     //     reinterpretation
1043     // * Extractors:
1044     //   * "<to>_from_<from>" for both fallible and infallible extractors
1045     //   * No unwrapping extractors
1046     //   * No truncating extractors
1047     //   * No signed-to-unsigned reinterpreting extractors
1048     for from in NumericType::all() {
1049         for to in NumericType::all() {
1050             if from == to {
1051                 continue;
1052             }
1053 
1054             let from_name = from.name();
1055             let to_name = to.name();
1056 
1057             let lossy = match (from.byte_width.cmp(&to.byte_width), from.signed, to.signed) {
1058                 // Widening with the same signedness is lossless.
1059                 (Ordering::Less, true, true) | (Ordering::Less, false, false) => false,
1060                 // Widening from unsigned to signed is lossless.
1061                 (Ordering::Less, false, true) => false,
1062                 // Widening from signed to unsigned is lossy.
1063                 (Ordering::Less, true, false) => true,
1064                 // Same width means we must be changing sign, since we skip
1065                 // `from == to`, and this is lossy.
1066                 (Ordering::Equal, _, _) => {
1067                     debug_assert_ne!(from.signed, to.signed);
1068                     true
1069                 }
1070                 // Narrowing is always lossy.
1071                 (Ordering::Greater, _, _) => true,
1072             };
1073 
1074             let (ctor, partial, rust_ret) = if lossy {
1075                 (
1076                     "try_into",
1077                     " partial",
1078                     Cow::from(format!("Option<{to_name}>")),
1079                 )
1080             } else {
1081                 ("into", "", Cow::from(to_name))
1082             };
1083 
1084             // Constructor.
1085             fmtln!(
1086                 isle,
1087                 "(decl pure{partial} {from_name}_{ctor}_{to_name} ({from_name}) {to_name})"
1088             );
1089             fmtln!(
1090                 isle,
1091                 "(extern constructor {from_name}_{ctor}_{to_name} {from_name}_{ctor}_{to_name})"
1092             );
1093             if !lossy {
1094                 fmtln!(
1095                     isle,
1096                     "(convert {from_name} {to_name} {from_name}_{ctor}_{to_name})"
1097                 );
1098             }
1099             fmtln!(rust, "#[inline]");
1100             fmtln!(
1101                 rust,
1102                 "fn {from_name}_{ctor}_{to_name}(&mut self, x: {from_name}) -> {rust_ret} {{"
1103             );
1104             rust.indent(|rust| {
1105                 if lossy {
1106                     fmtln!(rust, "{to_name}::try_from(x).ok()");
1107                 } else {
1108                     fmtln!(rust, "{to_name}::from(x)");
1109                 }
1110             });
1111             fmtln!(rust, "}}");
1112 
1113             // Unwrapping constructor.
1114             if lossy {
1115                 fmtln!(
1116                     isle,
1117                     "(decl pure {from_name}_unwrap_into_{to_name} ({from_name}) {to_name})"
1118                 );
1119                 fmtln!(
1120                     isle,
1121                     "(extern constructor {from_name}_unwrap_into_{to_name} {from_name}_unwrap_into_{to_name})"
1122                 );
1123                 fmtln!(rust, "#[inline]");
1124                 fmtln!(
1125                     rust,
1126                     "fn {from_name}_unwrap_into_{to_name}(&mut self, x: {from_name}) -> {to_name} {{"
1127                 );
1128                 rust.indent(|rust| {
1129                     fmtln!(rust, "{to_name}::try_from(x).unwrap()");
1130                 });
1131                 fmtln!(rust, "}}");
1132             }
1133 
1134             // Truncating constructor.
1135             if lossy && from.signed == to.signed {
1136                 fmtln!(
1137                     isle,
1138                     "(decl pure {from_name}_truncate_into_{to_name} ({from_name}) {to_name})"
1139                 );
1140                 fmtln!(
1141                     isle,
1142                     "(extern constructor {from_name}_truncate_into_{to_name} {from_name}_truncate_into_{to_name})"
1143                 );
1144                 fmtln!(rust, "#[inline]");
1145                 fmtln!(
1146                     rust,
1147                     "fn {from_name}_truncate_into_{to_name}(&mut self, x: {from_name}) -> {to_name} {{"
1148                 );
1149                 rust.indent(|rust| {
1150                     fmtln!(rust, "x as {to_name}");
1151                 });
1152                 fmtln!(rust, "}}");
1153             }
1154 
1155             // Signed-to-unsigned reinterpreting constructor.
1156             if from.byte_width == to.byte_width {
1157                 debug_assert_ne!(from.signed, to.signed);
1158                 let cast_name = if to.signed {
1159                     "cast_signed"
1160                 } else {
1161                     "cast_unsigned"
1162                 };
1163                 fmtln!(
1164                     isle,
1165                     "(decl pure {from_name}_{cast_name} ({from_name}) {to_name})"
1166                 );
1167                 fmtln!(
1168                     isle,
1169                     "(extern constructor {from_name}_{cast_name} {from_name}_{cast_name})"
1170                 );
1171                 fmtln!(rust, "#[inline]");
1172                 fmtln!(
1173                     rust,
1174                     "fn {from_name}_{cast_name}(&mut self, x: {from_name}) -> {to_name} {{"
1175                 );
1176                 rust.indent(|rust| {
1177                     // TODO: Once our MSRV is >= 1.87, we should use
1178                     // `x.cast_[un]signed()` here.
1179                     fmtln!(rust, "x as {to_name}");
1180                 });
1181                 fmtln!(rust, "}}");
1182             }
1183 
1184             // Extractor.
1185             fmtln!(
1186                 isle,
1187                 "(decl pure {to_name}_from_{from_name} ({to_name}) {from_name})"
1188             );
1189             fmtln!(
1190                 isle,
1191                 "(extern extractor {to_name}_from_{from_name} {from_name}_from_{to_name})"
1192             );
1193             fmtln!(rust, "#[inline]");
1194             fmtln!(
1195                 rust,
1196                 "fn {from_name}_from_{to_name}(&mut self, x: {from_name}) -> Option<{to_name}> {{"
1197             );
1198             rust.indent(|rust| {
1199                 if lossy {
1200                     fmtln!(rust, "x.try_into().ok()");
1201                 } else {
1202                     fmtln!(rust, "Some(x.into())");
1203                 }
1204             });
1205             fmtln!(rust, "}}");
1206 
1207             isle.empty_line();
1208             rust.empty_line();
1209         }
1210     }
1211 
1212     rust.indent_pop();
1213     fmtln!(rust, "}}");
1214     rust.indent_pop();
1215     fmtln!(rust, "}}");
1216 }
1217 
generate( formats: &[Rc<InstructionFormat>], all_inst: &AllInstructions, isle_numerics_filename: &str, rust_numerics_filename: &str, isle_opt_filename: &str, isle_lower_filename: &str, isle_dir: &std::path::Path, ) -> Result<(), error::Error>1218 pub(crate) fn generate(
1219     formats: &[Rc<InstructionFormat>],
1220     all_inst: &AllInstructions,
1221     isle_numerics_filename: &str,
1222     rust_numerics_filename: &str,
1223     isle_opt_filename: &str,
1224     isle_lower_filename: &str,
1225     isle_dir: &std::path::Path,
1226 ) -> Result<(), error::Error> {
1227     // Numerics
1228     let mut isle_fmt = Formatter::new(Language::Isle);
1229     let mut rust_fmt = Formatter::new(Language::Rust);
1230     gen_numerics_isle(&mut isle_fmt, &mut rust_fmt);
1231     isle_fmt.write(isle_numerics_filename, isle_dir)?;
1232     rust_fmt.write(rust_numerics_filename, isle_dir)?;
1233 
1234     // ISLE DSL: mid-end ("opt") generated bindings.
1235     let mut fmt = Formatter::new(Language::Isle);
1236     gen_opt_isle(&formats, all_inst, &mut fmt);
1237     fmt.write(isle_opt_filename, isle_dir)?;
1238 
1239     // ISLE DSL: lowering generated bindings.
1240     let mut fmt = Formatter::new(Language::Isle);
1241     gen_lower_isle(&formats, all_inst, &mut fmt);
1242     fmt.write(isle_lower_filename, isle_dir)?;
1243 
1244     Ok(())
1245 }
1246