Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/assembler-x64/meta/src/dsl/format.rs
1693 views
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.
20
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]
35
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]
46
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]
54
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.
63
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.
73
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]
88
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]
103
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]
118
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.
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
183
impl 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)]
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 {
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
264
impl 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`].
281
pub enum RegClass {
282
Gpr,
283
Xmm,
284
}
285
286
impl 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")]
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]
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 any of the operands accepts a register (i.e., not an
381
/// immediate); return `false` otherwise.
382
#[must_use]
383
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]
393
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]
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 {
429
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`.
513
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`.
523
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 {
532
fn default() -> Self {
533
Self::Read
534
}
535
}
536
537
impl core::fmt::Display for Mutability {
538
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::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]
564
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 {
573
fn default() -> Self {
574
Self::None
575
}
576
}
577
578
impl core::fmt::Display for Extension {
579
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::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.
606
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.
615
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 {
624
fn default() -> Self {
625
Self::None
626
}
627
}
628
629
impl core::fmt::Display for Eflags {
630
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::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
640