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 smallvec::{SmallVec, smallvec};
use std::fmt;
use std::string::String;
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 std::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)] 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)] 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)] pub fn to_reg_mem_imm(self) -> RegMemImm {
self.0
}
#[allow(dead_code)] 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
);
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,
}
}
}
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 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 {
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, Copy, PartialEq)]
pub enum CmpOpcode {
Cmp,
Test,
}
#[derive(Debug)]
pub(crate) enum InstructionSet {
SSE,
SSE2,
CMPXCHG16b,
SSSE3,
SSE41,
SSE42,
Popcnt,
Lzcnt,
BMI1,
#[allow(dead_code)] BMI2,
FMA,
AVX,
AVX2,
AVX512BITALG,
AVX512DQ,
AVX512F,
AVX512VBMI,
AVX512VL,
}
#[derive(Clone, Copy, PartialEq)]
#[allow(dead_code)] #[allow(missing_docs)]
pub enum SseOpcode {
Blendvpd,
Blendvps,
Comiss,
Comisd,
Cmpps,
Cmppd,
Cmpss,
Cmpsd,
Insertps,
Movlhps,
Pabsb,
Pabsw,
Pabsd,
Packssdw,
Packsswb,
Packusdw,
Packuswb,
Palignr,
Pavgb,
Pavgw,
Pblendvb,
Pcmpeqb,
Pcmpeqw,
Pcmpeqd,
Pcmpeqq,
Pcmpgtb,
Pcmpgtw,
Pcmpgtd,
Pcmpgtq,
Pmaddubsw,
Pmaddwd,
Pshufb,
Pshufd,
Ptest,
Rcpss,
Roundps,
Roundpd,
Roundss,
Roundsd,
Rsqrtss,
Shufps,
Ucomiss,
Ucomisd,
Pshuflw,
Pshufhw,
Pblendw,
Movddup,
}
impl SseOpcode {
pub(crate) fn available_from(&self) -> InstructionSet {
use InstructionSet::*;
match self {
SseOpcode::Comiss
| SseOpcode::Cmpps
| SseOpcode::Cmpss
| SseOpcode::Movlhps
| SseOpcode::Rcpss
| SseOpcode::Rsqrtss
| SseOpcode::Shufps
| SseOpcode::Ucomiss => SSE,
SseOpcode::Cmppd
| SseOpcode::Cmpsd
| SseOpcode::Comisd
| SseOpcode::Packssdw
| SseOpcode::Packsswb
| SseOpcode::Packuswb
| SseOpcode::Pavgb
| SseOpcode::Pavgw
| SseOpcode::Pcmpeqb
| SseOpcode::Pcmpeqw
| SseOpcode::Pcmpeqd
| SseOpcode::Pcmpgtb
| SseOpcode::Pcmpgtw
| SseOpcode::Pcmpgtd
| SseOpcode::Pmaddwd
| SseOpcode::Pshufd
| SseOpcode::Ucomisd
| SseOpcode::Pshuflw
| SseOpcode::Pshufhw => SSE2,
SseOpcode::Pabsb
| SseOpcode::Pabsw
| SseOpcode::Pabsd
| SseOpcode::Palignr
| SseOpcode::Pshufb
| SseOpcode::Pmaddubsw
| SseOpcode::Movddup => SSSE3,
SseOpcode::Blendvpd
| SseOpcode::Blendvps
| SseOpcode::Insertps
| SseOpcode::Packusdw
| SseOpcode::Pblendvb
| SseOpcode::Pcmpeqq
| SseOpcode::Ptest
| SseOpcode::Roundps
| SseOpcode::Roundpd
| SseOpcode::Roundss
| SseOpcode::Roundsd
| SseOpcode::Pblendw => SSE41,
SseOpcode::Pcmpgtq => SSE42,
}
}
pub(crate) fn src_size(&self) -> u8 {
match self {
_ => 8,
}
}
pub(crate) fn has_scalar_src2(self) -> bool {
false
}
}
impl fmt::Debug for SseOpcode {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let name = match self {
SseOpcode::Blendvpd => "blendvpd",
SseOpcode::Blendvps => "blendvps",
SseOpcode::Cmpps => "cmpps",
SseOpcode::Cmppd => "cmppd",
SseOpcode::Cmpss => "cmpss",
SseOpcode::Cmpsd => "cmpsd",
SseOpcode::Comiss => "comiss",
SseOpcode::Comisd => "comisd",
SseOpcode::Insertps => "insertps",
SseOpcode::Movlhps => "movlhps",
SseOpcode::Pabsb => "pabsb",
SseOpcode::Pabsw => "pabsw",
SseOpcode::Pabsd => "pabsd",
SseOpcode::Packssdw => "packssdw",
SseOpcode::Packsswb => "packsswb",
SseOpcode::Packusdw => "packusdw",
SseOpcode::Packuswb => "packuswb",
SseOpcode::Palignr => "palignr",
SseOpcode::Pavgb => "pavgb",
SseOpcode::Pavgw => "pavgw",
SseOpcode::Pblendvb => "pblendvb",
SseOpcode::Pcmpeqb => "pcmpeqb",
SseOpcode::Pcmpeqw => "pcmpeqw",
SseOpcode::Pcmpeqd => "pcmpeqd",
SseOpcode::Pcmpeqq => "pcmpeqq",
SseOpcode::Pcmpgtb => "pcmpgtb",
SseOpcode::Pcmpgtw => "pcmpgtw",
SseOpcode::Pcmpgtd => "pcmpgtd",
SseOpcode::Pcmpgtq => "pcmpgtq",
SseOpcode::Pmaddubsw => "pmaddubsw",
SseOpcode::Pmaddwd => "pmaddwd",
SseOpcode::Pshufb => "pshufb",
SseOpcode::Pshufd => "pshufd",
SseOpcode::Ptest => "ptest",
SseOpcode::Rcpss => "rcpss",
SseOpcode::Roundps => "roundps",
SseOpcode::Roundpd => "roundpd",
SseOpcode::Roundss => "roundss",
SseOpcode::Roundsd => "roundsd",
SseOpcode::Rsqrtss => "rsqrtss",
SseOpcode::Shufps => "shufps",
SseOpcode::Ucomiss => "ucomiss",
SseOpcode::Ucomisd => "ucomisd",
SseOpcode::Pshuflw => "pshuflw",
SseOpcode::Pshufhw => "pshufhw",
SseOpcode::Pblendw => "pblendw",
SseOpcode::Movddup => "movddup",
};
write!(fmt, "{name}")
}
}
impl fmt::Display for SseOpcode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
pub use crate::isa::x64::lower::isle::generated_code::AvxOpcode;
impl AvxOpcode {
pub(crate) fn available_from(&self) -> SmallVec<[InstructionSet; 2]> {
match self {
AvxOpcode::Vfmadd213ss
| AvxOpcode::Vfmadd213sd
| AvxOpcode::Vfmadd213ps
| AvxOpcode::Vfmadd213pd
| AvxOpcode::Vfmadd132ss
| AvxOpcode::Vfmadd132sd
| AvxOpcode::Vfmadd132ps
| AvxOpcode::Vfmadd132pd
| AvxOpcode::Vfnmadd213ss
| AvxOpcode::Vfnmadd213sd
| AvxOpcode::Vfnmadd213ps
| AvxOpcode::Vfnmadd213pd
| AvxOpcode::Vfnmadd132ss
| AvxOpcode::Vfnmadd132sd
| AvxOpcode::Vfnmadd132ps
| AvxOpcode::Vfnmadd132pd
| AvxOpcode::Vfmsub213ss
| AvxOpcode::Vfmsub213sd
| AvxOpcode::Vfmsub213ps
| AvxOpcode::Vfmsub213pd
| AvxOpcode::Vfmsub132ss
| AvxOpcode::Vfmsub132sd
| AvxOpcode::Vfmsub132ps
| AvxOpcode::Vfmsub132pd
| AvxOpcode::Vfnmsub213ss
| AvxOpcode::Vfnmsub213sd
| AvxOpcode::Vfnmsub213ps
| AvxOpcode::Vfnmsub213pd
| AvxOpcode::Vfnmsub132ss
| AvxOpcode::Vfnmsub132sd
| AvxOpcode::Vfnmsub132ps
| AvxOpcode::Vfnmsub132pd => smallvec![InstructionSet::FMA],
AvxOpcode::Vminps
| AvxOpcode::Vminpd
| AvxOpcode::Vmaxps
| AvxOpcode::Vmaxpd
| AvxOpcode::Vandnps
| AvxOpcode::Vandnpd
| AvxOpcode::Vpandn
| AvxOpcode::Vcmpps
| AvxOpcode::Vcmppd
| AvxOpcode::Vpsrlw
| AvxOpcode::Vpsrld
| AvxOpcode::Vpsrlq
| AvxOpcode::Vpaddb
| AvxOpcode::Vpaddw
| AvxOpcode::Vpaddd
| AvxOpcode::Vpaddq
| AvxOpcode::Vpaddsb
| AvxOpcode::Vpaddsw
| AvxOpcode::Vpaddusb
| AvxOpcode::Vpaddusw
| AvxOpcode::Vpsubb
| AvxOpcode::Vpsubw
| AvxOpcode::Vpsubd
| AvxOpcode::Vpsubq
| AvxOpcode::Vpsubsb
| AvxOpcode::Vpsubsw
| AvxOpcode::Vpsubusb
| AvxOpcode::Vpsubusw
| AvxOpcode::Vpavgb
| AvxOpcode::Vpavgw
| AvxOpcode::Vpand
| AvxOpcode::Vandps
| AvxOpcode::Vandpd
| AvxOpcode::Vpor
| AvxOpcode::Vorps
| AvxOpcode::Vorpd
| AvxOpcode::Vpxor
| AvxOpcode::Vxorps
| AvxOpcode::Vxorpd
| AvxOpcode::Vpmullw
| AvxOpcode::Vpmulld
| AvxOpcode::Vpmulhw
| AvxOpcode::Vpmulhd
| AvxOpcode::Vpmulhrsw
| AvxOpcode::Vpmulhuw
| AvxOpcode::Vpmuldq
| AvxOpcode::Vpmuludq
| AvxOpcode::Vpunpckhwd
| AvxOpcode::Vpunpcklwd
| AvxOpcode::Vunpcklps
| AvxOpcode::Vunpckhps
| AvxOpcode::Vaddps
| AvxOpcode::Vaddpd
| AvxOpcode::Vsubps
| AvxOpcode::Vsubpd
| AvxOpcode::Vmulps
| AvxOpcode::Vmulpd
| AvxOpcode::Vdivps
| AvxOpcode::Vdivpd
| AvxOpcode::Vpcmpeqb
| AvxOpcode::Vpcmpeqw
| AvxOpcode::Vpcmpeqd
| AvxOpcode::Vpcmpeqq
| AvxOpcode::Vpcmpgtb
| AvxOpcode::Vpcmpgtw
| AvxOpcode::Vpcmpgtd
| AvxOpcode::Vpcmpgtq
| AvxOpcode::Vblendvps
| AvxOpcode::Vblendvpd
| AvxOpcode::Vpblendvb
| AvxOpcode::Vmovlhps
| AvxOpcode::Vpminsb
| AvxOpcode::Vpminsw
| AvxOpcode::Vpminsd
| AvxOpcode::Vpminub
| AvxOpcode::Vpminuw
| AvxOpcode::Vpminud
| AvxOpcode::Vpmaxsb
| AvxOpcode::Vpmaxsw
| AvxOpcode::Vpmaxsd
| AvxOpcode::Vpmaxub
| AvxOpcode::Vpmaxuw
| AvxOpcode::Vpmaxud
| AvxOpcode::Vpunpcklbw
| AvxOpcode::Vpunpckhbw
| AvxOpcode::Vpacksswb
| AvxOpcode::Vpackssdw
| AvxOpcode::Vpackuswb
| AvxOpcode::Vpackusdw
| AvxOpcode::Vpalignr
| AvxOpcode::Vpinsrb
| AvxOpcode::Vpinsrw
| AvxOpcode::Vpinsrd
| AvxOpcode::Vpinsrq
| AvxOpcode::Vpmaddwd
| AvxOpcode::Vpmaddubsw
| AvxOpcode::Vinsertps
| AvxOpcode::Vpshufb
| AvxOpcode::Vshufps
| AvxOpcode::Vpsllw
| AvxOpcode::Vpslld
| AvxOpcode::Vpsllq
| AvxOpcode::Vpsraw
| AvxOpcode::Vpsrad
| AvxOpcode::Vpmovsxbw
| AvxOpcode::Vpmovzxbw
| AvxOpcode::Vpmovsxwd
| AvxOpcode::Vpmovzxwd
| AvxOpcode::Vpmovsxdq
| AvxOpcode::Vpmovzxdq
| AvxOpcode::Vaddss
| AvxOpcode::Vaddsd
| AvxOpcode::Vmulss
| AvxOpcode::Vmulsd
| AvxOpcode::Vsubss
| AvxOpcode::Vsubsd
| AvxOpcode::Vdivss
| AvxOpcode::Vdivsd
| AvxOpcode::Vpabsb
| AvxOpcode::Vpabsw
| AvxOpcode::Vpabsd
| AvxOpcode::Vminss
| AvxOpcode::Vminsd
| AvxOpcode::Vmaxss
| AvxOpcode::Vmaxsd
| AvxOpcode::Vsqrtps
| AvxOpcode::Vsqrtpd
| AvxOpcode::Vroundpd
| AvxOpcode::Vroundps
| AvxOpcode::Vcvtdq2pd
| AvxOpcode::Vcvtdq2ps
| AvxOpcode::Vcvtpd2ps
| AvxOpcode::Vcvtps2pd
| AvxOpcode::Vcvttpd2dq
| AvxOpcode::Vcvttps2dq
| AvxOpcode::Vphaddw
| AvxOpcode::Vphaddd
| AvxOpcode::Vpunpckldq
| AvxOpcode::Vpunpckhdq
| AvxOpcode::Vpunpcklqdq
| AvxOpcode::Vpunpckhqdq
| AvxOpcode::Vpshuflw
| AvxOpcode::Vpshufhw
| AvxOpcode::Vpshufd
| AvxOpcode::Vmovss
| AvxOpcode::Vmovsd
| AvxOpcode::Vmovups
| AvxOpcode::Vmovupd
| AvxOpcode::Vmovdqu
| AvxOpcode::Vpextrb
| AvxOpcode::Vpextrw
| AvxOpcode::Vpextrd
| AvxOpcode::Vpextrq
| AvxOpcode::Vpblendw
| AvxOpcode::Vmovddup
| AvxOpcode::Vbroadcastss
| AvxOpcode::Vmovd
| AvxOpcode::Vmovq
| AvxOpcode::Vmovmskps
| AvxOpcode::Vmovmskpd
| AvxOpcode::Vpmovmskb
| AvxOpcode::Vcvtsi2ss
| AvxOpcode::Vcvtsi2sd
| AvxOpcode::Vcvtss2sd
| AvxOpcode::Vcvtsd2ss
| AvxOpcode::Vsqrtss
| AvxOpcode::Vsqrtsd
| AvxOpcode::Vroundss
| AvxOpcode::Vroundsd
| AvxOpcode::Vunpcklpd
| AvxOpcode::Vptest
| AvxOpcode::Vucomiss
| AvxOpcode::Vucomisd => {
smallvec![InstructionSet::AVX]
}
AvxOpcode::Vpbroadcastb | AvxOpcode::Vpbroadcastw | AvxOpcode::Vpbroadcastd => {
smallvec![InstructionSet::AVX2]
}
}
}
pub(crate) fn is_commutative(&self) -> bool {
match *self {
AvxOpcode::Vpaddb
| AvxOpcode::Vpaddw
| AvxOpcode::Vpaddd
| AvxOpcode::Vpaddq
| AvxOpcode::Vpaddsb
| AvxOpcode::Vpaddsw
| AvxOpcode::Vpaddusb
| AvxOpcode::Vpaddusw
| AvxOpcode::Vpand
| AvxOpcode::Vandps
| AvxOpcode::Vandpd
| AvxOpcode::Vpor
| AvxOpcode::Vorps
| AvxOpcode::Vorpd
| AvxOpcode::Vpxor
| AvxOpcode::Vxorps
| AvxOpcode::Vxorpd
| AvxOpcode::Vpmuldq
| AvxOpcode::Vpmuludq
| AvxOpcode::Vaddps
| AvxOpcode::Vaddpd
| AvxOpcode::Vmulps
| AvxOpcode::Vmulpd
| AvxOpcode::Vpcmpeqb
| AvxOpcode::Vpcmpeqw
| AvxOpcode::Vpcmpeqd
| AvxOpcode::Vpcmpeqq
| AvxOpcode::Vaddss
| AvxOpcode::Vaddsd
| AvxOpcode::Vmulss
| AvxOpcode::Vmulsd => true,
_ => false,
}
}
}
impl fmt::Display for AvxOpcode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
format!("{self:?}").to_lowercase().fmt(f)
}
}
#[derive(Copy, Clone, PartialEq)]
#[allow(missing_docs)]
pub enum Avx512TupleType {
Full,
FullMem,
Mem128,
}
pub use crate::isa::x64::lower::isle::generated_code::Avx512Opcode;
impl Avx512Opcode {
pub(crate) fn available_from(&self) -> SmallVec<[InstructionSet; 2]> {
match self {
Avx512Opcode::Vcvtudq2ps
| Avx512Opcode::Vpabsq
| Avx512Opcode::Vpsraq
| Avx512Opcode::VpsraqImm => {
smallvec![InstructionSet::AVX512F, InstructionSet::AVX512VL]
}
Avx512Opcode::Vpermi2b => {
smallvec![InstructionSet::AVX512VL, InstructionSet::AVX512VBMI]
}
Avx512Opcode::Vpmullq => smallvec![InstructionSet::AVX512VL, InstructionSet::AVX512DQ],
Avx512Opcode::Vpopcntb => {
smallvec![InstructionSet::AVX512VL, InstructionSet::AVX512BITALG]
}
}
}
pub fn tuple_type(&self) -> Avx512TupleType {
use Avx512Opcode::*;
use Avx512TupleType::*;
match self {
Vcvtudq2ps | Vpabsq | Vpmullq | VpsraqImm => Full,
Vpermi2b | Vpopcntb => FullMem,
Vpsraq => Mem128,
}
}
}
impl fmt::Display for Avx512Opcode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = format!("{self:?}");
f.write_str(&s.to_lowercase())
}
}
#[allow(dead_code)]
#[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
}
pub(crate) fn to_type(&self) -> Type {
match self {
Self::Size8 => I8,
Self::Size16 => I16,
Self::Size32 => I32,
Self::Size64 => I64,
}
}
}
#[derive(Clone)]
#[allow(dead_code)]
pub enum FenceKind {
MFence,
LFence,
SFence,
}