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