use super::regs::{self};
use crate::ir::MemFlags;
use crate::ir::condcodes::{FloatCC, IntCC};
use crate::ir::types::*;
use crate::isa::x64::inst::Inst;
use crate::isa::x64::inst::regs::pretty_print_reg;
use crate::machinst::*;
use alloc::string::String;
use core::fmt;
pub trait ToWritableReg {
fn to_writable_reg(&self) -> Writable<Reg>;
}
pub trait FromWritableReg: Sized {
fn from_writable_reg(w: Writable<Reg>) -> Option<Self>;
}
macro_rules! newtype_of_reg {
(
$newtype_reg:ident,
$newtype_writable_reg:ident,
$newtype_option_writable_reg:ident,
reg_mem: ($($newtype_reg_mem:ident $(aligned:$aligned:ident)?),*),
reg_mem_imm: ($($newtype_reg_mem_imm:ident $(aligned:$aligned_imm:ident)?),*),
|$check_reg:ident| $check:expr
) => {
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct $newtype_reg(Reg);
impl PartialEq<Reg> for $newtype_reg {
fn eq(&self, other: &Reg) -> bool {
self.0 == *other
}
}
impl From<$newtype_reg> for Reg {
fn from(r: $newtype_reg) -> Self {
r.0
}
}
impl $newtype_reg {
pub fn new($check_reg: Reg) -> Option<Self> {
if $check {
Some(Self($check_reg))
} else {
None
}
}
pub fn unwrap_new($check_reg: Reg) -> Self {
if $check {
Self($check_reg)
} else {
panic!(
"cannot construct {} from register {:?} with register class {:?}",
stringify!($newtype_reg),
$check_reg,
$check_reg.class(),
)
}
}
pub fn to_reg(self) -> Reg {
self.0
}
}
impl core::ops::Deref for $newtype_reg {
type Target = Reg;
fn deref(&self) -> &Reg {
&self.0
}
}
impl AsMut<Reg> for $newtype_reg {
fn as_mut(&mut self) -> &mut Reg {
&mut self.0
}
}
pub type $newtype_writable_reg = Writable<$newtype_reg>;
#[allow(dead_code, reason = "Used by some newtypes and not others")]
pub type $newtype_option_writable_reg = Option<Writable<$newtype_reg>>;
impl ToWritableReg for $newtype_writable_reg {
fn to_writable_reg(&self) -> Writable<Reg> {
Writable::from_reg(self.to_reg().to_reg())
}
}
impl FromWritableReg for $newtype_writable_reg {
fn from_writable_reg(w: Writable<Reg>) -> Option<Self> {
Some(Writable::from_reg($newtype_reg::new(w.to_reg())?))
}
}
$(
#[derive(Clone, Debug)]
pub struct $newtype_reg_mem(RegMem);
impl From<$newtype_reg_mem> for RegMem {
fn from(rm: $newtype_reg_mem) -> Self {
rm.0
}
}
impl<'a> From<&'a $newtype_reg_mem> for &'a RegMem {
fn from(rm: &'a $newtype_reg_mem) -> &'a RegMem {
&rm.0
}
}
impl From<$newtype_reg> for $newtype_reg_mem {
fn from(r: $newtype_reg) -> Self {
$newtype_reg_mem(RegMem::reg(r.into()))
}
}
impl $newtype_reg_mem {
pub fn new(rm: RegMem) -> Option<Self> {
match rm {
RegMem::Mem { addr } => {
let mut _allow = true;
$(
if $aligned {
_allow = addr.aligned();
}
)?
if _allow {
Some(Self(RegMem::Mem { addr }))
} else {
None
}
}
RegMem::Reg { reg } => Some($newtype_reg::new(reg)?.into()),
}
}
pub fn unwrap_new(rm: RegMem) -> Self {
match rm {
RegMem::Mem { addr } => {
$(
if $aligned && !addr.aligned() {
panic!(
"cannot create {} from an unaligned memory address: {addr:?}",
stringify!($newtype_reg_mem),
);
}
)?
Self(RegMem::Mem { addr })
}
RegMem::Reg { reg } => $newtype_reg::unwrap_new(reg).into(),
}
}
pub fn to_reg_mem(self) -> RegMem {
self.0
}
#[allow(dead_code, reason = "Used by some newtypes and not others")]
pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
self.0.get_operands(collector);
}
}
impl PrettyPrint for $newtype_reg_mem {
fn pretty_print(&self, size: u8) -> String {
self.0.pretty_print(size)
}
}
)*
$(
#[derive(Clone, Debug)]
pub struct $newtype_reg_mem_imm(RegMemImm);
impl From<$newtype_reg_mem_imm> for RegMemImm {
fn from(rmi: $newtype_reg_mem_imm) -> RegMemImm {
rmi.0
}
}
impl<'a> From<&'a $newtype_reg_mem_imm> for &'a RegMemImm {
fn from(rmi: &'a $newtype_reg_mem_imm) -> &'a RegMemImm {
&rmi.0
}
}
impl From<$newtype_reg> for $newtype_reg_mem_imm {
fn from(r: $newtype_reg) -> Self {
$newtype_reg_mem_imm(RegMemImm::reg(r.into()))
}
}
impl $newtype_reg_mem_imm {
pub fn new(rmi: RegMemImm) -> Option<Self> {
match rmi {
RegMemImm::Imm { .. } => Some(Self(rmi)),
RegMemImm::Mem { addr } => {
let mut _allow = true;
$(
if $aligned_imm {
_allow = addr.aligned();
}
)?
if _allow {
Some(Self(RegMemImm::Mem { addr }))
} else {
None
}
}
RegMemImm::Reg { reg } => Some($newtype_reg::new(reg)?.into()),
}
}
pub fn unwrap_new(rmi: RegMemImm) -> Self {
match rmi {
RegMemImm::Imm { .. } => Self(rmi),
RegMemImm::Mem { addr } => {
$(
if $aligned_imm && !addr.aligned() {
panic!(
"cannot construct {} from unaligned memory address: {:?}",
stringify!($newtype_reg_mem_imm),
addr,
);
}
)?
Self(RegMemImm::Mem { addr })
}
RegMemImm::Reg { reg } => $newtype_reg::unwrap_new(reg).into(),
}
}
#[allow(dead_code, reason = "Used by some newtypes and not others")]
pub fn to_reg_mem_imm(self) -> RegMemImm {
self.0
}
#[allow(dead_code, reason = "Used by some newtypes and not others")]
pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
self.0.get_operands(collector);
}
}
impl PrettyPrint for $newtype_reg_mem_imm {
fn pretty_print(&self, size: u8) -> String {
self.0.pretty_print(size)
}
}
)*
};
}
newtype_of_reg!(
Gpr,
WritableGpr,
OptionWritableGpr,
reg_mem: (GprMem),
reg_mem_imm: (GprMemImm),
|reg| reg.class() == RegClass::Int
);
#[expect(missing_docs, reason = "self-describing fields")]
impl Gpr {
pub const RAX: Gpr = Gpr(regs::rax());
pub const RBX: Gpr = Gpr(regs::rbx());
pub const RCX: Gpr = Gpr(regs::rcx());
pub const RDX: Gpr = Gpr(regs::rdx());
pub const RSI: Gpr = Gpr(regs::rsi());
pub const RDI: Gpr = Gpr(regs::rdi());
pub const RSP: Gpr = Gpr(regs::rsp());
pub const RBP: Gpr = Gpr(regs::rbp());
pub const R8: Gpr = Gpr(regs::r8());
pub const R9: Gpr = Gpr(regs::r9());
pub const R10: Gpr = Gpr(regs::r10());
pub const R11: Gpr = Gpr(regs::r11());
pub const R12: Gpr = Gpr(regs::r12());
pub const R13: Gpr = Gpr(regs::r13());
pub const R14: Gpr = Gpr(regs::r14());
pub const R15: Gpr = Gpr(regs::r15());
}
newtype_of_reg!(
Xmm,
WritableXmm,
OptionWritableXmm,
reg_mem: (XmmMem, XmmMemAligned aligned:true),
reg_mem_imm: (XmmMemImm, XmmMemAlignedImm aligned:true),
|reg| reg.class() == RegClass::Float
);
pub use crate::isa::x64::lower::isle::generated_code::Amode;
impl Amode {
pub fn imm_reg(simm32: i32, base: Reg) -> Self {
debug_assert!(base.class() == RegClass::Int);
Self::ImmReg {
simm32,
base,
flags: MemFlags::trusted(),
}
}
pub fn imm_reg_reg_shift(simm32: i32, base: Gpr, index: Gpr, shift: u8) -> Self {
debug_assert!(base.class() == RegClass::Int);
debug_assert!(index.class() == RegClass::Int);
debug_assert!(shift <= 3);
Self::ImmRegRegShift {
simm32,
base,
index,
shift,
flags: MemFlags::trusted(),
}
}
pub(crate) fn rip_relative(target: MachLabel) -> Self {
Self::RipRelative { target }
}
pub fn with_flags(&self, flags: MemFlags) -> Self {
match self {
&Self::ImmReg { simm32, base, .. } => Self::ImmReg {
simm32,
base,
flags,
},
&Self::ImmRegRegShift {
simm32,
base,
index,
shift,
..
} => Self::ImmRegRegShift {
simm32,
base,
index,
shift,
flags,
},
_ => panic!("Amode {self:?} cannot take memflags"),
}
}
pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
match self {
Amode::ImmReg { base, .. } => {
if *base != regs::rbp() && *base != regs::rsp() {
collector.reg_use(base);
}
}
Amode::ImmRegRegShift { base, index, .. } => {
debug_assert_ne!(base.to_reg(), regs::rbp());
debug_assert_ne!(base.to_reg(), regs::rsp());
collector.reg_use(base);
debug_assert_ne!(index.to_reg(), regs::rbp());
debug_assert_ne!(index.to_reg(), regs::rsp());
collector.reg_use(index);
}
Amode::RipRelative { .. } => {
}
}
}
pub(crate) fn get_operands_late(&mut self, collector: &mut impl OperandVisitor) {
match self {
Amode::ImmReg { base, .. } => {
collector.reg_late_use(base);
}
Amode::ImmRegRegShift { base, index, .. } => {
collector.reg_late_use(base);
collector.reg_late_use(index);
}
Amode::RipRelative { .. } => {
}
}
}
pub(crate) fn get_flags(&self) -> MemFlags {
match self {
Amode::ImmReg { flags, .. } | Amode::ImmRegRegShift { flags, .. } => *flags,
Amode::RipRelative { .. } => MemFlags::trusted(),
}
}
pub(crate) fn offset(&self, offset: i32) -> Self {
let mut ret = self.clone();
match &mut ret {
&mut Amode::ImmReg { ref mut simm32, .. } => *simm32 += offset,
&mut Amode::ImmRegRegShift { ref mut simm32, .. } => *simm32 += offset,
_ => panic!("Cannot offset amode: {self:?}"),
}
ret
}
pub(crate) fn aligned(&self) -> bool {
self.get_flags().aligned()
}
}
impl PrettyPrint for Amode {
fn pretty_print(&self, _size: u8) -> String {
match self {
Amode::ImmReg { simm32, base, .. } => {
format!("{}({})", *simm32, pretty_print_reg(*base, 8))
}
Amode::ImmRegRegShift {
simm32,
base,
index,
shift,
..
} => format!(
"{}({},{},{})",
*simm32,
pretty_print_reg(base.to_reg(), 8),
pretty_print_reg(index.to_reg(), 8),
1 << shift
),
Amode::RipRelative { target } => format!("label{}(%rip)", target.as_u32()),
}
}
}
#[derive(Clone, Debug)]
pub enum SyntheticAmode {
Real(Amode),
IncomingArg {
offset: u32,
},
SlotOffset {
simm32: i32,
},
ConstantOffset(VCodeConstant),
}
impl SyntheticAmode {
pub fn real(amode: Amode) -> Self {
Self::Real(amode)
}
pub(crate) fn slot_offset(simm32: i32) -> Self {
SyntheticAmode::SlotOffset { simm32 }
}
pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
match self {
SyntheticAmode::Real(addr) => addr.get_operands(collector),
SyntheticAmode::IncomingArg { .. } => {
}
SyntheticAmode::SlotOffset { .. } => {
}
SyntheticAmode::ConstantOffset(_) => {}
}
}
pub(crate) fn get_operands_late(&mut self, collector: &mut impl OperandVisitor) {
match self {
SyntheticAmode::Real(addr) => addr.get_operands_late(collector),
SyntheticAmode::IncomingArg { .. } => {
}
SyntheticAmode::SlotOffset { .. } => {
}
SyntheticAmode::ConstantOffset(_) => {}
}
}
pub(crate) fn finalize(&self, frame: &FrameLayout, buffer: &mut MachBuffer<Inst>) -> Amode {
match self {
SyntheticAmode::Real(addr) => addr.clone(),
SyntheticAmode::IncomingArg { offset } => {
let args_max_fp_offset = frame.tail_args_size + frame.setup_area_size;
Amode::imm_reg(
i32::try_from(args_max_fp_offset - offset).unwrap(),
regs::rbp(),
)
}
SyntheticAmode::SlotOffset { simm32 } => {
let off = *simm32 as i64 + i64::from(frame.outgoing_args_size);
Amode::imm_reg(off.try_into().expect("invalid sp offset"), regs::rsp())
}
SyntheticAmode::ConstantOffset(c) => {
Amode::rip_relative(buffer.get_label_for_constant(*c))
}
}
}
pub(crate) fn aligned(&self) -> bool {
match self {
SyntheticAmode::Real(addr) => addr.aligned(),
&SyntheticAmode::IncomingArg { .. }
| SyntheticAmode::SlotOffset { .. }
| SyntheticAmode::ConstantOffset { .. } => true,
}
}
pub(crate) fn offset(&self, offset: i32) -> Self {
let mut ret = self.clone();
match &mut ret {
SyntheticAmode::Real(amode) => *amode = amode.offset(offset),
SyntheticAmode::SlotOffset { simm32 } => *simm32 += offset,
_ => panic!("Cannot offset SyntheticAmode: {self:?}"),
}
ret
}
}
impl From<Amode> for SyntheticAmode {
fn from(amode: Amode) -> SyntheticAmode {
SyntheticAmode::Real(amode)
}
}
impl From<VCodeConstant> for SyntheticAmode {
fn from(c: VCodeConstant) -> SyntheticAmode {
SyntheticAmode::ConstantOffset(c)
}
}
impl PrettyPrint for SyntheticAmode {
fn pretty_print(&self, _size: u8) -> String {
match self {
SyntheticAmode::Real(addr) => addr.pretty_print(8),
&SyntheticAmode::IncomingArg { offset } => {
format!("rbp(stack args max - {offset})")
}
SyntheticAmode::SlotOffset { simm32 } => {
format!("rsp({} + virtual offset)", *simm32)
}
SyntheticAmode::ConstantOffset(c) => format!("const({})", c.as_u32()),
}
}
}
#[derive(Clone, Debug)]
pub enum RegMemImm {
Reg {
reg: Reg,
},
Mem {
addr: SyntheticAmode,
},
Imm {
simm32: u32,
},
}
impl RegMemImm {
pub fn reg(reg: Reg) -> Self {
debug_assert!(reg.class() == RegClass::Int || reg.class() == RegClass::Float);
Self::Reg { reg }
}
pub fn mem(addr: impl Into<SyntheticAmode>) -> Self {
Self::Mem { addr: addr.into() }
}
pub fn imm(simm32: u32) -> Self {
Self::Imm { simm32 }
}
pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
match self {
Self::Reg { reg } => collector.reg_use(reg),
Self::Mem { addr } => addr.get_operands(collector),
Self::Imm { .. } => {}
}
}
}
impl From<RegMem> for RegMemImm {
fn from(rm: RegMem) -> RegMemImm {
match rm {
RegMem::Reg { reg } => RegMemImm::Reg { reg },
RegMem::Mem { addr } => RegMemImm::Mem { addr },
}
}
}
impl From<Reg> for RegMemImm {
fn from(reg: Reg) -> Self {
RegMemImm::Reg { reg }
}
}
impl PrettyPrint for RegMemImm {
fn pretty_print(&self, size: u8) -> String {
match self {
Self::Reg { reg } => pretty_print_reg(*reg, size),
Self::Mem { addr } => addr.pretty_print(size),
Self::Imm { simm32 } => format!("${}", *simm32 as i32),
}
}
}
#[derive(Clone, Debug)]
pub enum RegMem {
Reg {
reg: Reg,
},
Mem {
addr: SyntheticAmode,
},
}
impl RegMem {
pub fn reg(reg: Reg) -> Self {
debug_assert!(reg.class() == RegClass::Int || reg.class() == RegClass::Float);
Self::Reg { reg }
}
pub fn mem(addr: impl Into<SyntheticAmode>) -> Self {
Self::Mem { addr: addr.into() }
}
pub(crate) fn assert_regclass_is(&self, expected_reg_class: RegClass) {
if let Self::Reg { reg } = self {
debug_assert_eq!(reg.class(), expected_reg_class);
}
}
pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
match self {
RegMem::Reg { reg } => collector.reg_use(reg),
RegMem::Mem { addr, .. } => addr.get_operands(collector),
}
}
}
impl From<Reg> for RegMem {
fn from(reg: Reg) -> RegMem {
RegMem::Reg { reg }
}
}
impl From<Writable<Reg>> for RegMem {
fn from(r: Writable<Reg>) -> Self {
RegMem::reg(r.to_reg())
}
}
impl PrettyPrint for RegMem {
fn pretty_print(&self, size: u8) -> String {
match self {
RegMem::Reg { reg } => pretty_print_reg(*reg, size),
RegMem::Mem { addr, .. } => addr.pretty_print(size),
}
}
}
#[derive(Clone, PartialEq)]
pub enum ExtKind {
None,
SignExtend,
ZeroExtend,
}
#[derive(Clone, PartialEq)]
pub enum ExtMode {
BL,
BQ,
WL,
WQ,
LQ,
}
impl ExtMode {
pub(crate) fn new(from_bits: u16, to_bits: u16) -> Option<ExtMode> {
match (from_bits, to_bits) {
(1, 8) | (1, 16) | (1, 32) | (8, 16) | (8, 32) => Some(ExtMode::BL),
(1, 64) | (8, 64) => Some(ExtMode::BQ),
(16, 32) => Some(ExtMode::WL),
(16, 64) => Some(ExtMode::WQ),
(32, 64) => Some(ExtMode::LQ),
_ => None,
}
}
}
impl fmt::Debug for ExtMode {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let name = match self {
ExtMode::BL => "bl",
ExtMode::BQ => "bq",
ExtMode::WL => "wl",
ExtMode::WQ => "wq",
ExtMode::LQ => "lq",
};
write!(fmt, "{name}")
}
}
impl fmt::Display for ExtMode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
#[derive(Copy, Clone, PartialEq, Eq)]
#[repr(u8)]
pub enum CC {
O = 0,
NO = 1,
B = 2,
NB = 3,
Z = 4,
NZ = 5,
BE = 6,
NBE = 7,
S = 8,
NS = 9,
L = 12,
NL = 13,
LE = 14,
NLE = 15,
P = 10,
NP = 11,
}
impl CC {
pub(crate) fn from_intcc(intcc: IntCC) -> Self {
match intcc {
IntCC::Equal => CC::Z,
IntCC::NotEqual => CC::NZ,
IntCC::SignedGreaterThanOrEqual => CC::NL,
IntCC::SignedGreaterThan => CC::NLE,
IntCC::SignedLessThanOrEqual => CC::LE,
IntCC::SignedLessThan => CC::L,
IntCC::UnsignedGreaterThanOrEqual => CC::NB,
IntCC::UnsignedGreaterThan => CC::NBE,
IntCC::UnsignedLessThanOrEqual => CC::BE,
IntCC::UnsignedLessThan => CC::B,
}
}
pub(crate) fn invert(&self) -> Self {
match self {
CC::O => CC::NO,
CC::NO => CC::O,
CC::B => CC::NB,
CC::NB => CC::B,
CC::Z => CC::NZ,
CC::NZ => CC::Z,
CC::BE => CC::NBE,
CC::NBE => CC::BE,
CC::S => CC::NS,
CC::NS => CC::S,
CC::L => CC::NL,
CC::NL => CC::L,
CC::LE => CC::NLE,
CC::NLE => CC::LE,
CC::P => CC::NP,
CC::NP => CC::P,
}
}
pub(crate) fn get_enc(self) -> u8 {
self as u8
}
}
impl fmt::Debug for CC {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let name = match self {
CC::O => "o",
CC::NO => "no",
CC::B => "b",
CC::NB => "nb",
CC::Z => "z",
CC::NZ => "nz",
CC::BE => "be",
CC::NBE => "nbe",
CC::S => "s",
CC::NS => "ns",
CC::L => "l",
CC::NL => "nl",
CC::LE => "le",
CC::NLE => "nle",
CC::P => "p",
CC::NP => "np",
};
write!(fmt, "{name}")
}
}
impl fmt::Display for CC {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
#[derive(Clone, Copy)]
pub enum FcmpImm {
Equal = 0x00,
LessThan = 0x01,
LessThanOrEqual = 0x02,
Unordered = 0x03,
NotEqual = 0x04,
UnorderedOrGreaterThanOrEqual = 0x05,
UnorderedOrGreaterThan = 0x06,
Ordered = 0x07,
}
impl FcmpImm {
pub(crate) fn encode(self) -> u8 {
self as u8
}
}
impl From<FloatCC> for FcmpImm {
fn from(cond: FloatCC) -> Self {
match cond {
FloatCC::Equal => FcmpImm::Equal,
FloatCC::LessThan => FcmpImm::LessThan,
FloatCC::LessThanOrEqual => FcmpImm::LessThanOrEqual,
FloatCC::Unordered => FcmpImm::Unordered,
FloatCC::NotEqual => FcmpImm::NotEqual,
FloatCC::UnorderedOrGreaterThanOrEqual => FcmpImm::UnorderedOrGreaterThanOrEqual,
FloatCC::UnorderedOrGreaterThan => FcmpImm::UnorderedOrGreaterThan,
FloatCC::Ordered => FcmpImm::Ordered,
_ => panic!("unable to create comparison predicate for {cond}"),
}
}
}
#[derive(Clone, Copy)]
pub enum RoundImm {
RoundNearest = 0x00,
RoundDown = 0x01,
RoundUp = 0x02,
RoundZero = 0x03,
}
impl RoundImm {
pub(crate) fn encode(self) -> u8 {
self as u8
}
}
#[derive(Clone, Copy, PartialEq)]
pub enum OperandSize {
Size8,
Size16,
Size32,
Size64,
}
impl OperandSize {
pub(crate) fn from_bytes(num_bytes: u32) -> Self {
match num_bytes {
1 => OperandSize::Size8,
2 => OperandSize::Size16,
4 => OperandSize::Size32,
8 => OperandSize::Size64,
_ => unreachable!("Invalid OperandSize: {}", num_bytes),
}
}
pub(crate) fn from_ty(ty: Type) -> Self {
Self::from_bytes(ty.lane_type().bytes())
}
pub(crate) fn is_one_of(&self, sizes: &[Self]) -> bool {
sizes.iter().any(|val| *self == *val)
}
pub(crate) fn to_bytes(&self) -> u8 {
match self {
Self::Size8 => 1,
Self::Size16 => 2,
Self::Size32 => 4,
Self::Size64 => 8,
}
}
pub(crate) fn to_bits(&self) -> u8 {
self.to_bytes() * 8
}
}