Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/isa/x64/inst/args.rs
3073 views
1
//! Instruction operand sub-components (aka "parts"): definitions and printing.
2
3
use super::regs::{self};
4
use crate::ir::MemFlags;
5
use crate::ir::condcodes::{FloatCC, IntCC};
6
use crate::ir::types::*;
7
use crate::isa::x64::inst::Inst;
8
use crate::isa::x64::inst::regs::pretty_print_reg;
9
use crate::machinst::*;
10
use alloc::string::String;
11
use core::fmt;
12
13
/// An extension trait for converting `Writable{Xmm,Gpr}` to `Writable<Reg>`.
14
pub trait ToWritableReg {
15
/// Convert `Writable{Xmm,Gpr}` to `Writable<Reg>`.
16
fn to_writable_reg(&self) -> Writable<Reg>;
17
}
18
19
/// An extension trait for converting `Writable<Reg>` to `Writable{Xmm,Gpr}`.
20
pub trait FromWritableReg: Sized {
21
/// Convert `Writable<Reg>` to `Writable{Xmm,Gpr}`.
22
fn from_writable_reg(w: Writable<Reg>) -> Option<Self>;
23
}
24
25
/// A macro for defining a newtype of `Reg` that enforces some invariant about
26
/// the wrapped `Reg` (such as that it is of a particular register class).
27
macro_rules! newtype_of_reg {
28
(
29
$newtype_reg:ident,
30
$newtype_writable_reg:ident,
31
$newtype_option_writable_reg:ident,
32
reg_mem: ($($newtype_reg_mem:ident $(aligned:$aligned:ident)?),*),
33
reg_mem_imm: ($($newtype_reg_mem_imm:ident $(aligned:$aligned_imm:ident)?),*),
34
|$check_reg:ident| $check:expr
35
) => {
36
/// A newtype wrapper around `Reg`.
37
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
38
pub struct $newtype_reg(Reg);
39
40
impl PartialEq<Reg> for $newtype_reg {
41
fn eq(&self, other: &Reg) -> bool {
42
self.0 == *other
43
}
44
}
45
46
impl From<$newtype_reg> for Reg {
47
fn from(r: $newtype_reg) -> Self {
48
r.0
49
}
50
}
51
52
impl $newtype_reg {
53
/// Create this newtype from the given register, or return `None` if the register
54
/// is not a valid instance of this newtype.
55
pub fn new($check_reg: Reg) -> Option<Self> {
56
if $check {
57
Some(Self($check_reg))
58
} else {
59
None
60
}
61
}
62
63
/// Like `Self::new(r).unwrap()` but with a better panic message on
64
/// failure.
65
pub fn unwrap_new($check_reg: Reg) -> Self {
66
if $check {
67
Self($check_reg)
68
} else {
69
panic!(
70
"cannot construct {} from register {:?} with register class {:?}",
71
stringify!($newtype_reg),
72
$check_reg,
73
$check_reg.class(),
74
)
75
}
76
}
77
78
/// Get this newtype's underlying `Reg`.
79
pub fn to_reg(self) -> Reg {
80
self.0
81
}
82
}
83
84
// Convenience impl so that people working with this newtype can use it
85
// "just like" a plain `Reg`.
86
//
87
// NB: We cannot implement `DerefMut` because that would let people do
88
// nasty stuff like `*my_gpr.deref_mut() = some_xmm_reg`, breaking the
89
// invariants that `Gpr` provides.
90
impl core::ops::Deref for $newtype_reg {
91
type Target = Reg;
92
93
fn deref(&self) -> &Reg {
94
&self.0
95
}
96
}
97
98
/// If you know what you're doing, you can explicitly mutably borrow the
99
/// underlying `Reg`. Don't make it point to the wrong type of register
100
/// please.
101
impl AsMut<Reg> for $newtype_reg {
102
fn as_mut(&mut self) -> &mut Reg {
103
&mut self.0
104
}
105
}
106
107
/// Writable Gpr.
108
pub type $newtype_writable_reg = Writable<$newtype_reg>;
109
110
#[allow(dead_code, reason = "Used by some newtypes and not others")]
111
/// Optional writable Gpr.
112
pub type $newtype_option_writable_reg = Option<Writable<$newtype_reg>>;
113
114
impl ToWritableReg for $newtype_writable_reg {
115
fn to_writable_reg(&self) -> Writable<Reg> {
116
Writable::from_reg(self.to_reg().to_reg())
117
}
118
}
119
120
impl FromWritableReg for $newtype_writable_reg {
121
fn from_writable_reg(w: Writable<Reg>) -> Option<Self> {
122
Some(Writable::from_reg($newtype_reg::new(w.to_reg())?))
123
}
124
}
125
126
$(
127
/// A newtype wrapper around `RegMem` for general-purpose registers.
128
#[derive(Clone, Debug)]
129
pub struct $newtype_reg_mem(RegMem);
130
131
impl From<$newtype_reg_mem> for RegMem {
132
fn from(rm: $newtype_reg_mem) -> Self {
133
rm.0
134
}
135
}
136
impl<'a> From<&'a $newtype_reg_mem> for &'a RegMem {
137
fn from(rm: &'a $newtype_reg_mem) -> &'a RegMem {
138
&rm.0
139
}
140
}
141
142
impl From<$newtype_reg> for $newtype_reg_mem {
143
fn from(r: $newtype_reg) -> Self {
144
$newtype_reg_mem(RegMem::reg(r.into()))
145
}
146
}
147
148
impl $newtype_reg_mem {
149
/// Construct a `RegMem` newtype from the given `RegMem`, or return
150
/// `None` if the `RegMem` is not a valid instance of this `RegMem`
151
/// newtype.
152
pub fn new(rm: RegMem) -> Option<Self> {
153
match rm {
154
RegMem::Mem { addr } => {
155
let mut _allow = true;
156
$(
157
if $aligned {
158
_allow = addr.aligned();
159
}
160
)?
161
if _allow {
162
Some(Self(RegMem::Mem { addr }))
163
} else {
164
None
165
}
166
}
167
RegMem::Reg { reg } => Some($newtype_reg::new(reg)?.into()),
168
}
169
}
170
171
/// Like `Self::new(rm).unwrap()` but with better panic messages
172
/// in case of failure.
173
pub fn unwrap_new(rm: RegMem) -> Self {
174
match rm {
175
RegMem::Mem { addr } => {
176
$(
177
if $aligned && !addr.aligned() {
178
panic!(
179
"cannot create {} from an unaligned memory address: {addr:?}",
180
stringify!($newtype_reg_mem),
181
);
182
}
183
)?
184
Self(RegMem::Mem { addr })
185
}
186
RegMem::Reg { reg } => $newtype_reg::unwrap_new(reg).into(),
187
}
188
}
189
190
/// Convert this newtype into its underlying `RegMem`.
191
pub fn to_reg_mem(self) -> RegMem {
192
self.0
193
}
194
195
#[allow(dead_code, reason = "Used by some newtypes and not others")]
196
pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
197
self.0.get_operands(collector);
198
}
199
}
200
impl PrettyPrint for $newtype_reg_mem {
201
fn pretty_print(&self, size: u8) -> String {
202
self.0.pretty_print(size)
203
}
204
}
205
)*
206
207
$(
208
/// A newtype wrapper around `RegMemImm`.
209
#[derive(Clone, Debug)]
210
pub struct $newtype_reg_mem_imm(RegMemImm);
211
212
impl From<$newtype_reg_mem_imm> for RegMemImm {
213
fn from(rmi: $newtype_reg_mem_imm) -> RegMemImm {
214
rmi.0
215
}
216
}
217
impl<'a> From<&'a $newtype_reg_mem_imm> for &'a RegMemImm {
218
fn from(rmi: &'a $newtype_reg_mem_imm) -> &'a RegMemImm {
219
&rmi.0
220
}
221
}
222
223
impl From<$newtype_reg> for $newtype_reg_mem_imm {
224
fn from(r: $newtype_reg) -> Self {
225
$newtype_reg_mem_imm(RegMemImm::reg(r.into()))
226
}
227
}
228
229
impl $newtype_reg_mem_imm {
230
/// Construct this newtype from the given `RegMemImm`, or return
231
/// `None` if the `RegMemImm` is not a valid instance of this
232
/// newtype.
233
pub fn new(rmi: RegMemImm) -> Option<Self> {
234
match rmi {
235
RegMemImm::Imm { .. } => Some(Self(rmi)),
236
RegMemImm::Mem { addr } => {
237
let mut _allow = true;
238
$(
239
if $aligned_imm {
240
_allow = addr.aligned();
241
}
242
)?
243
if _allow {
244
Some(Self(RegMemImm::Mem { addr }))
245
} else {
246
None
247
}
248
}
249
RegMemImm::Reg { reg } => Some($newtype_reg::new(reg)?.into()),
250
}
251
}
252
253
/// Like `Self::new(rmi).unwrap()` but with better panic
254
/// messages in case of failure.
255
pub fn unwrap_new(rmi: RegMemImm) -> Self {
256
match rmi {
257
RegMemImm::Imm { .. } => Self(rmi),
258
RegMemImm::Mem { addr } => {
259
$(
260
if $aligned_imm && !addr.aligned() {
261
panic!(
262
"cannot construct {} from unaligned memory address: {:?}",
263
stringify!($newtype_reg_mem_imm),
264
addr,
265
);
266
}
267
)?
268
Self(RegMemImm::Mem { addr })
269
270
}
271
RegMemImm::Reg { reg } => $newtype_reg::unwrap_new(reg).into(),
272
}
273
}
274
275
/// Convert this newtype into its underlying `RegMemImm`.
276
#[allow(dead_code, reason = "Used by some newtypes and not others")]
277
pub fn to_reg_mem_imm(self) -> RegMemImm {
278
self.0
279
}
280
281
#[allow(dead_code, reason = "Used by some newtypes and not others")]
282
pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
283
self.0.get_operands(collector);
284
}
285
}
286
287
impl PrettyPrint for $newtype_reg_mem_imm {
288
fn pretty_print(&self, size: u8) -> String {
289
self.0.pretty_print(size)
290
}
291
}
292
)*
293
};
294
}
295
296
// Define a newtype of `Reg` for general-purpose registers.
297
newtype_of_reg!(
298
Gpr,
299
WritableGpr,
300
OptionWritableGpr,
301
reg_mem: (GprMem),
302
reg_mem_imm: (GprMemImm),
303
|reg| reg.class() == RegClass::Int
304
);
305
306
#[expect(missing_docs, reason = "self-describing fields")]
307
impl Gpr {
308
pub const RAX: Gpr = Gpr(regs::rax());
309
pub const RBX: Gpr = Gpr(regs::rbx());
310
pub const RCX: Gpr = Gpr(regs::rcx());
311
pub const RDX: Gpr = Gpr(regs::rdx());
312
pub const RSI: Gpr = Gpr(regs::rsi());
313
pub const RDI: Gpr = Gpr(regs::rdi());
314
pub const RSP: Gpr = Gpr(regs::rsp());
315
pub const RBP: Gpr = Gpr(regs::rbp());
316
pub const R8: Gpr = Gpr(regs::r8());
317
pub const R9: Gpr = Gpr(regs::r9());
318
pub const R10: Gpr = Gpr(regs::r10());
319
pub const R11: Gpr = Gpr(regs::r11());
320
pub const R12: Gpr = Gpr(regs::r12());
321
pub const R13: Gpr = Gpr(regs::r13());
322
pub const R14: Gpr = Gpr(regs::r14());
323
pub const R15: Gpr = Gpr(regs::r15());
324
}
325
326
// Define a newtype of `Reg` for XMM registers.
327
newtype_of_reg!(
328
Xmm,
329
WritableXmm,
330
OptionWritableXmm,
331
reg_mem: (XmmMem, XmmMemAligned aligned:true),
332
reg_mem_imm: (XmmMemImm, XmmMemAlignedImm aligned:true),
333
|reg| reg.class() == RegClass::Float
334
);
335
336
// N.B.: `Amode` is defined in `inst.isle`. We add some convenience
337
// constructors here.
338
339
// Re-export the type from the ISLE generated code.
340
pub use crate::isa::x64::lower::isle::generated_code::Amode;
341
342
impl Amode {
343
/// Create an immediate sign-extended and register addressing mode.
344
pub fn imm_reg(simm32: i32, base: Reg) -> Self {
345
debug_assert!(base.class() == RegClass::Int);
346
Self::ImmReg {
347
simm32,
348
base,
349
flags: MemFlags::trusted(),
350
}
351
}
352
353
/// Create a sign-extended-32-to-64 with register and shift addressing mode.
354
pub fn imm_reg_reg_shift(simm32: i32, base: Gpr, index: Gpr, shift: u8) -> Self {
355
debug_assert!(base.class() == RegClass::Int);
356
debug_assert!(index.class() == RegClass::Int);
357
debug_assert!(shift <= 3);
358
Self::ImmRegRegShift {
359
simm32,
360
base,
361
index,
362
shift,
363
flags: MemFlags::trusted(),
364
}
365
}
366
367
pub(crate) fn rip_relative(target: MachLabel) -> Self {
368
Self::RipRelative { target }
369
}
370
371
/// Set the specified [MemFlags] to the [Amode].
372
pub fn with_flags(&self, flags: MemFlags) -> Self {
373
match self {
374
&Self::ImmReg { simm32, base, .. } => Self::ImmReg {
375
simm32,
376
base,
377
flags,
378
},
379
&Self::ImmRegRegShift {
380
simm32,
381
base,
382
index,
383
shift,
384
..
385
} => Self::ImmRegRegShift {
386
simm32,
387
base,
388
index,
389
shift,
390
flags,
391
},
392
_ => panic!("Amode {self:?} cannot take memflags"),
393
}
394
}
395
396
/// Add the registers mentioned by `self` to `collector`.
397
pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
398
match self {
399
Amode::ImmReg { base, .. } => {
400
if *base != regs::rbp() && *base != regs::rsp() {
401
collector.reg_use(base);
402
}
403
}
404
Amode::ImmRegRegShift { base, index, .. } => {
405
debug_assert_ne!(base.to_reg(), regs::rbp());
406
debug_assert_ne!(base.to_reg(), regs::rsp());
407
collector.reg_use(base);
408
debug_assert_ne!(index.to_reg(), regs::rbp());
409
debug_assert_ne!(index.to_reg(), regs::rsp());
410
collector.reg_use(index);
411
}
412
Amode::RipRelative { .. } => {
413
// RIP isn't involved in regalloc.
414
}
415
}
416
}
417
418
/// Same as `get_operands`, but add the registers in the "late" phase.
419
pub(crate) fn get_operands_late(&mut self, collector: &mut impl OperandVisitor) {
420
match self {
421
Amode::ImmReg { base, .. } => {
422
collector.reg_late_use(base);
423
}
424
Amode::ImmRegRegShift { base, index, .. } => {
425
collector.reg_late_use(base);
426
collector.reg_late_use(index);
427
}
428
Amode::RipRelative { .. } => {
429
// RIP isn't involved in regalloc.
430
}
431
}
432
}
433
434
pub(crate) fn get_flags(&self) -> MemFlags {
435
match self {
436
Amode::ImmReg { flags, .. } | Amode::ImmRegRegShift { flags, .. } => *flags,
437
Amode::RipRelative { .. } => MemFlags::trusted(),
438
}
439
}
440
441
/// Offset the amode by a fixed offset.
442
pub(crate) fn offset(&self, offset: i32) -> Self {
443
let mut ret = self.clone();
444
match &mut ret {
445
&mut Amode::ImmReg { ref mut simm32, .. } => *simm32 += offset,
446
&mut Amode::ImmRegRegShift { ref mut simm32, .. } => *simm32 += offset,
447
_ => panic!("Cannot offset amode: {self:?}"),
448
}
449
ret
450
}
451
452
pub(crate) fn aligned(&self) -> bool {
453
self.get_flags().aligned()
454
}
455
}
456
457
impl PrettyPrint for Amode {
458
fn pretty_print(&self, _size: u8) -> String {
459
match self {
460
Amode::ImmReg { simm32, base, .. } => {
461
// Note: size is always 8; the address is 64 bits,
462
// even if the addressed operand is smaller.
463
format!("{}({})", *simm32, pretty_print_reg(*base, 8))
464
}
465
Amode::ImmRegRegShift {
466
simm32,
467
base,
468
index,
469
shift,
470
..
471
} => format!(
472
"{}({},{},{})",
473
*simm32,
474
pretty_print_reg(base.to_reg(), 8),
475
pretty_print_reg(index.to_reg(), 8),
476
1 << shift
477
),
478
Amode::RipRelative { target } => format!("label{}(%rip)", target.as_u32()),
479
}
480
}
481
}
482
483
/// A Memory Address. These denote a 64-bit value only.
484
/// Used for usual addressing modes as well as addressing modes used during compilation, when the
485
/// moving SP offset is not known.
486
#[derive(Clone, Debug)]
487
pub enum SyntheticAmode {
488
/// A real amode.
489
Real(Amode),
490
491
/// A (virtual) offset into the incoming argument area.
492
IncomingArg {
493
/// The downward offset from the start of the incoming argument area.
494
offset: u32,
495
},
496
497
/// A (virtual) offset to the slot area of the function frame, which lies just above the
498
/// outgoing arguments.
499
SlotOffset {
500
/// The offset into the slot area.
501
simm32: i32,
502
},
503
504
/// A virtual offset to a constant that will be emitted in the constant section of the buffer.
505
ConstantOffset(VCodeConstant),
506
}
507
508
impl SyntheticAmode {
509
/// Create a real addressing mode.
510
pub fn real(amode: Amode) -> Self {
511
Self::Real(amode)
512
}
513
514
pub(crate) fn slot_offset(simm32: i32) -> Self {
515
SyntheticAmode::SlotOffset { simm32 }
516
}
517
518
/// Add the registers mentioned by `self` to `collector`.
519
pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
520
match self {
521
SyntheticAmode::Real(addr) => addr.get_operands(collector),
522
SyntheticAmode::IncomingArg { .. } => {
523
// Nothing to do; the base is known and isn't involved in regalloc.
524
}
525
SyntheticAmode::SlotOffset { .. } => {
526
// Nothing to do; the base is SP and isn't involved in regalloc.
527
}
528
SyntheticAmode::ConstantOffset(_) => {}
529
}
530
}
531
532
/// Same as `get_operands`, but add the register in the "late" phase.
533
pub(crate) fn get_operands_late(&mut self, collector: &mut impl OperandVisitor) {
534
match self {
535
SyntheticAmode::Real(addr) => addr.get_operands_late(collector),
536
SyntheticAmode::IncomingArg { .. } => {
537
// Nothing to do; the base is known and isn't involved in regalloc.
538
}
539
SyntheticAmode::SlotOffset { .. } => {
540
// Nothing to do; the base is SP and isn't involved in regalloc.
541
}
542
SyntheticAmode::ConstantOffset(_) => {}
543
}
544
}
545
546
pub(crate) fn finalize(&self, frame: &FrameLayout, buffer: &mut MachBuffer<Inst>) -> Amode {
547
match self {
548
SyntheticAmode::Real(addr) => addr.clone(),
549
SyntheticAmode::IncomingArg { offset } => {
550
// NOTE: this could be made relative to RSP by adding additional
551
// offsets from the frame_layout.
552
let args_max_fp_offset = frame.tail_args_size + frame.setup_area_size;
553
Amode::imm_reg(
554
i32::try_from(args_max_fp_offset - offset).unwrap(),
555
regs::rbp(),
556
)
557
}
558
SyntheticAmode::SlotOffset { simm32 } => {
559
let off = *simm32 as i64 + i64::from(frame.outgoing_args_size);
560
Amode::imm_reg(off.try_into().expect("invalid sp offset"), regs::rsp())
561
}
562
SyntheticAmode::ConstantOffset(c) => {
563
Amode::rip_relative(buffer.get_label_for_constant(*c))
564
}
565
}
566
}
567
568
pub(crate) fn aligned(&self) -> bool {
569
match self {
570
SyntheticAmode::Real(addr) => addr.aligned(),
571
&SyntheticAmode::IncomingArg { .. }
572
| SyntheticAmode::SlotOffset { .. }
573
| SyntheticAmode::ConstantOffset { .. } => true,
574
}
575
}
576
577
/// Offset the synthetic amode by a fixed offset.
578
pub(crate) fn offset(&self, offset: i32) -> Self {
579
let mut ret = self.clone();
580
match &mut ret {
581
SyntheticAmode::Real(amode) => *amode = amode.offset(offset),
582
SyntheticAmode::SlotOffset { simm32 } => *simm32 += offset,
583
// `amode_offset` is used only in i128.load/store which
584
// takes a synthetic amode from `to_amode`; `to_amode` can
585
// only produce Real or SlotOffset amodes, never
586
// IncomingArg or ConstantOffset.
587
_ => panic!("Cannot offset SyntheticAmode: {self:?}"),
588
}
589
ret
590
}
591
}
592
593
impl From<Amode> for SyntheticAmode {
594
fn from(amode: Amode) -> SyntheticAmode {
595
SyntheticAmode::Real(amode)
596
}
597
}
598
599
impl From<VCodeConstant> for SyntheticAmode {
600
fn from(c: VCodeConstant) -> SyntheticAmode {
601
SyntheticAmode::ConstantOffset(c)
602
}
603
}
604
605
impl PrettyPrint for SyntheticAmode {
606
fn pretty_print(&self, _size: u8) -> String {
607
match self {
608
// See note in `Amode` regarding constant size of `8`.
609
SyntheticAmode::Real(addr) => addr.pretty_print(8),
610
&SyntheticAmode::IncomingArg { offset } => {
611
format!("rbp(stack args max - {offset})")
612
}
613
SyntheticAmode::SlotOffset { simm32 } => {
614
format!("rsp({} + virtual offset)", *simm32)
615
}
616
SyntheticAmode::ConstantOffset(c) => format!("const({})", c.as_u32()),
617
}
618
}
619
}
620
621
/// An operand which is either an integer Register, a value in Memory or an Immediate. This can
622
/// denote an 8, 16, 32 or 64 bit value. For the Immediate form, in the 8- and 16-bit case, only
623
/// the lower 8 or 16 bits of `simm32` is relevant. In the 64-bit case, the value denoted by
624
/// `simm32` is its sign-extension out to 64 bits.
625
#[derive(Clone, Debug)]
626
pub enum RegMemImm {
627
/// A register operand.
628
Reg {
629
/// The underlying register.
630
reg: Reg,
631
},
632
/// A memory operand.
633
Mem {
634
/// The memory address.
635
addr: SyntheticAmode,
636
},
637
/// An immediate operand.
638
Imm {
639
/// The immediate value.
640
simm32: u32,
641
},
642
}
643
644
impl RegMemImm {
645
/// Create a register operand.
646
pub fn reg(reg: Reg) -> Self {
647
debug_assert!(reg.class() == RegClass::Int || reg.class() == RegClass::Float);
648
Self::Reg { reg }
649
}
650
651
/// Create a memory operand.
652
pub fn mem(addr: impl Into<SyntheticAmode>) -> Self {
653
Self::Mem { addr: addr.into() }
654
}
655
656
/// Create an immediate operand.
657
pub fn imm(simm32: u32) -> Self {
658
Self::Imm { simm32 }
659
}
660
661
/// Add the regs mentioned by `self` to `collector`.
662
pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
663
match self {
664
Self::Reg { reg } => collector.reg_use(reg),
665
Self::Mem { addr } => addr.get_operands(collector),
666
Self::Imm { .. } => {}
667
}
668
}
669
}
670
671
impl From<RegMem> for RegMemImm {
672
fn from(rm: RegMem) -> RegMemImm {
673
match rm {
674
RegMem::Reg { reg } => RegMemImm::Reg { reg },
675
RegMem::Mem { addr } => RegMemImm::Mem { addr },
676
}
677
}
678
}
679
680
impl From<Reg> for RegMemImm {
681
fn from(reg: Reg) -> Self {
682
RegMemImm::Reg { reg }
683
}
684
}
685
686
impl PrettyPrint for RegMemImm {
687
fn pretty_print(&self, size: u8) -> String {
688
match self {
689
Self::Reg { reg } => pretty_print_reg(*reg, size),
690
Self::Mem { addr } => addr.pretty_print(size),
691
Self::Imm { simm32 } => format!("${}", *simm32 as i32),
692
}
693
}
694
}
695
696
/// An operand which is either an integer Register or a value in Memory. This can denote an 8, 16,
697
/// 32, 64, or 128 bit value.
698
#[derive(Clone, Debug)]
699
pub enum RegMem {
700
/// A register operand.
701
Reg {
702
/// The underlying register.
703
reg: Reg,
704
},
705
/// A memory operand.
706
Mem {
707
/// The memory address.
708
addr: SyntheticAmode,
709
},
710
}
711
712
impl RegMem {
713
/// Create a register operand.
714
pub fn reg(reg: Reg) -> Self {
715
debug_assert!(reg.class() == RegClass::Int || reg.class() == RegClass::Float);
716
Self::Reg { reg }
717
}
718
719
/// Create a memory operand.
720
pub fn mem(addr: impl Into<SyntheticAmode>) -> Self {
721
Self::Mem { addr: addr.into() }
722
}
723
/// Asserts that in register mode, the reg class is the one that's expected.
724
pub(crate) fn assert_regclass_is(&self, expected_reg_class: RegClass) {
725
if let Self::Reg { reg } = self {
726
debug_assert_eq!(reg.class(), expected_reg_class);
727
}
728
}
729
/// Add the regs mentioned by `self` to `collector`.
730
pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
731
match self {
732
RegMem::Reg { reg } => collector.reg_use(reg),
733
RegMem::Mem { addr, .. } => addr.get_operands(collector),
734
}
735
}
736
}
737
738
impl From<Reg> for RegMem {
739
fn from(reg: Reg) -> RegMem {
740
RegMem::Reg { reg }
741
}
742
}
743
744
impl From<Writable<Reg>> for RegMem {
745
fn from(r: Writable<Reg>) -> Self {
746
RegMem::reg(r.to_reg())
747
}
748
}
749
750
impl PrettyPrint for RegMem {
751
fn pretty_print(&self, size: u8) -> String {
752
match self {
753
RegMem::Reg { reg } => pretty_print_reg(*reg, size),
754
RegMem::Mem { addr, .. } => addr.pretty_print(size),
755
}
756
}
757
}
758
759
/// This defines the ways a value can be extended: either signed- or zero-extension, or none for
760
/// types that are not extended. Contrast with [ExtMode], which defines the widths from and to which
761
/// values can be extended.
762
#[derive(Clone, PartialEq)]
763
pub enum ExtKind {
764
/// No extension.
765
None,
766
/// Sign-extend.
767
SignExtend,
768
/// Zero-extend.
769
ZeroExtend,
770
}
771
772
/// These indicate ways of extending (widening) a value, using the Intel
773
/// naming: B(yte) = u8, W(ord) = u16, L(ong)word = u32, Q(uad)word = u64
774
#[derive(Clone, PartialEq)]
775
pub enum ExtMode {
776
/// Byte -> Longword.
777
BL,
778
/// Byte -> Quadword.
779
BQ,
780
/// Word -> Longword.
781
WL,
782
/// Word -> Quadword.
783
WQ,
784
/// Longword -> Quadword.
785
LQ,
786
}
787
788
impl ExtMode {
789
/// Calculate the `ExtMode` from passed bit lengths of the from/to types.
790
pub(crate) fn new(from_bits: u16, to_bits: u16) -> Option<ExtMode> {
791
match (from_bits, to_bits) {
792
(1, 8) | (1, 16) | (1, 32) | (8, 16) | (8, 32) => Some(ExtMode::BL),
793
(1, 64) | (8, 64) => Some(ExtMode::BQ),
794
(16, 32) => Some(ExtMode::WL),
795
(16, 64) => Some(ExtMode::WQ),
796
(32, 64) => Some(ExtMode::LQ),
797
_ => None,
798
}
799
}
800
}
801
802
impl fmt::Debug for ExtMode {
803
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
804
let name = match self {
805
ExtMode::BL => "bl",
806
ExtMode::BQ => "bq",
807
ExtMode::WL => "wl",
808
ExtMode::WQ => "wq",
809
ExtMode::LQ => "lq",
810
};
811
write!(fmt, "{name}")
812
}
813
}
814
815
impl fmt::Display for ExtMode {
816
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
817
fmt::Debug::fmt(self, f)
818
}
819
}
820
821
/// These indicate condition code tests. Not all are represented since not all are useful in
822
/// compiler-generated code.
823
#[derive(Copy, Clone, PartialEq, Eq)]
824
#[repr(u8)]
825
pub enum CC {
826
/// overflow
827
O = 0,
828
/// no overflow
829
NO = 1,
830
831
/// < unsigned
832
B = 2,
833
/// >= unsigned
834
NB = 3,
835
836
/// zero
837
Z = 4,
838
/// not-zero
839
NZ = 5,
840
841
/// <= unsigned
842
BE = 6,
843
/// > unsigned
844
NBE = 7,
845
846
/// negative
847
S = 8,
848
/// not-negative
849
NS = 9,
850
851
/// < signed
852
L = 12,
853
/// >= signed
854
NL = 13,
855
856
/// <= signed
857
LE = 14,
858
/// > signed
859
NLE = 15,
860
861
/// parity
862
P = 10,
863
864
/// not parity
865
NP = 11,
866
}
867
868
impl CC {
869
pub(crate) fn from_intcc(intcc: IntCC) -> Self {
870
match intcc {
871
IntCC::Equal => CC::Z,
872
IntCC::NotEqual => CC::NZ,
873
IntCC::SignedGreaterThanOrEqual => CC::NL,
874
IntCC::SignedGreaterThan => CC::NLE,
875
IntCC::SignedLessThanOrEqual => CC::LE,
876
IntCC::SignedLessThan => CC::L,
877
IntCC::UnsignedGreaterThanOrEqual => CC::NB,
878
IntCC::UnsignedGreaterThan => CC::NBE,
879
IntCC::UnsignedLessThanOrEqual => CC::BE,
880
IntCC::UnsignedLessThan => CC::B,
881
}
882
}
883
884
pub(crate) fn invert(&self) -> Self {
885
match self {
886
CC::O => CC::NO,
887
CC::NO => CC::O,
888
889
CC::B => CC::NB,
890
CC::NB => CC::B,
891
892
CC::Z => CC::NZ,
893
CC::NZ => CC::Z,
894
895
CC::BE => CC::NBE,
896
CC::NBE => CC::BE,
897
898
CC::S => CC::NS,
899
CC::NS => CC::S,
900
901
CC::L => CC::NL,
902
CC::NL => CC::L,
903
904
CC::LE => CC::NLE,
905
CC::NLE => CC::LE,
906
907
CC::P => CC::NP,
908
CC::NP => CC::P,
909
}
910
}
911
912
pub(crate) fn get_enc(self) -> u8 {
913
self as u8
914
}
915
}
916
917
impl fmt::Debug for CC {
918
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
919
let name = match self {
920
CC::O => "o",
921
CC::NO => "no",
922
CC::B => "b",
923
CC::NB => "nb",
924
CC::Z => "z",
925
CC::NZ => "nz",
926
CC::BE => "be",
927
CC::NBE => "nbe",
928
CC::S => "s",
929
CC::NS => "ns",
930
CC::L => "l",
931
CC::NL => "nl",
932
CC::LE => "le",
933
CC::NLE => "nle",
934
CC::P => "p",
935
CC::NP => "np",
936
};
937
write!(fmt, "{name}")
938
}
939
}
940
941
impl fmt::Display for CC {
942
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
943
fmt::Debug::fmt(self, f)
944
}
945
}
946
947
/// Encode the ways that floats can be compared. This is used in float comparisons such as `cmpps`,
948
/// e.g.; it is distinguished from other float comparisons (e.g. `ucomiss`) in that those use EFLAGS
949
/// whereas [FcmpImm] is used as an immediate.
950
#[derive(Clone, Copy)]
951
pub enum FcmpImm {
952
/// Equal comparison.
953
Equal = 0x00,
954
/// Less than comparison.
955
LessThan = 0x01,
956
/// Less than or equal comparison.
957
LessThanOrEqual = 0x02,
958
/// Unordered.
959
Unordered = 0x03,
960
/// Not equal comparison.
961
NotEqual = 0x04,
962
/// Unordered of greater than or equal comparison.
963
UnorderedOrGreaterThanOrEqual = 0x05,
964
/// Unordered or greater than comparison.
965
UnorderedOrGreaterThan = 0x06,
966
/// Ordered.
967
Ordered = 0x07,
968
}
969
970
impl FcmpImm {
971
pub(crate) fn encode(self) -> u8 {
972
self as u8
973
}
974
}
975
976
impl From<FloatCC> for FcmpImm {
977
fn from(cond: FloatCC) -> Self {
978
match cond {
979
FloatCC::Equal => FcmpImm::Equal,
980
FloatCC::LessThan => FcmpImm::LessThan,
981
FloatCC::LessThanOrEqual => FcmpImm::LessThanOrEqual,
982
FloatCC::Unordered => FcmpImm::Unordered,
983
FloatCC::NotEqual => FcmpImm::NotEqual,
984
FloatCC::UnorderedOrGreaterThanOrEqual => FcmpImm::UnorderedOrGreaterThanOrEqual,
985
FloatCC::UnorderedOrGreaterThan => FcmpImm::UnorderedOrGreaterThan,
986
FloatCC::Ordered => FcmpImm::Ordered,
987
_ => panic!("unable to create comparison predicate for {cond}"),
988
}
989
}
990
}
991
992
/// Encode the rounding modes used as part of the Rounding Control field.
993
/// Note, these rounding immediates only consider the rounding control field
994
/// (i.e. the rounding mode) which only take up the first two bits when encoded.
995
/// However the rounding immediate which this field helps make up, also includes
996
/// bits 3 and 4 which define the rounding select and precision mask respectively.
997
/// These two bits are not defined here and are implicitly set to zero when encoded.
998
#[derive(Clone, Copy)]
999
pub enum RoundImm {
1000
/// Round to nearest mode.
1001
RoundNearest = 0x00,
1002
/// Round down mode.
1003
RoundDown = 0x01,
1004
/// Round up mode.
1005
RoundUp = 0x02,
1006
/// Round to zero mode.
1007
RoundZero = 0x03,
1008
}
1009
1010
impl RoundImm {
1011
pub(crate) fn encode(self) -> u8 {
1012
self as u8
1013
}
1014
}
1015
1016
/// An operand's size in bits.
1017
#[derive(Clone, Copy, PartialEq)]
1018
pub enum OperandSize {
1019
/// 8-bit.
1020
Size8,
1021
/// 16-bit.
1022
Size16,
1023
/// 32-bit.
1024
Size32,
1025
/// 64-bit.
1026
Size64,
1027
}
1028
1029
impl OperandSize {
1030
pub(crate) fn from_bytes(num_bytes: u32) -> Self {
1031
match num_bytes {
1032
1 => OperandSize::Size8,
1033
2 => OperandSize::Size16,
1034
4 => OperandSize::Size32,
1035
8 => OperandSize::Size64,
1036
_ => unreachable!("Invalid OperandSize: {}", num_bytes),
1037
}
1038
}
1039
1040
// Computes the OperandSize for a given type.
1041
// For vectors, the OperandSize of the lanes is returned.
1042
pub(crate) fn from_ty(ty: Type) -> Self {
1043
Self::from_bytes(ty.lane_type().bytes())
1044
}
1045
1046
// Check that the value of self is one of the allowed sizes.
1047
pub(crate) fn is_one_of(&self, sizes: &[Self]) -> bool {
1048
sizes.iter().any(|val| *self == *val)
1049
}
1050
1051
pub(crate) fn to_bytes(&self) -> u8 {
1052
match self {
1053
Self::Size8 => 1,
1054
Self::Size16 => 2,
1055
Self::Size32 => 4,
1056
Self::Size64 => 8,
1057
}
1058
}
1059
1060
pub(crate) fn to_bits(&self) -> u8 {
1061
self.to_bytes() * 8
1062
}
1063
}
1064
1065