Skip to main content

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            self.generate_xed_mnemonic_function(f);
64            f.empty_line();
65            self.generate_encode_function(f);
66            f.empty_line();
67            self.generate_visit_function(f);
68            f.empty_line();
69            self.generate_is_available_function(f);
70            f.empty_line();
71            self.generate_features_function(f);
72            f.empty_line();
73            self.generate_num_registers_function(f);
74        });
75    }
76
77    // `fn new(<params>) -> Self { ... }`
78    pub fn generate_new_function(&self, f: &mut Formatter) {
79        let params = comma_join(
80            self.format
81                .operands
82                .iter()
83                .map(|o| format!("{}: impl Into<{}>", o.location, o.generate_type()))
84                .chain(if self.has_trap {
85                    Some("trap: impl Into<TrapCode>".to_string())
86                } else {
87                    None
88                }),
89        );
90        fmtln!(f, "#[must_use]");
91        f.add_block(&format!("pub fn new({params}) -> Self"), |f| {
92            f.add_block("Self", |f| {
93                for o in &self.format.operands {
94                    let loc = o.location;
95                    fmtln!(f, "{loc}: {loc}.into(),");
96                }
97                if self.has_trap {
98                    fmtln!(f, "trap: trap.into(),");
99                }
100            });
101        });
102    }
103
104    /// `fn mnemonic(&self) -> &'static str { ... }`
105    pub fn generate_mnemonic_function(&self, f: &mut Formatter) {
106        use dsl::Customization::*;
107
108        fmtln!(f, "#[must_use]");
109        fmtln!(f, "#[inline]");
110        f.add_block(
111            &format!("pub fn mnemonic(&self) -> alloc::borrow::Cow<'static, str>"),
112            |f| {
113                if self.custom.contains(Mnemonic) {
114                    fmtln!(f, "crate::custom::mnemonic::{}(self)", self.name());
115                } else {
116                    fmtln!(f, "alloc::borrow::Cow::Borrowed(\"{}\")", self.mnemonic);
117                }
118            },
119        );
120    }
121
122    /// `fn xed_mnemonic(&self) -> Cow<'static, str> { ... }`
123    ///
124    /// This is the mnemonic Intel XED prints for this encoding; it differs from
125    /// [`Self::generate_mnemonic_function`] often enough that the fuzzer's XED
126    /// oracle compares against it rather than reconciling the two after the
127    /// fact. See [`dsl::Inst::xed_mnemonics`].
128    pub fn generate_xed_mnemonic_function(&self, f: &mut Formatter) {
129        let (reg, mem) = self.xed_mnemonics();
130        fmtln!(f, "#[must_use]");
131        fmtln!(f, "#[inline]");
132        f.add_block(
133            &format!("pub fn xed_mnemonic(&self) -> alloc::borrow::Cow<'static, str>"),
134            |f| {
135                let borrow = |m| format!("alloc::borrow::Cow::Borrowed(\"{m}\")");
136                let loc = self.format.uses_memory();
137                match loc {
138                    // The marker is only appended when the `r/m` operand holds
139                    // memory; a memory-only operand always does.
140                    Some(loc) if reg != mem && !loc.is_memory_only() => {
141                        fmtln!(f, "if self.{loc}.is_memory() {{");
142                        f.indent(|f| fmtln!(f, "{}", borrow(&mem)));
143                        fmtln!(f, "}} else {{");
144                        f.indent(|f| fmtln!(f, "{}", borrow(&reg)));
145                        fmtln!(f, "}}");
146                    }
147                    Some(_) if reg != mem => fmtln!(f, "{}", borrow(&mem)),
148                    _ => fmtln!(f, "{}", borrow(&reg)),
149                }
150            },
151        );
152    }
153
154    /// `fn encode(&self, ...) { ... }`
155    fn generate_encode_function(&self, f: &mut Formatter) {
156        use dsl::Customization::*;
157
158        f.add_block(
159            &format!("pub fn encode(&self, buf: &mut impl CodeSink)"),
160            |f| {
161                if self.custom.contains(Encode) {
162                    fmtln!(f, "crate::custom::encode::{}(self, buf);", self.name());
163                } else {
164                    self.generate_possible_trap(f);
165                    match &self.encoding {
166                        dsl::Encoding::Rex(rex) => self.format.generate_rex_encoding(f, rex),
167                        dsl::Encoding::Vex(vex) => self.format.generate_vex_encoding(f, vex),
168                        dsl::Encoding::Evex(evex) => self.format.generate_evex_encoding(f, evex),
169                    }
170                }
171            },
172        );
173    }
174
175    // `buf.add_trap(...)`
176    fn generate_possible_trap(&self, f: &mut Formatter) {
177        if self.has_trap {
178            f.comment("Emit trap.");
179            fmtln!(f, "buf.add_trap(self.trap);");
180        } else if let Some(op) = self.format.uses_memory() {
181            use dsl::OperandKind::*;
182            f.comment("Emit trap.");
183            match op.kind() {
184                Mem(_) => {
185                    f.add_block(
186                        &format!("if let Some(trap_code) = self.{op}.trap_code()"),
187                        |f| {
188                            fmtln!(f, "buf.add_trap(trap_code);");
189                        },
190                    );
191                }
192                RegMem(_) => {
193                    let ty = op.reg_class().unwrap();
194                    f.add_block(&format!("if let {ty}Mem::Mem({op}) = &self.{op}"), |f| {
195                        f.add_block(&format!("if let Some(trap_code) = {op}.trap_code()"), |f| {
196                            fmtln!(f, "buf.add_trap(trap_code);");
197                        });
198                    });
199                }
200                _ => unreachable!(),
201            }
202        }
203    }
204
205    /// `fn visit(&self, ...) { ... }`
206    fn generate_visit_function(&self, f: &mut Formatter) {
207        use dsl::{Customization::*, OperandKind::*};
208        let extra_generic_bound = if self.requires_generic() {
209            ""
210        } else {
211            "<R: Registers>"
212        };
213        let visitor = if self.format.operands.is_empty() && !self.custom.contains(Visit) {
214            "_"
215        } else {
216            "visitor"
217        };
218        f.add_block(&format!("pub fn visit{extra_generic_bound}(&mut self, {visitor}: &mut impl RegisterVisitor<R>)"), |f| {
219            if self.custom.contains(Visit) {
220                fmtln!(f, "crate::custom::visit::{}(self, visitor)", self.name());
221                return;
222            }
223            for o in &self.format.operands {
224                let mutability = o.mutability.generate_snake_case();
225                let reg = o.location.reg_class();
226                match o.location.kind() {
227                    Imm(_) => {
228                        // Immediates do not need register allocation.
229                        //
230                        // If an instruction happens to only have immediates
231                        // then generate a dummy use of the `visitor` variable
232                        // to suppress unused variables warnings.
233                        fmtln!(f, "let _ = visitor;");
234                    }
235                    FixedReg(loc) => {
236                        let reg_lower = reg.unwrap().to_string().to_lowercase();
237                        fmtln!(f, "let enc = self.{loc}.expected_enc();");
238                        fmtln!(f, "visitor.fixed_{mutability}_{reg_lower}(&mut self.{loc}.0, enc);");
239                    }
240                    Reg(loc) => {
241                        let reg_lower = reg.unwrap().to_string().to_lowercase();
242                        fmtln!(f, "visitor.{mutability}_{reg_lower}(self.{loc}.as_mut());");
243                    }
244                    RegMem(loc) => {
245                        let reg = reg.unwrap();
246                        let reg_lower = reg.to_string().to_lowercase();
247                        fmtln!(f, "visitor.{mutability}_{reg_lower}_mem(&mut self.{loc});");
248                    }
249                    Mem(loc) => {
250                        // Note that this is always "read" because from a
251                        // regalloc perspective when using an amode it means
252                        // that the while a write is happening that's to
253                        // memory, not registers.
254                        fmtln!(f, "visitor.read_amode(&mut self.{loc});");
255                    }
256
257                }
258            }
259        });
260    }
261
262    /// `fn is_available(&self, ...) -> bool { ... }`
263    fn generate_is_available_function(&self, f: &mut Formatter) {
264        fmtln!(f, "#[must_use]");
265        f.add_block(
266            "pub fn is_available(&self, features: &impl AvailableFeatures) -> bool",
267            |f| {
268                let expr = self.features.generate_boolean_expr("features");
269                fmtln!(f, "{expr}");
270            },
271        );
272    }
273
274    /// `fn features(&self) -> Features { ... }`
275    fn generate_features_function(&self, f: &mut Formatter) {
276        fmtln!(f, "#[must_use]");
277        f.add_block("pub fn features(&self) -> &'static Features", |f| {
278            self.features.generate_constructor_expr(f);
279        });
280    }
281
282    /// `fn num_registers_available(&self) -> usize { ... }`
283    fn generate_num_registers_function(&self, f: &mut Formatter) {
284        fmtln!(f, "#[must_use]");
285        f.add_block("pub fn num_registers_available(&self) -> usize", |f| {
286            use dsl::Encoding::*;
287            let n = match self.encoding {
288                Rex(_) | Vex(_) => 16,
289                Evex(_) => 32,
290            };
291            fmtln!(f, "{n}")
292        });
293    }
294
295    /// `impl Display for <inst> { ... }`
296    pub fn generate_display_impl(&self, f: &mut Formatter) {
297        use crate::dsl::Customization::*;
298        let impl_block = self.generate_impl_block_start();
299        let struct_name = self.struct_name_with_generic();
300        f.add_block(
301            &format!("{impl_block} core::fmt::Display for {struct_name}"),
302            |f| {
303                f.add_block(
304                    "fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result",
305                    |f| {
306                        if self.custom.contains(Display) {
307                            fmtln!(f, "crate::custom::display::{}(f, self)", self.name());
308                            return;
309                        }
310
311                        fmtln!(f, "let name = if f.alternate() {{ self.xed_mnemonic() }} else {{ self.mnemonic() }};");
312                        if self.format.operands.is_empty() {
313                            fmtln!(f, "f.write_str(&name)");
314                            return;
315                        }
316                        for op in self.format.operands.iter() {
317                            let location = op.location;
318                            let to_string = location.generate_to_string(op.extension);
319                            match location.generate_to_string_xed(op.extension) {
320                                Some(xed) => fmtln!(
321                                    f,
322                                    "let {location} = if f.alternate() {{ {xed} }} else {{ {to_string} }};"
323                                ),
324                                None => fmtln!(f, "let {location} = {to_string};"),
325                            }
326                        }
327                        let ordered_ops = self.format.generate_att_style_operands();
328                        let xed_ops = self.format.generate_xed_style_operands();
329                        let mut implicit_ops = self.format.generate_implicit_operands();
330                        if self.has_trap {
331                            fmtln!(f, "let trap = self.trap;");
332                            if implicit_ops.is_empty() {
333                                implicit_ops.push_str(" ;; {trap}");
334                            } else {
335                                implicit_ops.push_str(", {trap}");
336                            }
337                        }
338                        if implicit_ops.is_empty() && xed_ops == ordered_ops {
339                            fmtln!(f, "write!(f, \"{{name}} {ordered_ops}\")");
340                        } else {
341                            // The implicit-operand and trap annotations are
342                            // ours; XED has no equivalent, and it leaves the
343                            // fixed `%xmm0` mask implicit too.
344                            fmtln!(f, "if f.alternate() {{");
345                            f.indent(|f| fmtln!(f, "write!(f, \"{{name}} {xed_ops}\")"));
346                            fmtln!(f, "}} else {{");
347                            f.indent(|f| {
348                                fmtln!(f, "write!(f, \"{{name}} {ordered_ops}{implicit_ops}\")")
349                            });
350                            fmtln!(f, "}}");
351                        }
352                    },
353                );
354            },
355        );
356    }
357
358    /// `impl From<struct> for Inst { ... }`
359    pub fn generate_from_impl(&self, f: &mut Formatter) {
360        let struct_name_r = self.struct_name_with_generic();
361        let variant_name = self.name();
362        f.add_block(
363            &format!("impl<R: Registers> From<{struct_name_r}> for Inst<R>"),
364            |f| {
365                f.add_block(&format!("fn from(inst: {struct_name_r}) -> Self"), |f| {
366                    fmtln!(f, "Self::{variant_name}(inst)");
367                });
368            },
369        );
370    }
371}
372
373fn comma_join<S: Into<String>>(items: impl Iterator<Item = S>) -> String {
374    items.map(Into::into).collect::<Vec<_>>().join(", ")
375}