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, nd: bool) -> String {
58        self.ordered_operands(nd, false)
59    }
60
61    /// Like [`Self::generate_att_style_operands`], but omits the fixed `%xmm0`
62    /// mask operand, which XED leaves implicit.
63    #[must_use]
64    pub(crate) fn generate_xed_style_operands(&self, nd: bool) -> String {
65        self.ordered_operands(nd, true)
66    }
67
68    /// Shared operand ordering for AT&T-style printing.
69    ///
70    /// Normally this is just the reverse of the DSL's Intel-style order. APX
71    /// "new data destination" (NDD) forms are the exception: their
72    /// architectural destination is the `vvvv`-encoded register, which the DSL
73    /// must list in the middle so that the positional slot assignment in
74    /// `generate_vex_or_evex_prefix` maps operands to `reg`/`vvvv`/`rm`
75    /// correctly. Blindly reversing would therefore print the destination in
76    /// the middle. Instead, for `ND = 1` the sources keep their DSL order and
77    /// the destination is printed last, which matches both AT&T convention and
78    /// the XED disassembly used as a fuzzing oracle.
79    #[must_use]
80    fn ordered_operands(&self, nd: bool, xed_style: bool) -> String {
81        let ops = self
82            .operands
83            .iter()
84            .filter(|o| !o.implicit && !(xed_style && o.location == dsl::Location::xmm0));
85        let ordered: Vec<&dsl::Operand> = if nd {
86            let (dst, srcs): (Vec<_>, Vec<_>) =
87                ops.partition(|o| matches!(o.mutability, dsl::Mutability::Write));
88            srcs.into_iter().chain(dst).collect()
89        } else {
90            ops.rev().collect()
91        };
92        ordered
93            .into_iter()
94            .map(|o| format!("{{{}}}", o.location))
95            .collect::<Vec<_>>()
96            .join(", ")
97    }
98
99    #[must_use]
100    pub(crate) fn generate_implicit_operands(&self) -> String {
101        let ops: Vec<_> = self
102            .operands
103            .iter()
104            .filter(|o| o.implicit)
105            .map(|o| format!("{{{}}}", o.location))
106            .collect();
107        if ops.is_empty() {
108            String::new()
109        } else {
110            format!(" ;; implicit: {}", ops.join(", "))
111        }
112    }
113
114    pub(crate) fn generate_rex_encoding(&self, f: &mut Formatter, rex: &dsl::Rex) {
115        self.generate_prefixes(f, rex);
116        let style = self.generate_rex_prefix(f, rex);
117        rex.generate_opcodes(f, self.locations().next());
118        self.generate_modrm_byte(f, style);
119        self.generate_immediate(f, style);
120    }
121
122    pub fn generate_vex_encoding(&self, f: &mut Formatter, vex: &dsl::Vex) {
123        let style = self.generate_vex_prefix(f, vex);
124        vex.generate_opcode(f);
125        self.generate_modrm_byte(f, style);
126        self.generate_immediate(f, style);
127    }
128
129    pub fn generate_evex_encoding(&self, f: &mut Formatter, evex: &dsl::Evex) {
130        let style = self.generate_evex_prefix(f, evex);
131        evex.generate_opcode(f);
132        self.generate_modrm_byte(f, style);
133        self.generate_immediate(f, style);
134    }
135
136    /// `buf.put1(...);`
137    fn generate_prefixes(&self, f: &mut Formatter, rex: &dsl::Rex) {
138        if !rex.opcodes.prefixes.is_empty() {
139            f.empty_line();
140            f.comment("Emit prefixes.");
141        }
142        if let Some(group1) = &rex.opcodes.prefixes.group1 {
143            fmtln!(f, "buf.put1({group1});");
144        }
145        if let Some(group2) = &rex.opcodes.prefixes.group2 {
146            fmtln!(f, "buf.put1({group2});");
147        }
148        if let Some(group3) = &rex.opcodes.prefixes.group3 {
149            fmtln!(f, "buf.put1({group3});");
150        }
151        if let Some(group4) = &rex.opcodes.prefixes.group4 {
152            fmtln!(f, "buf.put1({group4});");
153        }
154    }
155
156    fn generate_rex_prefix(&self, f: &mut Formatter, rex: &dsl::Rex) -> ModRmStyle {
157        use dsl::OperandKind::{FixedReg, Imm, Mem, Reg, RegMem};
158
159        // If this instruction has only immediates there's no rex/modrm/etc, so
160        // skip everything below.
161        match self.operands_by_kind().as_slice() {
162            [] | [Imm(_)] => return ModRmStyle::None,
163            _ => {}
164        }
165
166        f.empty_line();
167        f.comment("Possibly emit REX prefix.");
168
169        let find_8bit_registers =
170            |l: &dsl::Location| l.bits() == 8 && matches!(l.kind(), Reg(_) | RegMem(_));
171        let uses_8bit = self.locations().any(find_8bit_registers);
172        fmtln!(f, "let uses_8bit = {uses_8bit};");
173        fmtln!(f, "let w_bit = {};", rex.w.as_bool());
174        let bits = "w_bit, uses_8bit";
175
176        let style = match self.operands_by_kind().as_slice() {
177            [FixedReg(dst), FixedReg(_)] | [FixedReg(dst)] | [FixedReg(dst), Imm(_)] => {
178                // TODO: don't emit REX byte here.
179                assert_eq!(rex.unwrap_digit(), None);
180                fmtln!(f, "let digit = 0;");
181                fmtln!(f, "let dst = self.{dst}.enc();");
182                fmtln!(f, "let rex = RexPrefix::with_digit(digit, dst, {bits});");
183                ModRmStyle::None
184            }
185            [Reg(dst)] => {
186                assert_eq!(rex.unwrap_digit(), None);
187                assert!(rex.opcode_mod.is_some());
188                fmtln!(f, "let dst = self.{dst}.enc();");
189                fmtln!(f, "let rex = RexPrefix::one_op(dst, {bits});");
190                ModRmStyle::None
191            }
192            [Reg(dst), Imm(_)] => match rex.unwrap_digit() {
193                Some(digit) => {
194                    fmtln!(f, "let digit = 0x{digit:x};");
195                    fmtln!(f, "let dst = self.{dst}.enc();");
196                    fmtln!(f, "let rex = RexPrefix::two_op(digit, dst, {bits});");
197                    ModRmStyle::Reg {
198                        reg: ModRmReg::Digit(digit),
199                        rm: *dst,
200                    }
201                }
202                None => {
203                    assert!(rex.opcode_mod.is_some());
204                    fmtln!(f, "let dst = self.{dst}.enc();");
205                    fmtln!(f, "let rex = RexPrefix::one_op(dst, {bits});");
206                    ModRmStyle::None
207                }
208            },
209            [FixedReg(_), RegMem(mem)]
210            | [FixedReg(_), FixedReg(_), RegMem(mem)]
211            | [RegMem(mem), FixedReg(_)]
212            | [Mem(mem), Imm(_)]
213            | [RegMem(mem), Imm(_)]
214            | [RegMem(mem)]
215            | [FixedReg(_), FixedReg(_), FixedReg(_), FixedReg(_), Mem(mem)] => {
216                let digit = rex.unwrap_digit().unwrap();
217                fmtln!(f, "let digit = 0x{digit:x};");
218                fmtln!(f, "let rex = self.{mem}.as_rex_prefix(digit, {bits});");
219                ModRmStyle::RegMem {
220                    reg: ModRmReg::Digit(digit),
221                    rm: *mem,
222                    evex_scaling: None,
223                }
224            }
225            [Reg(reg), RegMem(mem) | Mem(mem)]
226            | [Reg(reg), RegMem(mem), Imm(_) | FixedReg(_)]
227            | [RegMem(mem) | Mem(mem), Reg(reg)]
228            | [RegMem(mem) | Mem(mem), Reg(reg), Imm(_) | FixedReg(_)] => {
229                fmtln!(f, "let reg = self.{reg}.enc();");
230                fmtln!(f, "let rex = self.{mem}.as_rex_prefix(reg, {bits});");
231                ModRmStyle::RegMem {
232                    reg: ModRmReg::Reg(*reg),
233                    rm: *mem,
234                    evex_scaling: None,
235                }
236            }
237            [Reg(dst), Reg(src), Imm(_)] | [Reg(dst), Reg(src)] => {
238                fmtln!(f, "let reg = self.{dst}.enc();");
239                fmtln!(f, "let rm = self.{src}.enc();");
240                fmtln!(f, "let rex = RexPrefix::two_op(reg, rm, {bits});");
241                ModRmStyle::Reg {
242                    reg: ModRmReg::Reg(*dst),
243                    rm: *src,
244                }
245            }
246
247            unknown => unimplemented!("unknown pattern: {unknown:?}"),
248        };
249
250        fmtln!(f, "rex.encode(buf);");
251        style
252    }
253
254    fn generate_vex_prefix(&self, f: &mut Formatter, vex: &dsl::Vex) -> ModRmStyle {
255        f.empty_line();
256        f.comment("Emit VEX prefix.");
257        fmtln!(f, "let len = {:#03b};", vex.length.vex_bits());
258        fmtln!(f, "let pp = {:#04b};", vex.pp.map_or(0b00, |pp| pp.bits()));
259        fmtln!(f, "let mmmmm = {:#07b};", vex.mmmmm.unwrap().bits());
260        fmtln!(f, "let w = {};", vex.w.as_bool());
261        let bits = "len, pp, mmmmm, w";
262
263        self.generate_vex_or_evex_prefix(
264            f,
265            "VexPrefix",
266            &bits,
267            vex.is4,
268            None,
269            "two_op",
270            "three_op",
271            || vex.unwrap_digit(),
272        )
273    }
274
275    fn generate_evex_prefix(&self, f: &mut Formatter, evex: &dsl::Evex) -> ModRmStyle {
276        f.empty_line();
277        f.comment("Emit EVEX prefix.");
278
279        // Intel APX promotes legacy GPR instructions into "EVEX map 4" using a
280        // re-purposed payload layout (see `EvexPrefix::legacy` in the runtime
281        // assembler and section 3.1.2.3.1 of the APX spec). In that case we
282        // emit the ND/NF bits and select the `legacy_*` constructors instead of
283        // the AVX-512 ones.
284        let apx_legacy = matches!(evex.apx, Some(dsl::ApxClass::LegacyGpr));
285
286        fmtln!(f, "let pp = {:#04b};", evex.pp.map_or(0b00, |pp| pp.bits()));
287        fmtln!(f, "let mmm = {:#07b};", evex.mmm.unwrap().bits());
288        fmtln!(f, "let w = {};", evex.w.as_bool());
289
290        let (bits, two_op, three_op);
291        if apx_legacy {
292            fmtln!(f, "let nd = {};", evex.nd == Some(true));
293            fmtln!(f, "let nf = {};", evex.nf == Some(true));
294            bits = String::from("pp, mmm, w, nd, nf");
295            two_op = "legacy_two_op";
296            three_op = "legacy_three_op";
297        } else {
298            let ll = evex.length.evex_bits();
299            fmtln!(f, "let ll = {ll:#04b};");
300            // NB: when bcast is supported in the future the `evex_scaling`
301            // calculation for `Full` and `Half` below need to be updated.
302            fmtln!(f, "let bcast = false;");
303            bits = String::from("ll, pp, mmm, w, bcast");
304            two_op = "two_op";
305            three_op = "three_op";
306        }
307        let bcast = false;
308        let is4 = false;
309
310        let length_bytes = match evex.length {
311            dsl::Length::LZ | dsl::Length::LIG => unimplemented!(),
312            dsl::Length::L128 => 16,
313            dsl::Length::L256 => 32,
314            dsl::Length::L512 => 64,
315        };
316
317        // Figure out, according to table 2-34 and 2-35 in the Intel manual,
318        // what the scaling factor is for 8-bit displacements to pass through to
319        // encoding.
320        //
321        // The compressed-displacement scheme of section 2.7.5 only applies to
322        // the vector EVEX encodings. APX promotes legacy general-purpose-
323        // register instructions into extended-EVEX "map 4", and those keep
324        // legacy displacement semantics: `disp8` is a plain byte offset rather
325        // than a multiple of a tuple-derived scaling factor `N`. Scaling them
326        // would emit an offset wrong by a factor of `N`. Note this is specific
327        // to the legacy-GPR class; promoted vector instructions and APX-extended
328        // AVX-512 instructions still use compressed displacements.
329        let evex_scaling = if matches!(evex.apx, Some(dsl::ApxClass::LegacyGpr)) {
330            None
331        } else {
332            Some(match evex.tuple_type {
333                dsl::TupleType::Full => {
334                    assert!(!bcast);
335                    length_bytes
336                }
337                dsl::TupleType::Half => {
338                    assert!(!bcast);
339                    length_bytes / 2
340                }
341                dsl::TupleType::FullMem => length_bytes,
342                // FIXME: according to table 2-35 this needs to take into account
343                // "InputSize" which isn't accounted for in our `Evex` structure at
344                // this time.
345                dsl::TupleType::Tuple1Scalar => unimplemented!(),
346                dsl::TupleType::Tuple1Fixed => unimplemented!(),
347                dsl::TupleType::Tuple2 => unimplemented!(),
348                dsl::TupleType::Tuple4 => unimplemented!(),
349                dsl::TupleType::Tuple8 => 32,
350                dsl::TupleType::HalfMem => length_bytes / 2,
351                dsl::TupleType::QuarterMem => length_bytes / 4,
352                dsl::TupleType::EigthMem => length_bytes / 8,
353                dsl::TupleType::Mem128 => 16,
354                dsl::TupleType::Movddup => match evex.length {
355                    dsl::Length::LZ | dsl::Length::LIG => unimplemented!(),
356                    dsl::Length::L128 => 8,
357                    dsl::Length::L256 => 32,
358                    dsl::Length::L512 => 64,
359                },
360            })
361        };
362
363        self.generate_vex_or_evex_prefix(
364            f,
365            "EvexPrefix",
366            &bits,
367            is4,
368            evex_scaling,
369            two_op,
370            three_op,
371            || evex.unwrap_digit(),
372        )
373    }
374
375    /// Helper function to generate either a vex or evex prefix, mostly handling
376    /// all the operand formats and structures here the same between the two
377    /// forms.
378    fn generate_vex_or_evex_prefix(
379        &self,
380        f: &mut Formatter,
381        prefix_type: &str,
382        bits: &str,
383        is4: bool,
384        evex_scaling: Option<i8>,
385        two_op: &str,
386        three_op: &str,
387        unwrap_digit: impl Fn() -> Option<u8>,
388    ) -> ModRmStyle {
389        use dsl::OperandKind::{FixedReg, Imm, Mem, Reg, RegMem};
390
391        let style = match self.operands_by_kind().as_slice() {
392            [Reg(reg), Reg(vvvv), Reg(rm)] => {
393                assert!(!is4);
394                fmtln!(f, "let reg = self.{reg}.enc();");
395                fmtln!(f, "let vvvv = self.{vvvv}.enc();");
396                fmtln!(f, "let rm = self.{rm}.encode_bx_regs();");
397                fmtln!(
398                    f,
399                    "let prefix = {prefix_type}::{three_op}(reg, vvvv, rm, {bits});"
400                );
401                ModRmStyle::Reg {
402                    reg: ModRmReg::Reg(*reg),
403                    rm: *rm,
404                }
405            }
406            [Reg(reg), Reg(vvvv), RegMem(rm)]
407            | [Reg(reg), Reg(vvvv), Mem(rm)]
408            | [Reg(reg), Reg(vvvv), RegMem(rm), Imm(_) | FixedReg(_)]
409            | [Reg(reg), RegMem(rm), Reg(vvvv)] => {
410                assert!(!is4);
411                fmtln!(f, "let reg = self.{reg}.enc();");
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::RegMem {
419                    reg: ModRmReg::Reg(*reg),
420                    rm: *rm,
421                    evex_scaling,
422                }
423            }
424            [Reg(reg), Reg(vvvv), RegMem(rm), Reg(r_is4)] => {
425                assert!(is4);
426                fmtln!(f, "let reg = self.{reg}.enc();");
427                fmtln!(f, "let vvvv = self.{vvvv}.enc();");
428                fmtln!(f, "let rm = self.{rm}.encode_bx_regs();");
429                fmtln!(
430                    f,
431                    "let prefix = {prefix_type}::{three_op}(reg, vvvv, rm, {bits});"
432                );
433                ModRmStyle::RegMemIs4 {
434                    reg: ModRmReg::Reg(*reg),
435                    rm: *rm,
436                    is4: *r_is4,
437                    evex_scaling,
438                }
439            }
440            [Reg(reg_or_vvvv), RegMem(rm)]
441            | [RegMem(rm), Reg(reg_or_vvvv)]
442            | [Reg(reg_or_vvvv), RegMem(rm), Imm(_)] => match unwrap_digit() {
443                Some(digit) => {
444                    assert!(!is4);
445                    let vvvv = reg_or_vvvv;
446                    fmtln!(f, "let reg = {digit:#x};");
447                    fmtln!(f, "let vvvv = self.{vvvv}.enc();");
448                    fmtln!(f, "let rm = self.{rm}.encode_bx_regs();");
449                    fmtln!(
450                        f,
451                        "let prefix = {prefix_type}::{three_op}(reg, vvvv, rm, {bits});"
452                    );
453                    ModRmStyle::RegMem {
454                        reg: ModRmReg::Digit(digit),
455                        rm: *rm,
456                        evex_scaling,
457                    }
458                }
459                None => {
460                    assert!(!is4);
461                    let reg = reg_or_vvvv;
462                    fmtln!(f, "let reg = self.{reg}.enc();");
463                    fmtln!(f, "let rm = self.{rm}.encode_bx_regs();");
464                    fmtln!(f, "let prefix = {prefix_type}::{two_op}(reg, rm, {bits});");
465                    ModRmStyle::RegMem {
466                        reg: ModRmReg::Reg(*reg),
467                        rm: *rm,
468                        evex_scaling,
469                    }
470                }
471            },
472            [Reg(reg_or_vvvv), Reg(rm)] | [Reg(reg_or_vvvv), Reg(rm), Imm(_)] => {
473                match unwrap_digit() {
474                    Some(digit) => {
475                        assert!(!is4);
476                        let vvvv = reg_or_vvvv;
477                        fmtln!(f, "let reg = {digit:#x};");
478                        fmtln!(f, "let vvvv = self.{vvvv}.enc();");
479                        fmtln!(f, "let rm = self.{rm}.encode_bx_regs();");
480                        fmtln!(
481                            f,
482                            "let prefix = {prefix_type}::{three_op}(reg, vvvv, rm, {bits});"
483                        );
484                        ModRmStyle::Reg {
485                            reg: ModRmReg::Digit(digit),
486                            rm: *rm,
487                        }
488                    }
489                    None => {
490                        assert!(!is4);
491                        let reg = reg_or_vvvv;
492                        fmtln!(f, "let reg = self.{reg}.enc();");
493                        fmtln!(f, "let rm = self.{rm}.encode_bx_regs();");
494                        fmtln!(f, "let prefix = {prefix_type}::{two_op}(reg, rm, {bits});");
495                        ModRmStyle::Reg {
496                            reg: ModRmReg::Reg(*reg),
497                            rm: *rm,
498                        }
499                    }
500                }
501            }
502            [Reg(reg), Mem(rm)] | [Mem(rm), Reg(reg)] | [RegMem(rm), Reg(reg), Imm(_)] => {
503                assert!(!is4);
504                fmtln!(f, "let reg = self.{reg}.enc();");
505                fmtln!(f, "let rm = self.{rm}.encode_bx_regs();");
506                fmtln!(f, "let prefix = {prefix_type}::{two_op}(reg, rm, {bits});");
507                ModRmStyle::RegMem {
508                    reg: ModRmReg::Reg(*reg),
509                    rm: *rm,
510                    evex_scaling,
511                }
512            }
513            unknown => unimplemented!("unknown pattern: {unknown:?}"),
514        };
515
516        fmtln!(f, "prefix.encode(buf);");
517        style
518    }
519
520    fn generate_modrm_byte(&self, f: &mut Formatter, modrm_style: ModRmStyle) {
521        let operands = self.operands_by_kind();
522        let bytes_at_end = match operands.as_slice() {
523            [.., dsl::OperandKind::Imm(imm)] => imm.bytes(),
524            _ => match modrm_style {
525                ModRmStyle::RegMemIs4 { .. } => 1,
526                _ => 0,
527            },
528        };
529
530        f.empty_line();
531
532        match modrm_style {
533            ModRmStyle::None => f.comment("No need to emit a ModRM byte."),
534            _ => f.comment("Emit ModR/M byte."),
535        }
536
537        match modrm_style {
538            ModRmStyle::None => {}
539            ModRmStyle::RegMem {
540                reg,
541                rm,
542                evex_scaling,
543            }
544            | ModRmStyle::RegMemIs4 {
545                reg,
546                rm,
547                is4: _,
548                evex_scaling,
549            } => {
550                match reg {
551                    ModRmReg::Reg(reg) => fmtln!(f, "let reg = self.{reg}.enc();"),
552                    ModRmReg::Digit(digit) => fmtln!(f, "let reg = {digit:#x};"),
553                }
554                fmtln!(
555                    f,
556                    "self.{rm}.encode_rex_suffixes(buf, reg, {bytes_at_end}, {evex_scaling:?});"
557                );
558            }
559            ModRmStyle::Reg { reg, rm } => {
560                match reg {
561                    ModRmReg::Reg(reg) => fmtln!(f, "let reg = self.{reg}.enc();"),
562                    ModRmReg::Digit(digit) => fmtln!(f, "let reg = {digit:#x};"),
563                }
564                fmtln!(f, "self.{rm}.encode_modrm(buf, reg);");
565            }
566        }
567    }
568
569    fn generate_immediate(&self, f: &mut Formatter, modrm_style: ModRmStyle) {
570        use dsl::OperandKind::Imm;
571        match self.operands_by_kind().as_slice() {
572            [prefix @ .., Imm(imm)] => {
573                assert!(!prefix.iter().any(|o| matches!(o, Imm(_))));
574                f.empty_line();
575                f.comment("Emit immediate.");
576                fmtln!(f, "self.{imm}.encode(buf);");
577            }
578            unknown => {
579                if let ModRmStyle::RegMemIs4 { is4, .. } = modrm_style {
580                    fmtln!(f, "buf.put1(self.{is4}.enc() << 4);");
581                }
582
583                // Do nothing: no immediates expected.
584                assert!(!unknown.iter().any(|o| matches!(o, Imm(_))));
585            }
586        }
587    }
588}
589
590impl dsl::Rex {
591    // `buf.put1(...);`
592    fn generate_opcodes(&self, f: &mut Formatter, first_op: Option<&dsl::Location>) {
593        f.empty_line();
594        f.comment("Emit opcode(s).");
595        if self.opcodes.escape {
596            fmtln!(f, "buf.put1(0x0f);");
597        }
598        if self.opcode_mod.is_some() {
599            let first_op = first_op.expect("Expected first operand for opcode_mod");
600            assert!(matches!(first_op.kind(), dsl::OperandKind::Reg(_)));
601            fmtln!(f, "let low_bits = self.{first_op}.enc() & 0b111;");
602            fmtln!(f, "buf.put1(0x{:x} | low_bits);", self.opcodes.primary);
603        } else {
604            fmtln!(f, "buf.put1(0x{:x});", self.opcodes.primary);
605        }
606        if let Some(secondary) = self.opcodes.secondary {
607            fmtln!(f, "buf.put1(0x{:x});", secondary);
608        }
609    }
610}
611
612impl dsl::Vex {
613    // `buf.put1(...);`
614    fn generate_opcode(&self, f: &mut Formatter) {
615        f.empty_line();
616        f.comment("Emit opcode.");
617        fmtln!(f, "buf.put1(0x{:x});", self.opcode);
618    }
619}
620
621impl dsl::Evex {
622    // `buf.put1(...);`
623    fn generate_opcode(&self, f: &mut Formatter) {
624        f.empty_line();
625        f.comment("Emit opcode.");
626        fmtln!(f, "buf.put1(0x{:x});", self.opcode);
627    }
628}