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.
fmt(name: impl Into<String>, operands: impl IntoIterator<Item = Operand>) -> Format20 pub 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]
rw(op: impl Into<Operand>) -> Operand35 pub 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]
r(op: impl Into<Operand>) -> Operand46 pub 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]
w(op: impl Into<Operand>) -> Operand54 pub 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.
align(location: Location) -> Operand63 pub 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.
implicit(location: Location) -> Operand73 pub 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]
sxq(location: Location) -> Operand88 pub 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]
sxl(location: Location) -> Operand103 pub 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]
sxw(location: Location) -> Operand118 pub 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)]
128 pub 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 
140 impl Format {
141     /// Iterate over the operand locations.
locations(&self) -> impl Iterator<Item = &Location> + '_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.
uses_memory(&self) -> Option<Location>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]
uses_register(&self) -> bool162     pub fn uses_register(&self) -> bool {
163         self.locations().any(Location::uses_register)
164     }
165 
166     /// Collect into operand kinds.
operands_by_kind(&self) -> Vec<OperandKind>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.
flags(mut self, eflags: Eflags) -> Self172     pub fn flags(mut self, eflags: Eflags) -> Self {
173         self.eflags = eflags;
174         self
175     }
176 
177     /// Return true if an instruction uses EFLAGS.
uses_eflags(&self) -> bool178     pub fn uses_eflags(&self) -> bool {
179         self.eflags != Eflags::None
180     }
181 }
182 
183 impl core::fmt::Display for Format {
fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result184     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)]
218 pub 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 
234 impl core::fmt::Display for Operand {
fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result235     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 
264 impl From<Location> for Operand {
from(location: Location) -> Self265     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`].
281 pub enum RegClass {
282     Gpr,
283     Xmm,
284 }
285 
286 impl core::fmt::Display for RegClass {
fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result287     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")]
298 pub 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 
350 impl Location {
351     /// Return the number of bits accessed.
352     #[must_use]
bits(&self) -> u16353     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]
bytes(&self) -> u16366     pub fn bytes(&self) -> u16 {
367         self.bits() / 8
368     }
369 
370     /// Return `true` if the location accesses memory; `false` otherwise.
371     #[must_use]
uses_memory(&self) -> bool372     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 any of the operands accepts a register (i.e., not an
381     /// immediate); return `false` otherwise.
382     #[must_use]
uses_register(&self) -> bool383     pub fn uses_register(&self) -> bool {
384         use OperandKind::*;
385         match self.kind() {
386             Imm(_) => false,
387             FixedReg(_) | Reg(_) | RegMem(_) | Mem(_) => true,
388         }
389     }
390 
391     /// Convert the location to an [`OperandKind`].
392     #[must_use]
kind(&self) -> OperandKind393     pub fn kind(&self) -> OperandKind {
394         use Location::*;
395         match self {
396             al | ax | eax | rax | rbx | cl | rcx | dx | edx | rdx | xmm0 => {
397                 OperandKind::FixedReg(*self)
398             }
399             imm8 | imm16 | imm32 | imm64 => OperandKind::Imm(*self),
400             r8 | r16 | r32 | r32a | r32b | r64 | r64a | r64b | xmm1 | xmm2 | xmm3 => {
401                 OperandKind::Reg(*self)
402             }
403             rm8 | rm16 | rm32 | rm64 | xmm_m8 | xmm_m16 | xmm_m32 | xmm_m64 | xmm_m128 => {
404                 OperandKind::RegMem(*self)
405             }
406             m8 | m16 | m32 | m64 | m128 => OperandKind::Mem(*self),
407         }
408     }
409 
410     /// If a location directly uses data from a register, return the register
411     /// class; otherwise, return `None`. Memory-only locations, though their
412     /// address is stored in a register, use data from memory and thus also
413     /// return `None`.
414     #[must_use]
reg_class(&self) -> Option<RegClass>415     pub fn reg_class(&self) -> Option<RegClass> {
416         use Location::*;
417         match self {
418             imm8 | imm16 | imm32 | imm64 | m8 | m16 | m32 | m64 | m128 => None,
419             al | ax | eax | rax | rbx | cl | rcx | dx | edx | rdx | r8 | r16 | r32 | r32a
420             | r32b | r64 | r64a | r64b | rm8 | rm16 | rm32 | rm64 => Some(RegClass::Gpr),
421             xmm1 | xmm2 | xmm3 | xmm_m8 | xmm_m16 | xmm_m32 | xmm_m64 | xmm_m128 | xmm0 => {
422                 Some(RegClass::Xmm)
423             }
424         }
425     }
426 }
427 
428 impl core::fmt::Display for Location {
fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result429     fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
430         use Location::*;
431         match self {
432             imm8 => write!(f, "imm8"),
433             imm16 => write!(f, "imm16"),
434             imm32 => write!(f, "imm32"),
435             imm64 => write!(f, "imm64"),
436 
437             al => write!(f, "al"),
438             ax => write!(f, "ax"),
439             eax => write!(f, "eax"),
440             rax => write!(f, "rax"),
441             rbx => write!(f, "rbx"),
442             cl => write!(f, "cl"),
443             rcx => write!(f, "rcx"),
444             dx => write!(f, "dx"),
445             edx => write!(f, "edx"),
446             rdx => write!(f, "rdx"),
447             xmm0 => write!(f, "xmm0"),
448 
449             r8 => write!(f, "r8"),
450             r16 => write!(f, "r16"),
451             r32 => write!(f, "r32"),
452             r32a => write!(f, "r32a"),
453             r32b => write!(f, "r32b"),
454             r64 => write!(f, "r64"),
455             r64a => write!(f, "r64a"),
456             r64b => write!(f, "r64b"),
457             rm8 => write!(f, "rm8"),
458             rm16 => write!(f, "rm16"),
459             rm32 => write!(f, "rm32"),
460             rm64 => write!(f, "rm64"),
461 
462             xmm1 => write!(f, "xmm1"),
463             xmm2 => write!(f, "xmm2"),
464             xmm3 => write!(f, "xmm3"),
465             xmm_m8 => write!(f, "xmm_m8"),
466             xmm_m16 => write!(f, "xmm_m16"),
467             xmm_m32 => write!(f, "xmm_m32"),
468             xmm_m64 => write!(f, "xmm_m64"),
469             xmm_m128 => write!(f, "xmm_m128"),
470 
471             m8 => write!(f, "m8"),
472             m16 => write!(f, "m16"),
473             m32 => write!(f, "m32"),
474             m64 => write!(f, "m64"),
475             m128 => write!(f, "m128"),
476         }
477     }
478 }
479 
480 /// Organize the operand locations by kind.
481 ///
482 /// ```
483 /// # use cranelift_assembler_x64_meta::dsl::{OperandKind, Location};
484 /// let k: OperandKind = Location::imm32.kind();
485 /// ```
486 #[derive(Clone, Copy, Debug)]
487 pub enum OperandKind {
488     FixedReg(Location),
489     Imm(Location),
490     Reg(Location),
491     RegMem(Location),
492     Mem(Location),
493 }
494 
495 /// x64 operands can be mutable or not.
496 ///
497 /// ```
498 /// # use cranelift_assembler_x64_meta::dsl::{r, rw, Location::r8, Mutability};
499 /// assert_eq!(r(r8).mutability, Mutability::Read);
500 /// assert_eq!(rw(r8).mutability, Mutability::ReadWrite);
501 /// ```
502 #[derive(Clone, Copy, Debug, PartialEq)]
503 pub enum Mutability {
504     Read,
505     ReadWrite,
506     Write,
507 }
508 
509 impl Mutability {
510     /// Returns whether this represents a read of the operand in question.
511     ///
512     /// Note that for read/write operands this returns `true`.
is_read(&self) -> bool513     pub fn is_read(&self) -> bool {
514         match self {
515             Mutability::Read | Mutability::ReadWrite => true,
516             Mutability::Write => false,
517         }
518     }
519 
520     /// Returns whether this represents a write of the operand in question.
521     ///
522     /// Note that for read/write operands this returns `true`.
is_write(&self) -> bool523     pub fn is_write(&self) -> bool {
524         match self {
525             Mutability::Read => false,
526             Mutability::ReadWrite | Mutability::Write => true,
527         }
528     }
529 }
530 
531 impl Default for Mutability {
default() -> Self532     fn default() -> Self {
533         Self::Read
534     }
535 }
536 
537 impl core::fmt::Display for Mutability {
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result538     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
539         match self {
540             Self::Read => write!(f, "r"),
541             Self::ReadWrite => write!(f, "rw"),
542             Self::Write => write!(f, "w"),
543         }
544     }
545 }
546 
547 /// x64 operands may be sign- or zero-extended.
548 ///
549 /// ```
550 /// # use cranelift_assembler_x64_meta::dsl::{Location::r8, sxw, Extension};
551 /// assert_eq!(sxw(r8).extension, Extension::SignExtendWord);
552 /// ```
553 #[derive(Clone, Copy, Debug, PartialEq)]
554 pub enum Extension {
555     None,
556     SignExtendQuad,
557     SignExtendLong,
558     SignExtendWord,
559 }
560 
561 impl Extension {
562     /// Check if the extension is sign-extended.
563     #[must_use]
is_sign_extended(&self) -> bool564     pub fn is_sign_extended(&self) -> bool {
565         matches!(
566             self,
567             Self::SignExtendQuad | Self::SignExtendLong | Self::SignExtendWord
568         )
569     }
570 }
571 
572 impl Default for Extension {
default() -> Self573     fn default() -> Self {
574         Self::None
575     }
576 }
577 
578 impl core::fmt::Display for Extension {
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result579     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
580         match self {
581             Extension::None => write!(f, ""),
582             Extension::SignExtendQuad => write!(f, "sxq"),
583             Extension::SignExtendLong => write!(f, "sxl"),
584             Extension::SignExtendWord => write!(f, "sxw"),
585         }
586     }
587 }
588 
589 /// Describes if an instruction uses EFLAGS, and whether it reads, writes, or
590 /// reads/writes the EFLAGS register.
591 /// In the future, we might want to model specific EFLAGS bits instead of the
592 /// entire EFLAGS register.
593 /// Some related discussion in this GitHub issue
594 /// <https://github.com/bytecodealliance/wasmtime/issues/10298>
595 #[derive(Clone, Copy, Debug, PartialEq)]
596 pub enum Eflags {
597     None,
598     R,
599     W,
600     RW,
601 }
602 
603 impl Eflags {
604     /// Returns whether this represents a read of any bit in the EFLAGS
605     /// register.
is_read(&self) -> bool606     pub fn is_read(&self) -> bool {
607         match self {
608             Eflags::None | Eflags::W => false,
609             Eflags::R | Eflags::RW => true,
610         }
611     }
612 
613     /// Returns whether this represents a writes to any bit in the EFLAGS
614     /// register.
is_write(&self) -> bool615     pub fn is_write(&self) -> bool {
616         match self {
617             Eflags::None | Eflags::R => false,
618             Eflags::W | Eflags::RW => true,
619         }
620     }
621 }
622 
623 impl Default for Eflags {
default() -> Self624     fn default() -> Self {
625         Self::None
626     }
627 }
628 
629 impl core::fmt::Display for Eflags {
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result630     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
631         match self {
632             Self::None => write!(f, ""),
633             Self::R => write!(f, "r"),
634             Self::W => write!(f, "w"),
635             Self::RW => write!(f, "rw"),
636         }
637     }
638 }
639