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
1693 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 std::fmt;
11
use std::string::String;
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 std::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
578
impl From<Amode> for SyntheticAmode {
579
fn from(amode: Amode) -> SyntheticAmode {
580
SyntheticAmode::Real(amode)
581
}
582
}
583
584
impl From<VCodeConstant> for SyntheticAmode {
585
fn from(c: VCodeConstant) -> SyntheticAmode {
586
SyntheticAmode::ConstantOffset(c)
587
}
588
}
589
590
impl PrettyPrint for SyntheticAmode {
591
fn pretty_print(&self, _size: u8) -> String {
592
match self {
593
// See note in `Amode` regarding constant size of `8`.
594
SyntheticAmode::Real(addr) => addr.pretty_print(8),
595
&SyntheticAmode::IncomingArg { offset } => {
596
format!("rbp(stack args max - {offset})")
597
}
598
SyntheticAmode::SlotOffset { simm32 } => {
599
format!("rsp({} + virtual offset)", *simm32)
600
}
601
SyntheticAmode::ConstantOffset(c) => format!("const({})", c.as_u32()),
602
}
603
}
604
}
605
606
/// An operand which is either an integer Register, a value in Memory or an Immediate. This can
607
/// denote an 8, 16, 32 or 64 bit value. For the Immediate form, in the 8- and 16-bit case, only
608
/// the lower 8 or 16 bits of `simm32` is relevant. In the 64-bit case, the value denoted by
609
/// `simm32` is its sign-extension out to 64 bits.
610
#[derive(Clone, Debug)]
611
pub enum RegMemImm {
612
/// A register operand.
613
Reg {
614
/// The underlying register.
615
reg: Reg,
616
},
617
/// A memory operand.
618
Mem {
619
/// The memory address.
620
addr: SyntheticAmode,
621
},
622
/// An immediate operand.
623
Imm {
624
/// The immediate value.
625
simm32: u32,
626
},
627
}
628
629
impl RegMemImm {
630
/// Create a register operand.
631
pub fn reg(reg: Reg) -> Self {
632
debug_assert!(reg.class() == RegClass::Int || reg.class() == RegClass::Float);
633
Self::Reg { reg }
634
}
635
636
/// Create a memory operand.
637
pub fn mem(addr: impl Into<SyntheticAmode>) -> Self {
638
Self::Mem { addr: addr.into() }
639
}
640
641
/// Create an immediate operand.
642
pub fn imm(simm32: u32) -> Self {
643
Self::Imm { simm32 }
644
}
645
646
/// Add the regs mentioned by `self` to `collector`.
647
pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
648
match self {
649
Self::Reg { reg } => collector.reg_use(reg),
650
Self::Mem { addr } => addr.get_operands(collector),
651
Self::Imm { .. } => {}
652
}
653
}
654
}
655
656
impl From<RegMem> for RegMemImm {
657
fn from(rm: RegMem) -> RegMemImm {
658
match rm {
659
RegMem::Reg { reg } => RegMemImm::Reg { reg },
660
RegMem::Mem { addr } => RegMemImm::Mem { addr },
661
}
662
}
663
}
664
665
impl From<Reg> for RegMemImm {
666
fn from(reg: Reg) -> Self {
667
RegMemImm::Reg { reg }
668
}
669
}
670
671
impl PrettyPrint for RegMemImm {
672
fn pretty_print(&self, size: u8) -> String {
673
match self {
674
Self::Reg { reg } => pretty_print_reg(*reg, size),
675
Self::Mem { addr } => addr.pretty_print(size),
676
Self::Imm { simm32 } => format!("${}", *simm32 as i32),
677
}
678
}
679
}
680
681
/// An operand which is either an integer Register or a value in Memory. This can denote an 8, 16,
682
/// 32, 64, or 128 bit value.
683
#[derive(Clone, Debug)]
684
pub enum RegMem {
685
/// A register operand.
686
Reg {
687
/// The underlying register.
688
reg: Reg,
689
},
690
/// A memory operand.
691
Mem {
692
/// The memory address.
693
addr: SyntheticAmode,
694
},
695
}
696
697
impl RegMem {
698
/// Create a register operand.
699
pub fn reg(reg: Reg) -> Self {
700
debug_assert!(reg.class() == RegClass::Int || reg.class() == RegClass::Float);
701
Self::Reg { reg }
702
}
703
704
/// Create a memory operand.
705
pub fn mem(addr: impl Into<SyntheticAmode>) -> Self {
706
Self::Mem { addr: addr.into() }
707
}
708
/// Asserts that in register mode, the reg class is the one that's expected.
709
pub(crate) fn assert_regclass_is(&self, expected_reg_class: RegClass) {
710
if let Self::Reg { reg } = self {
711
debug_assert_eq!(reg.class(), expected_reg_class);
712
}
713
}
714
/// Add the regs mentioned by `self` to `collector`.
715
pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
716
match self {
717
RegMem::Reg { reg } => collector.reg_use(reg),
718
RegMem::Mem { addr, .. } => addr.get_operands(collector),
719
}
720
}
721
}
722
723
impl From<Reg> for RegMem {
724
fn from(reg: Reg) -> RegMem {
725
RegMem::Reg { reg }
726
}
727
}
728
729
impl From<Writable<Reg>> for RegMem {
730
fn from(r: Writable<Reg>) -> Self {
731
RegMem::reg(r.to_reg())
732
}
733
}
734
735
impl PrettyPrint for RegMem {
736
fn pretty_print(&self, size: u8) -> String {
737
match self {
738
RegMem::Reg { reg } => pretty_print_reg(*reg, size),
739
RegMem::Mem { addr, .. } => addr.pretty_print(size),
740
}
741
}
742
}
743
744
/// This defines the ways a value can be extended: either signed- or zero-extension, or none for
745
/// types that are not extended. Contrast with [ExtMode], which defines the widths from and to which
746
/// values can be extended.
747
#[derive(Clone, PartialEq)]
748
pub enum ExtKind {
749
/// No extension.
750
None,
751
/// Sign-extend.
752
SignExtend,
753
/// Zero-extend.
754
ZeroExtend,
755
}
756
757
/// These indicate ways of extending (widening) a value, using the Intel
758
/// naming: B(yte) = u8, W(ord) = u16, L(ong)word = u32, Q(uad)word = u64
759
#[derive(Clone, PartialEq)]
760
pub enum ExtMode {
761
/// Byte -> Longword.
762
BL,
763
/// Byte -> Quadword.
764
BQ,
765
/// Word -> Longword.
766
WL,
767
/// Word -> Quadword.
768
WQ,
769
/// Longword -> Quadword.
770
LQ,
771
}
772
773
impl ExtMode {
774
/// Calculate the `ExtMode` from passed bit lengths of the from/to types.
775
pub(crate) fn new(from_bits: u16, to_bits: u16) -> Option<ExtMode> {
776
match (from_bits, to_bits) {
777
(1, 8) | (1, 16) | (1, 32) | (8, 16) | (8, 32) => Some(ExtMode::BL),
778
(1, 64) | (8, 64) => Some(ExtMode::BQ),
779
(16, 32) => Some(ExtMode::WL),
780
(16, 64) => Some(ExtMode::WQ),
781
(32, 64) => Some(ExtMode::LQ),
782
_ => None,
783
}
784
}
785
}
786
787
impl fmt::Debug for ExtMode {
788
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
789
let name = match self {
790
ExtMode::BL => "bl",
791
ExtMode::BQ => "bq",
792
ExtMode::WL => "wl",
793
ExtMode::WQ => "wq",
794
ExtMode::LQ => "lq",
795
};
796
write!(fmt, "{name}")
797
}
798
}
799
800
impl fmt::Display for ExtMode {
801
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
802
fmt::Debug::fmt(self, f)
803
}
804
}
805
806
/// These indicate condition code tests. Not all are represented since not all are useful in
807
/// compiler-generated code.
808
#[derive(Copy, Clone, PartialEq, Eq)]
809
#[repr(u8)]
810
pub enum CC {
811
/// overflow
812
O = 0,
813
/// no overflow
814
NO = 1,
815
816
/// < unsigned
817
B = 2,
818
/// >= unsigned
819
NB = 3,
820
821
/// zero
822
Z = 4,
823
/// not-zero
824
NZ = 5,
825
826
/// <= unsigned
827
BE = 6,
828
/// > unsigned
829
NBE = 7,
830
831
/// negative
832
S = 8,
833
/// not-negative
834
NS = 9,
835
836
/// < signed
837
L = 12,
838
/// >= signed
839
NL = 13,
840
841
/// <= signed
842
LE = 14,
843
/// > signed
844
NLE = 15,
845
846
/// parity
847
P = 10,
848
849
/// not parity
850
NP = 11,
851
}
852
853
impl CC {
854
pub(crate) fn from_intcc(intcc: IntCC) -> Self {
855
match intcc {
856
IntCC::Equal => CC::Z,
857
IntCC::NotEqual => CC::NZ,
858
IntCC::SignedGreaterThanOrEqual => CC::NL,
859
IntCC::SignedGreaterThan => CC::NLE,
860
IntCC::SignedLessThanOrEqual => CC::LE,
861
IntCC::SignedLessThan => CC::L,
862
IntCC::UnsignedGreaterThanOrEqual => CC::NB,
863
IntCC::UnsignedGreaterThan => CC::NBE,
864
IntCC::UnsignedLessThanOrEqual => CC::BE,
865
IntCC::UnsignedLessThan => CC::B,
866
}
867
}
868
869
pub(crate) fn invert(&self) -> Self {
870
match self {
871
CC::O => CC::NO,
872
CC::NO => CC::O,
873
874
CC::B => CC::NB,
875
CC::NB => CC::B,
876
877
CC::Z => CC::NZ,
878
CC::NZ => CC::Z,
879
880
CC::BE => CC::NBE,
881
CC::NBE => CC::BE,
882
883
CC::S => CC::NS,
884
CC::NS => CC::S,
885
886
CC::L => CC::NL,
887
CC::NL => CC::L,
888
889
CC::LE => CC::NLE,
890
CC::NLE => CC::LE,
891
892
CC::P => CC::NP,
893
CC::NP => CC::P,
894
}
895
}
896
897
pub(crate) fn get_enc(self) -> u8 {
898
self as u8
899
}
900
}
901
902
impl fmt::Debug for CC {
903
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
904
let name = match self {
905
CC::O => "o",
906
CC::NO => "no",
907
CC::B => "b",
908
CC::NB => "nb",
909
CC::Z => "z",
910
CC::NZ => "nz",
911
CC::BE => "be",
912
CC::NBE => "nbe",
913
CC::S => "s",
914
CC::NS => "ns",
915
CC::L => "l",
916
CC::NL => "nl",
917
CC::LE => "le",
918
CC::NLE => "nle",
919
CC::P => "p",
920
CC::NP => "np",
921
};
922
write!(fmt, "{name}")
923
}
924
}
925
926
impl fmt::Display for CC {
927
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
928
fmt::Debug::fmt(self, f)
929
}
930
}
931
932
/// Encode the ways that floats can be compared. This is used in float comparisons such as `cmpps`,
933
/// e.g.; it is distinguished from other float comparisons (e.g. `ucomiss`) in that those use EFLAGS
934
/// whereas [FcmpImm] is used as an immediate.
935
#[derive(Clone, Copy)]
936
pub enum FcmpImm {
937
/// Equal comparison.
938
Equal = 0x00,
939
/// Less than comparison.
940
LessThan = 0x01,
941
/// Less than or equal comparison.
942
LessThanOrEqual = 0x02,
943
/// Unordered.
944
Unordered = 0x03,
945
/// Not equal comparison.
946
NotEqual = 0x04,
947
/// Unordered of greater than or equal comparison.
948
UnorderedOrGreaterThanOrEqual = 0x05,
949
/// Unordered or greater than comparison.
950
UnorderedOrGreaterThan = 0x06,
951
/// Ordered.
952
Ordered = 0x07,
953
}
954
955
impl FcmpImm {
956
pub(crate) fn encode(self) -> u8 {
957
self as u8
958
}
959
}
960
961
impl From<FloatCC> for FcmpImm {
962
fn from(cond: FloatCC) -> Self {
963
match cond {
964
FloatCC::Equal => FcmpImm::Equal,
965
FloatCC::LessThan => FcmpImm::LessThan,
966
FloatCC::LessThanOrEqual => FcmpImm::LessThanOrEqual,
967
FloatCC::Unordered => FcmpImm::Unordered,
968
FloatCC::NotEqual => FcmpImm::NotEqual,
969
FloatCC::UnorderedOrGreaterThanOrEqual => FcmpImm::UnorderedOrGreaterThanOrEqual,
970
FloatCC::UnorderedOrGreaterThan => FcmpImm::UnorderedOrGreaterThan,
971
FloatCC::Ordered => FcmpImm::Ordered,
972
_ => panic!("unable to create comparison predicate for {cond}"),
973
}
974
}
975
}
976
977
/// Encode the rounding modes used as part of the Rounding Control field.
978
/// Note, these rounding immediates only consider the rounding control field
979
/// (i.e. the rounding mode) which only take up the first two bits when encoded.
980
/// However the rounding immediate which this field helps make up, also includes
981
/// bits 3 and 4 which define the rounding select and precision mask respectively.
982
/// These two bits are not defined here and are implicitly set to zero when encoded.
983
#[derive(Clone, Copy)]
984
pub enum RoundImm {
985
/// Round to nearest mode.
986
RoundNearest = 0x00,
987
/// Round down mode.
988
RoundDown = 0x01,
989
/// Round up mode.
990
RoundUp = 0x02,
991
/// Round to zero mode.
992
RoundZero = 0x03,
993
}
994
995
impl RoundImm {
996
pub(crate) fn encode(self) -> u8 {
997
self as u8
998
}
999
}
1000
1001
/// An operand's size in bits.
1002
#[derive(Clone, Copy, PartialEq)]
1003
pub enum OperandSize {
1004
/// 8-bit.
1005
Size8,
1006
/// 16-bit.
1007
Size16,
1008
/// 32-bit.
1009
Size32,
1010
/// 64-bit.
1011
Size64,
1012
}
1013
1014
impl OperandSize {
1015
pub(crate) fn from_bytes(num_bytes: u32) -> Self {
1016
match num_bytes {
1017
1 => OperandSize::Size8,
1018
2 => OperandSize::Size16,
1019
4 => OperandSize::Size32,
1020
8 => OperandSize::Size64,
1021
_ => unreachable!("Invalid OperandSize: {}", num_bytes),
1022
}
1023
}
1024
1025
// Computes the OperandSize for a given type.
1026
// For vectors, the OperandSize of the lanes is returned.
1027
pub(crate) fn from_ty(ty: Type) -> Self {
1028
Self::from_bytes(ty.lane_type().bytes())
1029
}
1030
1031
// Check that the value of self is one of the allowed sizes.
1032
pub(crate) fn is_one_of(&self, sizes: &[Self]) -> bool {
1033
sizes.iter().any(|val| *self == *val)
1034
}
1035
1036
pub(crate) fn to_bytes(&self) -> u8 {
1037
match self {
1038
Self::Size8 => 1,
1039
Self::Size16 => 2,
1040
Self::Size32 => 4,
1041
Self::Size64 => 8,
1042
}
1043
}
1044
1045
pub(crate) fn to_bits(&self) -> u8 {
1046
self.to_bytes() * 8
1047
}
1048
}
1049
1050