Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/assembler-x64/meta/src/dsl/encoding.rs
1693 views
1
//! A DSL for describing x64 encodings.
2
//!
3
//! Intended use:
4
//! - construct an encoding using an abbreviated helper, e.g., [`rex`]
5
//! - then, configure the encoding using builder methods, e.g., [`Rex::w`]
6
//!
7
//! ```
8
//! # use cranelift_assembler_x64_meta::dsl::rex;
9
//! let enc = rex(0x25).w().id();
10
//! assert_eq!(enc.to_string(), "REX.W + 0x25 id")
11
//! ```
12
//!
13
//! This module references the Intel® 64 and IA-32 Architectures Software
14
//! Development Manual, Volume 2: [link].
15
//!
16
//! [link]: https://software.intel.com/content/www/us/en/develop/articles/intel-sdm.html
17
18
use super::{Operand, OperandKind};
19
use core::fmt;
20
21
/// An abbreviated constructor for REX-encoded instructions.
22
#[must_use]
23
pub fn rex(opcode: impl Into<Opcodes>) -> Rex {
24
Rex {
25
opcodes: opcode.into(),
26
w: WBit::W0,
27
modrm: None,
28
imm: Imm::None,
29
opcode_mod: None,
30
}
31
}
32
33
/// An abbreviated constructor for VEX-encoded instructions.
34
#[must_use]
35
pub fn vex(length: Length) -> Vex {
36
Vex {
37
length,
38
pp: None,
39
mmmmm: None,
40
w: WBit::WIG,
41
opcode: u8::MAX,
42
modrm: None,
43
imm: Imm::None,
44
is4: false,
45
}
46
}
47
48
/// An abbreviated constructor for EVEX-encoded instructions.
49
#[must_use]
50
pub fn evex(length: Length, tuple_type: TupleType) -> Evex {
51
Evex {
52
length,
53
pp: None,
54
mmm: None,
55
w: WBit::WIG,
56
opcode: u8::MAX,
57
modrm: None,
58
imm: Imm::None,
59
tuple_type,
60
}
61
}
62
63
/// Enumerate the ways x64 encodes instructions.
64
pub enum Encoding {
65
Rex(Rex),
66
Vex(Vex),
67
Evex(Evex),
68
}
69
70
impl Encoding {
71
/// Check that the encoding is valid for the given operands; this can find
72
/// issues earlier, before generating any Rust code.
73
pub fn validate(&self, operands: &[Operand]) {
74
match self {
75
Encoding::Rex(rex) => rex.validate(operands),
76
Encoding::Vex(vex) => vex.validate(operands),
77
Encoding::Evex(evex) => evex.validate(operands),
78
}
79
}
80
81
/// Return the opcode for this encoding.
82
pub fn opcode(&self) -> u8 {
83
match self {
84
Encoding::Rex(rex) => rex.opcodes.opcode(),
85
Encoding::Vex(vex) => vex.opcode,
86
Encoding::Evex(evex) => evex.opcode,
87
}
88
}
89
}
90
91
impl fmt::Display for Encoding {
92
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
93
match self {
94
Encoding::Rex(rex) => write!(f, "{rex}"),
95
Encoding::Vex(vex) => write!(f, "{vex}"),
96
Encoding::Evex(evex) => write!(f, "{evex}"),
97
}
98
}
99
}
100
101
#[derive(Clone, Copy, PartialEq)]
102
pub enum ModRmKind {
103
/// Models `/digit`.
104
///
105
/// From the reference manual: "a digit between 0 and 7 indicates that the
106
/// ModR/M byte of the instruction uses only the r/m (register or memory)
107
/// operand. The reg field contains the digit that provides an extension to
108
/// the instruction's opcode."
109
Digit(u8),
110
111
/// Models `/r`.
112
///
113
/// From the reference manual: "indicates that the ModR/M byte of the
114
/// instruction contains a register operand and an r/m operand."
115
Reg,
116
}
117
118
impl ModRmKind {
119
/// Return the digit extending the opcode, if available.
120
#[must_use]
121
pub fn digit(&self) -> Option<u8> {
122
match self {
123
Self::Digit(digit) => Some(*digit),
124
_ => None,
125
}
126
}
127
128
/// Return the digit extending the opcode.
129
///
130
/// # Panics
131
///
132
/// Panics if not extension was defined.
133
pub fn unwrap_digit(&self) -> u8 {
134
self.digit().expect("expected an extension digit")
135
}
136
}
137
138
impl fmt::Display for ModRmKind {
139
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
140
match self {
141
ModRmKind::Digit(digit) => write!(f, "/{digit}"),
142
ModRmKind::Reg => write!(f, "/r"),
143
}
144
}
145
}
146
147
/// The traditional x64 encoding.
148
///
149
/// We use the "REX" name here in a slightly unorthodox way: "REX" is the name
150
/// for the optional _byte_ extending the number of available registers, e.g.,
151
/// but we use it here to distinguish this from other encoding formats (e.g.,
152
/// VEX, EVEX). The "REX" _byte_ is still optional in this encoding and only
153
/// emitted when necessary.
154
pub struct Rex {
155
/// The opcodes for this instruction.
156
///
157
/// Multi-byte opcodes are handled by passing an array of opcodes (including
158
/// prefixes like `0x66` and escape bytes like `0x0f`) to the constructor.
159
/// E.g., `66 0F 54` (`ANDPD`) is expressed as follows:
160
///
161
/// ```
162
/// # use cranelift_assembler_x64_meta::dsl::rex;
163
/// let enc = rex([0x66, 0x0f, 0x54]);
164
/// ```
165
pub opcodes: Opcodes,
166
/// Indicates setting the REX.W bit.
167
///
168
/// From the reference manual: "Indicates the use of a REX prefix that
169
/// affects operand size or instruction semantics. The ordering of the REX
170
/// prefix and other optional/mandatory instruction prefixes are discussed
171
/// in chapter 2. Note that REX prefixes that promote legacy instructions to
172
/// 64-bit behavior are not listed explicitly in the opcode column."
173
pub w: WBit,
174
/// Indicates modifications to the ModR/M byte.
175
pub modrm: Option<ModRmKind>,
176
/// The number of bits used as an immediate operand to the instruction.
177
pub imm: Imm,
178
/// Used for `+rb`, `+rw`, `+rd`, and `+ro` instructions, which encode `reg`
179
/// bits in the opcode byte; if `Some`, this contains the expected bit width
180
/// of `reg`.
181
///
182
/// From the reference manual: "[...] the lower 3 bits of the opcode byte is
183
/// used to encode the register operand without a modR/M byte. The
184
/// instruction lists the corresponding hexadecimal value of the opcode byte
185
/// with low 3 bits as 000b. In non-64-bit mode, a register code, from 0
186
/// through 7, is added to the hexadecimal value of the opcode byte. In
187
/// 64-bit mode, indicates the four bit field of REX.b and opcode[2:0] field
188
/// encodes the register operand of the instruction. “+ro” is applicable
189
/// only in 64-bit mode."
190
pub opcode_mod: Option<OpcodeMod>,
191
}
192
193
impl Rex {
194
/// Set the `REX.W` bit.
195
#[must_use]
196
pub fn w(self) -> Self {
197
Self {
198
w: WBit::W1,
199
..self
200
}
201
}
202
203
/// Set the ModR/M byte to contain a register operand and an r/m operand;
204
/// equivalent to `/r` in the reference manual.
205
#[must_use]
206
pub fn r(self) -> Self {
207
Self {
208
modrm: Some(ModRmKind::Reg),
209
..self
210
}
211
}
212
213
/// Set the digit extending the opcode; equivalent to `/<digit>` in the
214
/// reference manual.
215
///
216
/// # Panics
217
///
218
/// Panics if `extension` is too large.
219
#[must_use]
220
pub fn digit(self, extension: u8) -> Self {
221
assert!(extension <= 0b111, "must fit in 3 bits");
222
Self {
223
modrm: Some(ModRmKind::Digit(extension)),
224
..self
225
}
226
}
227
228
/// Retrieve the digit extending the opcode, if available.
229
#[must_use]
230
pub fn unwrap_digit(&self) -> Option<u8> {
231
match self.modrm {
232
Some(ModRmKind::Digit(digit)) => Some(digit),
233
_ => None,
234
}
235
}
236
237
/// Append a byte-sized immediate operand (8-bit); equivalent to `ib` in the
238
/// reference manual.
239
///
240
/// # Panics
241
///
242
/// Panics if an immediate operand is already set.
243
#[must_use]
244
pub fn ib(self) -> Self {
245
assert_eq!(self.imm, Imm::None);
246
Self {
247
imm: Imm::ib,
248
..self
249
}
250
}
251
252
/// Append a word-sized immediate operand (16-bit); equivalent to `iw` in
253
/// the reference manual.
254
///
255
/// # Panics
256
///
257
/// Panics if an immediate operand is already set.
258
#[must_use]
259
pub fn iw(self) -> Self {
260
assert_eq!(self.imm, Imm::None);
261
Self {
262
imm: Imm::iw,
263
..self
264
}
265
}
266
267
/// Append a doubleword-sized immediate operand (32-bit); equivalent to `id`
268
/// in the reference manual.
269
///
270
/// # Panics
271
///
272
/// Panics if an immediate operand is already set.
273
#[must_use]
274
pub fn id(self) -> Self {
275
assert_eq!(self.imm, Imm::None);
276
Self {
277
imm: Imm::id,
278
..self
279
}
280
}
281
282
/// Append a quadword-sized immediate operand (64-bit); equivalent to `io`
283
/// in the reference manual.
284
///
285
/// # Panics
286
///
287
/// Panics if an immediate operand is already set.
288
#[must_use]
289
pub fn io(self) -> Self {
290
assert_eq!(self.imm, Imm::None);
291
Self {
292
imm: Imm::io,
293
..self
294
}
295
}
296
297
/// Modify the opcode byte with bits from an 8-bit `reg`; equivalent to
298
/// `+rb` in the reference manual.
299
#[must_use]
300
pub fn rb(self) -> Self {
301
Self {
302
opcode_mod: Some(OpcodeMod::rb),
303
..self
304
}
305
}
306
307
/// Modify the opcode byte with bits from a 16-bit `reg`; equivalent to
308
/// `+rw` in the reference manual.
309
#[must_use]
310
pub fn rw(self) -> Self {
311
Self {
312
opcode_mod: Some(OpcodeMod::rw),
313
..self
314
}
315
}
316
317
/// Modify the opcode byte with bits from a 32-bit `reg`; equivalent to
318
/// `+rd` in the reference manual.
319
#[must_use]
320
pub fn rd(self) -> Self {
321
Self {
322
opcode_mod: Some(OpcodeMod::rd),
323
..self
324
}
325
}
326
327
/// Modify the opcode byte with bits from a 64-bit `reg`; equivalent to
328
/// `+ro` in the reference manual.
329
#[must_use]
330
pub fn ro(self) -> Self {
331
Self {
332
opcode_mod: Some(OpcodeMod::ro),
333
..self
334
}
335
}
336
337
/// Check a subset of the rules for valid encodings outlined in chapter 2,
338
/// _Instruction Format_, of the Intel® 64 and IA-32 Architectures Software
339
/// Developer’s Manual, Volume 2A.
340
fn validate(&self, operands: &[Operand]) {
341
if let Some(OperandKind::Imm(op)) = operands
342
.iter()
343
.map(|o| o.location.kind())
344
.find(|k| matches!(k, OperandKind::Imm(_)))
345
{
346
assert_eq!(
347
op.bits(),
348
self.imm.bits(),
349
"for an immediate, the encoding width must match the declared operand width"
350
);
351
}
352
353
if let Some(opcode_mod) = &self.opcode_mod {
354
assert!(
355
self.opcodes.primary & 0b111 == 0,
356
"the lower three bits of the opcode byte should be 0"
357
);
358
assert!(
359
operands
360
.iter()
361
.all(|o| o.location.bits() == opcode_mod.bits().into()),
362
"the opcode modifier width must match the operand widths"
363
);
364
}
365
366
assert!(!matches!(self.w, WBit::WIG));
367
}
368
}
369
370
impl From<Rex> for Encoding {
371
fn from(rex: Rex) -> Encoding {
372
Encoding::Rex(rex)
373
}
374
}
375
376
impl fmt::Display for Rex {
377
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
378
if let Some(group1) = &self.opcodes.prefixes.group1 {
379
write!(f, "{group1} + ")?;
380
}
381
if let Some(group2) = &self.opcodes.prefixes.group2 {
382
write!(f, "{group2} + ")?;
383
}
384
if let Some(group3) = &self.opcodes.prefixes.group3 {
385
write!(f, "{group3} + ")?;
386
}
387
if let Some(group4) = &self.opcodes.prefixes.group4 {
388
write!(f, "{group4} + ")?;
389
}
390
if self.w.as_bool() {
391
write!(f, "REX.W + ")?;
392
}
393
if self.opcodes.escape {
394
write!(f, "0x0F + ")?;
395
}
396
write!(f, "{:#04X}", self.opcodes.primary)?;
397
if let Some(secondary) = self.opcodes.secondary {
398
write!(f, " {secondary:#04X}")?;
399
}
400
if let Some(modrm) = self.modrm {
401
write!(f, " {modrm}")?;
402
}
403
if let Some(opcode_mod) = &self.opcode_mod {
404
write!(f, " {opcode_mod}")?;
405
}
406
if self.imm != Imm::None {
407
write!(f, " {}", self.imm)?;
408
}
409
Ok(())
410
}
411
}
412
413
/// Describe an instruction's opcodes. From section 2.1.2 "Opcodes" in the
414
/// reference manual:
415
///
416
/// > A primary opcode can be 1, 2, or 3 bytes in length. An additional 3-bit
417
/// > opcode field is sometimes encoded in the ModR/M byte. Smaller fields can
418
/// > be defined within the primary opcode. Such fields define the direction of
419
/// > operation, size of displacements, register encoding, condition codes, or
420
/// > sign extension. Encoding fields used by an opcode vary depending on the
421
/// > class of operation.
422
/// >
423
/// > Two-byte opcode formats for general-purpose and SIMD instructions consist
424
/// > of one of the following:
425
/// > - An escape opcode byte `0FH` as the primary opcode and a second opcode
426
/// > byte.
427
/// > - A mandatory prefix (`66H`, `F2H`, or `F3H`), an escape opcode byte, and
428
/// > a second opcode byte (same as previous bullet).
429
/// >
430
/// > For example, `CVTDQ2PD` consists of the following sequence: `F3 0F E6`.
431
/// > The first byte is a mandatory prefix (it is not considered as a repeat
432
/// > prefix).
433
/// >
434
/// > Three-byte opcode formats for general-purpose and SIMD instructions
435
/// > consist of one of the following:
436
/// > - An escape opcode byte `0FH` as the primary opcode, plus two additional
437
/// > opcode bytes.
438
/// > - A mandatory prefix (`66H`, `F2H`, or `F3H`), an escape opcode byte, plus
439
/// > two additional opcode bytes (same as previous bullet).
440
/// >
441
/// > For example, `PHADDW` for XMM registers consists of the following
442
/// > sequence: `66 0F 38 01`. The first byte is the mandatory prefix.
443
pub struct Opcodes {
444
/// The prefix bytes for this instruction.
445
pub prefixes: Prefixes,
446
/// Indicates the use of an escape opcode byte, `0x0f`.
447
pub escape: bool,
448
/// The primary opcode.
449
pub primary: u8,
450
/// Some instructions (e.g., SIMD) may have a secondary opcode.
451
pub secondary: Option<u8>,
452
}
453
454
impl Opcodes {
455
/// Return the main opcode for this instruction.
456
///
457
/// Note that [`Rex`]-encoded instructions have a complex opcode scheme (see
458
/// [`Opcodes`] documentation); the opcode one is usually looking for is the
459
/// last one. This returns the last opcode: the secondary opcode if one is
460
/// available and the primary otherwise.
461
fn opcode(&self) -> u8 {
462
if let Some(secondary) = self.secondary {
463
secondary
464
} else {
465
self.primary
466
}
467
}
468
}
469
470
impl From<u8> for Opcodes {
471
fn from(primary: u8) -> Opcodes {
472
Opcodes {
473
prefixes: Prefixes::default(),
474
escape: false,
475
primary,
476
secondary: None,
477
}
478
}
479
}
480
481
impl<const N: usize> From<[u8; N]> for Opcodes {
482
fn from(bytes: [u8; N]) -> Self {
483
let (prefixes, remaining) = Prefixes::parse(&bytes);
484
let (escape, primary, secondary) = match remaining {
485
[primary] => (false, *primary, None),
486
[0x0f, primary] => (true, *primary, None),
487
[0x0f, primary, secondary] => (true, *primary, Some(*secondary)),
488
_ => panic!(
489
"invalid opcodes after prefix; expected [opcode], [0x0f, opcode], or [0x0f, opcode, opcode], found {remaining:x?}"
490
),
491
};
492
Self {
493
prefixes,
494
escape,
495
primary,
496
secondary,
497
}
498
}
499
}
500
501
/// The allowed prefixes for an instruction. From the reference manual (section
502
/// 2.1.1):
503
///
504
/// > Instruction prefixes are divided into four groups, each with a set of
505
/// > allowable prefix codes. For each instruction, it is only useful to include
506
/// > up to one prefix code from each of the four groups (Groups 1, 2, 3, 4).
507
/// > Groups 1 through 4 may be placed in any order relative to each other.
508
#[derive(Default)]
509
pub struct Prefixes {
510
pub group1: Option<Group1Prefix>,
511
pub group2: Option<Group2Prefix>,
512
pub group3: Option<Group3Prefix>,
513
pub group4: Option<Group4Prefix>,
514
}
515
516
impl Prefixes {
517
/// Parse a slice of `bytes` into a set of prefixes, returning both the
518
/// configured [`Prefixes`] as well as any remaining bytes.
519
fn parse(mut bytes: &[u8]) -> (Self, &[u8]) {
520
let mut prefixes = Self::default();
521
while !bytes.is_empty() && prefixes.try_assign(bytes[0]).is_ok() {
522
bytes = &bytes[1..];
523
}
524
(prefixes, bytes)
525
}
526
527
/// Attempt to parse a `byte` as a prefix and, if successful, assigns it to
528
/// the correct prefix group.
529
///
530
/// # Panics
531
///
532
/// This function panics if the prefix for a group is already set; this
533
/// disallows specifying multiple prefixes per group.
534
fn try_assign(&mut self, byte: u8) -> Result<(), ()> {
535
if let Ok(p) = Group1Prefix::try_from(byte) {
536
assert!(self.group1.is_none());
537
self.group1 = Some(p);
538
Ok(())
539
} else if let Ok(p) = Group2Prefix::try_from(byte) {
540
assert!(self.group2.is_none());
541
self.group2 = Some(p);
542
Ok(())
543
} else if let Ok(p) = Group3Prefix::try_from(byte) {
544
assert!(self.group3.is_none());
545
self.group3 = Some(p);
546
Ok(())
547
} else if let Ok(p) = Group4Prefix::try_from(byte) {
548
assert!(self.group4.is_none());
549
self.group4 = Some(p);
550
Ok(())
551
} else {
552
Err(())
553
}
554
}
555
556
/// Check if any prefix is present.
557
pub fn is_empty(&self) -> bool {
558
self.group1.is_none()
559
&& self.group2.is_none()
560
&& self.group3.is_none()
561
&& self.group4.is_none()
562
}
563
}
564
565
pub enum Group1Prefix {
566
/// The LOCK prefix (`0xf0`). From the reference manual:
567
///
568
/// > The LOCK prefix (F0H) forces an operation that ensures exclusive use
569
/// > of shared memory in a multiprocessor environment. See "LOCK—Assert
570
/// > LOCK# Signal Prefix" in Chapter 3, Instruction Set Reference, A-L, for
571
/// > a description of this prefix.
572
Lock,
573
/// A REPNE/REPNZ prefix (`0xf2`) or a BND prefix under certain conditions.
574
/// `REP*` prefixes apply only to string and input/output instructions but
575
/// can be used as mandatory prefixes in other kinds of instructions (e.g.,
576
/// SIMD) From the reference manual:
577
///
578
/// > Repeat prefixes (F2H, F3H) cause an instruction to be repeated for
579
/// > each element of a string. Use these prefixes only with string and I/O
580
/// > instructions (MOVS, CMPS, SCAS, LODS, STOS, INS, and OUTS). Use of
581
/// > repeat prefixes and/or undefined opcodes with other Intel 64 or IA-32
582
/// > instructions is reserved; such use may cause unpredictable behavior.
583
/// >
584
/// > Some instructions may use F2H, F3H as a mandatory prefix to express
585
/// > distinct functionality.
586
REPNorBND,
587
/// A REPE/REPZ prefix (`0xf3`); `REP*` prefixes apply only to string and
588
/// input/output instructions but can be used as mandatory prefixes in other
589
/// kinds of instructions (e.g., SIMD). See `REPNorBND` for more details.
590
REP_,
591
}
592
593
impl TryFrom<u8> for Group1Prefix {
594
type Error = u8;
595
fn try_from(byte: u8) -> Result<Self, Self::Error> {
596
Ok(match byte {
597
0xF0 => Group1Prefix::Lock,
598
0xF2 => Group1Prefix::REPNorBND,
599
0xF3 => Group1Prefix::REP_,
600
byte => return Err(byte),
601
})
602
}
603
}
604
605
impl fmt::Display for Group1Prefix {
606
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
607
match self {
608
Group1Prefix::Lock => write!(f, "0xF0"),
609
Group1Prefix::REPNorBND => write!(f, "0xF2"),
610
Group1Prefix::REP_ => write!(f, "0xF3"),
611
}
612
}
613
}
614
615
/// Contains the segment override prefixes or a (deprecated) branch hint when
616
/// used on a `Jcc` instruction. Note that using the segment override prefixes
617
/// on a branch instruction is reserved. See section 2.1.1, "Instruction
618
/// Prefixes," in the reference manual.
619
pub enum Group2Prefix {
620
/// The CS segment override prefix (`0x2e`); also the "branch not taken"
621
/// hint.
622
CSorBNT,
623
/// The SS segment override prefix (`0x36`).
624
SS,
625
/// The DS segment override prefix (`0x3e`); also the "branch taken" hint.
626
DSorBT,
627
/// The ES segment override prefix (`0x26`).
628
ES,
629
/// The FS segment override prefix (`0x64`).
630
FS,
631
/// The GS segment override prefix (`0x65`).
632
GS,
633
}
634
635
impl TryFrom<u8> for Group2Prefix {
636
type Error = u8;
637
fn try_from(byte: u8) -> Result<Self, Self::Error> {
638
Ok(match byte {
639
0x2E => Group2Prefix::CSorBNT,
640
0x36 => Group2Prefix::SS,
641
0x3E => Group2Prefix::DSorBT,
642
0x26 => Group2Prefix::ES,
643
0x64 => Group2Prefix::FS,
644
0x65 => Group2Prefix::GS,
645
byte => return Err(byte),
646
})
647
}
648
}
649
650
impl fmt::Display for Group2Prefix {
651
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
652
match self {
653
Group2Prefix::CSorBNT => write!(f, "0x2E"),
654
Group2Prefix::SS => write!(f, "0x36"),
655
Group2Prefix::DSorBT => write!(f, "0x3E"),
656
Group2Prefix::ES => write!(f, "0x26"),
657
Group2Prefix::FS => write!(f, "0x64"),
658
Group2Prefix::GS => write!(f, "0x65"),
659
}
660
}
661
}
662
663
/// Contains the operand-size override prefix (`0x66`); also used as a SIMD
664
/// prefix. From the reference manual:
665
///
666
/// > The operand-size override prefix allows a program to switch between 16-
667
/// > and 32-bit operand sizes. Either size can be the default; use of the
668
/// > prefix selects the non-default size. Some SSE2/SSE3/SSSE3/SSE4
669
/// > instructions and instructions using a three-byte sequence of primary
670
/// > opcode bytes may use 66H as a mandatory prefix to express distinct
671
/// > functionality.
672
pub enum Group3Prefix {
673
OperandSizeOverride,
674
}
675
676
impl TryFrom<u8> for Group3Prefix {
677
type Error = u8;
678
fn try_from(byte: u8) -> Result<Self, Self::Error> {
679
Ok(match byte {
680
0x66 => Group3Prefix::OperandSizeOverride,
681
byte => return Err(byte),
682
})
683
}
684
}
685
686
impl fmt::Display for Group3Prefix {
687
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
688
match self {
689
Group3Prefix::OperandSizeOverride => write!(f, "0x66"),
690
}
691
}
692
}
693
694
/// Contains the address-size override prefix (`0x67`). From the reference
695
/// manual:
696
///
697
/// > The address-size override prefix (67H) allows programs to switch between
698
/// > 16- and 32-bit addressing. Either size can be the default; the prefix
699
/// > selects the non-default size.
700
pub enum Group4Prefix {
701
AddressSizeOverride,
702
}
703
704
impl TryFrom<u8> for Group4Prefix {
705
type Error = u8;
706
fn try_from(byte: u8) -> Result<Self, Self::Error> {
707
Ok(match byte {
708
0x67 => Group4Prefix::AddressSizeOverride,
709
byte => return Err(byte),
710
})
711
}
712
}
713
714
impl fmt::Display for Group4Prefix {
715
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
716
match self {
717
Group4Prefix::AddressSizeOverride => write!(f, "0x67"),
718
}
719
}
720
}
721
722
/// Indicate the size of an immediate operand. From the reference manual:
723
///
724
/// > A 1-byte (ib), 2-byte (iw), 4-byte (id) or 8-byte (io) immediate operand
725
/// > to the instruction that follows the opcode, ModR/M bytes or scale-indexing
726
/// > bytes. The opcode determines if the operand is a signed value. All words,
727
/// > doublewords, and quadwords are given with the low-order byte first.
728
#[derive(Debug, PartialEq)]
729
#[allow(non_camel_case_types, reason = "makes DSL definitions easier to read")]
730
pub enum Imm {
731
None,
732
ib,
733
iw,
734
id,
735
io,
736
}
737
738
impl Imm {
739
fn bits(&self) -> u16 {
740
match self {
741
Self::None => 0,
742
Self::ib => 8,
743
Self::iw => 16,
744
Self::id => 32,
745
Self::io => 64,
746
}
747
}
748
}
749
750
impl fmt::Display for Imm {
751
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
752
match self {
753
Self::None => write!(f, ""),
754
Self::ib => write!(f, "ib"),
755
Self::iw => write!(f, "iw"),
756
Self::id => write!(f, "id"),
757
Self::io => write!(f, "io"),
758
}
759
}
760
}
761
762
/// Indicate the size of the `reg` used when modifying the lower three bits of
763
/// the opcode byte; this corresponds to the `+rb`, `+rw`, `+rd`, and `+ro`
764
/// modifiers in the reference manual.
765
///
766
/// ```
767
/// # use cranelift_assembler_x64_meta::dsl::{rex};
768
/// // The `bswap` instruction extends the opcode byte:
769
/// let enc = rex([0x0F, 0xC8]).rd();
770
/// assert_eq!(enc.to_string(), "0x0F + 0xC8 +rd");
771
/// ```
772
#[derive(Clone, Copy, Debug, PartialEq)]
773
#[allow(non_camel_case_types, reason = "makes DSL definitions easier to read")]
774
pub enum OpcodeMod {
775
rb,
776
rw,
777
rd,
778
ro,
779
}
780
781
impl OpcodeMod {
782
fn bits(&self) -> u8 {
783
match self {
784
Self::rb => 8,
785
Self::rw => 16,
786
Self::rd => 32,
787
Self::ro => 64,
788
}
789
}
790
}
791
792
impl fmt::Display for OpcodeMod {
793
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
794
match self {
795
Self::rb => write!(f, "+rb"),
796
Self::rw => write!(f, "+rw"),
797
Self::rd => write!(f, "+rd"),
798
Self::ro => write!(f, "+ro"),
799
}
800
}
801
}
802
803
/// Contains the legacy prefixes allowed for VEX-encoded instructions.
804
///
805
/// VEX encodes a subset of [`Group1Prefix`] and `0x66` (see [`Group3Prefix`])
806
/// as part of the `pp` bit field.
807
#[derive(Clone, Copy, PartialEq)]
808
pub enum VexPrefix {
809
_66,
810
_F2,
811
_F3,
812
}
813
814
impl VexPrefix {
815
/// Encode the `pp` bits.
816
#[inline(always)]
817
pub(crate) fn bits(self) -> u8 {
818
match self {
819
Self::_66 => 0b01,
820
Self::_F3 => 0b10,
821
Self::_F2 => 0b11,
822
}
823
}
824
}
825
826
impl fmt::Display for VexPrefix {
827
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
828
match self {
829
Self::_66 => write!(f, "66"),
830
Self::_F3 => write!(f, "F3"),
831
Self::_F2 => write!(f, "F2"),
832
}
833
}
834
}
835
836
/// Contains the escape sequences allowed for VEX-encoded instructions.
837
///
838
/// VEX encodes these in the `mmmmmm` bit field.
839
#[derive(Clone, Copy, PartialEq)]
840
pub enum VexEscape {
841
_0F,
842
_0F3A,
843
_0F38,
844
}
845
846
impl VexEscape {
847
/// Encode the `m-mmmm` bits.
848
#[inline(always)]
849
pub(crate) fn bits(&self) -> u8 {
850
match self {
851
Self::_0F => 0b01,
852
Self::_0F38 => 0b10,
853
Self::_0F3A => 0b11,
854
}
855
}
856
}
857
858
impl fmt::Display for VexEscape {
859
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
860
match self {
861
Self::_0F => write!(f, "0F"),
862
Self::_0F3A => write!(f, "0F3A"),
863
Self::_0F38 => write!(f, "0F38"),
864
}
865
}
866
}
867
868
/// Contains vector length definitions.
869
///
870
/// VEX encodes these in the `L` bit field, a single bit with `128-bit = 0` and
871
/// `256-bit = 1`. For convenience, we also include the `LIG` and `LZ` syntax,
872
/// used by the reference manual, and always set these to `0`.
873
///
874
/// EVEX encodes this in the `L'L` bits, two bits that typically indicate the
875
/// vector length for packed vector instructions but can also be used for
876
/// rounding control for floating-point instructions with rounding semantics
877
/// (see section 2.7.1 in the reference manual).
878
pub enum Length {
879
/// 128-bit vector length.
880
L128,
881
/// 256-bit vector length.
882
L256,
883
/// 512-bit vector length; invalid for VEX instructions.
884
L512,
885
/// Force the length bits to `0`, but not necessarily for 128-bit operation.
886
/// From the reference manual: "The VEX.L must be encoded to be 0B, an #UD
887
/// occurs if VEX.L is not zero."
888
LZ,
889
/// The length bits are ignored (e.g., for floating point scalar
890
/// instructions). This assembler will emit `0`.
891
LIG,
892
}
893
894
impl Length {
895
/// Encode the `VEX.L` bit.
896
pub fn vex_bits(&self) -> u8 {
897
match self {
898
Self::L128 | Self::LIG | Self::LZ => 0b0,
899
Self::L256 => 0b1,
900
Self::L512 => unreachable!("VEX does not support 512-bit vector length"),
901
}
902
}
903
904
/// Encode the `EVEX.L'L` bits.
905
///
906
/// See section 2.7.10, Vector Length Orthogonality, in the reference manual
907
pub fn evex_bits(&self) -> u8 {
908
match self {
909
Self::L128 | Self::LIG | Self::LZ => 0b00,
910
Self::L256 => 0b01,
911
Self::L512 => 0b10,
912
}
913
}
914
}
915
916
impl fmt::Display for Length {
917
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
918
match self {
919
Self::L128 => write!(f, "128"),
920
Self::L256 => write!(f, "256"),
921
Self::L512 => write!(f, "512"),
922
Self::LIG => write!(f, "LIG"),
923
Self::LZ => write!(f, "LZ"),
924
}
925
}
926
}
927
928
/// Model the `W` bit.
929
pub enum WBit {
930
/// The `W` bit is ignored; equivalent to `.WIG` in the manual.
931
WIG,
932
/// The `W` bit is set to `0`; equivalent to `.W0` in the manual.
933
W0,
934
/// The `W` bit is set to `1`; equivalent to `.W1` in the manual.
935
W1,
936
}
937
938
impl WBit {
939
/// Return `true` if the `W` bit is ignored; this is useful to check in the
940
/// DSL for the default case.
941
fn is_ignored(&self) -> bool {
942
match self {
943
Self::WIG => true,
944
Self::W0 | Self::W1 => false,
945
}
946
}
947
948
/// Return `true` if the `W` bit is set (`W1`); otherwise, return `false`.
949
pub(crate) fn as_bool(&self) -> bool {
950
match self {
951
Self::W1 => true,
952
Self::W0 | Self::WIG => false,
953
}
954
}
955
}
956
957
impl fmt::Display for WBit {
958
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
959
match self {
960
Self::WIG => write!(f, "WIG"),
961
Self::W0 => write!(f, "W0"),
962
Self::W1 => write!(f, "W1"),
963
}
964
}
965
}
966
967
/// The VEX encoding, introduced for AVX instructions.
968
///
969
/// ```
970
/// # use cranelift_assembler_x64_meta::dsl::{vex, Length::L128};
971
/// // To encode a BLENDPD instruction in the manual: VEX.128.66.0F3A.WIG 0D /r ib
972
/// let enc = vex(L128)._66()._0f3a().wig().op(0x0D).r().ib();
973
/// assert_eq!(enc.to_string(), "VEX.128.66.0F3A.WIG 0x0D /r ib");
974
/// ```
975
pub struct Vex {
976
/// The length of the operand (e.g., 128-bit or 256-bit).
977
pub length: Length,
978
/// Any SIMD prefixes, but encoded in the `VEX.pp` bit field.
979
pub pp: Option<VexPrefix>,
980
/// Any leading map bytes, but encoded in the `VEX.mmmmm` bit field.
981
pub mmmmm: Option<VexEscape>,
982
/// The `W` bit.
983
pub w: WBit,
984
/// VEX-encoded instructions have a single-byte opcode. Other prefix-related
985
/// bytes (see [`Opcodes`]) are encoded in the VEX prefixes (see `pp`,
986
/// `mmmmmm`). From the reference manual: "One (and only one) opcode byte
987
/// follows the 2 or 3 byte VEX."
988
pub opcode: u8,
989
/// See [`Rex.modrm`](Rex.modrm).
990
pub modrm: Option<ModRmKind>,
991
/// See [`Rex.imm`](Rex.imm).
992
pub imm: Imm,
993
/// See [`Vex::is4`]
994
pub is4: bool,
995
}
996
997
impl Vex {
998
/// Set the `pp` field to use [`VexPrefix::_66`]; equivalent to `.66` in the
999
/// manual.
1000
pub fn _66(self) -> Self {
1001
assert!(self.pp.is_none());
1002
Self {
1003
pp: Some(VexPrefix::_66),
1004
..self
1005
}
1006
}
1007
1008
/// Set the `pp` field to use [`VexPrefix::_F2`]; equivalent to `.F2` in the
1009
/// manual.
1010
pub fn _f2(self) -> Self {
1011
assert!(self.pp.is_none());
1012
Self {
1013
pp: Some(VexPrefix::_F2),
1014
..self
1015
}
1016
}
1017
1018
/// Set the `pp` field to use [`VexPrefix::_F3`]; equivalent to `.F3` in the
1019
/// manual.
1020
pub fn _f3(self) -> Self {
1021
assert!(self.pp.is_none());
1022
Self {
1023
pp: Some(VexPrefix::_F3),
1024
..self
1025
}
1026
}
1027
1028
/// Set the `mmmmmm` field to use [`VexEscape::_0F`]; equivalent to `.0F` in
1029
/// the manual.
1030
pub fn _0f(self) -> Self {
1031
assert!(self.mmmmm.is_none());
1032
Self {
1033
mmmmm: Some(VexEscape::_0F),
1034
..self
1035
}
1036
}
1037
1038
/// Set the `mmmmmm` field to use [`VexEscape::_0F3A`]; equivalent to
1039
/// `.0F3A` in the manual.
1040
pub fn _0f3a(self) -> Self {
1041
assert!(self.mmmmm.is_none());
1042
Self {
1043
mmmmm: Some(VexEscape::_0F3A),
1044
..self
1045
}
1046
}
1047
1048
/// Set the `mmmmmm` field to use [`VexEscape::_0F38`]; equivalent to
1049
/// `.0F38` in the manual.
1050
pub fn _0f38(self) -> Self {
1051
assert!(self.mmmmm.is_none());
1052
Self {
1053
mmmmm: Some(VexEscape::_0F38),
1054
..self
1055
}
1056
}
1057
1058
/// Set the `W` bit to `0`; equivalent to `.W0` in the manual.
1059
pub fn w0(self) -> Self {
1060
assert!(self.w.is_ignored());
1061
Self {
1062
w: WBit::W0,
1063
..self
1064
}
1065
}
1066
1067
/// Set the `W` bit to `1`; equivalent to `.W1` in the manual.
1068
pub fn w1(self) -> Self {
1069
assert!(self.w.is_ignored());
1070
Self {
1071
w: WBit::W1,
1072
..self
1073
}
1074
}
1075
1076
/// Ignore the `W` bit; equivalent to `.WIG` in the manual.
1077
pub fn wig(self) -> Self {
1078
assert!(self.w.is_ignored());
1079
Self {
1080
w: WBit::WIG,
1081
..self
1082
}
1083
}
1084
1085
/// Set the single opcode for this VEX-encoded instruction.
1086
pub fn op(self, opcode: u8) -> Self {
1087
assert_eq!(self.opcode, u8::MAX);
1088
Self { opcode, ..self }
1089
}
1090
1091
/// Set the ModR/M byte to contain a register operand; see [`Rex::r`].
1092
pub fn r(self) -> Self {
1093
assert!(self.modrm.is_none());
1094
Self {
1095
modrm: Some(ModRmKind::Reg),
1096
..self
1097
}
1098
}
1099
1100
/// Append a byte-sized immediate operand (8-bit); equivalent to `ib` in the
1101
/// reference manual.
1102
///
1103
/// # Panics
1104
///
1105
/// Panics if an immediate operand is already set.
1106
#[must_use]
1107
pub fn ib(self) -> Self {
1108
assert_eq!(self.imm, Imm::None);
1109
Self {
1110
imm: Imm::ib,
1111
..self
1112
}
1113
}
1114
1115
/// Append a word-sized immediate operand (16-bit); equivalent to `iw` in
1116
/// the reference manual.
1117
///
1118
/// # Panics
1119
///
1120
/// Panics if an immediate operand is already set.
1121
#[must_use]
1122
pub fn iw(self) -> Self {
1123
assert_eq!(self.imm, Imm::None);
1124
Self {
1125
imm: Imm::iw,
1126
..self
1127
}
1128
}
1129
1130
/// Append a doubleword-sized immediate operand (32-bit); equivalent to `id`
1131
/// in the reference manual.
1132
///
1133
/// # Panics
1134
///
1135
/// Panics if an immediate operand is already set.
1136
#[must_use]
1137
pub fn id(self) -> Self {
1138
assert_eq!(self.imm, Imm::None);
1139
Self {
1140
imm: Imm::id,
1141
..self
1142
}
1143
}
1144
1145
/// Append a quadword-sized immediate operand (64-bit); equivalent to `io`
1146
/// in the reference manual.
1147
///
1148
/// # Panics
1149
///
1150
/// Panics if an immediate operand is already set.
1151
#[must_use]
1152
pub fn io(self) -> Self {
1153
assert_eq!(self.imm, Imm::None);
1154
Self {
1155
imm: Imm::io,
1156
..self
1157
}
1158
}
1159
1160
/// Set the digit extending the opcode; equivalent to `/<digit>` in the
1161
/// reference manual.
1162
///
1163
/// # Panics
1164
///
1165
/// Panics if `extension` is too large.
1166
#[must_use]
1167
pub fn digit(self, extension: u8) -> Self {
1168
assert!(extension <= 0b111, "must fit in 3 bits");
1169
Self {
1170
modrm: Some(ModRmKind::Digit(extension)),
1171
..self
1172
}
1173
}
1174
1175
/// An 8-bit immediate byte is present containing a source register
1176
/// specifier in either imm8[7:4] (for 64-bit
1177
/// mode) or imm8[6:4] (for 32-bit mode), and instruction-specific payload
1178
/// in imm8[3:0].
1179
pub fn is4(self) -> Self {
1180
Self { is4: true, ..self }
1181
}
1182
1183
fn validate(&self, _operands: &[Operand]) {
1184
assert!(self.opcode != u8::MAX);
1185
assert!(self.mmmmm.is_some());
1186
assert!(!matches!(self.length, Length::L512));
1187
}
1188
1189
/// Retrieve the digit extending the opcode, if available.
1190
#[must_use]
1191
pub fn unwrap_digit(&self) -> Option<u8> {
1192
match self.modrm {
1193
Some(ModRmKind::Digit(digit)) => Some(digit),
1194
_ => None,
1195
}
1196
}
1197
}
1198
1199
impl From<Vex> for Encoding {
1200
fn from(vex: Vex) -> Encoding {
1201
Encoding::Vex(vex)
1202
}
1203
}
1204
1205
impl fmt::Display for Vex {
1206
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1207
write!(f, "VEX.{}", self.length)?;
1208
if let Some(pp) = self.pp {
1209
write!(f, ".{pp}")?;
1210
}
1211
if let Some(mmmmm) = self.mmmmm {
1212
write!(f, ".{mmmmm}")?;
1213
}
1214
write!(f, ".{} {:#04X}", self.w, self.opcode)?;
1215
if let Some(modrm) = self.modrm {
1216
write!(f, " {modrm}")?;
1217
}
1218
if self.imm != Imm::None {
1219
write!(f, " {}", self.imm)?;
1220
}
1221
Ok(())
1222
}
1223
}
1224
1225
pub struct Evex {
1226
/// The vector length of the operand (e.g., 128-bit, 256-bit, or 512-bit).
1227
pub length: Length,
1228
/// Any SIMD prefixes, but encoded in the `EVEX.pp` bit field (see similar:
1229
/// [`Vex::pp`]).
1230
pub pp: Option<VexPrefix>,
1231
/// The `mmm` bits.
1232
///
1233
/// Bits `1:0` are identical to the lowest 2 bits of `VEX.mmmmm`; EVEX adds
1234
/// one more bit here. From the reference manual: "provides access to up to
1235
/// eight decoding maps. Currently, only the following decoding maps are
1236
/// supported: 1, 2, 3, 5, and 6. Map ids 1, 2, and 3 are denoted by 0F,
1237
/// 0F38, and 0F3A, respectively, in the instruction encoding descriptions."
1238
pub mmm: Option<VexEscape>,
1239
/// The `W` bit.
1240
pub w: WBit,
1241
/// EVEX-encoded instructions opcode byte"
1242
pub opcode: u8,
1243
/// See [`Rex.modrm`](Rex.modrm).
1244
pub modrm: Option<ModRmKind>,
1245
/// See [`Rex.imm`](Rex.imm).
1246
pub imm: Imm,
1247
/// The "Tuple Type" corresponding to scaling of the 8-bit displacement
1248
/// parameter for memory operands. See [`TupleType`] for more information.
1249
pub tuple_type: TupleType,
1250
}
1251
1252
impl Evex {
1253
/// Set the `pp` field to use [`VexPrefix::_66`]; equivalent to `.66` in the
1254
/// manual.
1255
pub fn _66(self) -> Self {
1256
assert!(self.pp.is_none());
1257
Self {
1258
pp: Some(VexPrefix::_66),
1259
..self
1260
}
1261
}
1262
1263
/// Set the `pp` field to use [`VexPrefix::_F2`]; equivalent to `.F2` in the
1264
/// manual.
1265
pub fn _f2(self) -> Self {
1266
assert!(self.pp.is_none());
1267
Self {
1268
pp: Some(VexPrefix::_F2),
1269
..self
1270
}
1271
}
1272
1273
/// Set the `pp` field to use [`VexPrefix::_F3`]; equivalent to `.F3` in the
1274
/// manual.
1275
pub fn _f3(self) -> Self {
1276
assert!(self.pp.is_none());
1277
Self {
1278
pp: Some(VexPrefix::_F3),
1279
..self
1280
}
1281
}
1282
1283
/// Set the `mmmmmm` field to use [`VexEscape::_0F`]; equivalent to `.0F` in
1284
/// the manual.
1285
pub fn _0f(self) -> Self {
1286
assert!(self.mmm.is_none());
1287
Self {
1288
mmm: Some(VexEscape::_0F),
1289
..self
1290
}
1291
}
1292
1293
/// Set the `mmmmmm` field to use [`VexEscape::_0F3A`]; equivalent to
1294
/// `.0F3A` in the manual.
1295
pub fn _0f3a(self) -> Self {
1296
assert!(self.mmm.is_none());
1297
Self {
1298
mmm: Some(VexEscape::_0F3A),
1299
..self
1300
}
1301
}
1302
1303
/// Set the `mmmmmm` field to use [`VexEscape::_0F38`]; equivalent to
1304
/// `.0F38` in the manual.
1305
pub fn _0f38(self) -> Self {
1306
assert!(self.mmm.is_none());
1307
Self {
1308
mmm: Some(VexEscape::_0F38),
1309
..self
1310
}
1311
}
1312
1313
/// Set the `W` bit to `0`; equivalent to `.W0` in the manual.
1314
pub fn w0(self) -> Self {
1315
assert!(self.w.is_ignored());
1316
Self {
1317
w: WBit::W0,
1318
..self
1319
}
1320
}
1321
1322
/// Set the `W` bit to `1`; equivalent to `.W1` in the manual.
1323
pub fn w1(self) -> Self {
1324
assert!(self.w.is_ignored());
1325
Self {
1326
w: WBit::W1,
1327
..self
1328
}
1329
}
1330
1331
/// Ignore the `W` bit; equivalent to `.WIG` in the manual.
1332
pub fn wig(self) -> Self {
1333
assert!(self.w.is_ignored());
1334
Self {
1335
w: WBit::WIG,
1336
..self
1337
}
1338
}
1339
1340
/// Set the single opcode for this VEX-encoded instruction.
1341
pub fn op(self, opcode: u8) -> Self {
1342
assert_eq!(self.opcode, u8::MAX);
1343
Self { opcode, ..self }
1344
}
1345
1346
/// Set the ModR/M byte to contain a register operand; see [`Rex::r`].
1347
pub fn r(self) -> Self {
1348
assert!(self.modrm.is_none());
1349
Self {
1350
modrm: Some(ModRmKind::Reg),
1351
..self
1352
}
1353
}
1354
1355
fn validate(&self, _operands: &[Operand]) {
1356
assert!(self.opcode != u8::MAX);
1357
assert!(self.mmm.is_some());
1358
}
1359
1360
/// Retrieve the digit extending the opcode, if available.
1361
#[must_use]
1362
pub fn unwrap_digit(&self) -> Option<u8> {
1363
match self.modrm {
1364
Some(ModRmKind::Digit(digit)) => Some(digit),
1365
_ => None,
1366
}
1367
}
1368
1369
/// Set the digit extending the opcode; equivalent to `/<digit>` in the
1370
/// reference manual.
1371
///
1372
/// # Panics
1373
///
1374
/// Panics if `extension` is too large.
1375
#[must_use]
1376
pub fn digit(self, extension: u8) -> Self {
1377
assert!(extension <= 0b111, "must fit in 3 bits");
1378
Self {
1379
modrm: Some(ModRmKind::Digit(extension)),
1380
..self
1381
}
1382
}
1383
1384
/// Append a byte-sized immediate operand (8-bit); equivalent to `ib` in the
1385
/// reference manual.
1386
///
1387
/// # Panics
1388
///
1389
/// Panics if an immediate operand is already set.
1390
#[must_use]
1391
pub fn ib(self) -> Self {
1392
assert_eq!(self.imm, Imm::None);
1393
Self {
1394
imm: Imm::ib,
1395
..self
1396
}
1397
}
1398
}
1399
1400
impl From<Evex> for Encoding {
1401
fn from(evex: Evex) -> Encoding {
1402
Encoding::Evex(evex)
1403
}
1404
}
1405
1406
impl fmt::Display for Evex {
1407
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1408
write!(f, "EVEX.{}", self.length)?;
1409
if let Some(pp) = self.pp {
1410
write!(f, ".{pp}")?;
1411
}
1412
if let Some(mmmmm) = self.mmm {
1413
write!(f, ".{mmmmm}")?;
1414
}
1415
write!(f, ".{} {:#04X}", self.w, self.opcode)?;
1416
if let Some(modrm) = self.modrm {
1417
write!(f, " {modrm}")?;
1418
}
1419
if self.imm != Imm::None {
1420
write!(f, " {}", self.imm)?;
1421
}
1422
Ok(())
1423
}
1424
}
1425
1426
/// Tuple Type definitions used in EVEX encodings.
1427
///
1428
/// This enumeration corresponds to table 2-34 and 2-35 in the Intel manual.
1429
/// This is a property of all instruction formats listed in the encoding table
1430
/// for each instruction.
1431
#[expect(missing_docs, reason = "matching manual names")]
1432
pub enum TupleType {
1433
Full,
1434
Half,
1435
FullMem,
1436
Tuple1Scalar,
1437
Tuple1Fixed,
1438
Tuple2,
1439
Tuple4,
1440
Tuple8,
1441
HalfMem,
1442
QuarterMem,
1443
EigthMem,
1444
Mem128,
1445
Movddup,
1446
}
1447
1448