Skip to main content

cranelift_assembler_x64_meta/dsl/
format.rs

1//! A DSL for describing x64 instruction formats--the shape of the operands.
2//!
3//! Every instruction has a format that corresponds to its encoding's expected
4//! operands. The format is what allows us to generate code that accepts
5//! operands of the right type and check that the operands are used in the right
6//! way.
7//!
8//! The entry point for this module is [`fmt`].
9//!
10//! ```
11//! # use cranelift_assembler_x64_meta::dsl::{fmt, rw, r, Location::*};
12//! let f = fmt("rm", [rw(r32), r(rm32)]);
13//! assert_eq!(f.to_string(), "rm(r32[rw], rm32)")
14//! ```
15
16/// An abbreviated constructor for an instruction "format."
17///
18/// These model what the reference manual calls "instruction operand encodings,"
19/// usually defined in a table after an instruction's opcodes.
20pub fn fmt(name: impl Into<String>, operands: impl IntoIterator<Item = Operand>) -> Format {
21    Format {
22        name: name.into(),
23        operands: operands.into_iter().collect(),
24        eflags: Eflags::default(),
25    }
26}
27
28/// An abbreviated constructor for a "read-write" operand.
29///
30/// # Panics
31///
32/// This function panics if the location is an immediate (i.e., an immediate
33/// cannot be written to).
34#[must_use]
35pub fn rw(op: impl Into<Operand>) -> Operand {
36    let op = op.into();
37    assert!(!matches!(op.location.kind(), OperandKind::Imm(_)));
38    Operand {
39        mutability: Mutability::ReadWrite,
40        ..op
41    }
42}
43
44/// An abbreviated constructor for a "read" operand.
45#[must_use]
46pub fn r(op: impl Into<Operand>) -> Operand {
47    let op = op.into();
48    assert!(op.mutability.is_read());
49    op
50}
51
52/// An abbreviated constructor for a "write" operand.
53#[must_use]
54pub fn w(op: impl Into<Operand>) -> Operand {
55    let op = op.into();
56    Operand {
57        mutability: Mutability::Write,
58        ..op
59    }
60}
61
62/// An abbreviated constructor for a memory operand that requires alignment.
63pub fn align(location: Location) -> Operand {
64    assert!(location.uses_memory());
65    Operand {
66        align: true,
67        ..Operand::from(location)
68    }
69}
70
71/// An abbreviated constructor for an operand that is used by the instruction
72/// but not visible in its disassembly.
73pub fn implicit(location: Location) -> Operand {
74    assert!(matches!(location.kind(), OperandKind::FixedReg(_)));
75    Operand {
76        implicit: true,
77        ..Operand::from(location)
78    }
79}
80
81/// An abbreviated constructor for a "read" operand that is sign-extended to 64
82/// bits (quadword).
83///
84/// # Panics
85///
86/// This function panics if the location size is too large to extend.
87#[must_use]
88pub fn sxq(location: Location) -> Operand {
89    assert!(location.bits() <= 64);
90    Operand {
91        extension: Extension::SignExtendQuad,
92        ..Operand::from(location)
93    }
94}
95
96/// An abbreviated constructor for a "read" operand that is sign-extended to 32
97/// bits (longword).
98///
99/// # Panics
100///
101/// This function panics if the location size is too large to extend.
102#[must_use]
103pub fn sxl(location: Location) -> Operand {
104    assert!(location.bits() <= 32);
105    Operand {
106        extension: Extension::SignExtendLong,
107        ..Operand::from(location)
108    }
109}
110
111/// An abbreviated constructor for a "read" operand that is sign-extended to 16
112/// bits (word).
113///
114/// # Panics
115///
116/// This function panics if the location size is too large to extend.
117#[must_use]
118pub fn sxw(location: Location) -> Operand {
119    assert!(location.bits() <= 16);
120    Operand {
121        extension: Extension::SignExtendWord,
122        ..Operand::from(location)
123    }
124}
125
126/// A format describes the operands for an instruction.
127#[derive(Clone)]
128pub struct Format {
129    /// This name, when combined with the instruction mnemonic, uniquely
130    /// identifies an instruction. The reference manual uses this name in the
131    /// "Instruction Operand Encoding" table.
132    pub name: String,
133    /// These operands should match the "Instruction" column in the reference
134    /// manual.
135    pub operands: Vec<Operand>,
136    /// This should match eflags description of an instruction.
137    pub eflags: Eflags,
138}
139
140impl Format {
141    /// Iterate over the operand locations.
142    pub fn locations(&self) -> impl Iterator<Item = &Location> + '_ {
143        self.operands.iter().map(|o| &o.location)
144    }
145
146    /// Return the location of the operand that uses memory, if any; return
147    /// `None` otherwise.
148    pub fn uses_memory(&self) -> Option<Location> {
149        debug_assert!(
150            self.locations()
151                .copied()
152                .filter(Location::uses_memory)
153                .count()
154                <= 1
155        );
156        self.locations().copied().find(Location::uses_memory)
157    }
158
159    /// Return `true` if any of the operands accepts a register (i.e., not an
160    /// immediate); return `false` otherwise.
161    #[must_use]
162    pub fn uses_register(&self) -> bool {
163        self.locations().any(Location::uses_register)
164    }
165
166    /// Collect into operand kinds.
167    pub fn operands_by_kind(&self) -> Vec<OperandKind> {
168        self.locations().map(Location::kind).collect()
169    }
170
171    /// Set the EFLAGS mutability for this instruction.
172    pub fn flags(mut self, eflags: Eflags) -> Self {
173        self.eflags = eflags;
174        self
175    }
176
177    /// Return true if an instruction uses EFLAGS.
178    pub fn uses_eflags(&self) -> bool {
179        self.eflags != Eflags::None
180    }
181}
182
183impl core::fmt::Display for Format {
184    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
185        let Format {
186            name,
187            operands,
188            eflags,
189        } = self;
190        let operands = operands
191            .iter()
192            .map(|operand| format!("{operand}"))
193            .collect::<Vec<_>>()
194            .join(", ");
195        write!(f, "{name}({operands})")?;
196
197        if *eflags != Eflags::None {
198            write!(f, "[flags:{eflags}]")?;
199        }
200
201        Ok(())
202    }
203}
204
205/// An x64 operand.
206///
207/// This is designed to look and feel like the operands as expressed in Intel's
208/// _Instruction Set Reference_.
209///
210/// ```
211/// # use cranelift_assembler_x64_meta::dsl::{align, r, rw, sxq, Location::*};
212/// assert_eq!(r(r8).to_string(), "r8");
213/// assert_eq!(rw(rm16).to_string(), "rm16[rw]");
214/// assert_eq!(sxq(imm32).to_string(), "imm32[sxq]");
215/// assert_eq!(align(xmm_m128).to_string(), "xmm_m128[align]");
216/// ```
217#[derive(Clone, Copy, Debug, PartialEq)]
218pub struct Operand {
219    /// The location of the data: memory, register, immediate.
220    pub location: Location,
221    /// An operand can be read-only or read-write.
222    pub mutability: Mutability,
223    /// Some operands are sign- or zero-extended.
224    pub extension: Extension,
225    /// Some memory operands require alignment; `true` indicates that the memory
226    /// address used in the operand must align to the size of the operand (e.g.,
227    /// `m128` must be 16-byte aligned).
228    pub align: bool,
229    /// Some register operands are implicit: that is, they do not appear in the
230    /// disassembled output even though they are used in the instruction.
231    pub implicit: bool,
232}
233
234impl core::fmt::Display for Operand {
235    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
236        let Self {
237            location,
238            mutability,
239            extension,
240            align,
241            implicit,
242        } = self;
243        write!(f, "{location}")?;
244        let mut flags = vec![];
245        if !matches!(mutability, Mutability::Read) {
246            flags.push(format!("{mutability}"));
247        }
248        if !matches!(extension, Extension::None) {
249            flags.push(format!("{extension}"));
250        }
251        if *align != false {
252            flags.push("align".to_owned());
253        }
254        if *implicit {
255            flags.push("implicit".to_owned());
256        }
257        if !flags.is_empty() {
258            write!(f, "[{}]", flags.join(","))?;
259        }
260        Ok(())
261    }
262}
263
264impl From<Location> for Operand {
265    fn from(location: Location) -> Self {
266        let mutability = Mutability::default();
267        let extension = Extension::default();
268        let align = false;
269        let implicit = false;
270        Self {
271            location,
272            mutability,
273            extension,
274            align,
275            implicit,
276        }
277    }
278}
279
280/// The kind of register used in a [`Location`].
281pub enum RegClass {
282    Gpr,
283    Xmm,
284}
285
286impl core::fmt::Display for RegClass {
287    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
288        match self {
289            RegClass::Gpr => write!(f, "Gpr"),
290            RegClass::Xmm => write!(f, "Xmm"),
291        }
292    }
293}
294
295/// An operand location, as expressed in Intel's _Instruction Set Reference_.
296#[derive(Clone, Copy, Debug, PartialEq)]
297#[allow(non_camel_case_types, reason = "makes DSL definitions easier to read")]
298pub enum Location {
299    // Fixed registers.
300    al,
301    ax,
302    eax,
303    rax,
304    rbx,
305    dx,
306    edx,
307    rdx,
308    cl,
309    rcx,
310    xmm0,
311
312    // Immediate values.
313    imm8,
314    imm16,
315    imm32,
316    imm64,
317
318    // General-purpose registers, and their memory forms.
319    r8,
320    r16,
321    r32,
322    r32a,
323    r32b,
324    r64,
325    r64a,
326    r64b,
327    rm8,
328    rm16,
329    rm32,
330    rm64,
331
332    // XMM registers, and their memory forms.
333    xmm1,
334    xmm2,
335    xmm3,
336    xmm_m8,
337    xmm_m16,
338    xmm_m32,
339    xmm_m64,
340    xmm_m128,
341
342    // Memory-only locations.
343    m8,
344    m16,
345    m32,
346    m64,
347    m128,
348}
349
350impl Location {
351    /// Return the number of bits accessed.
352    #[must_use]
353    pub fn bits(&self) -> u16 {
354        use Location::*;
355        match self {
356            al | cl | imm8 | r8 | rm8 | m8 | xmm_m8 => 8,
357            ax | dx | imm16 | r16 | rm16 | m16 | xmm_m16 => 16,
358            eax | edx | imm32 | r32 | r32a | r32b | rm32 | m32 | xmm_m32 => 32,
359            rax | rbx | rcx | rdx | imm64 | r64 | r64a | r64b | rm64 | m64 | xmm_m64 => 64,
360            xmm1 | xmm2 | xmm3 | xmm_m128 | xmm0 | m128 => 128,
361        }
362    }
363
364    /// Return the number of bytes accessed, for convenience.
365    #[must_use]
366    pub fn bytes(&self) -> u16 {
367        self.bits() / 8
368    }
369
370    /// Return `true` if the location accesses memory; `false` otherwise.
371    #[must_use]
372    pub fn uses_memory(&self) -> bool {
373        use OperandKind::*;
374        match self.kind() {
375            FixedReg(_) | Imm(_) | Reg(_) => false,
376            RegMem(_) | Mem(_) => true,
377        }
378    }
379
380    /// Return `true` if this operand is always memory (i.e., not a `r/m` that
381    /// may hold a register).
382    #[must_use]
383    pub fn is_memory_only(&self) -> bool {
384        matches!(self.kind(), OperandKind::Mem(_))
385    }
386
387    /// Return `true` if any of the operands accepts a register (i.e., not an
388    /// immediate); return `false` otherwise.
389    #[must_use]
390    pub fn uses_register(&self) -> bool {
391        use OperandKind::*;
392        match self.kind() {
393            Imm(_) => false,
394            FixedReg(_) | Reg(_) | RegMem(_) | Mem(_) => true,
395        }
396    }
397
398    /// Convert the location to an [`OperandKind`].
399    #[must_use]
400    pub fn kind(&self) -> OperandKind {
401        use Location::*;
402        match self {
403            al | ax | eax | rax | rbx | cl | rcx | dx | edx | rdx | xmm0 => {
404                OperandKind::FixedReg(*self)
405            }
406            imm8 | imm16 | imm32 | imm64 => OperandKind::Imm(*self),
407            r8 | r16 | r32 | r32a | r32b | r64 | r64a | r64b | xmm1 | xmm2 | xmm3 => {
408                OperandKind::Reg(*self)
409            }
410            rm8 | rm16 | rm32 | rm64 | xmm_m8 | xmm_m16 | xmm_m32 | xmm_m64 | xmm_m128 => {
411                OperandKind::RegMem(*self)
412            }
413            m8 | m16 | m32 | m64 | m128 => OperandKind::Mem(*self),
414        }
415    }
416
417    /// If a location directly uses data from a register, return the register
418    /// class; otherwise, return `None`. Memory-only locations, though their
419    /// address is stored in a register, use data from memory and thus also
420    /// return `None`.
421    #[must_use]
422    pub fn reg_class(&self) -> Option<RegClass> {
423        use Location::*;
424        match self {
425            imm8 | imm16 | imm32 | imm64 | m8 | m16 | m32 | m64 | m128 => None,
426            al | ax | eax | rax | rbx | cl | rcx | dx | edx | rdx | r8 | r16 | r32 | r32a
427            | r32b | r64 | r64a | r64b | rm8 | rm16 | rm32 | rm64 => Some(RegClass::Gpr),
428            xmm1 | xmm2 | xmm3 | xmm_m8 | xmm_m16 | xmm_m32 | xmm_m64 | xmm_m128 | xmm0 => {
429                Some(RegClass::Xmm)
430            }
431        }
432    }
433}
434
435impl core::fmt::Display for Location {
436    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
437        use Location::*;
438        match self {
439            imm8 => write!(f, "imm8"),
440            imm16 => write!(f, "imm16"),
441            imm32 => write!(f, "imm32"),
442            imm64 => write!(f, "imm64"),
443
444            al => write!(f, "al"),
445            ax => write!(f, "ax"),
446            eax => write!(f, "eax"),
447            rax => write!(f, "rax"),
448            rbx => write!(f, "rbx"),
449            cl => write!(f, "cl"),
450            rcx => write!(f, "rcx"),
451            dx => write!(f, "dx"),
452            edx => write!(f, "edx"),
453            rdx => write!(f, "rdx"),
454            xmm0 => write!(f, "xmm0"),
455
456            r8 => write!(f, "r8"),
457            r16 => write!(f, "r16"),
458            r32 => write!(f, "r32"),
459            r32a => write!(f, "r32a"),
460            r32b => write!(f, "r32b"),
461            r64 => write!(f, "r64"),
462            r64a => write!(f, "r64a"),
463            r64b => write!(f, "r64b"),
464            rm8 => write!(f, "rm8"),
465            rm16 => write!(f, "rm16"),
466            rm32 => write!(f, "rm32"),
467            rm64 => write!(f, "rm64"),
468
469            xmm1 => write!(f, "xmm1"),
470            xmm2 => write!(f, "xmm2"),
471            xmm3 => write!(f, "xmm3"),
472            xmm_m8 => write!(f, "xmm_m8"),
473            xmm_m16 => write!(f, "xmm_m16"),
474            xmm_m32 => write!(f, "xmm_m32"),
475            xmm_m64 => write!(f, "xmm_m64"),
476            xmm_m128 => write!(f, "xmm_m128"),
477
478            m8 => write!(f, "m8"),
479            m16 => write!(f, "m16"),
480            m32 => write!(f, "m32"),
481            m64 => write!(f, "m64"),
482            m128 => write!(f, "m128"),
483        }
484    }
485}
486
487/// Organize the operand locations by kind.
488///
489/// ```
490/// # use cranelift_assembler_x64_meta::dsl::{OperandKind, Location};
491/// let k: OperandKind = Location::imm32.kind();
492/// ```
493#[derive(Clone, Copy, Debug)]
494pub enum OperandKind {
495    FixedReg(Location),
496    Imm(Location),
497    Reg(Location),
498    RegMem(Location),
499    Mem(Location),
500}
501
502/// x64 operands can be mutable or not.
503///
504/// ```
505/// # use cranelift_assembler_x64_meta::dsl::{r, rw, Location::r8, Mutability};
506/// assert_eq!(r(r8).mutability, Mutability::Read);
507/// assert_eq!(rw(r8).mutability, Mutability::ReadWrite);
508/// ```
509#[derive(Clone, Copy, Debug, PartialEq)]
510pub enum Mutability {
511    Read,
512    ReadWrite,
513    Write,
514}
515
516impl Mutability {
517    /// Returns whether this represents a read of the operand in question.
518    ///
519    /// Note that for read/write operands this returns `true`.
520    pub fn is_read(&self) -> bool {
521        match self {
522            Mutability::Read | Mutability::ReadWrite => true,
523            Mutability::Write => false,
524        }
525    }
526
527    /// Returns whether this represents a write of the operand in question.
528    ///
529    /// Note that for read/write operands this returns `true`.
530    pub fn is_write(&self) -> bool {
531        match self {
532            Mutability::Read => false,
533            Mutability::ReadWrite | Mutability::Write => true,
534        }
535    }
536}
537
538impl Default for Mutability {
539    fn default() -> Self {
540        Self::Read
541    }
542}
543
544impl core::fmt::Display for Mutability {
545    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
546        match self {
547            Self::Read => write!(f, "r"),
548            Self::ReadWrite => write!(f, "rw"),
549            Self::Write => write!(f, "w"),
550        }
551    }
552}
553
554/// x64 operands may be sign- or zero-extended.
555///
556/// ```
557/// # use cranelift_assembler_x64_meta::dsl::{Location::r8, sxw, Extension};
558/// assert_eq!(sxw(r8).extension, Extension::SignExtendWord);
559/// ```
560#[derive(Clone, Copy, Debug, PartialEq)]
561pub enum Extension {
562    None,
563    SignExtendQuad,
564    SignExtendLong,
565    SignExtendWord,
566}
567
568impl Extension {
569    /// Check if the extension is sign-extended.
570    #[must_use]
571    pub fn is_sign_extended(&self) -> bool {
572        matches!(
573            self,
574            Self::SignExtendQuad | Self::SignExtendLong | Self::SignExtendWord
575        )
576    }
577}
578
579impl Default for Extension {
580    fn default() -> Self {
581        Self::None
582    }
583}
584
585impl core::fmt::Display for Extension {
586    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
587        match self {
588            Extension::None => write!(f, ""),
589            Extension::SignExtendQuad => write!(f, "sxq"),
590            Extension::SignExtendLong => write!(f, "sxl"),
591            Extension::SignExtendWord => write!(f, "sxw"),
592        }
593    }
594}
595
596/// Describes if an instruction uses EFLAGS, and whether it reads, writes, or
597/// reads/writes the EFLAGS register.
598/// In the future, we might want to model specific EFLAGS bits instead of the
599/// entire EFLAGS register.
600/// Some related discussion in this GitHub issue
601/// <https://github.com/bytecodealliance/wasmtime/issues/10298>
602#[derive(Clone, Copy, Debug, PartialEq)]
603pub enum Eflags {
604    None,
605    R,
606    W,
607    RW,
608}
609
610impl Eflags {
611    /// Returns whether this represents a read of any bit in the EFLAGS
612    /// register.
613    pub fn is_read(&self) -> bool {
614        match self {
615            Eflags::None | Eflags::W => false,
616            Eflags::R | Eflags::RW => true,
617        }
618    }
619
620    /// Returns whether this represents a writes to any bit in the EFLAGS
621    /// register.
622    pub fn is_write(&self) -> bool {
623        match self {
624            Eflags::None | Eflags::R => false,
625            Eflags::W | Eflags::RW => true,
626        }
627    }
628}
629
630impl Default for Eflags {
631    fn default() -> Self {
632        Self::None
633    }
634}
635
636impl core::fmt::Display for Eflags {
637    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
638        match self {
639            Self::None => write!(f, ""),
640            Self::R => write!(f, "r"),
641            Self::W => write!(f, "w"),
642            Self::RW => write!(f, "rw"),
643        }
644    }
645}