cranelift_assembler_x64_meta/generate/
inst.rs

1use super::{Formatter, fmtln, generate_derive, generate_derive_arbitrary_bounds};
2use crate::dsl;
3
4impl dsl::Inst {
5    /// `struct <inst> { <op>: Reg, <op>: Reg, ... }`
6    pub fn generate_struct(&self, f: &mut Formatter) {
7        let struct_name = self.struct_name_with_generic();
8        let where_clause = if self.requires_generic() {
9            "where R: Registers"
10        } else {
11            ""
12        };
13
14        fmtln!(f, "/// `{self}`");
15        generate_derive(f);
16        if self.requires_generic() {
17            generate_derive_arbitrary_bounds(f);
18        }
19        f.add_block(&format!("pub struct {struct_name} {where_clause}"), |f| {
20            for k in &self.format.operands {
21                let loc = k.location;
22                let ty = k.generate_type();
23                fmtln!(f, "pub {loc}: {ty},");
24            }
25
26            if self.has_trap {
27                fmtln!(f, "pub trap: TrapCode,");
28            }
29        });
30    }
31
32    fn requires_generic(&self) -> bool {
33        self.format.uses_register()
34    }
35
36    /// `<struct_name><R>`
37    pub(crate) fn struct_name_with_generic(&self) -> String {
38        let struct_name = self.name();
39        if self.requires_generic() {
40            format!("{struct_name}<R>")
41        } else {
42            struct_name
43        }
44    }
45
46    /// `impl...`
47    fn generate_impl_block_start(&self) -> &str {
48        if self.requires_generic() {
49            "impl<R: Registers>"
50        } else {
51            "impl"
52        }
53    }
54
55    /// `impl <inst> { ... }`
56    pub fn generate_struct_impl(&self, f: &mut Formatter) {
57        let impl_block = self.generate_impl_block_start();
58        let struct_name = self.struct_name_with_generic();
59        f.add_block(&format!("{impl_block} {struct_name}"), |f| {
60            self.generate_new_function(f);
61            f.empty_line();
62            self.generate_mnemonic_function(f);
63            f.empty_line();
64            self.generate_encode_function(f);
65            f.empty_line();
66            self.generate_visit_function(f);
67            f.empty_line();
68            self.generate_is_available_function(f);
69            f.empty_line();
70            self.generate_features_function(f);
71            f.empty_line();
72            self.generate_num_registers_function(f);
73        });
74    }
75
76    // `fn new(<params>) -> Self { ... }`
77    pub fn generate_new_function(&self, f: &mut Formatter) {
78        let params = comma_join(
79            self.format
80                .operands
81                .iter()
82                .map(|o| format!("{}: impl Into<{}>", o.location, o.generate_type()))
83                .chain(if self.has_trap {
84                    Some("trap: impl Into<TrapCode>".to_string())
85                } else {
86                    None
87                }),
88        );
89        fmtln!(f, "#[must_use]");
90        f.add_block(&format!("pub fn new({params}) -> Self"), |f| {
91            f.add_block("Self", |f| {
92                for o in &self.format.operands {
93                    let loc = o.location;
94                    fmtln!(f, "{loc}: {loc}.into(),");
95                }
96                if self.has_trap {
97                    fmtln!(f, "trap: trap.into(),");
98                }
99            });
100        });
101    }
102
103    /// `fn mnemonic(&self) -> &'static str { ... }`
104    pub fn generate_mnemonic_function(&self, f: &mut Formatter) {
105        use dsl::Customization::*;
106
107        fmtln!(f, "#[must_use]");
108        fmtln!(f, "#[inline]");
109        f.add_block(
110            &format!("pub fn mnemonic(&self) -> std::borrow::Cow<'static, str>"),
111            |f| {
112                if self.custom.contains(Mnemonic) {
113                    fmtln!(f, "crate::custom::mnemonic::{}(self)", self.name());
114                } else {
115                    fmtln!(f, "std::borrow::Cow::Borrowed(\"{}\")", self.mnemonic);
116                }
117            },
118        );
119    }
120
121    /// `fn encode(&self, ...) { ... }`
122    fn generate_encode_function(&self, f: &mut Formatter) {
123        use dsl::Customization::*;
124
125        f.add_block(
126            &format!("pub fn encode(&self, buf: &mut impl CodeSink)"),
127            |f| {
128                if self.custom.contains(Encode) {
129                    fmtln!(f, "crate::custom::encode::{}(self, buf);", self.name());
130                } else {
131                    self.generate_possible_trap(f);
132                    match &self.encoding {
133                        dsl::Encoding::Rex(rex) => self.format.generate_rex_encoding(f, rex),
134                        dsl::Encoding::Vex(vex) => self.format.generate_vex_encoding(f, vex),
135                        dsl::Encoding::Evex(evex) => self.format.generate_evex_encoding(f, evex),
136                    }
137                }
138            },
139        );
140    }
141
142    // `buf.add_trap(...)`
143    fn generate_possible_trap(&self, f: &mut Formatter) {
144        if self.has_trap {
145            f.comment("Emit trap.");
146            fmtln!(f, "buf.add_trap(self.trap);");
147        } else if let Some(op) = self.format.uses_memory() {
148            use dsl::OperandKind::*;
149            f.comment("Emit trap.");
150            match op.kind() {
151                Mem(_) => {
152                    f.add_block(
153                        &format!("if let Some(trap_code) = self.{op}.trap_code()"),
154                        |f| {
155                            fmtln!(f, "buf.add_trap(trap_code);");
156                        },
157                    );
158                }
159                RegMem(_) => {
160                    let ty = op.reg_class().unwrap();
161                    f.add_block(&format!("if let {ty}Mem::Mem({op}) = &self.{op}"), |f| {
162                        f.add_block(&format!("if let Some(trap_code) = {op}.trap_code()"), |f| {
163                            fmtln!(f, "buf.add_trap(trap_code);");
164                        });
165                    });
166                }
167                _ => unreachable!(),
168            }
169        }
170    }
171
172    /// `fn visit(&self, ...) { ... }`
173    fn generate_visit_function(&self, f: &mut Formatter) {
174        use dsl::{Customization::*, OperandKind::*};
175        let extra_generic_bound = if self.requires_generic() {
176            ""
177        } else {
178            "<R: Registers>"
179        };
180        let visitor = if self.format.operands.is_empty() && !self.custom.contains(Visit) {
181            "_"
182        } else {
183            "visitor"
184        };
185        f.add_block(&format!("pub fn visit{extra_generic_bound}(&mut self, {visitor}: &mut impl RegisterVisitor<R>)"), |f| {
186            if self.custom.contains(Visit) {
187                fmtln!(f, "crate::custom::visit::{}(self, visitor)", self.name());
188                return;
189            }
190            for o in &self.format.operands {
191                let mutability = o.mutability.generate_snake_case();
192                let reg = o.location.reg_class();
193                match o.location.kind() {
194                    Imm(_) => {
195                        // Immediates do not need register allocation.
196                        //
197                        // If an instruction happens to only have immediates
198                        // then generate a dummy use of the `visitor` variable
199                        // to suppress unused variables warnings.
200                        fmtln!(f, "let _ = visitor;");
201                    }
202                    FixedReg(loc) => {
203                        let reg_lower = reg.unwrap().to_string().to_lowercase();
204                        fmtln!(f, "let enc = self.{loc}.expected_enc();");
205                        fmtln!(f, "visitor.fixed_{mutability}_{reg_lower}(&mut self.{loc}.0, enc);");
206                    }
207                    Reg(loc) => {
208                        let reg_lower = reg.unwrap().to_string().to_lowercase();
209                        fmtln!(f, "visitor.{mutability}_{reg_lower}(self.{loc}.as_mut());");
210                    }
211                    RegMem(loc) => {
212                        let reg = reg.unwrap();
213                        let reg_lower = reg.to_string().to_lowercase();
214                        fmtln!(f, "visitor.{mutability}_{reg_lower}_mem(&mut self.{loc});");
215                    }
216                    Mem(loc) => {
217                        // Note that this is always "read" because from a
218                        // regalloc perspective when using an amode it means
219                        // that the while a write is happening that's to
220                        // memory, not registers.
221                        fmtln!(f, "visitor.read_amode(&mut self.{loc});");
222                    }
223
224                }
225            }
226        });
227    }
228
229    /// `fn is_available(&self, ...) -> bool { ... }`
230    fn generate_is_available_function(&self, f: &mut Formatter) {
231        fmtln!(f, "#[must_use]");
232        f.add_block(
233            "pub fn is_available(&self, features: &impl AvailableFeatures) -> bool",
234            |f| {
235                let expr = self.features.generate_boolean_expr("features");
236                fmtln!(f, "{expr}");
237            },
238        );
239    }
240
241    /// `fn features(&self) -> Features { ... }`
242    fn generate_features_function(&self, f: &mut Formatter) {
243        fmtln!(f, "#[must_use]");
244        f.add_block("pub fn features(&self) -> &'static Features", |f| {
245            self.features.generate_constructor_expr(f);
246        });
247    }
248
249    /// `fn num_registers_available(&self) -> usize { ... }`
250    fn generate_num_registers_function(&self, f: &mut Formatter) {
251        fmtln!(f, "#[must_use]");
252        f.add_block("pub fn num_registers_available(&self) -> usize", |f| {
253            use dsl::Encoding::*;
254            let n = match self.encoding {
255                Rex(_) | Vex(_) => 16,
256                Evex(_) => 32,
257            };
258            fmtln!(f, "{n}")
259        });
260    }
261
262    /// `impl Display for <inst> { ... }`
263    pub fn generate_display_impl(&self, f: &mut Formatter) {
264        use crate::dsl::Customization::*;
265        let impl_block = self.generate_impl_block_start();
266        let struct_name = self.struct_name_with_generic();
267        f.add_block(
268            &format!("{impl_block} std::fmt::Display for {struct_name}"),
269            |f| {
270                f.add_block(
271                    "fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result",
272                    |f| {
273                        if self.custom.contains(Display) {
274                            fmtln!(f, "crate::custom::display::{}(f, self)", self.name());
275                            return;
276                        }
277
278                        fmtln!(f, "let name = self.mnemonic();");
279                        if self.format.operands.is_empty() {
280                            fmtln!(f, "f.write_str(&name)");
281                            return;
282                        }
283                        for op in self.format.operands.iter() {
284                            let location = op.location;
285                            let to_string = location.generate_to_string(op.extension);
286                            fmtln!(f, "let {location} = {to_string};");
287                        }
288                        let ordered_ops = self.format.generate_att_style_operands();
289                        let mut implicit_ops = self.format.generate_implicit_operands();
290                        if self.has_trap {
291                            fmtln!(f, "let trap = self.trap;");
292                            if implicit_ops.is_empty() {
293                                implicit_ops.push_str(" ;; {trap}");
294                            } else {
295                                implicit_ops.push_str(", {trap}");
296                            }
297                        }
298                        fmtln!(f, "write!(f, \"{{name}} {ordered_ops}{implicit_ops}\")");
299                    },
300                );
301            },
302        );
303    }
304
305    /// `impl From<struct> for Inst { ... }`
306    pub fn generate_from_impl(&self, f: &mut Formatter) {
307        let struct_name_r = self.struct_name_with_generic();
308        let variant_name = self.name();
309        f.add_block(
310            &format!("impl<R: Registers> From<{struct_name_r}> for Inst<R>"),
311            |f| {
312                f.add_block(&format!("fn from(inst: {struct_name_r}) -> Self"), |f| {
313                    fmtln!(f, "Self::{variant_name}(inst)");
314                });
315            },
316        );
317    }
318}
319
320fn comma_join<S: Into<String>>(items: impl Iterator<Item = S>) -> String {
321    items.map(Into::into).collect::<Vec<_>>().join(", ")
322}