cranelift_assembler_x64_meta/
generate.rs

1//! Contains the code-generation logic to emit for the DSL-defined instructions.
2
3mod features;
4mod format;
5mod inst;
6mod operand;
7
8use crate::dsl;
9use cranelift_srcgen::{Formatter, fmtln};
10
11/// Generate the Rust assembler code; e.g., `enum Inst { ... }`.
12pub 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 { ... }`
33fn 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`.
48fn 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 std::fmt::Display for Inst { ... }`
57fn generate_inst_display_impl(f: &mut Formatter, insts: &[dsl::Inst]) {
58    f.add_block("impl<R: Registers> std::fmt::Display for Inst<R>", |f| {
59        f.add_block(
60            "fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result",
61            |f| {
62                match_variants(f, insts, "fmt(f)");
63            },
64        );
65    });
66}
67
68fn 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(...)]`
95fn 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.
105fn 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}