1 //! Contains the code-generation logic to emit for the DSL-defined instructions.
2 
3 mod features;
4 mod format;
5 mod inst;
6 mod operand;
7 
8 use crate::dsl;
9 use cranelift_srcgen::{Formatter, fmtln};
10 
11 /// Generate the Rust assembler code; e.g., `enum Inst { ... }`.
rust_assembler(f: &mut Formatter, insts: &[dsl::Inst])12 pub fn rust_assembler(f: &mut Formatter, insts: &[dsl::Inst]) {
13     // Generate "all instructions" enum.
14     generate_inst_enum(f, insts);
15     generate_inst_impls(f, insts);
16     generate_inst_display_impl(f, insts);
17 
18     // Generate per-instruction structs.
19     f.empty_line();
20     for inst in insts {
21         inst.generate_struct(f);
22         inst.generate_struct_impl(f);
23         inst.generate_display_impl(f);
24         inst.generate_from_impl(f);
25         f.empty_line();
26     }
27 
28     // Generate the `Feature` trait.
29     dsl::Feature::generate_macro(f);
30 }
31 
32 /// `enum Inst { ... }`
generate_inst_enum(f: &mut Formatter, insts: &[dsl::Inst])33 fn generate_inst_enum(f: &mut Formatter, insts: &[dsl::Inst]) {
34     fmtln!(f, "#[doc(hidden)]");
35     generate_derive(f);
36     generate_derive_arbitrary_bounds(f);
37     f.add_block("pub enum Inst<R: Registers>", |f| {
38         for inst in insts {
39             let variant_name = inst.name();
40             let struct_name = inst.struct_name_with_generic();
41             fmtln!(f, "{variant_name}({struct_name}),");
42         }
43     });
44 }
45 
46 /// Helper for emitting `match self { ... }` blocks over all instructions. For each instruction in
47 /// `insts`, this generate a separate match arm containing `invoke`.
match_variants(f: &mut Formatter, insts: &[dsl::Inst], invoke: &str)48 fn match_variants(f: &mut Formatter, insts: &[dsl::Inst], invoke: &str) {
49     f.add_block("match self", |f| {
50         for inst in insts.iter().map(|i| i.name()) {
51             fmtln!(f, "Self::{inst}(i) => i.{invoke},");
52         }
53     });
54 }
55 
56 /// `impl core::fmt::Display for Inst { ... }`
generate_inst_display_impl(f: &mut Formatter, insts: &[dsl::Inst])57 fn generate_inst_display_impl(f: &mut Formatter, insts: &[dsl::Inst]) {
58     f.add_block("impl<R: Registers> core::fmt::Display for Inst<R>", |f| {
59         f.add_block(
60             "fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result",
61             |f| {
62                 match_variants(f, insts, "fmt(f)");
63             },
64         );
65     });
66 }
67 
generate_inst_impls(f: &mut Formatter, insts: &[dsl::Inst])68 fn generate_inst_impls(f: &mut Formatter, insts: &[dsl::Inst]) {
69     f.add_block("impl<R: Registers> Inst<R>", |f| {
70         f.add_block("pub fn encode(&self, b: &mut impl CodeSink)", |f| {
71             match_variants(f, insts, "encode(b)");
72         });
73         f.add_block(
74             "pub fn visit(&mut self, v: &mut impl RegisterVisitor<R>)",
75             |f| {
76                 match_variants(f, insts, "visit(v)");
77             },
78         );
79         f.add_block(
80             "pub fn is_available(&self, f: &impl AvailableFeatures) -> bool",
81             |f| {
82                 match_variants(f, insts, "is_available(f)");
83             },
84         );
85         f.add_block("pub fn features(&self) -> &'static Features", |f| {
86             match_variants(f, insts, "features()");
87         });
88         f.add_block("pub fn num_registers_available(&self) -> usize", |f| {
89             match_variants(f, insts, "num_registers_available()");
90         });
91     });
92 }
93 
94 /// `#[derive(...)]`
generate_derive(f: &mut Formatter)95 fn generate_derive(f: &mut Formatter) {
96     fmtln!(f, "#[derive(Copy, Clone, Debug)]");
97     fmtln!(
98         f,
99         "#[cfg_attr(any(test, feature = \"fuzz\"), derive(arbitrary::Arbitrary))]"
100     );
101 }
102 
103 /// Adds a custom bound to the `Arbitrary` implementation which ensures that
104 /// the associated registers are all `Arbitrary` as well.
generate_derive_arbitrary_bounds(f: &mut Formatter)105 fn generate_derive_arbitrary_bounds(f: &mut Formatter) {
106     fmtln!(
107         f,
108         "#[cfg_attr(any(test, feature = \"fuzz\"), arbitrary(bound = \"R: crate::fuzz::RegistersArbitrary\"))]"
109     );
110 }
111