cranelift_assembler_x64_meta/
dsl.rs

1//! Defines a domain-specific language (DSL) for describing x64 instructions.
2//!
3//! This language is intended to be:
4//! - compact--i.e., define an x64 instruction on a single line, and
5//! - a close-to-direct mapping of what we read in the x64 reference manual.
6
7mod custom;
8mod encoding;
9mod features;
10pub mod format;
11
12pub use custom::{Custom, Customization};
13pub use encoding::{Encoding, ModRmKind, OpcodeMod};
14pub use encoding::{
15    Group1Prefix, Group2Prefix, Group3Prefix, Group4Prefix, Opcodes, Prefixes, Rex, rex,
16};
17pub use encoding::{Vex, VexEscape, VexLength, VexPrefix, vex};
18pub use features::{ALL_FEATURES, Feature, Features};
19pub use format::{Eflags, Extension, Format, Location, Mutability, Operand, OperandKind, RegClass};
20pub use format::{align, fmt, implicit, r, rw, sxl, sxq, sxw, w};
21
22/// Abbreviated constructor for an x64 instruction.
23pub fn inst(
24    mnemonic: impl Into<String>,
25    format: Format,
26    encoding: impl Into<Encoding>,
27    features: impl Into<Features>,
28) -> Inst {
29    let encoding = encoding.into();
30    encoding.validate(&format.operands);
31    Inst {
32        mnemonic: mnemonic.into(),
33        format,
34        encoding,
35        features: features.into(),
36        alternate: None,
37        has_trap: false,
38        custom: Custom::default(),
39    }
40}
41
42/// An x64 instruction.
43///
44/// Use [`inst`] to construct this within the
45/// [`instructions`](super::instructions) module. This structure is designed to
46/// represent all of the information for one instruction (a table row) in the
47/// x64 _Instruction Set Reference_ or at least enough to generate code to emit
48/// the instruction.
49pub struct Inst {
50    /// The instruction name as represented in the x64 reference manual. This is
51    /// the pretty-printed name used for disassembly. Multiple instructions may
52    /// have the same mnemonic, though; the combination of this field and the
53    /// format name must be unique (see [`Inst::name`]).
54    pub mnemonic: String,
55    /// The instruction operands, typically represented in the "Instruction"
56    /// column of the x64 reference manual.
57    pub format: Format,
58    /// The instruction encoding, typically represented in the "Opcode" column
59    /// of the x64 reference manual.
60    pub encoding: Encoding,
61    /// The CPU features required to use this instruction; this combines the
62    /// "64-bit/32-bit Mode Support" and "CPUID Feature Flag" columns of the x64
63    /// reference manual.
64    pub features: Features,
65    /// An alternate version of this instruction, if it exists.
66    pub alternate: Option<Alternate>,
67    /// Whether or not this instruction can trap and thus needs a `TrapCode`
68    /// payload in the instruction itself.
69    pub has_trap: bool,
70    /// Whether or not this instruction uses custom, external functions
71    /// instead of Rust code generated by this crate.
72    pub custom: Custom,
73}
74
75impl Inst {
76    /// The unique name for this instruction.
77    ///
78    /// To avoid ambiguity, this name combines the instruction mnemonic and the
79    /// format name in snake case. This is used in generated code to name the
80    /// instruction `struct` and builder functions.
81    ///
82    /// In rare cases, this `<mnemonic>_<format>` scheme does not uniquely
83    /// identify an instruction in x64 ISA (e.g., some extended versions,
84    /// VEX/EVEX). In these cases, we append a minimal identifier to
85    /// the format name (e.g., `sx*`) to keep this unique.
86    #[must_use]
87    pub fn name(&self) -> String {
88        format!(
89            "{}_{}",
90            self.mnemonic.to_lowercase(),
91            self.format.name.to_lowercase()
92        )
93    }
94
95    /// Flags this instruction as being able to trap, so needs a `TrapCode` at
96    /// compile time to track this.
97    pub fn has_trap(mut self) -> Self {
98        self.has_trap = true;
99        self
100    }
101
102    /// Indicate this instruction as needing custom processing.
103    pub fn custom(mut self, custom: impl Into<Custom>) -> Self {
104        self.custom = custom.into();
105        self
106    }
107
108    /// Sets the alternate version of this instruction, if it exists.
109    pub fn alt(mut self, feature: Feature, alternate: impl Into<String>) -> Self {
110        self.alternate = Some(Alternate {
111            feature,
112            name: alternate.into(),
113        });
114        self
115    }
116}
117
118impl core::fmt::Display for Inst {
119    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
120        let Inst {
121            mnemonic: name,
122            format,
123            encoding,
124            features,
125            alternate,
126            has_trap,
127            custom,
128        } = self;
129        write!(f, "{name}: {format} => {encoding}")?;
130        if !features.is_empty() {
131            write!(f, " [{features}]")?;
132        }
133        if let Some(alternate) = alternate {
134            write!(f, " (alternate: {alternate})")?;
135        }
136        if *has_trap {
137            write!(f, " has_trap")?;
138        }
139        if !custom.is_empty() {
140            write!(f, " custom({custom})")?;
141        }
142        Ok(())
143    }
144}
145
146/// An alternate version of an instruction.
147///
148/// Some AVX-specific context: some instructions have the same semantics in
149/// their SSE and AVX encodings. In these cases, we use this structure to record
150/// the name of the upgraded version of the instruction, allowing us to replace
151/// the SSE instruction with its AVX version during lowering. For AVX, using the
152/// VEX-encoded instruction is typically better than its legacy SSE version:
153/// - VEX can encode three operands
154/// - VEX allows unaligned memory access (avoids additional `MOVUPS`)
155/// - VEX can compact byte-long prefixes into the VEX prefix
156/// - VEX instructions zero the upper bits of XMM registers by default
157pub struct Alternate {
158    /// Indicate the feature check to use to trigger the replacement.
159    pub feature: Feature,
160    /// The full name (see [`Inst::name`]) of the instruction used for
161    /// replacement.
162    pub name: String,
163}
164
165impl core::fmt::Display for Alternate {
166    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
167        write!(f, "{} => {}", self.feature, self.name)
168    }
169}