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