Skip to main content

cranelift_assembler_x64_meta/generate/
format.rs

1//! Generate format-related Rust code; this also includes generation of encoding
2//! Rust code.
3use super::{Formatter, fmtln};
4use crate::dsl;
5
6/// Different methods of emitting a ModR/M operand and encoding various bits and
7/// pieces of information into it. The REX/VEX formats plus the operand kinds
8/// dictate how exactly each instruction uses this, if at all.
9#[derive(Copy, Clone)]
10enum ModRmStyle {
11    /// This instruction does not use a ModR/M byte.
12    None,
13
14    /// The R/M bits are encoded with `rm` which is a `Gpr` or `Xmm` (it does
15    /// not have a "mem" possibility), and the Reg/Opcode bits are encoded
16    /// with `reg`.
17    Reg { reg: ModRmReg, rm: dsl::Location },
18
19    /// The R/M bits are encoded with `rm` which is a `GprMem` or `XmmMem`, and
20    /// the Reg/Opcode bits are encoded with `reg`.
21    RegMem {
22        reg: ModRmReg,
23        rm: dsl::Location,
24        evex_scaling: Option<i8>,
25    },
26
27    /// Same as `RegMem` above except that this is also used for VEX-encoded
28    /// instructions with "/is4" which indicates that the 4th register operand
29    /// is encoded in a byte after the ModR/M byte.
30    RegMemIs4 {
31        reg: ModRmReg,
32        rm: dsl::Location,
33        is4: dsl::Location,
34        evex_scaling: Option<i8>,
35    },
36}
37
38/// Different methods of encoding the Reg/Opcode bits in a ModR/M byte.
39#[derive(Copy, Clone)]
40enum ModRmReg {
41    /// A static set of bits is used.
42    Digit(u8),
43    /// A runtime-defined register is used with this field name.
44    Reg(dsl::Location),
45}
46
47impl dsl::Format {
48    /// Re-order the Intel-style operand order to accommodate ATT-style
49    /// printing.
50    ///
51    /// This is an unfortunate necessity to match Cranelift's current
52    /// disassembly, which uses AT&T-style printing. The plan is to eventually
53    /// transition to Intel-style printing (and avoid this awkward reordering)
54    /// once Cranelift has switched to using this assembler predominantly
55    /// (TODO).
56    #[must_use]
57    pub(crate) fn generate_att_style_operands(&self) -> String {
58        let ordered_ops: Vec<_> = self
59            .operands
60            .iter()
61            .filter(|o| !o.implicit)
62            .rev()
63            .map(|o| format!("{{{}}}", o.location))
64            .collect();
65        ordered_ops.join(", ")
66    }
67
68    /// Like [`Self::generate_att_style_operands`], but omits the fixed `%xmm0`
69    /// mask operand, which XED leaves implicit.
70    #[must_use]
71    pub(crate) fn generate_xed_style_operands(&self) -> String {
72        let ordered_ops: Vec<_> = self
73            .operands
74            .iter()
75            .filter(|o| !o.implicit && o.location != dsl::Location::xmm0)
76            .rev()
77            .map(|o| format!("{{{}}}", o.location))
78            .collect();
79        ordered_ops.join(", ")
80    }
81
82    #[must_use]
83    pub(crate) fn generate_implicit_operands(&self) -> String {
84        let ops: Vec<_> = self
85            .operands
86            .iter()
87            .filter(|o| o.implicit)
88            .map(|o| format!("{{{}}}", o.location))
89            .collect();
90        if ops.is_empty() {
91            String::new()
92        } else {
93            format!(" ;; implicit: {}", ops.join(", "))
94        }
95    }
96
97    pub(crate) fn generate_rex_encoding(&self, f: &mut Formatter, rex: &dsl::Rex) {
98        self.generate_prefixes(f, rex);
99        let style = self.generate_rex_prefix(f, rex);
100        rex.generate_opcodes(f, self.locations().next());
101        self.generate_modrm_byte(f, style);
102        self.generate_immediate(f, style);
103    }
104
105    pub fn generate_vex_encoding(&self, f: &mut Formatter, vex: &dsl::Vex) {
106        let style = self.generate_vex_prefix(f, vex);
107        vex.generate_opcode(f);
108        self.generate_modrm_byte(f, style);
109        self.generate_immediate(f, style);
110    }
111
112    pub fn generate_evex_encoding(&self, f: &mut Formatter, evex: &dsl::Evex) {
113        let style = self.generate_evex_prefix(f, evex);
114        evex.generate_opcode(f);
115        self.generate_modrm_byte(f, style);
116        self.generate_immediate(f, style);
117    }
118
119    /// `buf.put1(...);`
120    fn generate_prefixes(&self, f: &mut Formatter, rex: &dsl::Rex) {
121        if !rex.opcodes.prefixes.is_empty() {
122            f.empty_line();
123            f.comment("Emit prefixes.");
124        }
125        if let Some(group1) = &rex.opcodes.prefixes.group1 {
126            fmtln!(f, "buf.put1({group1});");
127        }
128        if let Some(group2) = &rex.opcodes.prefixes.group2 {
129            fmtln!(f, "buf.put1({group2});");
130        }
131        if let Some(group3) = &rex.opcodes.prefixes.group3 {
132            fmtln!(f, "buf.put1({group3});");
133        }
134        if let Some(group4) = &rex.opcodes.prefixes.group4 {
135            fmtln!(f, "buf.put1({group4});");
136        }
137    }
138
139    fn generate_rex_prefix(&self, f: &mut Formatter, rex: &dsl::Rex) -> ModRmStyle {
140        use dsl::OperandKind::{FixedReg, Imm, Mem, Reg, RegMem};
141
142        // If this instruction has only immediates there's no rex/modrm/etc, so
143        // skip everything below.
144        match self.operands_by_kind().as_slice() {
145            [] | [Imm(_)] => return ModRmStyle::None,
146            _ => {}
147        }
148
149        f.empty_line();
150        f.comment("Possibly emit REX prefix.");
151
152        let find_8bit_registers =
153            |l: &dsl::Location| l.bits() == 8 && matches!(l.kind(), Reg(_) | RegMem(_));
154        let uses_8bit = self.locations().any(find_8bit_registers);
155        fmtln!(f, "let uses_8bit = {uses_8bit};");
156        fmtln!(f, "let w_bit = {};", rex.w.as_bool());
157        let bits = "w_bit, uses_8bit";
158
159        let style = match self.operands_by_kind().as_slice() {
160            [FixedReg(dst), FixedReg(_)] | [FixedReg(dst)] | [FixedReg(dst), Imm(_)] => {
161                // TODO: don't emit REX byte here.
162                assert_eq!(rex.unwrap_digit(), None);
163                fmtln!(f, "let digit = 0;");
164                fmtln!(f, "let dst = self.{dst}.enc();");
165                fmtln!(f, "let rex = RexPrefix::with_digit(digit, dst, {bits});");
166                ModRmStyle::None
167            }
168            [Reg(dst)] => {
169                assert_eq!(rex.unwrap_digit(), None);
170                assert!(rex.opcode_mod.is_some());
171                fmtln!(f, "let dst = self.{dst}.enc();");
172                fmtln!(f, "let rex = RexPrefix::one_op(dst, {bits});");
173                ModRmStyle::None
174            }
175            [Reg(dst), Imm(_)] => match rex.unwrap_digit() {
176                Some(digit) => {
177                    fmtln!(f, "let digit = 0x{digit:x};");
178                    fmtln!(f, "let dst = self.{dst}.enc();");
179                    fmtln!(f, "let rex = RexPrefix::two_op(digit, dst, {bits});");
180                    ModRmStyle::Reg {
181                        reg: ModRmReg::Digit(digit),
182                        rm: *dst,
183                    }
184                }
185                None => {
186                    assert!(rex.opcode_mod.is_some());
187                    fmtln!(f, "let dst = self.{dst}.enc();");
188                    fmtln!(f, "let rex = RexPrefix::one_op(dst, {bits});");
189                    ModRmStyle::None
190                }
191            },
192            [FixedReg(_), RegMem(mem)]
193            | [FixedReg(_), FixedReg(_), RegMem(mem)]
194            | [RegMem(mem), FixedReg(_)]
195            | [Mem(mem), Imm(_)]
196            | [RegMem(mem), Imm(_)]
197            | [RegMem(mem)]
198            | [FixedReg(_), FixedReg(_), FixedReg(_), FixedReg(_), Mem(mem)] => {
199                let digit = rex.unwrap_digit().unwrap();
200                fmtln!(f, "let digit = 0x{digit:x};");
201                fmtln!(f, "let rex = self.{mem}.as_rex_prefix(digit, {bits});");
202                ModRmStyle::RegMem {
203                    reg: ModRmReg::Digit(digit),
204                    rm: *mem,
205                    evex_scaling: None,
206                }
207            }
208            [Reg(reg), RegMem(mem) | Mem(mem)]
209            | [Reg(reg), RegMem(mem), Imm(_) | FixedReg(_)]
210            | [RegMem(mem) | Mem(mem), Reg(reg)]
211            | [RegMem(mem) | Mem(mem), Reg(reg), Imm(_) | FixedReg(_)] => {
212                fmtln!(f, "let reg = self.{reg}.enc();");
213                fmtln!(f, "let rex = self.{mem}.as_rex_prefix(reg, {bits});");
214                ModRmStyle::RegMem {
215                    reg: ModRmReg::Reg(*reg),
216                    rm: *mem,
217                    evex_scaling: None,
218                }
219            }
220            [Reg(dst), Reg(src), Imm(_)] | [Reg(dst), Reg(src)] => {
221                fmtln!(f, "let reg = self.{dst}.enc();");
222                fmtln!(f, "let rm = self.{src}.enc();");
223                fmtln!(f, "let rex = RexPrefix::two_op(reg, rm, {bits});");
224                ModRmStyle::Reg {
225                    reg: ModRmReg::Reg(*dst),
226                    rm: *src,
227                }
228            }
229
230            unknown => unimplemented!("unknown pattern: {unknown:?}"),
231        };
232
233        fmtln!(f, "rex.encode(buf);");
234        style
235    }
236
237    fn generate_vex_prefix(&self, f: &mut Formatter, vex: &dsl::Vex) -> ModRmStyle {
238        f.empty_line();
239        f.comment("Emit VEX prefix.");
240        fmtln!(f, "let len = {:#03b};", vex.length.vex_bits());
241        fmtln!(f, "let pp = {:#04b};", vex.pp.map_or(0b00, |pp| pp.bits()));
242        fmtln!(f, "let mmmmm = {:#07b};", vex.mmmmm.unwrap().bits());
243        fmtln!(f, "let w = {};", vex.w.as_bool());
244        let bits = "len, pp, mmmmm, w";
245
246        self.generate_vex_or_evex_prefix(f, "VexPrefix", &bits, vex.is4, None, || {
247            vex.unwrap_digit()
248        })
249    }
250
251    fn generate_evex_prefix(&self, f: &mut Formatter, evex: &dsl::Evex) -> ModRmStyle {
252        f.empty_line();
253        f.comment("Emit EVEX prefix.");
254        let ll = evex.length.evex_bits();
255        fmtln!(f, "let ll = {ll:#04b};");
256        fmtln!(f, "let pp = {:#04b};", evex.pp.map_or(0b00, |pp| pp.bits()));
257        fmtln!(f, "let mmm = {:#07b};", evex.mmm.unwrap().bits());
258        fmtln!(f, "let w = {};", evex.w.as_bool());
259        // NB: when bcast is supported in the future the `evex_scaling`
260        // calculation for `Full` and `Half` below need to be updated.
261        let bcast = false;
262        fmtln!(f, "let bcast = {bcast};");
263        let bits = format!("ll, pp, mmm, w, bcast");
264        let is4 = false;
265
266        let length_bytes = match evex.length {
267            dsl::Length::LZ | dsl::Length::LIG => unimplemented!(),
268            dsl::Length::L128 => 16,
269            dsl::Length::L256 => 32,
270            dsl::Length::L512 => 64,
271        };
272
273        // Figure out, according to table 2-34 and 2-35 in the Intel manual,
274        // what the scaling factor is for 8-bit displacements to pass through to
275        // encoding.
276        let evex_scaling = Some(match evex.tuple_type {
277            dsl::TupleType::Full => {
278                assert!(!bcast);
279                length_bytes
280            }
281            dsl::TupleType::Half => {
282                assert!(!bcast);
283                length_bytes / 2
284            }
285            dsl::TupleType::FullMem => length_bytes,
286            // FIXME: according to table 2-35 this needs to take into account
287            // "InputSize" which isn't accounted for in our `Evex` structure at
288            // this time.
289            dsl::TupleType::Tuple1Scalar => unimplemented!(),
290            dsl::TupleType::Tuple1Fixed => unimplemented!(),
291            dsl::TupleType::Tuple2 => unimplemented!(),
292            dsl::TupleType::Tuple4 => unimplemented!(),
293            dsl::TupleType::Tuple8 => 32,
294            dsl::TupleType::HalfMem => length_bytes / 2,
295            dsl::TupleType::QuarterMem => length_bytes / 4,
296            dsl::TupleType::EigthMem => length_bytes / 8,
297            dsl::TupleType::Mem128 => 16,
298            dsl::TupleType::Movddup => match evex.length {
299                dsl::Length::LZ | dsl::Length::LIG => unimplemented!(),
300                dsl::Length::L128 => 8,
301                dsl::Length::L256 => 32,
302                dsl::Length::L512 => 64,
303            },
304        });
305
306        self.generate_vex_or_evex_prefix(f, "EvexPrefix", &bits, is4, evex_scaling, || {
307            evex.unwrap_digit()
308        })
309    }
310
311    /// Helper function to generate either a vex or evex prefix, mostly handling
312    /// all the operand formats and structures here the same between the two
313    /// forms.
314    fn generate_vex_or_evex_prefix(
315        &self,
316        f: &mut Formatter,
317        prefix_type: &str,
318        bits: &str,
319        is4: bool,
320        evex_scaling: Option<i8>,
321        unwrap_digit: impl Fn() -> Option<u8>,
322    ) -> ModRmStyle {
323        use dsl::OperandKind::{FixedReg, Imm, Mem, Reg, RegMem};
324
325        let style = match self.operands_by_kind().as_slice() {
326            [Reg(reg), Reg(vvvv), Reg(rm)] => {
327                assert!(!is4);
328                fmtln!(f, "let reg = self.{reg}.enc();");
329                fmtln!(f, "let vvvv = self.{vvvv}.enc();");
330                fmtln!(f, "let rm = self.{rm}.encode_bx_regs();");
331                fmtln!(
332                    f,
333                    "let prefix = {prefix_type}::three_op(reg, vvvv, rm, {bits});"
334                );
335                ModRmStyle::Reg {
336                    reg: ModRmReg::Reg(*reg),
337                    rm: *rm,
338                }
339            }
340            [Reg(reg), Reg(vvvv), RegMem(rm)]
341            | [Reg(reg), Reg(vvvv), Mem(rm)]
342            | [Reg(reg), Reg(vvvv), RegMem(rm), Imm(_) | FixedReg(_)]
343            | [Reg(reg), RegMem(rm), Reg(vvvv)] => {
344                assert!(!is4);
345                fmtln!(f, "let reg = self.{reg}.enc();");
346                fmtln!(f, "let vvvv = self.{vvvv}.enc();");
347                fmtln!(f, "let rm = self.{rm}.encode_bx_regs();");
348                fmtln!(
349                    f,
350                    "let prefix = {prefix_type}::three_op(reg, vvvv, rm, {bits});"
351                );
352                ModRmStyle::RegMem {
353                    reg: ModRmReg::Reg(*reg),
354                    rm: *rm,
355                    evex_scaling,
356                }
357            }
358            [Reg(reg), Reg(vvvv), RegMem(rm), Reg(r_is4)] => {
359                assert!(is4);
360                fmtln!(f, "let reg = self.{reg}.enc();");
361                fmtln!(f, "let vvvv = self.{vvvv}.enc();");
362                fmtln!(f, "let rm = self.{rm}.encode_bx_regs();");
363                fmtln!(
364                    f,
365                    "let prefix = {prefix_type}::three_op(reg, vvvv, rm, {bits});"
366                );
367                ModRmStyle::RegMemIs4 {
368                    reg: ModRmReg::Reg(*reg),
369                    rm: *rm,
370                    is4: *r_is4,
371                    evex_scaling,
372                }
373            }
374            [Reg(reg_or_vvvv), RegMem(rm)]
375            | [RegMem(rm), Reg(reg_or_vvvv)]
376            | [Reg(reg_or_vvvv), RegMem(rm), Imm(_)] => match unwrap_digit() {
377                Some(digit) => {
378                    assert!(!is4);
379                    let vvvv = reg_or_vvvv;
380                    fmtln!(f, "let reg = {digit:#x};");
381                    fmtln!(f, "let vvvv = self.{vvvv}.enc();");
382                    fmtln!(f, "let rm = self.{rm}.encode_bx_regs();");
383                    fmtln!(
384                        f,
385                        "let prefix = {prefix_type}::three_op(reg, vvvv, rm, {bits});"
386                    );
387                    ModRmStyle::RegMem {
388                        reg: ModRmReg::Digit(digit),
389                        rm: *rm,
390                        evex_scaling,
391                    }
392                }
393                None => {
394                    assert!(!is4);
395                    let reg = reg_or_vvvv;
396                    fmtln!(f, "let reg = self.{reg}.enc();");
397                    fmtln!(f, "let rm = self.{rm}.encode_bx_regs();");
398                    fmtln!(f, "let prefix = {prefix_type}::two_op(reg, rm, {bits});");
399                    ModRmStyle::RegMem {
400                        reg: ModRmReg::Reg(*reg),
401                        rm: *rm,
402                        evex_scaling,
403                    }
404                }
405            },
406            [Reg(reg_or_vvvv), Reg(rm)] | [Reg(reg_or_vvvv), Reg(rm), Imm(_)] => {
407                match unwrap_digit() {
408                    Some(digit) => {
409                        assert!(!is4);
410                        let vvvv = reg_or_vvvv;
411                        fmtln!(f, "let reg = {digit:#x};");
412                        fmtln!(f, "let vvvv = self.{vvvv}.enc();");
413                        fmtln!(f, "let rm = self.{rm}.encode_bx_regs();");
414                        fmtln!(
415                            f,
416                            "let prefix = {prefix_type}::three_op(reg, vvvv, rm, {bits});"
417                        );
418                        ModRmStyle::Reg {
419                            reg: ModRmReg::Digit(digit),
420                            rm: *rm,
421                        }
422                    }
423                    None => {
424                        assert!(!is4);
425                        let reg = reg_or_vvvv;
426                        fmtln!(f, "let reg = self.{reg}.enc();");
427                        fmtln!(f, "let rm = self.{rm}.encode_bx_regs();");
428                        fmtln!(f, "let prefix = {prefix_type}::two_op(reg, rm, {bits});");
429                        ModRmStyle::Reg {
430                            reg: ModRmReg::Reg(*reg),
431                            rm: *rm,
432                        }
433                    }
434                }
435            }
436            [Reg(reg), Mem(rm)] | [Mem(rm), Reg(reg)] | [RegMem(rm), Reg(reg), Imm(_)] => {
437                assert!(!is4);
438                fmtln!(f, "let reg = self.{reg}.enc();");
439                fmtln!(f, "let rm = self.{rm}.encode_bx_regs();");
440                fmtln!(f, "let prefix = {prefix_type}::two_op(reg, rm, {bits});");
441                ModRmStyle::RegMem {
442                    reg: ModRmReg::Reg(*reg),
443                    rm: *rm,
444                    evex_scaling,
445                }
446            }
447            unknown => unimplemented!("unknown pattern: {unknown:?}"),
448        };
449
450        fmtln!(f, "prefix.encode(buf);");
451        style
452    }
453
454    fn generate_modrm_byte(&self, f: &mut Formatter, modrm_style: ModRmStyle) {
455        let operands = self.operands_by_kind();
456        let bytes_at_end = match operands.as_slice() {
457            [.., dsl::OperandKind::Imm(imm)] => imm.bytes(),
458            _ => match modrm_style {
459                ModRmStyle::RegMemIs4 { .. } => 1,
460                _ => 0,
461            },
462        };
463
464        f.empty_line();
465
466        match modrm_style {
467            ModRmStyle::None => f.comment("No need to emit a ModRM byte."),
468            _ => f.comment("Emit ModR/M byte."),
469        }
470
471        match modrm_style {
472            ModRmStyle::None => {}
473            ModRmStyle::RegMem {
474                reg,
475                rm,
476                evex_scaling,
477            }
478            | ModRmStyle::RegMemIs4 {
479                reg,
480                rm,
481                is4: _,
482                evex_scaling,
483            } => {
484                match reg {
485                    ModRmReg::Reg(reg) => fmtln!(f, "let reg = self.{reg}.enc();"),
486                    ModRmReg::Digit(digit) => fmtln!(f, "let reg = {digit:#x};"),
487                }
488                fmtln!(
489                    f,
490                    "self.{rm}.encode_rex_suffixes(buf, reg, {bytes_at_end}, {evex_scaling:?});"
491                );
492            }
493            ModRmStyle::Reg { reg, rm } => {
494                match reg {
495                    ModRmReg::Reg(reg) => fmtln!(f, "let reg = self.{reg}.enc();"),
496                    ModRmReg::Digit(digit) => fmtln!(f, "let reg = {digit:#x};"),
497                }
498                fmtln!(f, "self.{rm}.encode_modrm(buf, reg);");
499            }
500        }
501    }
502
503    fn generate_immediate(&self, f: &mut Formatter, modrm_style: ModRmStyle) {
504        use dsl::OperandKind::Imm;
505        match self.operands_by_kind().as_slice() {
506            [prefix @ .., Imm(imm)] => {
507                assert!(!prefix.iter().any(|o| matches!(o, Imm(_))));
508                f.empty_line();
509                f.comment("Emit immediate.");
510                fmtln!(f, "self.{imm}.encode(buf);");
511            }
512            unknown => {
513                if let ModRmStyle::RegMemIs4 { is4, .. } = modrm_style {
514                    fmtln!(f, "buf.put1(self.{is4}.enc() << 4);");
515                }
516
517                // Do nothing: no immediates expected.
518                assert!(!unknown.iter().any(|o| matches!(o, Imm(_))));
519            }
520        }
521    }
522}
523
524impl dsl::Rex {
525    // `buf.put1(...);`
526    fn generate_opcodes(&self, f: &mut Formatter, first_op: Option<&dsl::Location>) {
527        f.empty_line();
528        f.comment("Emit opcode(s).");
529        if self.opcodes.escape {
530            fmtln!(f, "buf.put1(0x0f);");
531        }
532        if self.opcode_mod.is_some() {
533            let first_op = first_op.expect("Expected first operand for opcode_mod");
534            assert!(matches!(first_op.kind(), dsl::OperandKind::Reg(_)));
535            fmtln!(f, "let low_bits = self.{first_op}.enc() & 0b111;");
536            fmtln!(f, "buf.put1(0x{:x} | low_bits);", self.opcodes.primary);
537        } else {
538            fmtln!(f, "buf.put1(0x{:x});", self.opcodes.primary);
539        }
540        if let Some(secondary) = self.opcodes.secondary {
541            fmtln!(f, "buf.put1(0x{:x});", secondary);
542        }
543    }
544}
545
546impl dsl::Vex {
547    // `buf.put1(...);`
548    fn generate_opcode(&self, f: &mut Formatter) {
549        f.empty_line();
550        f.comment("Emit opcode.");
551        fmtln!(f, "buf.put1(0x{:x});", self.opcode);
552    }
553}
554
555impl dsl::Evex {
556    // `buf.put1(...);`
557    fn generate_opcode(&self, f: &mut Formatter) {
558        f.empty_line();
559        f.comment("Emit opcode.");
560        fmtln!(f, "buf.put1(0x{:x});", self.opcode);
561    }
562}