use std::fmt;
use crate::{
ir::{value::ConstValue, variable::SsaVarId},
target::{Target, VectorShuffleMask},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum CmpKind {
Eq,
Ne,
Lt,
Le,
Gt,
Ge,
}
impl fmt::Display for CmpKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Eq => write!(f, "=="),
Self::Ne => write!(f, "!="),
Self::Lt => write!(f, "<"),
Self::Le => write!(f, "<="),
Self::Gt => write!(f, ">"),
Self::Ge => write!(f, ">="),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Signedness {
Signed,
Unsigned,
}
impl Signedness {
#[must_use]
pub const fn from_unsigned(unsigned: bool) -> Self {
if unsigned {
Self::Unsigned
} else {
Self::Signed
}
}
#[must_use]
pub const fn is_unsigned(self) -> bool {
matches!(self, Self::Unsigned)
}
}
impl fmt::Display for Signedness {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Signed => write!(f, "signed"),
Self::Unsigned => write!(f, "unsigned"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct MemoryEffect<'a, T: Target> {
pub addr: SsaVarId,
pub reads: bool,
pub writes: bool,
pub value_type: Option<&'a T::Type>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum FenceKind {
Full,
Acquire,
Release,
AcqRel,
SeqCst,
}
impl fmt::Display for FenceKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Full => write!(f, "full"),
Self::Acquire => write!(f, "acquire"),
Self::Release => write!(f, "release"),
Self::AcqRel => write!(f, "acqrel"),
Self::SeqCst => write!(f, "seqcst"),
}
}
}
impl FenceKind {
#[must_use]
pub const fn ordering(self) -> AtomicOrdering {
match self {
Self::Full | Self::SeqCst => AtomicOrdering::SeqCst,
Self::Acquire => AtomicOrdering::Acquire,
Self::Release => AtomicOrdering::Release,
Self::AcqRel => AtomicOrdering::AcqRel,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum AtomicOrdering {
Relaxed,
Acquire,
Release,
AcqRel,
SeqCst,
}
impl fmt::Display for AtomicOrdering {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Relaxed => write!(f, "relaxed"),
Self::Acquire => write!(f, "acquire"),
Self::Release => write!(f, "release"),
Self::AcqRel => write!(f, "acqrel"),
Self::SeqCst => write!(f, "seqcst"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum AtomicAccessWidth {
Bits8,
Bits16,
Bits32,
Bits64,
Bits128,
Pointer,
}
impl AtomicAccessWidth {
#[must_use]
pub const fn bits(self) -> Option<u32> {
match self {
Self::Bits8 => Some(8),
Self::Bits16 => Some(16),
Self::Bits32 => Some(32),
Self::Bits64 => Some(64),
Self::Bits128 => Some(128),
Self::Pointer => None,
}
}
}
impl fmt::Display for AtomicAccessWidth {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Bits8 => write!(f, "i8"),
Self::Bits16 => write!(f, "i16"),
Self::Bits32 => write!(f, "i32"),
Self::Bits64 => write!(f, "i64"),
Self::Bits128 => write!(f, "i128"),
Self::Pointer => write!(f, "ptr"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum AtomicRmwOp {
Xchg,
Add,
Sub,
And,
Or,
Xor,
Min,
Max,
AndNot,
MinU,
MaxU,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum OperandRole {
Def,
FlagsDef,
Use,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum SsaOpClass {
Synthetic,
Scalar,
Boolean,
Flags,
Vector,
Memory,
Atomic,
Call,
Control,
NativeOpaque,
NativeIntrinsic,
WideArithmetic,
Prefix,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum SsaSimilarityClass {
Synthetic,
Constant,
Arithmetic,
Bitwise,
ShiftRotate,
Compare,
Boolean,
Select,
Conversion,
TypeFlow,
MemoryRead,
MemoryWrite,
MemoryReadWrite,
Allocation,
Atomic,
Fence,
Call,
Control,
Vector,
Flags,
WideArithmetic,
NativeOpaque,
Prefix,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct SsaFeatureToken {
pub opcode: &'static str,
pub op_class: SsaOpClass,
pub similarity_class: SsaSimilarityClass,
pub effect_kind: SsaEffectKind,
pub def_count: usize,
pub use_count: usize,
pub may_throw: bool,
}
impl fmt::Display for SsaFeatureToken {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"op={};class={:?};sim={:?};effect={:?};defs={};uses={};throw={}",
self.opcode,
self.op_class,
self.similarity_class,
self.effect_kind,
self.def_count,
self.use_count,
self.may_throw
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum SsaEffectKind {
Pure,
Read,
Write,
ReadWrite,
Fence,
Atomic,
Call,
Opaque,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum MemoryEffectLocation {
None,
Unknown,
Stack,
Heap,
Global,
Code,
Io,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum MemoryAccessSemantics {
None,
Normal,
Volatile,
Atomic,
Fence,
Opaque,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum TrapClass {
None,
Unknown,
MemoryFault,
NullAccess,
Bounds,
DivideByZero,
Overflow,
InvalidCast,
UserThrow,
IllegalInstruction,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum ControlEffect {
None,
Terminator,
Call,
Return,
Throw,
Opaque,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SsaEffects {
pub kind: SsaEffectKind,
pub may_throw: bool,
pub memory: MemoryEffectLocation,
pub memory_semantics: MemoryAccessSemantics,
pub volatile: bool,
pub ordering: Option<AtomicOrdering>,
pub trap: TrapClass,
pub control: ControlEffect,
}
impl SsaEffects {
#[must_use]
pub const fn pure() -> Self {
Self {
kind: SsaEffectKind::Pure,
may_throw: false,
memory: MemoryEffectLocation::None,
memory_semantics: MemoryAccessSemantics::None,
volatile: false,
ordering: None,
trap: TrapClass::None,
control: ControlEffect::None,
}
}
#[must_use]
pub const fn new(kind: SsaEffectKind, may_throw: bool) -> Self {
let memory_semantics = match kind {
SsaEffectKind::Pure => MemoryAccessSemantics::None,
SsaEffectKind::Fence => MemoryAccessSemantics::Fence,
SsaEffectKind::Atomic => MemoryAccessSemantics::Atomic,
SsaEffectKind::Opaque | SsaEffectKind::Call => MemoryAccessSemantics::Opaque,
SsaEffectKind::Read | SsaEffectKind::Write | SsaEffectKind::ReadWrite => {
MemoryAccessSemantics::Normal
}
};
let memory = match kind {
SsaEffectKind::Pure | SsaEffectKind::Fence => MemoryEffectLocation::None,
SsaEffectKind::Read
| SsaEffectKind::Write
| SsaEffectKind::ReadWrite
| SsaEffectKind::Atomic
| SsaEffectKind::Call
| SsaEffectKind::Opaque => MemoryEffectLocation::Unknown,
};
let trap = if may_throw {
TrapClass::Unknown
} else {
TrapClass::None
};
let control = match kind {
SsaEffectKind::Call => ControlEffect::Call,
SsaEffectKind::Opaque => ControlEffect::Opaque,
SsaEffectKind::Pure
| SsaEffectKind::Read
| SsaEffectKind::Write
| SsaEffectKind::ReadWrite
| SsaEffectKind::Fence
| SsaEffectKind::Atomic => ControlEffect::None,
};
Self {
kind,
may_throw,
memory,
memory_semantics,
volatile: false,
ordering: None,
trap,
control,
}
}
#[must_use]
pub const fn with_memory(mut self, memory: MemoryEffectLocation) -> Self {
self.memory = memory;
self
}
#[must_use]
pub const fn volatile(mut self) -> Self {
self.volatile = true;
self
}
#[must_use]
pub const fn atomic_ordering(mut self, ordering: AtomicOrdering) -> Self {
self.memory_semantics = MemoryAccessSemantics::Atomic;
self.ordering = Some(ordering);
self
}
#[must_use]
pub const fn fence_ordering(mut self, ordering: AtomicOrdering) -> Self {
self.memory_semantics = MemoryAccessSemantics::Fence;
self.ordering = Some(ordering);
self
}
#[must_use]
pub const fn with_trap(mut self, trap: TrapClass) -> Self {
self.trap = trap;
self.may_throw = !matches!(trap, TrapClass::None);
self
}
#[must_use]
pub const fn with_control(mut self, control: ControlEffect) -> Self {
self.control = control;
if matches!(self.kind, SsaEffectKind::Opaque)
&& !matches!(control, ControlEffect::None | ControlEffect::Opaque)
{
self.memory = MemoryEffectLocation::None;
self.memory_semantics = MemoryAccessSemantics::None;
}
self
}
#[must_use]
pub const fn is_pure(self) -> bool {
matches!(self.kind, SsaEffectKind::Pure) && !self.may_throw
}
#[must_use]
pub const fn reads_memory(self) -> bool {
matches!(
self.kind,
SsaEffectKind::Read
| SsaEffectKind::ReadWrite
| SsaEffectKind::Atomic
| SsaEffectKind::Call
| SsaEffectKind::Opaque
)
}
#[must_use]
pub const fn writes_memory(self) -> bool {
matches!(
self.kind,
SsaEffectKind::Write
| SsaEffectKind::ReadWrite
| SsaEffectKind::Atomic
| SsaEffectKind::Call
| SsaEffectKind::Opaque
)
}
#[must_use]
pub const fn removable_when_unused(self) -> bool {
self.is_pure()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NativeInstructionMetadata {
pub architecture: Option<String>,
pub address: Option<u64>,
pub raw_bytes: Vec<u8>,
}
impl NativeInstructionMetadata {
#[must_use]
pub fn new(architecture: Option<String>, address: Option<u64>, raw_bytes: Vec<u8>) -> Self {
Self {
architecture,
address,
raw_bytes,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NativeOpaqueData {
pub mnemonic: String,
pub metadata: Option<NativeInstructionMetadata>,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
pub clobbers: Vec<NativeClobber>,
pub effects: SsaEffects,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum NativeIntrinsicId {
Cpuid,
Rdtsc,
Rdtscp,
Rdmsr,
Wrmsr,
Rdpmc,
Xgetbv,
Xsetbv,
SystemCall,
SystemReturn,
BitDeposit,
BitExtract,
Crc32,
RandomNumber,
RandomSeed,
PointerAuth(PacKind),
Hypervisor,
Privileged,
ControlRegister,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum PacKind {
Sign,
Authenticate,
Strip,
GenericMac,
}
impl fmt::Display for PacKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match self {
Self::Sign => "sign",
Self::Authenticate => "auth",
Self::Strip => "strip",
Self::GenericMac => "genmac",
};
f.write_str(name)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct KindedVecData<K> {
pub kind: K,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VecImm8Data {
pub imm8: u8,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NativeKindedData<K> {
pub kind: K,
pub mnemonic: String,
pub metadata: Option<NativeInstructionMetadata>,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
pub clobbers: Vec<NativeClobber>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u16)]
#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
pub enum TranscendentalKind {
Sin,
Cos,
SinCos,
Tan,
Atan,
Exp2m1,
Ylog2,
Ylog2p1,
Rem,
Rem1,
Scale,
Extract,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u16)]
#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
pub enum FpuControlKind {
LoadControlWord,
StoreControlWord,
StoreStatusWord,
LoadEnvironment,
StoreEnvironment,
Save,
Restore,
SaveExtended,
RestoreExtended,
ClearExceptions,
DecrementStackTop,
IncrementStackTop,
FreeRegister,
NoOp,
Wait,
Initialize,
EmptyMmxState,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NativeIntrinsicData {
pub id: NativeIntrinsicId,
pub mnemonic: String,
pub metadata: Option<NativeInstructionMetadata>,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
pub clobbers: Vec<NativeClobber>,
pub effects: SsaEffects,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum SysRegNamespace {
X86Msr,
X86Xcr,
X86ControlReg,
X86DebugReg,
Arm64System,
RiscvCsr,
MipsCop0,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum SystemOpKind {
CpuId,
Timestamp {
aux: bool,
},
ReadSysReg {
namespace: SysRegNamespace,
},
WriteSysReg {
namespace: SysRegNamespace,
},
ReadPerfCounter,
SystemCall,
SystemReturn,
Trap {
vector: Option<u8>,
},
InterruptReturn,
CacheMaintenance,
TlbMaintenance,
Barrier,
Privileged,
Hypervisor,
HardwareEngine,
Transaction(SystemTransactionKind),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum SystemTransactionKind {
Start,
Commit,
Cancel,
Test,
}
impl SystemOpKind {
#[must_use]
pub const fn effects(self) -> SsaEffects {
match self {
Self::CpuId
| Self::Timestamp { .. }
| Self::ReadSysReg { .. }
| Self::ReadPerfCounter => SsaEffects::new(SsaEffectKind::Read, false),
Self::WriteSysReg { .. }
| Self::Privileged
| Self::CacheMaintenance
| Self::TlbMaintenance
| Self::HardwareEngine
| Self::Transaction(_) => SsaEffects::new(SsaEffectKind::Write, false),
Self::Barrier => {
SsaEffects::new(SsaEffectKind::Fence, false).fence_ordering(AtomicOrdering::SeqCst)
}
Self::SystemCall | Self::SystemReturn | Self::Hypervisor | Self::Trap { .. } => {
SsaEffects::new(SsaEffectKind::Call, true).with_control(ControlEffect::Call)
}
Self::InterruptReturn => {
SsaEffects::new(SsaEffectKind::Call, false).with_control(ControlEffect::Call)
}
}
}
#[must_use]
pub const fn kind_str(self) -> &'static str {
match self {
Self::CpuId => "system.cpuid",
Self::Timestamp { .. } => "system.timestamp",
Self::ReadSysReg { .. } => "system.sysreg.read",
Self::WriteSysReg { .. } => "system.sysreg.write",
Self::ReadPerfCounter => "system.perfcounter",
Self::SystemCall => "system.syscall",
Self::SystemReturn => "system.sysreturn",
Self::Trap { .. } => "system.trap",
Self::InterruptReturn => "system.iret",
Self::CacheMaintenance => "system.cache",
Self::TlbMaintenance => "system.tlb",
Self::Barrier => "system.barrier",
Self::Privileged => "system.privileged",
Self::Hypervisor => "system.hypervisor",
Self::HardwareEngine => "system.hwengine",
Self::Transaction(SystemTransactionKind::Start) => "system.txn.start",
Self::Transaction(SystemTransactionKind::Commit) => "system.txn.commit",
Self::Transaction(SystemTransactionKind::Cancel) => "system.txn.cancel",
Self::Transaction(SystemTransactionKind::Test) => "system.txn.test",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum ComputeKind {
BitDeposit,
BitExtract,
Checksum,
Random {
from_entropy: bool,
},
PointerAuth(PacKind),
MipsDspAccumulate,
}
impl ComputeKind {
#[must_use]
pub const fn effects(self) -> SsaEffects {
match self {
Self::BitDeposit
| Self::BitExtract
| Self::Checksum
| Self::PointerAuth(_)
| Self::MipsDspAccumulate => SsaEffects::new(SsaEffectKind::Pure, false),
Self::Random { .. } => SsaEffects::new(SsaEffectKind::Read, false),
}
}
#[must_use]
pub const fn kind_str(self) -> &'static str {
match self {
Self::BitDeposit => "compute.pdep",
Self::BitExtract => "compute.pext",
Self::Checksum => "compute.crc32",
Self::Random {
from_entropy: false,
} => "compute.rdrand",
Self::Random { from_entropy: true } => "compute.rdseed",
Self::PointerAuth(_) => "compute.pac",
Self::MipsDspAccumulate => "compute.mips_dsp_acc",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u16)]
#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
pub enum BcdAdjustKind {
DecimalAddAdjust,
DecimalSubAdjust,
AsciiAddAdjust,
AsciiSubAdjust,
AsciiMulAdjust,
AsciiDivAdjust,
}
impl BcdAdjustKind {
#[must_use]
pub const fn effects(self) -> SsaEffects {
SsaEffects::new(SsaEffectKind::Pure, false)
}
#[must_use]
pub const fn kind_str(self) -> &'static str {
match self {
Self::DecimalAddAdjust => "bcd.daa",
Self::DecimalSubAdjust => "bcd.das",
Self::AsciiAddAdjust => "bcd.aaa",
Self::AsciiSubAdjust => "bcd.aas",
Self::AsciiMulAdjust => "bcd.aam",
Self::AsciiDivAdjust => "bcd.aad",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BcdAdjustData {
pub kind: BcdAdjustKind,
pub base: u8,
pub mnemonic: String,
pub metadata: Option<NativeInstructionMetadata>,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
pub clobbers: Vec<NativeClobber>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u16)]
#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
pub enum VectorCryptoKind {
AesEncrypt,
AesEncryptLast,
AesDecrypt,
AesDecryptLast,
AesInvMixColumns,
AesKeygenAssist,
AesEncryptRound,
AesDecryptRound,
AesMixColumns,
Sha1Rounds4,
Sha1NextE,
Sha1Msg1,
Sha1Msg2,
Sha256Rounds2,
Sha256Msg1,
Sha256Msg2,
Sha512Rounds2,
Sha512Msg1,
Sha512Msg2,
Sm3Msg1,
Sm3Msg2,
Sm3Rounds2,
Sm4Key,
Sm4Rounds,
Gf2p8Affine,
Gf2p8AffineInv,
Gf2p8Mul,
CarrylessMul,
AesEncryptKeyLocker,
AesDecryptKeyLocker,
Sha1HashChoose,
Sha1HashMajority,
Sha1HashParity,
Sha1FixedRotate,
Sha1ScheduleUpdate0,
Sha1ScheduleUpdate1,
Sha256Hash,
Sha256Hash2,
Sha256ScheduleUpdate0,
Sha256ScheduleUpdate1,
Sm3PartW1,
Sm3PartW2,
Sm3SS1,
Sm3TT1A,
Sm3TT1B,
Sm3TT2A,
Sm3TT2B,
PolynomialMultiply,
}
impl VectorCryptoKind {
#[must_use]
pub const fn effects(self) -> SsaEffects {
SsaEffects::new(SsaEffectKind::Pure, false)
}
#[must_use]
pub const fn kind_str(self) -> &'static str {
match self {
Self::AesEncrypt => "crypto.aesenc",
Self::AesEncryptLast => "crypto.aesenclast",
Self::AesDecrypt => "crypto.aesdec",
Self::AesDecryptLast => "crypto.aesdeclast",
Self::AesInvMixColumns => "crypto.aesimc",
Self::AesKeygenAssist => "crypto.aeskeygenassist",
Self::AesEncryptRound => "crypto.aese",
Self::AesDecryptRound => "crypto.aesd",
Self::AesMixColumns => "crypto.aesmc",
Self::Sha1Rounds4 => "crypto.sha1rnds4",
Self::Sha1NextE => "crypto.sha1nexte",
Self::Sha1Msg1 => "crypto.sha1msg1",
Self::Sha1Msg2 => "crypto.sha1msg2",
Self::Sha256Rounds2 => "crypto.sha256rnds2",
Self::Sha256Msg1 => "crypto.sha256msg1",
Self::Sha256Msg2 => "crypto.sha256msg2",
Self::Sha512Rounds2 => "crypto.sha512rnds2",
Self::Sha512Msg1 => "crypto.sha512msg1",
Self::Sha512Msg2 => "crypto.sha512msg2",
Self::Sm3Msg1 => "crypto.sm3msg1",
Self::Sm3Msg2 => "crypto.sm3msg2",
Self::Sm3Rounds2 => "crypto.sm3rnds2",
Self::Sm4Key => "crypto.sm4key",
Self::Sm4Rounds => "crypto.sm4rnds",
Self::Gf2p8Affine => "crypto.gf2p8affine",
Self::Gf2p8AffineInv => "crypto.gf2p8affineinv",
Self::Gf2p8Mul => "crypto.gf2p8mul",
Self::CarrylessMul => "crypto.pclmulqdq",
Self::AesEncryptKeyLocker => "crypto.aesenckl",
Self::AesDecryptKeyLocker => "crypto.aesdeckl",
Self::Sha1HashChoose => "crypto.sha1c",
Self::Sha1HashMajority => "crypto.sha1m",
Self::Sha1HashParity => "crypto.sha1p",
Self::Sha1FixedRotate => "crypto.sha1h",
Self::Sha1ScheduleUpdate0 => "crypto.sha1su0",
Self::Sha1ScheduleUpdate1 => "crypto.sha1su1",
Self::Sha256Hash => "crypto.sha256h",
Self::Sha256Hash2 => "crypto.sha256h2",
Self::Sha256ScheduleUpdate0 => "crypto.sha256su0",
Self::Sha256ScheduleUpdate1 => "crypto.sha256su1",
Self::Sm3PartW1 => "crypto.sm3partw1",
Self::Sm3PartW2 => "crypto.sm3partw2",
Self::Sm3SS1 => "crypto.sm3ss1",
Self::Sm3TT1A => "crypto.sm3tt1a",
Self::Sm3TT1B => "crypto.sm3tt1b",
Self::Sm3TT2A => "crypto.sm3tt2a",
Self::Sm3TT2B => "crypto.sm3tt2b",
Self::PolynomialMultiply => "crypto.pmull",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum TileDotKind {
Int8SignedSigned,
Int8SignedUnsigned,
Int8UnsignedSigned,
Int8UnsignedUnsigned,
Bf16,
Fp16,
ComplexFp16Real,
ComplexFp16Imaginary,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum TileOpKind {
DotProduct(TileDotKind),
Load,
Store,
Zero,
LoadConfig,
StoreConfig,
Release,
}
impl TileOpKind {
#[must_use]
pub const fn effects(self) -> SsaEffects {
match self {
Self::DotProduct(_) | Self::Zero => SsaEffects::new(SsaEffectKind::Pure, false),
Self::Load | Self::LoadConfig => SsaEffects::new(SsaEffectKind::Read, false),
Self::Store | Self::StoreConfig | Self::Release => {
SsaEffects::new(SsaEffectKind::Write, false)
}
}
}
#[must_use]
pub const fn kind_str(self) -> &'static str {
match self {
Self::DotProduct(TileDotKind::Int8SignedSigned) => "tile.dp.bssd",
Self::DotProduct(TileDotKind::Int8SignedUnsigned) => "tile.dp.bsud",
Self::DotProduct(TileDotKind::Int8UnsignedSigned) => "tile.dp.busd",
Self::DotProduct(TileDotKind::Int8UnsignedUnsigned) => "tile.dp.buud",
Self::DotProduct(TileDotKind::Bf16) => "tile.dp.bf16ps",
Self::DotProduct(TileDotKind::Fp16) => "tile.dp.fp16ps",
Self::DotProduct(TileDotKind::ComplexFp16Real) => "tile.cmm.rlfp16ps",
Self::DotProduct(TileDotKind::ComplexFp16Imaginary) => "tile.cmm.imfp16ps",
Self::Load => "tile.load",
Self::Store => "tile.store",
Self::Zero => "tile.zero",
Self::LoadConfig => "tile.ldcfg",
Self::StoreConfig => "tile.stcfg",
Self::Release => "tile.release",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VectorPermuteData {
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u16)]
#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
pub enum VectorMaddKind {
MultiplyAddS16,
MultiplyAddU8S8Sat,
DotProductU8S8,
DotProductU8S8Sat,
DotProductS16,
DotProductS16Sat,
MultiplyAdd52Lo,
MultiplyAdd52Hi,
DotProductBf16,
MultiplyAccumulate,
MultiplyAccumulateSat,
MultiplyAccumulatePairs,
MultiplyAccumulatePairsSat,
DotProductS8S8,
DotProductS8S8Sat,
DotProductS8U8,
DotProductS8U8Sat,
DotProductU8U8,
DotProductU8U8Sat,
DotProductS16U16,
DotProductS16U16Sat,
DotProductU16S16,
DotProductU16S16Sat,
DotProductU16U16,
DotProductU16U16Sat,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u16)]
#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
pub enum ComplexMulKind {
Multiply,
ConjugateMultiply,
MultiplyAdd,
ConjugateMultiplyAdd,
}
impl ComplexMulKind {
#[must_use]
pub const fn kind_str(self) -> &'static str {
match self {
Self::Multiply => "vector.cmul",
Self::ConjugateMultiply => "vector.cmul.conj",
Self::MultiplyAdd => "vector.cmadd",
Self::ConjugateMultiplyAdd => "vector.cmadd.conj",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VectorDotProductData {
pub imm8: u8,
pub element_bits: u16,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VectorIntDotProductData {
pub signed_a: bool,
pub signed_b: bool,
pub source_bits: u16,
pub dest_bits: u16,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VectorShuffleBitsData {
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VectorIntersectData {
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VectorBitfieldData {
pub insert: bool,
pub index: u8,
pub length: u8,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum ByteMoveCondition {
Zero,
NonZero,
Negative,
NonNegative,
}
impl ByteMoveCondition {
#[must_use]
pub const fn kind_str(self) -> &'static str {
match self {
Self::Zero => "z",
Self::NonZero => "nz",
Self::Negative => "lz",
Self::NonNegative => "gez",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VectorConditionalMoveData {
pub condition: ByteMoveCondition,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VectorStringCompareData {
pub imm8: u8,
pub explicit_length: bool,
pub result_index: bool,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VectorHorizontalMinPosData {
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VectorHorizontalReduceData {
pub subtract: bool,
pub unsigned: bool,
pub source_bits: u16,
pub dest_bits: u16,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VectorPackNarrowData {
pub unsigned: bool,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum SvePermuteKind {
Index,
InsertShift,
Compact,
Splice,
ExtractLastActive,
ExtractLastBefore,
CopyLastActive,
CopyLastBefore,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum FpHelperKind {
ReciprocalExponent,
ExtractExponent,
ExpAccelerate,
TrigMulAdd,
TrigSelectMul,
TrigSelect,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum PredicateGenKind {
True,
False,
Next,
First,
ReadFfr,
UnpackHi,
UnpackLo,
Select,
HazardRw,
HazardWr,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VectorSmeOuterProductData {
pub subtract: bool,
pub signed_a: bool,
pub signed_b: bool,
pub float: bool,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VectorMatrixMulAccData {
pub signed_a: bool,
pub signed_b: bool,
pub float: bool,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VectorReverseChunksData {
pub chunk_bits: u16,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VectorCountAdjustData {
pub decrement: bool,
pub saturate: bool,
pub signed: bool,
pub by_predicate: bool,
pub element_bits: u16,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VectorExtendInLaneData {
pub signed: bool,
pub source_bits: u16,
pub element_bits: u16,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VectorElementCountData {
pub element_bits: u16,
pub multiplier: u32,
pub outputs: Vec<SsaVarId>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VectorSveAddressGenData {
pub signed_extend: Option<bool>,
pub shift: u8,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum SveComputeKind {
AddCarryBottom,
AddCarryTop,
SubCarryBottom,
SubCarryTop,
BitDeposit,
BitExtract,
BitGroup,
Histogram,
HistogramSegment,
MatchElements,
NoMatchElements,
ClampSigned,
ClampUnsigned,
DivideReversedSigned,
DivideReversedUnsigned,
InterleaveXorBottomTop,
InterleaveXorTopBottom,
SaturatingAbs,
SaturatingNeg,
LogicalNot,
ComplexDotProduct,
ComplexMulAddRounding,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VectorSveComputeData {
pub op: SveComputeKind,
pub element_bits: u16,
pub rotation: u16,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum PredicateOpKind {
CountActive,
Test,
SetFirstFault,
WriteFirstFault,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VectorPredicateOpData {
pub op: PredicateOpKind,
pub element_bits: u16,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum SmeMiscKind {
AddHorizontal,
AddVertical,
ZeroTiles,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VectorSmeMiscData {
pub op: SmeMiscKind,
pub element_bits: u16,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VectorStructLoadReplicateData {
pub count: u8,
pub element_bits: u16,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u16)]
#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
pub enum FlagAdjustKind {
InvertCarry,
RotateMaskInsert,
SetNzFrom8,
SetNzFrom16,
ConvertToFpFlags,
ConvertFromFpFlags,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VectorComplexAddData {
pub rotate_270: bool,
pub saturate: bool,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VectorPredicateBreakData {
pub after: bool,
pub pair: bool,
pub propagate: bool,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VectorPredicateWhileData {
pub kind: VectorCompareKind,
pub unsigned: bool,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VectorNarrowSaturateData {
pub signed_src: bool,
pub unsigned_dst: bool,
pub rounding: bool,
pub shift: u8,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
}
impl VectorMaddKind {
#[must_use]
pub const fn kind_str(self) -> &'static str {
match self {
Self::MultiplyAddS16 => "vector.madd.s16",
Self::MultiplyAddU8S8Sat => "vector.madd.u8s8.sat",
Self::DotProductU8S8 => "vector.dp.u8s8",
Self::DotProductU8S8Sat => "vector.dp.u8s8.sat",
Self::DotProductS16 => "vector.dp.s16",
Self::DotProductS16Sat => "vector.dp.s16.sat",
Self::MultiplyAdd52Lo => "vector.madd52.lo",
Self::MultiplyAdd52Hi => "vector.madd52.hi",
Self::DotProductBf16 => "vector.dp.bf16",
Self::MultiplyAccumulate => "vector.mac",
Self::MultiplyAccumulateSat => "vector.mac.sat",
Self::MultiplyAccumulatePairs => "vector.macd",
Self::MultiplyAccumulatePairsSat => "vector.macd.sat",
Self::DotProductS8S8 => "vector.dp.s8s8",
Self::DotProductS8S8Sat => "vector.dp.s8s8.sat",
Self::DotProductS8U8 => "vector.dp.s8u8",
Self::DotProductS8U8Sat => "vector.dp.s8u8.sat",
Self::DotProductU8U8 => "vector.dp.u8u8",
Self::DotProductU8U8Sat => "vector.dp.u8u8.sat",
Self::DotProductS16U16 => "vector.dp.s16u16",
Self::DotProductS16U16Sat => "vector.dp.s16u16.sat",
Self::DotProductU16S16 => "vector.dp.u16s16",
Self::DotProductU16S16Sat => "vector.dp.u16s16.sat",
Self::DotProductU16U16 => "vector.dp.u16u16",
Self::DotProductU16U16Sat => "vector.dp.u16u16.sat",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum BlockStringKind {
Compare,
Scan,
Load,
}
impl BlockStringKind {
#[must_use]
pub const fn effects(self) -> SsaEffects {
match self {
Self::Load => SsaEffects::new(SsaEffectKind::Read, false),
Self::Compare | Self::Scan => SsaEffects::new(SsaEffectKind::ReadWrite, false),
}
}
#[must_use]
pub const fn kind_str(self) -> &'static str {
match self {
Self::Compare => "blockstring.cmps",
Self::Scan => "blockstring.scas",
Self::Load => "blockstring.lods",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum BlockStringPrefix {
Repeat,
RepeatEqual,
RepeatNotEqual,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BlockStringOpData {
pub kind: BlockStringKind,
pub prefix: BlockStringPrefix,
pub element_bits: u16,
pub mnemonic: String,
pub metadata: Option<NativeInstructionMetadata>,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
pub clobbers: Vec<NativeClobber>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct WideCmpXchgData {
pub wide: bool,
pub mnemonic: String,
pub metadata: Option<NativeInstructionMetadata>,
pub outputs: Vec<SsaVarId>,
pub inputs: Vec<SsaVarId>,
pub clobbers: Vec<NativeClobber>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NativeRegister {
pub architecture: String,
pub bank: String,
pub base: String,
pub name: String,
pub bit_offset: u32,
pub bit_width: u32,
}
impl NativeRegister {
#[must_use]
pub fn new(
architecture: impl Into<String>,
bank: impl Into<String>,
base: impl Into<String>,
name: impl Into<String>,
bit_offset: u32,
bit_width: u32,
) -> Option<Self> {
let architecture = architecture.into();
let bank = bank.into();
let base = base.into();
let name = name.into();
if architecture.is_empty() || bank.is_empty() || base.is_empty() || name.is_empty() {
return None;
}
if bit_width == 0 {
return None;
}
Some(Self {
architecture,
bank,
base,
name,
bit_offset,
bit_width,
})
}
#[must_use]
pub fn aliases(&self, other: &Self) -> bool {
if self.architecture != other.architecture
|| self.bank != other.bank
|| self.base != other.base
{
return false;
}
let self_end = self.bit_offset.saturating_add(self.bit_width);
let other_end = other.bit_offset.saturating_add(other.bit_width);
self.bit_offset < other_end && other.bit_offset < self_end
}
#[must_use]
pub fn is_valid(&self) -> bool {
!self.architecture.is_empty()
&& !self.bank.is_empty()
&& !self.base.is_empty()
&& !self.name.is_empty()
&& self.bit_width != 0
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum NativeStateLocation {
Register(NativeRegister),
RegisterClass(String),
Flags(String),
StackPointer,
ProgramCounter,
VectorLength,
VectorConfig,
PredicateState(String),
ControlRegister(String),
Memory(String),
Other(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum NativeStateAccessKind {
Read,
Write,
ReadWrite,
Clobber,
}
impl NativeStateAccessKind {
#[must_use]
pub const fn reads(self) -> bool {
matches!(self, Self::Read | Self::ReadWrite)
}
#[must_use]
pub const fn writes(self) -> bool {
matches!(self, Self::Write | Self::ReadWrite | Self::Clobber)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NativeStateAccess {
pub location: NativeStateLocation,
pub kind: NativeStateAccessKind,
pub width_bits: Option<u32>,
pub implicit: bool,
}
impl NativeStateAccess {
#[must_use]
pub fn new(
location: NativeStateLocation,
kind: NativeStateAccessKind,
width_bits: Option<u32>,
implicit: bool,
) -> Option<Self> {
if let Some(0) = width_bits {
return None;
}
Some(Self {
location,
kind,
width_bits,
implicit,
})
}
#[must_use]
pub fn implicit_read(location: NativeStateLocation, width_bits: Option<u32>) -> Option<Self> {
Self::new(location, NativeStateAccessKind::Read, width_bits, true)
}
#[must_use]
pub fn implicit_write(location: NativeStateLocation, width_bits: Option<u32>) -> Option<Self> {
Self::new(location, NativeStateAccessKind::Write, width_bits, true)
}
#[must_use]
pub fn implicit_read_write(
location: NativeStateLocation,
width_bits: Option<u32>,
) -> Option<Self> {
Self::new(location, NativeStateAccessKind::ReadWrite, width_bits, true)
}
#[must_use]
pub const fn reads(&self) -> bool {
self.kind.reads()
}
#[must_use]
pub const fn writes(&self) -> bool {
self.kind.writes()
}
#[must_use]
pub fn is_valid(&self) -> bool {
if let Some(0) = self.width_bits {
return false;
}
match &self.location {
NativeStateLocation::Register(register) => register.is_valid(),
NativeStateLocation::RegisterClass(name)
| NativeStateLocation::Flags(name)
| NativeStateLocation::PredicateState(name)
| NativeStateLocation::ControlRegister(name)
| NativeStateLocation::Memory(name)
| NativeStateLocation::Other(name) => !name.is_empty(),
NativeStateLocation::StackPointer
| NativeStateLocation::ProgramCounter
| NativeStateLocation::VectorLength
| NativeStateLocation::VectorConfig => true,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum NativeClobber {
MachineState(NativeStateAccess),
Register(NativeRegister),
RegisterClass(String),
Flags(String),
Memory(String),
Other(String),
}
impl NativeClobber {
#[must_use]
pub fn touches_registers(&self) -> bool {
match self {
Self::MachineState(access) => matches!(
access.location,
NativeStateLocation::Register(_) | NativeStateLocation::RegisterClass(_)
),
Self::Register(_) | Self::RegisterClass(_) => true,
Self::Flags(_) | Self::Memory(_) | Self::Other(_) => false,
}
}
#[must_use]
pub fn touches_flags(&self) -> bool {
match self {
Self::MachineState(access) => matches!(access.location, NativeStateLocation::Flags(_)),
Self::Flags(_) => true,
Self::Register(_) | Self::RegisterClass(_) | Self::Memory(_) | Self::Other(_) => false,
}
}
#[must_use]
pub fn touches_memory(&self) -> bool {
match self {
Self::MachineState(access) => matches!(access.location, NativeStateLocation::Memory(_)),
Self::Memory(_) => true,
Self::Register(_) | Self::RegisterClass(_) | Self::Flags(_) | Self::Other(_) => false,
}
}
}
impl fmt::Display for AtomicRmwOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Xchg => write!(f, "xchg"),
Self::Add => write!(f, "add"),
Self::Sub => write!(f, "sub"),
Self::And => write!(f, "and"),
Self::Or => write!(f, "or"),
Self::Xor => write!(f, "xor"),
Self::Min => write!(f, "min"),
Self::Max => write!(f, "max"),
Self::AndNot => write!(f, "andnot"),
Self::MinU => write!(f, "minu"),
Self::MaxU => write!(f, "maxu"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FlagsMask(u16);
impl FlagsMask {
pub const CARRY: Self = Self(1 << 0);
pub const PARITY: Self = Self(1 << 1);
pub const ADJUST: Self = Self(1 << 2);
pub const ZERO: Self = Self(1 << 3);
pub const SIGN: Self = Self(1 << 4);
pub const OVERFLOW: Self = Self(1 << 5);
pub const fn from_bits(bits: u16) -> Self {
Self(bits)
}
pub const fn bits(self) -> u16 {
self.0
}
pub const fn is_empty(self) -> bool {
self.0 == 0
}
pub const fn contains(self, other: Self) -> bool {
self.0 & other.0 == other.0
}
pub const fn union(self, other: Self) -> Self {
Self(self.0 | other.0)
}
pub const fn x86_status() -> Self {
Self(
Self::CARRY.0
| Self::PARITY.0
| Self::ADJUST.0
| Self::ZERO.0
| Self::SIGN.0
| Self::OVERFLOW.0,
)
}
pub const fn from_flag_bit(bit: NativeFlagBit) -> Self {
match bit {
NativeFlagBit::Carry => Self::CARRY,
NativeFlagBit::Parity => Self::PARITY,
NativeFlagBit::Adjust => Self::ADJUST,
NativeFlagBit::Zero => Self::ZERO,
NativeFlagBit::Sign => Self::SIGN,
NativeFlagBit::Overflow => Self::OVERFLOW,
}
}
}
impl fmt::Display for FlagsMask {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut first = true;
if self.0 & Self::CARRY.0 != 0 {
if !first {
write!(f, ",")?;
}
write!(f, "CF")?;
first = false;
}
if self.0 & Self::PARITY.0 != 0 {
if !first {
write!(f, ",")?;
}
write!(f, "PF")?;
first = false;
}
if self.0 & Self::ADJUST.0 != 0 {
if !first {
write!(f, ",")?;
}
write!(f, "AF")?;
first = false;
}
if self.0 & Self::ZERO.0 != 0 {
if !first {
write!(f, ",")?;
}
write!(f, "ZF")?;
first = false;
}
if self.0 & Self::SIGN.0 != 0 {
if !first {
write!(f, ",")?;
}
write!(f, "SF")?;
first = false;
}
if self.0 & Self::OVERFLOW.0 != 0 {
if !first {
write!(f, ",")?;
}
write!(f, "OF")?;
first = false;
}
if first {
write!(f, "none")?;
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum NativeFlagBit {
Carry,
Parity,
Adjust,
Zero,
Sign,
Overflow,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum FlagWriteState {
Defined,
Undefined,
Preserved,
Cleared,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FlagWrite {
pub bit: NativeFlagBit,
pub state: FlagWriteState,
}
impl FlagWrite {
#[must_use]
pub const fn new(bit: NativeFlagBit, state: FlagWriteState) -> Self {
Self { bit, state }
}
#[must_use]
pub const fn defined(bit: NativeFlagBit) -> Self {
Self::new(bit, FlagWriteState::Defined)
}
#[must_use]
pub const fn undefined(bit: NativeFlagBit) -> Self {
Self::new(bit, FlagWriteState::Undefined)
}
#[must_use]
pub const fn preserved(bit: NativeFlagBit) -> Self {
Self::new(bit, FlagWriteState::Preserved)
}
#[must_use]
pub const fn cleared(bit: NativeFlagBit) -> Self {
Self::new(bit, FlagWriteState::Cleared)
}
}
const X86_STATUS_DEFINED: &[FlagWrite] = &[
FlagWrite::defined(NativeFlagBit::Carry),
FlagWrite::defined(NativeFlagBit::Parity),
FlagWrite::defined(NativeFlagBit::Adjust),
FlagWrite::defined(NativeFlagBit::Zero),
FlagWrite::defined(NativeFlagBit::Sign),
FlagWrite::defined(NativeFlagBit::Overflow),
];
const X86_LOGICAL_WRITES: &[FlagWrite] = &[
FlagWrite::cleared(NativeFlagBit::Carry),
FlagWrite::defined(NativeFlagBit::Parity),
FlagWrite::undefined(NativeFlagBit::Adjust),
FlagWrite::defined(NativeFlagBit::Zero),
FlagWrite::defined(NativeFlagBit::Sign),
FlagWrite::cleared(NativeFlagBit::Overflow),
];
const X86_MUL_WRITES: &[FlagWrite] = &[
FlagWrite::defined(NativeFlagBit::Carry),
FlagWrite::undefined(NativeFlagBit::Parity),
FlagWrite::undefined(NativeFlagBit::Adjust),
FlagWrite::undefined(NativeFlagBit::Zero),
FlagWrite::undefined(NativeFlagBit::Sign),
FlagWrite::defined(NativeFlagBit::Overflow),
];
const X86_ROTATE_WRITES: &[FlagWrite] = &[
FlagWrite::defined(NativeFlagBit::Carry),
FlagWrite::preserved(NativeFlagBit::Parity),
FlagWrite::preserved(NativeFlagBit::Adjust),
FlagWrite::preserved(NativeFlagBit::Zero),
FlagWrite::preserved(NativeFlagBit::Sign),
FlagWrite::defined(NativeFlagBit::Overflow),
];
const AARCH64_NZCV_DEFINED: &[FlagWrite] = &[
FlagWrite::defined(NativeFlagBit::Sign),
FlagWrite::defined(NativeFlagBit::Zero),
FlagWrite::defined(NativeFlagBit::Carry),
FlagWrite::defined(NativeFlagBit::Overflow),
];
const AARCH64_LOGICAL_WRITES: &[FlagWrite] = &[
FlagWrite::defined(NativeFlagBit::Sign),
FlagWrite::defined(NativeFlagBit::Zero),
FlagWrite::cleared(NativeFlagBit::Carry),
FlagWrite::cleared(NativeFlagBit::Overflow),
];
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum FlagProducerSemantics {
X86Arithmetic,
X86Logical,
X86Multiply,
X86Shift,
X86Rotate,
AArch64Arithmetic,
AArch64Logical,
}
impl FlagProducerSemantics {
#[must_use]
pub const fn writes(self) -> &'static [FlagWrite] {
match self {
Self::X86Arithmetic | Self::X86Shift => X86_STATUS_DEFINED,
Self::X86Logical => X86_LOGICAL_WRITES,
Self::X86Multiply => X86_MUL_WRITES,
Self::X86Rotate => X86_ROTATE_WRITES,
Self::AArch64Arithmetic => AARCH64_NZCV_DEFINED,
Self::AArch64Logical => AARCH64_LOGICAL_WRITES,
}
}
#[must_use]
pub fn defined_mask(self) -> FlagsMask {
let mut mask = FlagsMask::from_bits(0);
for write in self.writes() {
if matches!(
write.state,
FlagWriteState::Defined | FlagWriteState::Cleared
) {
mask = mask.union(FlagsMask::from_flag_bit(write.bit));
}
}
mask
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum FlagCondition {
Carry,
NotCarry,
Zero,
NotZero,
Overflow,
NotOverflow,
Negative,
Positive,
ParityEven,
ParityOdd,
}
impl fmt::Display for FlagCondition {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Carry => write!(f, "carry"),
Self::NotCarry => write!(f, "not_carry"),
Self::Zero => write!(f, "zero"),
Self::NotZero => write!(f, "not_zero"),
Self::Overflow => write!(f, "overflow"),
Self::NotOverflow => write!(f, "not_overflow"),
Self::Negative => write!(f, "negative"),
Self::Positive => write!(f, "positive"),
Self::ParityEven => write!(f, "parity_even"),
Self::ParityOdd => write!(f, "parity_odd"),
}
}
}
impl FlagCondition {
#[must_use]
pub const fn required_flags(self) -> FlagsMask {
match self {
Self::Carry | Self::NotCarry => FlagsMask::CARRY,
Self::Zero | Self::NotZero => FlagsMask::ZERO,
Self::Overflow | Self::NotOverflow => FlagsMask::OVERFLOW,
Self::Negative | Self::Positive => FlagsMask::SIGN,
Self::ParityEven | Self::ParityOdd => FlagsMask::PARITY,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum BinaryOpKind {
Add,
AddOvf,
Sub,
SubOvf,
Mul,
MulOvf,
Div,
Rem,
And,
Or,
Xor,
Shl,
Shr,
Ceq,
Clt,
Cgt,
Rol,
Ror,
Rcl,
Rcr,
}
impl fmt::Display for BinaryOpKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Add => write!(f, "add"),
Self::AddOvf => write!(f, "add.ovf"),
Self::Sub => write!(f, "sub"),
Self::SubOvf => write!(f, "sub.ovf"),
Self::Mul => write!(f, "mul"),
Self::MulOvf => write!(f, "mul.ovf"),
Self::Div => write!(f, "div"),
Self::Rem => write!(f, "rem"),
Self::And => write!(f, "and"),
Self::Or => write!(f, "or"),
Self::Xor => write!(f, "xor"),
Self::Shl => write!(f, "shl"),
Self::Shr => write!(f, "shr"),
Self::Ceq => write!(f, "ceq"),
Self::Clt => write!(f, "clt"),
Self::Cgt => write!(f, "cgt"),
Self::Rol => write!(f, "rol"),
Self::Ror => write!(f, "ror"),
Self::Rcl => write!(f, "rcl"),
Self::Rcr => write!(f, "rcr"),
}
}
}
impl BinaryOpKind {
#[must_use]
pub const fn is_commutative(self) -> bool {
matches!(
self,
Self::Add
| Self::AddOvf
| Self::Mul
| Self::MulOvf
| Self::And
| Self::Or
| Self::Xor
| Self::Ceq
)
}
#[must_use]
pub const fn is_comparison(self) -> bool {
matches!(self, Self::Ceq | Self::Clt | Self::Cgt)
}
#[must_use]
pub const fn swapped(self) -> Self {
match self {
Self::Clt => Self::Cgt,
Self::Cgt => Self::Clt,
other => other,
}
}
#[must_use]
pub const fn is_signedness_sensitive(self) -> bool {
matches!(
self,
Self::Div | Self::Rem | Self::Shr | Self::Clt | Self::Cgt
)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BinaryOpInfo {
pub kind: BinaryOpKind,
pub dest: SsaVarId,
pub left: SsaVarId,
pub right: SsaVarId,
pub unsigned: bool,
pub flags: Option<SsaVarId>,
}
impl BinaryOpInfo {
#[must_use]
pub fn normalized(self) -> Self {
if self.right.index() < self.left.index() {
if self.kind.is_commutative() {
Self {
left: self.right,
right: self.left,
..self
}
} else if self.kind.is_comparison() {
Self {
kind: self.kind.swapped(),
left: self.right,
right: self.left,
..self
}
} else {
self
}
} else {
self
}
}
#[must_use]
pub fn value_key(self) -> (BinaryOpKind, bool, SsaVarId, SsaVarId) {
let unsigned = if self.kind.is_signedness_sensitive() {
self.unsigned
} else {
false };
(self.kind, unsigned, self.left, self.right)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum UnaryOpKind {
Neg,
Not,
Ckfinite,
BSwap,
BRev,
BitScanForward,
BitScanReverse,
Popcount,
Parity,
}
impl fmt::Display for UnaryOpKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Neg => write!(f, "neg"),
Self::Not => write!(f, "not"),
Self::Ckfinite => write!(f, "ckfinite"),
Self::BSwap => write!(f, "bswap"),
Self::BRev => write!(f, "brev"),
Self::BitScanForward => write!(f, "bsf"),
Self::BitScanReverse => write!(f, "bsr"),
Self::Popcount => write!(f, "popcnt"),
Self::Parity => write!(f, "parity"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct UnaryOpInfo {
pub kind: UnaryOpKind,
pub dest: SsaVarId,
pub operand: SsaVarId,
}
pub struct SsaDefs<'a> {
primary: Option<SsaVarId>,
secondary: Option<SsaVarId>,
extra: Option<std::slice::Iter<'a, SsaVarId>>,
}
impl<'a> SsaDefs<'a> {
#[must_use]
pub fn new(
primary: Option<SsaVarId>,
secondary: Option<SsaVarId>,
extra: Option<&'a [SsaVarId]>,
) -> Self {
Self {
primary,
secondary,
extra: extra.map(<[SsaVarId]>::iter),
}
}
}
impl Iterator for SsaDefs<'_> {
type Item = SsaVarId;
fn next(&mut self) -> Option<Self::Item> {
if let Some(primary) = self.primary.take() {
return Some(primary);
}
if let Some(secondary) = self.secondary.take() {
return Some(secondary);
}
self.extra.as_mut()?.next().copied()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum VectorElementKind {
Integer,
Float,
#[default]
Unknown,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VectorElement {
pub kind: VectorElementKind,
pub bits: u32,
pub scalar: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum VectorUnaryKind {
Neg,
Not,
Popcount,
Abs,
HorizontalAdd,
Sqrt,
Round,
Reciprocal,
ReciprocalSqrt,
GetExponent,
GetMantissa,
RoundScale,
Reduce,
Identity,
LeadingZeros,
Fraction,
Conflict,
Exp2,
Log2,
LeadingSignBits,
LeadingOnes,
Truncate,
Floor,
Sign,
BitReverse,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum VectorBinaryKind {
Add,
Sub,
Mul,
Div,
Min,
Max,
And,
Or,
Xor,
Shl,
Shr,
AndNot,
MulHigh,
MulHighRound,
Avg,
AbsDiff,
SatAdd,
SatSub,
Scale,
Range,
Rol,
Ror,
VariableShiftLogical,
VariableShiftArithmetic,
ApplySign,
ReciprocalIter1,
ReciprocalIter2,
ReciprocalSqrtIter1,
MaxMagnitude,
MulNegate,
OrNot,
AddAbs,
Rem,
Nor,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum VectorTernaryKind {
Fma,
Select,
FixupImm,
FunnelLeft,
FunnelRight,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum VectorCompareKind {
Eq,
Ne,
Lt,
Le,
Gt,
Ge,
Unordered,
Ordered,
NotLt,
NotLe,
NotGe,
NotGt,
AlwaysTrue,
AlwaysFalse,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum VectorCastKind {
Signed,
Unsigned,
Float,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum VectorMaskUnaryKind {
Not,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum VectorMaskBinaryKind {
And,
Or,
Xor,
AndNot,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum VectorReduceKind {
Add,
Mul,
And,
Or,
Xor,
Min,
Max,
Any,
All,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum VectorBitmaskKind {
LaneMostSignificantBits,
PredicateBits,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum VectorMaskMode {
Merge,
Zero,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum VectorPackKind {
Compress,
Expand,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum VectorFaultMode {
Normal,
FirstFault,
FaultOnlyFirst,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum VectorSegmentLayout {
Interleaved,
Consecutive,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(bound(
serialize = "T::Type: serde::Serialize, T::TypeRef: serde::Serialize, \
T::MethodRef: serde::Serialize, T::FieldRef: serde::Serialize, \
T::SigRef: serde::Serialize",
deserialize = "T::Type: serde::Deserialize<'de>, T::TypeRef: serde::Deserialize<'de>, \
T::MethodRef: serde::Deserialize<'de>, T::FieldRef: serde::Deserialize<'de>, \
T::SigRef: serde::Deserialize<'de>"
))
)]
pub enum SsaOp<T: Target> {
Const {
dest: SsaVarId,
value: ConstValue<T>,
},
Add {
dest: SsaVarId,
left: SsaVarId,
right: SsaVarId,
flags: Option<SsaVarId>,
},
AddOvf {
dest: SsaVarId,
left: SsaVarId,
right: SsaVarId,
unsigned: bool,
flags: Option<SsaVarId>,
},
Sub {
dest: SsaVarId,
left: SsaVarId,
right: SsaVarId,
flags: Option<SsaVarId>,
},
SubOvf {
dest: SsaVarId,
left: SsaVarId,
right: SsaVarId,
unsigned: bool,
flags: Option<SsaVarId>,
},
Mul {
dest: SsaVarId,
left: SsaVarId,
right: SsaVarId,
flags: Option<SsaVarId>,
},
MulOvf {
dest: SsaVarId,
left: SsaVarId,
right: SsaVarId,
unsigned: bool,
flags: Option<SsaVarId>,
},
WideMul {
low: SsaVarId,
high: SsaVarId,
left: SsaVarId,
right: SsaVarId,
unsigned: bool,
},
Div {
dest: SsaVarId,
left: SsaVarId,
right: SsaVarId,
unsigned: bool,
flags: Option<SsaVarId>,
},
Rem {
dest: SsaVarId,
left: SsaVarId,
right: SsaVarId,
unsigned: bool,
flags: Option<SsaVarId>,
},
FloatCompareFlags {
flags: SsaVarId,
left: SsaVarId,
right: SsaVarId,
signaling: bool,
},
WideDiv {
quotient: SsaVarId,
remainder: SsaVarId,
high: SsaVarId,
low: SsaVarId,
divisor: SsaVarId,
unsigned: bool,
},
Neg {
dest: SsaVarId,
operand: SsaVarId,
flags: Option<SsaVarId>,
},
And {
dest: SsaVarId,
left: SsaVarId,
right: SsaVarId,
flags: Option<SsaVarId>,
},
Or {
dest: SsaVarId,
left: SsaVarId,
right: SsaVarId,
flags: Option<SsaVarId>,
},
Xor {
dest: SsaVarId,
left: SsaVarId,
right: SsaVarId,
flags: Option<SsaVarId>,
},
Not {
dest: SsaVarId,
operand: SsaVarId,
flags: Option<SsaVarId>,
},
Shl {
dest: SsaVarId,
value: SsaVarId,
amount: SsaVarId,
flags: Option<SsaVarId>,
},
Shr {
dest: SsaVarId,
value: SsaVarId,
amount: SsaVarId,
unsigned: bool,
flags: Option<SsaVarId>,
},
Rol {
dest: SsaVarId,
value: SsaVarId,
amount: SsaVarId,
},
Ror {
dest: SsaVarId,
value: SsaVarId,
amount: SsaVarId,
},
Rcl {
dest: SsaVarId,
value: SsaVarId,
amount: SsaVarId,
},
Rcr {
dest: SsaVarId,
value: SsaVarId,
amount: SsaVarId,
},
BSwap {
dest: SsaVarId,
src: SsaVarId,
},
BRev {
dest: SsaVarId,
src: SsaVarId,
},
BitScanForward {
dest: SsaVarId,
src: SsaVarId,
},
BitScanReverse {
dest: SsaVarId,
src: SsaVarId,
},
Popcount {
dest: SsaVarId,
src: SsaVarId,
},
Parity {
dest: SsaVarId,
src: SsaVarId,
},
Ceq {
dest: SsaVarId,
left: SsaVarId,
right: SsaVarId,
},
Clt {
dest: SsaVarId,
left: SsaVarId,
right: SsaVarId,
unsigned: bool,
},
Cgt {
dest: SsaVarId,
left: SsaVarId,
right: SsaVarId,
unsigned: bool,
},
BoolAnd {
dest: SsaVarId,
left: SsaVarId,
right: SsaVarId,
},
BoolOr {
dest: SsaVarId,
left: SsaVarId,
right: SsaVarId,
},
BoolXor {
dest: SsaVarId,
left: SsaVarId,
right: SsaVarId,
},
BoolNot {
dest: SsaVarId,
value: SsaVarId,
},
IntConv {
dest: SsaVarId,
operand: SsaVarId,
target: T::Type,
overflow_check: bool,
unsigned: bool,
},
IntToPtr {
dest: SsaVarId,
operand: SsaVarId,
target: T::Type,
},
PtrToInt {
dest: SsaVarId,
operand: SsaVarId,
target: T::Type,
},
IntToFloat {
dest: SsaVarId,
operand: SsaVarId,
target: T::Type,
unsigned: bool,
},
FloatToInt {
dest: SsaVarId,
operand: SsaVarId,
target: T::Type,
overflow_check: bool,
unsigned: bool,
},
FloatConv {
dest: SsaVarId,
operand: SsaVarId,
target: T::Type,
},
Bitcast {
dest: SsaVarId,
operand: SsaVarId,
target: T::Type,
},
PtrAdd {
dest: SsaVarId,
base: SsaVarId,
index: Option<SsaVarId>,
stride: u64,
offset: i64,
result_type: T::Type,
},
Select {
dest: SsaVarId,
condition: SsaVarId,
true_val: SsaVarId,
false_val: SsaVarId,
},
ReadFlags {
dest: SsaVarId,
flags: SsaVarId,
mask: FlagsMask,
},
VectorUnary {
dest: SsaVarId,
value: SsaVarId,
kind: VectorUnaryKind,
element: VectorElement,
},
VectorBinary {
dest: SsaVarId,
left: SsaVarId,
right: SsaVarId,
kind: VectorBinaryKind,
element: VectorElement,
},
VectorTernary {
dest: SsaVarId,
first: SsaVarId,
second: SsaVarId,
third: SsaVarId,
kind: VectorTernaryKind,
},
VectorPredicatedUnary {
dest: SsaVarId,
value: SsaVarId,
mask: SsaVarId,
passthrough: Option<SsaVarId>,
kind: VectorUnaryKind,
mode: VectorMaskMode,
},
VectorPredicatedBinary {
dest: SsaVarId,
left: SsaVarId,
right: SsaVarId,
mask: SsaVarId,
passthrough: Option<SsaVarId>,
kind: VectorBinaryKind,
mode: VectorMaskMode,
},
VectorPredicatedTernary {
dest: SsaVarId,
first: SsaVarId,
second: SsaVarId,
third: SsaVarId,
mask: SsaVarId,
passthrough: Option<SsaVarId>,
kind: VectorTernaryKind,
mode: VectorMaskMode,
},
VectorCompare {
dest: SsaVarId,
left: SsaVarId,
right: SsaVarId,
kind: VectorCompareKind,
unsigned: bool,
},
VectorLoad {
dest: SsaVarId,
addr: SsaVarId,
vector_type: T::Type,
},
VectorStore {
addr: SsaVarId,
value: SsaVarId,
vector_type: T::Type,
},
VectorMaskedLoad {
dest: SsaVarId,
addr: SsaVarId,
mask: SsaVarId,
passthrough: Option<SsaVarId>,
vector_type: T::Type,
mode: VectorMaskMode,
},
VectorMaskedStore {
addr: SsaVarId,
value: SsaVarId,
mask: SsaVarId,
vector_type: T::Type,
},
VectorBroadcastLoad {
dest: SsaVarId,
addr: SsaVarId,
vector_type: T::Type,
},
VectorGather {
dest: SsaVarId,
base: SsaVarId,
indices: SsaVarId,
mask: SsaVarId,
passthrough: Option<SsaVarId>,
vector_type: T::Type,
mode: VectorMaskMode,
},
VectorFaultingLoad {
dest: SsaVarId,
fault: Option<SsaVarId>,
addr: SsaVarId,
mask: Option<SsaVarId>,
passthrough: Option<SsaVarId>,
vector_type: T::Type,
fault_mode: VectorFaultMode,
mask_mode: VectorMaskMode,
},
VectorSegmentLoad {
dests: Vec<SsaVarId>,
base: SsaVarId,
mask: Option<SsaVarId>,
vector_type: T::Type,
segments: u32,
layout: VectorSegmentLayout,
},
VectorScatter {
base: SsaVarId,
indices: SsaVarId,
value: SsaVarId,
mask: SsaVarId,
vector_type: T::Type,
},
VectorSegmentStore {
base: SsaVarId,
values: Vec<SsaVarId>,
mask: Option<SsaVarId>,
vector_type: T::Type,
segments: u32,
layout: VectorSegmentLayout,
},
VectorExtract {
dest: SsaVarId,
vector: SsaVarId,
lane: u32,
},
VectorInsert {
dest: SsaVarId,
vector: SsaVarId,
lane: u32,
value: SsaVarId,
},
VectorSplat {
dest: SsaVarId,
value: SsaVarId,
vector_type: T::Type,
},
VectorShuffle {
dest: SsaVarId,
left: SsaVarId,
right: Option<SsaVarId>,
mask: VectorShuffleMask,
},
VectorCast {
dest: SsaVarId,
value: SsaVarId,
target_type: T::Type,
kind: VectorCastKind,
},
VectorReinterpret {
dest: SsaVarId,
value: SsaVarId,
target_type: T::Type,
},
VectorPack {
dest: SsaVarId,
value: SsaVarId,
mask: SsaVarId,
passthrough: Option<SsaVarId>,
vector_type: T::Type,
element_bits: u32,
kind: VectorPackKind,
mode: VectorMaskMode,
},
VectorPackLoad {
dest: SsaVarId,
addr: SsaVarId,
mask: SsaVarId,
passthrough: Option<SsaVarId>,
vector_type: T::Type,
element_bits: u32,
kind: VectorPackKind,
mode: VectorMaskMode,
},
VectorPackStore {
addr: SsaVarId,
value: SsaVarId,
mask: SsaVarId,
vector_type: T::Type,
element_bits: u32,
kind: VectorPackKind,
},
VectorZeroUpper {
all: bool,
},
VectorMaskUnary {
dest: SsaVarId,
mask: SsaVarId,
kind: VectorMaskUnaryKind,
},
VectorMaskBinary {
dest: SsaVarId,
left: SsaVarId,
right: SsaVarId,
kind: VectorMaskBinaryKind,
},
VectorReduce {
dest: SsaVarId,
value: SsaVarId,
kind: VectorReduceKind,
},
VectorBitmask {
dest: SsaVarId,
value: SsaVarId,
kind: VectorBitmaskKind,
},
Jump {
target: usize,
},
Branch {
condition: SsaVarId,
true_target: usize,
false_target: usize,
},
BranchCmp {
left: SsaVarId,
right: SsaVarId,
cmp: CmpKind,
unsigned: bool,
true_target: usize,
false_target: usize,
},
BranchFlags {
flags: SsaVarId,
condition: FlagCondition,
true_target: usize,
false_target: usize,
},
Switch {
value: SsaVarId,
targets: Vec<usize>,
default: usize,
},
IndirectBranch {
target: SsaVarId,
resolved_targets: Vec<usize>,
},
Return {
value: Option<SsaVarId>,
},
LoadField {
dest: SsaVarId,
object: SsaVarId,
field: T::FieldRef,
},
StoreField {
object: SsaVarId,
field: T::FieldRef,
value: SsaVarId,
},
LoadStaticField {
dest: SsaVarId,
field: T::FieldRef,
},
StoreStaticField {
field: T::FieldRef,
value: SsaVarId,
},
LoadFieldAddr {
dest: SsaVarId,
object: SsaVarId,
field: T::FieldRef,
},
LoadStaticFieldAddr {
dest: SsaVarId,
field: T::FieldRef,
},
LoadElement {
dest: SsaVarId,
array: SsaVarId,
index: SsaVarId,
elem_type: T::Type,
},
StoreElement {
array: SsaVarId,
index: SsaVarId,
value: SsaVarId,
elem_type: T::Type,
},
LoadElementAddr {
dest: SsaVarId,
array: SsaVarId,
index: SsaVarId,
elem_type: T::TypeRef,
},
ArrayLength {
dest: SsaVarId,
array: SsaVarId,
},
LoadIndirect {
dest: SsaVarId,
addr: SsaVarId,
value_type: T::Type,
},
StoreIndirect {
addr: SsaVarId,
value: SsaVarId,
value_type: T::Type,
},
NewObj {
dest: SsaVarId,
ctor: T::MethodRef,
args: Vec<SsaVarId>,
},
NewArr {
dest: SsaVarId,
elem_type: T::TypeRef,
length: SsaVarId,
},
CastClass {
dest: SsaVarId,
object: SsaVarId,
target_type: T::TypeRef,
},
IsInst {
dest: SsaVarId,
object: SsaVarId,
target_type: T::TypeRef,
},
Box {
dest: SsaVarId,
value: SsaVarId,
value_type: T::TypeRef,
},
Unbox {
dest: SsaVarId,
object: SsaVarId,
value_type: T::TypeRef,
},
UnboxAny {
dest: SsaVarId,
object: SsaVarId,
value_type: T::TypeRef,
},
SizeOf {
dest: SsaVarId,
value_type: T::TypeRef,
},
LoadToken {
dest: SsaVarId,
token: T::TypeRef,
},
Call {
dest: Option<SsaVarId>,
method: T::MethodRef,
args: Vec<SsaVarId>,
},
CallVirt {
dest: Option<SsaVarId>,
method: T::MethodRef,
args: Vec<SsaVarId>,
},
CallIndirect {
dest: Option<SsaVarId>,
fptr: SsaVarId,
signature: T::SigRef,
args: Vec<SsaVarId>,
},
LoadFunctionPtr {
dest: SsaVarId,
method: T::MethodRef,
},
LoadVirtFunctionPtr {
dest: SsaVarId,
object: SsaVarId,
method: T::MethodRef,
},
LoadArg {
dest: SsaVarId,
arg_index: u16,
},
LoadLocal {
dest: SsaVarId,
local_index: u16,
},
LoadArgAddr {
dest: SsaVarId,
arg_index: u16,
},
LoadLocalAddr {
dest: SsaVarId,
local_index: u16,
},
Copy {
dest: SsaVarId,
src: SsaVarId,
},
Pop {
value: SsaVarId,
},
Throw {
exception: SsaVarId,
},
Rethrow,
EndFinally,
EndFilter {
result: SsaVarId,
},
InterruptReturn,
Unreachable,
Leave {
target: usize,
},
InitBlk {
dest_addr: SsaVarId,
value: SsaVarId,
size: SsaVarId,
},
CopyBlk {
dest_addr: SsaVarId,
src_addr: SsaVarId,
size: SsaVarId,
},
Fence {
kind: FenceKind,
},
NativeOpaque(Box<NativeOpaqueData>),
NativeIntrinsic(Box<NativeIntrinsicData>),
SystemOp(Box<NativeKindedData<SystemOpKind>>),
ComputeOp(Box<NativeKindedData<ComputeKind>>),
BcdAdjust(Box<BcdAdjustData>),
VectorCrypto(Box<KindedVecData<VectorCryptoKind>>),
TileOp(Box<KindedVecData<TileOpKind>>),
VectorPermute(Box<VectorPermuteData>),
VectorMultiplyAdd(Box<KindedVecData<VectorMaddKind>>),
VectorPackNarrow(Box<VectorPackNarrowData>),
VectorNarrowSaturate(Box<VectorNarrowSaturateData>),
VectorPredicateWhile(Box<VectorPredicateWhileData>),
VectorPredicateBreak(Box<VectorPredicateBreakData>),
VectorComplexAdd(Box<VectorComplexAddData>),
VectorCountAdjust(Box<VectorCountAdjustData>),
VectorExtendInLane(Box<VectorExtendInLaneData>),
VectorElementCount(Box<VectorElementCountData>),
VectorSveAddressGen(Box<VectorSveAddressGenData>),
FlagAdjust(Box<KindedVecData<FlagAdjustKind>>),
VectorStructLoadReplicate(Box<VectorStructLoadReplicateData>),
VectorSmeMisc(Box<VectorSmeMiscData>),
VectorPredicateOp(Box<VectorPredicateOpData>),
VectorSveCompute(Box<VectorSveComputeData>),
VectorReverseChunks(Box<VectorReverseChunksData>),
VectorMatrixMulAcc(Box<VectorMatrixMulAccData>),
VectorSmeOuterProduct(Box<VectorSmeOuterProductData>),
VectorPredicateGen(Box<KindedVecData<PredicateGenKind>>),
VectorFpHelper(Box<KindedVecData<FpHelperKind>>),
VectorSvePermute(Box<KindedVecData<SvePermuteKind>>),
VectorTernaryLogic(Box<VecImm8Data>),
VectorDotProduct(Box<VectorDotProductData>),
VectorMultiSad(Box<VecImm8Data>),
VectorIntDotProduct(Box<VectorIntDotProductData>),
VectorStringCompare(Box<VectorStringCompareData>),
VectorBitfield(Box<VectorBitfieldData>),
VectorIntersect(Box<VectorIntersectData>),
VectorShuffleBits(Box<VectorShuffleBitsData>),
VectorConditionalMove(Box<VectorConditionalMoveData>),
VectorHorizontalMinPos(Box<VectorHorizontalMinPosData>),
VectorComplexMul(Box<KindedVecData<ComplexMulKind>>),
VectorClassify(Box<VecImm8Data>),
VectorHorizontalReduce(Box<VectorHorizontalReduceData>),
BlockString(Box<BlockStringOpData>),
WideCompareExchange(Box<WideCmpXchgData>),
ComputeFlags {
dest: SsaVarId,
inputs: Vec<SsaVarId>,
},
CallClobber {
outputs: Vec<SsaVarId>,
},
CmpXchg {
dest: SsaVarId,
addr: SsaVarId,
expected: SsaVarId,
desired: SsaVarId,
},
AtomicRmw {
dest: SsaVarId,
addr: SsaVarId,
value: SsaVarId,
op: AtomicRmwOp,
},
AtomicLoad {
dest: SsaVarId,
addr: SsaVarId,
value_type: T::Type,
ordering: AtomicOrdering,
width: AtomicAccessWidth,
volatile: bool,
},
AtomicStore {
addr: SsaVarId,
value: SsaVarId,
value_type: T::Type,
ordering: AtomicOrdering,
width: AtomicAccessWidth,
volatile: bool,
},
AtomicStoreConditional {
status: SsaVarId,
addr: SsaVarId,
value: SsaVarId,
value_type: T::Type,
success_ordering: AtomicOrdering,
failure_ordering: AtomicOrdering,
width: AtomicAccessWidth,
volatile: bool,
},
AtomicPairLoad {
first: SsaVarId,
second: SsaVarId,
addr: SsaVarId,
first_type: T::Type,
second_type: T::Type,
ordering: AtomicOrdering,
width: AtomicAccessWidth,
volatile: bool,
},
AtomicPairStoreConditional {
status: SsaVarId,
addr: SsaVarId,
first_value: SsaVarId,
second_value: SsaVarId,
first_type: T::Type,
second_type: T::Type,
success_ordering: AtomicOrdering,
failure_ordering: AtomicOrdering,
width: AtomicAccessWidth,
volatile: bool,
},
AtomicExchange {
dest: SsaVarId,
addr: SsaVarId,
value: SsaVarId,
ordering: AtomicOrdering,
width: AtomicAccessWidth,
volatile: bool,
},
AtomicLockRmw {
dest: SsaVarId,
addr: SsaVarId,
value: SsaVarId,
op: AtomicRmwOp,
ordering: AtomicOrdering,
width: AtomicAccessWidth,
volatile: bool,
},
AtomicCmpXchg {
old: SsaVarId,
success: Option<SsaVarId>,
addr: SsaVarId,
expected: SsaVarId,
desired: SsaVarId,
success_ordering: AtomicOrdering,
failure_ordering: AtomicOrdering,
width: AtomicAccessWidth,
weak: bool,
volatile: bool,
},
AtomicPairCmpXchg {
old_first: SsaVarId,
old_second: SsaVarId,
addr: SsaVarId,
expected_first: SsaVarId,
expected_second: SsaVarId,
desired_first: SsaVarId,
desired_second: SsaVarId,
success_ordering: AtomicOrdering,
failure_ordering: AtomicOrdering,
width: AtomicAccessWidth,
weak: bool,
volatile: bool,
},
InitObj {
dest_addr: SsaVarId,
value_type: T::TypeRef,
},
CopyObj {
dest_addr: SsaVarId,
src_addr: SsaVarId,
value_type: T::TypeRef,
},
LoadObj {
dest: SsaVarId,
src_addr: SsaVarId,
value_type: T::TypeRef,
},
StoreObj {
dest_addr: SsaVarId,
value: SsaVarId,
value_type: T::TypeRef,
},
Nop,
Break,
Ckfinite {
dest: SsaVarId,
operand: SsaVarId,
},
FpClassify {
dest: SsaVarId,
operand: SsaVarId,
},
FpTranscendental(Box<KindedVecData<TranscendentalKind>>),
FpuControl(Box<KindedVecData<FpuControlKind>>),
LocalAlloc {
dest: SsaVarId,
size: SsaVarId,
},
Constrained {
constraint_type: T::TypeRef,
},
Volatile,
Unaligned {
alignment: u8,
},
TailPrefix,
Readonly,
Phi {
dest: SsaVarId,
operands: Vec<(usize, SsaVarId)>,
},
}
impl<T: Target> SsaOp<T> {
#[must_use]
pub fn dest(&self) -> Option<SsaVarId> {
let mut dest = None;
self.visit_operands(|role, var| {
if dest.is_none() && matches!(role, OperandRole::Def) {
dest = Some(var);
}
});
dest
}
#[must_use]
pub fn defs(&self) -> SsaDefs<'_> {
match self {
Self::NativeOpaque(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::NativeIntrinsic(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::SystemOp(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::ComputeOp(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::BcdAdjust(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorCrypto(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::TileOp(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorPermute(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorMultiplyAdd(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorPackNarrow(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorNarrowSaturate(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorPredicateWhile(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorPredicateBreak(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorComplexAdd(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorCountAdjust(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorExtendInLane(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorElementCount(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorSveAddressGen(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::FlagAdjust(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorStructLoadReplicate(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorSmeMisc(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorPredicateOp(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorSveCompute(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorReverseChunks(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorMatrixMulAcc(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorSmeOuterProduct(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorPredicateGen(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorFpHelper(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorSvePermute(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorTernaryLogic(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorDotProduct(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorMultiSad(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorIntDotProduct(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorStringCompare(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorBitfield(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorIntersect(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorShuffleBits(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorConditionalMove(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorHorizontalMinPos(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorComplexMul(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorClassify(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorHorizontalReduce(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::BlockString(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::WideCompareExchange(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::CallClobber { outputs } => SsaDefs::new(None, None, Some(outputs)),
Self::ComputeFlags { dest, .. } => SsaDefs::new(Some(*dest), None, None),
Self::FpTranscendental(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::FpuControl(data) => SsaDefs::new(None, None, Some(&data.outputs)),
Self::VectorSegmentLoad { dests, .. } => SsaDefs::new(None, None, Some(dests)),
Self::VectorFaultingLoad { dest, fault, .. } => SsaDefs::new(Some(*dest), *fault, None),
Self::AtomicCmpXchg { old, success, .. } => SsaDefs::new(Some(*old), *success, None),
Self::AtomicPairLoad { first, second, .. } => {
SsaDefs::new(Some(*first), Some(*second), None)
}
Self::AtomicPairCmpXchg {
old_first,
old_second,
..
} => SsaDefs::new(Some(*old_first), Some(*old_second), None),
Self::WideMul { low, high, .. } => SsaDefs::new(Some(*low), Some(*high), None),
Self::WideDiv {
quotient,
remainder,
..
} => SsaDefs::new(Some(*quotient), Some(*remainder), None),
_ => SsaDefs::new(self.dest(), self.flags_dest(), None),
}
}
pub fn set_dest(&mut self, new_dest: SsaVarId) -> bool {
match self {
Self::Const { dest, .. }
| Self::Add { dest, .. }
| Self::AddOvf { dest, .. }
| Self::Sub { dest, .. }
| Self::SubOvf { dest, .. }
| Self::Mul { dest, .. }
| Self::MulOvf { dest, .. }
| Self::WideMul { low: dest, .. }
| Self::Div { dest, .. }
| Self::Rem { dest, .. }
| Self::WideDiv { quotient: dest, .. }
| Self::Neg { dest, .. }
| Self::And { dest, .. }
| Self::Or { dest, .. }
| Self::Xor { dest, .. }
| Self::Not { dest, .. }
| Self::Shl { dest, .. }
| Self::Shr { dest, .. }
| Self::Ceq { dest, .. }
| Self::Clt { dest, .. }
| Self::Cgt { dest, .. }
| Self::BoolAnd { dest, .. }
| Self::BoolOr { dest, .. }
| Self::BoolXor { dest, .. }
| Self::BoolNot { dest, .. }
| Self::IntConv { dest, .. }
| Self::IntToPtr { dest, .. }
| Self::PtrToInt { dest, .. }
| Self::IntToFloat { dest, .. }
| Self::FloatToInt { dest, .. }
| Self::FloatConv { dest, .. }
| Self::Bitcast { dest, .. }
| Self::LoadField { dest, .. }
| Self::LoadStaticField { dest, .. }
| Self::LoadFieldAddr { dest, .. }
| Self::LoadStaticFieldAddr { dest, .. }
| Self::LoadElement { dest, .. }
| Self::LoadElementAddr { dest, .. }
| Self::PtrAdd { dest, .. }
| Self::ArrayLength { dest, .. }
| Self::LoadIndirect { dest, .. }
| Self::NewObj { dest, .. }
| Self::NewArr { dest, .. }
| Self::CastClass { dest, .. }
| Self::IsInst { dest, .. }
| Self::Box { dest, .. }
| Self::Unbox { dest, .. }
| Self::UnboxAny { dest, .. }
| Self::SizeOf { dest, .. }
| Self::LoadToken { dest, .. }
| Self::LoadFunctionPtr { dest, .. }
| Self::LoadVirtFunctionPtr { dest, .. }
| Self::LoadArg { dest, .. }
| Self::LoadLocal { dest, .. }
| Self::LoadArgAddr { dest, .. }
| Self::LoadLocalAddr { dest, .. }
| Self::Copy { dest, .. }
| Self::Ckfinite { dest, .. }
| Self::FpClassify { dest, .. }
| Self::LocalAlloc { dest, .. }
| Self::LoadObj { dest, .. }
| Self::Phi { dest, .. }
| Self::Rol { dest, .. }
| Self::Ror { dest, .. }
| Self::Rcl { dest, .. }
| Self::Rcr { dest, .. }
| Self::BSwap { dest, .. }
| Self::BRev { dest, .. }
| Self::BitScanForward { dest, .. }
| Self::BitScanReverse { dest, .. }
| Self::Popcount { dest, .. }
| Self::Parity { dest, .. }
| Self::ComputeFlags { dest, .. }
| Self::Select { dest, .. }
| Self::CmpXchg { dest, .. }
| Self::AtomicRmw { dest, .. }
| Self::AtomicLoad { dest, .. }
| Self::AtomicExchange { dest, .. }
| Self::AtomicLockRmw { dest, .. }
| Self::AtomicStoreConditional { status: dest, .. }
| Self::AtomicPairLoad { first: dest, .. }
| Self::AtomicPairStoreConditional { status: dest, .. }
| Self::AtomicPairCmpXchg {
old_first: dest, ..
}
| Self::ReadFlags { dest, .. }
| Self::VectorUnary { dest, .. }
| Self::VectorBinary { dest, .. }
| Self::VectorTernary { dest, .. }
| Self::VectorPredicatedUnary { dest, .. }
| Self::VectorPredicatedBinary { dest, .. }
| Self::VectorPredicatedTernary { dest, .. }
| Self::VectorCompare { dest, .. }
| Self::VectorLoad { dest, .. }
| Self::VectorMaskedLoad { dest, .. }
| Self::VectorBroadcastLoad { dest, .. }
| Self::VectorGather { dest, .. }
| Self::VectorFaultingLoad { dest, .. }
| Self::VectorExtract { dest, .. }
| Self::VectorInsert { dest, .. }
| Self::VectorSplat { dest, .. }
| Self::VectorShuffle { dest, .. }
| Self::VectorCast { dest, .. }
| Self::VectorReinterpret { dest, .. }
| Self::VectorPack { dest, .. }
| Self::VectorPackLoad { dest, .. }
| Self::VectorMaskUnary { dest, .. }
| Self::VectorMaskBinary { dest, .. }
| Self::VectorReduce { dest, .. }
| Self::VectorBitmask { dest, .. } => {
*dest = new_dest;
true
}
Self::NativeOpaque(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::CallClobber { outputs } => {
if let Some(first) = outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::NativeIntrinsic(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::SystemOp(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::ComputeOp(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::BcdAdjust(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorCrypto(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::TileOp(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorPermute(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorMultiplyAdd(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorPackNarrow(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorNarrowSaturate(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorPredicateWhile(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorPredicateBreak(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorComplexAdd(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorCountAdjust(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorExtendInLane(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorElementCount(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorSveAddressGen(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::FlagAdjust(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorStructLoadReplicate(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorSmeMisc(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorPredicateOp(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorSveCompute(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorReverseChunks(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorMatrixMulAcc(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorSmeOuterProduct(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorPredicateGen(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorFpHelper(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorSvePermute(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorTernaryLogic(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorDotProduct(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorMultiSad(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorIntDotProduct(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorStringCompare(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorBitfield(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorIntersect(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorShuffleBits(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorConditionalMove(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorHorizontalMinPos(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorComplexMul(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorClassify(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorHorizontalReduce(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::BlockString(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::WideCompareExchange(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::FpTranscendental(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::FpuControl(data) => {
if let Some(first) = data.outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::VectorSegmentLoad { dests: outputs, .. } => {
if let Some(first) = outputs.first_mut() {
*first = new_dest;
true
} else {
false
}
}
Self::AtomicCmpXchg { old, .. } => {
*old = new_dest;
true
}
Self::Call { dest, .. }
| Self::CallVirt { dest, .. }
| Self::CallIndirect { dest, .. } => {
*dest = Some(new_dest);
true
}
Self::StoreField { .. }
| Self::StoreStaticField { .. }
| Self::StoreElement { .. }
| Self::StoreIndirect { .. }
| Self::AtomicStore { .. }
| Self::FloatCompareFlags { .. }
| Self::Jump { .. }
| Self::Branch { .. }
| Self::BranchCmp { .. }
| Self::IndirectBranch { .. }
| Self::Switch { .. }
| Self::Return { .. }
| Self::Pop { .. }
| Self::Throw { .. }
| Self::Rethrow
| Self::EndFinally
| Self::EndFilter { .. }
| Self::Leave { .. }
| Self::InitBlk { .. }
| Self::CopyBlk { .. }
| Self::InitObj { .. }
| Self::CopyObj { .. }
| Self::StoreObj { .. }
| Self::Nop
| Self::Break
| Self::Constrained { .. }
| Self::Volatile
| Self::Unaligned { .. }
| Self::TailPrefix
| Self::Readonly
| Self::Fence { .. }
| Self::InterruptReturn
| Self::BranchFlags { .. }
| Self::VectorStore { .. }
| Self::VectorMaskedStore { .. }
| Self::VectorScatter { .. }
| Self::VectorSegmentStore { .. }
| Self::VectorPackStore { .. }
| Self::VectorZeroUpper { .. }
| Self::Unreachable => false,
}
}
pub fn replace_def(&mut self, old_var: SsaVarId, new_var: SsaVarId) -> bool {
let mut changed = false;
self.visit_operands_mut(|role, var| {
if matches!(role, OperandRole::Def | OperandRole::FlagsDef) && *var == old_var {
*var = new_var;
changed = true;
}
});
changed
}
pub fn flags_dest(&self) -> Option<SsaVarId> {
let mut flags = None;
self.visit_operands(|role, var| {
if flags.is_none() && matches!(role, OperandRole::FlagsDef) {
flags = Some(var);
}
});
flags
}
#[allow(clippy::match_same_arms)] pub fn visit_operands<F>(&self, mut f: F)
where
F: FnMut(OperandRole, SsaVarId),
{
match self {
Self::PtrAdd {
dest, base, index, ..
} => {
f(OperandRole::Def, *dest);
f(OperandRole::Use, *base);
if let Some(index) = index {
f(OperandRole::Use, *index);
}
}
Self::Const { dest, .. }
| Self::LoadStaticField { dest, .. }
| Self::LoadStaticFieldAddr { dest, .. }
| Self::SizeOf { dest, .. }
| Self::LoadToken { dest, .. }
| Self::LoadFunctionPtr { dest, .. }
| Self::LoadArg { dest, .. }
| Self::LoadLocal { dest, .. }
| Self::LoadArgAddr { dest, .. }
| Self::LoadLocalAddr { dest, .. } => f(OperandRole::Def, *dest),
Self::ComputeFlags { dest, inputs } => {
f(OperandRole::Def, *dest);
for input in inputs {
f(OperandRole::Use, *input);
}
}
Self::CallClobber { outputs } => {
for output in outputs {
f(OperandRole::Def, *output);
}
}
Self::Add {
dest,
left,
right,
flags,
..
}
| Self::AddOvf {
dest,
left,
right,
flags,
..
}
| Self::Sub {
dest,
left,
right,
flags,
..
}
| Self::SubOvf {
dest,
left,
right,
flags,
..
}
| Self::Mul {
dest,
left,
right,
flags,
..
}
| Self::MulOvf {
dest,
left,
right,
flags,
..
}
| Self::Div {
dest,
left,
right,
flags,
..
}
| Self::Rem {
dest,
left,
right,
flags,
..
}
| Self::And {
dest,
left,
right,
flags,
..
}
| Self::Or {
dest,
left,
right,
flags,
..
}
| Self::Xor {
dest,
left,
right,
flags,
..
} => {
f(OperandRole::Def, *dest);
if let Some(flags_v) = flags {
f(OperandRole::FlagsDef, *flags_v);
}
f(OperandRole::Use, *left);
f(OperandRole::Use, *right);
}
Self::Neg {
dest,
operand,
flags,
..
}
| Self::Not {
dest,
operand,
flags,
..
} => {
f(OperandRole::Def, *dest);
if let Some(flags_v) = flags {
f(OperandRole::FlagsDef, *flags_v);
}
f(OperandRole::Use, *operand);
}
Self::Shl {
dest,
value,
amount,
flags,
..
}
| Self::Shr {
dest,
value,
amount,
flags,
..
} => {
f(OperandRole::Def, *dest);
if let Some(flags_v) = flags {
f(OperandRole::FlagsDef, *flags_v);
}
f(OperandRole::Use, *value);
f(OperandRole::Use, *amount);
}
Self::Rol {
dest,
value,
amount,
..
}
| Self::Ror {
dest,
value,
amount,
..
}
| Self::Rcl {
dest,
value,
amount,
..
}
| Self::Rcr {
dest,
value,
amount,
..
} => {
f(OperandRole::Def, *dest);
f(OperandRole::Use, *value);
f(OperandRole::Use, *amount);
}
Self::WideMul {
low,
high,
left,
right,
..
} => {
f(OperandRole::Def, *low);
f(OperandRole::Def, *high);
f(OperandRole::Use, *left);
f(OperandRole::Use, *right);
}
Self::WideDiv {
quotient,
remainder,
high,
low,
divisor,
..
} => {
f(OperandRole::Def, *quotient);
f(OperandRole::Def, *remainder);
f(OperandRole::Use, *high);
f(OperandRole::Use, *low);
f(OperandRole::Use, *divisor);
}
Self::Ceq {
dest, left, right, ..
}
| Self::Clt {
dest, left, right, ..
}
| Self::Cgt {
dest, left, right, ..
}
| Self::BoolAnd {
dest, left, right, ..
}
| Self::BoolOr {
dest, left, right, ..
}
| Self::BoolXor {
dest, left, right, ..
}
| Self::VectorBinary {
dest, left, right, ..
}
| Self::VectorCompare {
dest, left, right, ..
}
| Self::VectorMaskBinary {
dest, left, right, ..
} => {
f(OperandRole::Def, *dest);
f(OperandRole::Use, *left);
f(OperandRole::Use, *right);
}
Self::FloatCompareFlags {
flags, left, right, ..
} => {
f(OperandRole::FlagsDef, *flags);
f(OperandRole::Use, *left);
f(OperandRole::Use, *right);
}
Self::BranchCmp { left, right, .. } => {
f(OperandRole::Use, *left);
f(OperandRole::Use, *right);
}
Self::BoolNot { dest, value, .. }
| Self::Copy {
dest, src: value, ..
}
| Self::IntConv {
dest,
operand: value,
..
}
| Self::IntToPtr {
dest,
operand: value,
..
}
| Self::PtrToInt {
dest,
operand: value,
..
}
| Self::IntToFloat {
dest,
operand: value,
..
}
| Self::FloatToInt {
dest,
operand: value,
..
}
| Self::FloatConv {
dest,
operand: value,
..
}
| Self::Bitcast {
dest,
operand: value,
..
}
| Self::Ckfinite {
dest,
operand: value,
..
}
| Self::FpClassify {
dest,
operand: value,
..
}
| Self::BSwap {
dest, src: value, ..
}
| Self::BRev {
dest, src: value, ..
}
| Self::BitScanForward {
dest, src: value, ..
}
| Self::BitScanReverse {
dest, src: value, ..
}
| Self::Popcount {
dest, src: value, ..
}
| Self::Parity {
dest, src: value, ..
}
| Self::LoadField {
dest,
object: value,
..
}
| Self::LoadFieldAddr {
dest,
object: value,
..
}
| Self::ArrayLength {
dest, array: value, ..
}
| Self::LoadIndirect {
dest, addr: value, ..
}
| Self::AtomicLoad {
dest, addr: value, ..
}
| Self::VectorUnary { dest, value, .. }
| Self::VectorSplat { dest, value, .. }
| Self::VectorCast { dest, value, .. }
| Self::VectorReinterpret { dest, value, .. }
| Self::VectorLoad {
dest, addr: value, ..
}
| Self::VectorBroadcastLoad {
dest, addr: value, ..
}
| Self::VectorExtract {
dest,
vector: value,
..
}
| Self::VectorMaskUnary {
dest, mask: value, ..
}
| Self::VectorReduce { dest, value, .. }
| Self::VectorBitmask { dest, value, .. }
| Self::NewArr {
dest,
length: value,
..
}
| Self::Box { dest, value, .. }
| Self::LoadVirtFunctionPtr {
dest,
object: value,
..
}
| Self::LocalAlloc {
dest, size: value, ..
}
| Self::CastClass {
dest,
object: value,
..
}
| Self::IsInst {
dest,
object: value,
..
}
| Self::Unbox {
dest,
object: value,
..
}
| Self::UnboxAny {
dest,
object: value,
..
}
| Self::LoadObj {
dest,
src_addr: value,
..
}
| Self::ReadFlags {
dest, flags: value, ..
} => {
f(OperandRole::Def, *dest);
f(OperandRole::Use, *value);
}
Self::Branch {
condition: value, ..
}
| Self::Switch { value, .. }
| Self::StoreStaticField { value, .. }
| Self::Pop { value }
| Self::Throw { exception: value }
| Self::EndFilter { result: value }
| Self::InitObj {
dest_addr: value, ..
}
| Self::IndirectBranch { target: value, .. }
| Self::BranchFlags { flags: value, .. } => f(OperandRole::Use, *value),
Self::LoadElement {
dest,
array: a,
index: b,
..
}
| Self::LoadElementAddr {
dest,
array: a,
index: b,
..
}
| Self::AtomicRmw {
dest,
addr: a,
value: b,
..
}
| Self::AtomicExchange {
dest,
addr: a,
value: b,
..
}
| Self::AtomicLockRmw {
dest,
addr: a,
value: b,
..
}
| Self::AtomicStoreConditional {
status: dest,
addr: a,
value: b,
..
}
| Self::VectorInsert {
dest,
vector: a,
value: b,
..
} => {
f(OperandRole::Def, *dest);
f(OperandRole::Use, *a);
f(OperandRole::Use, *b);
}
Self::StoreField {
object: a,
value: b,
..
}
| Self::StoreIndirect {
addr: a, value: b, ..
}
| Self::AtomicStore {
addr: a, value: b, ..
}
| Self::VectorStore {
addr: a, value: b, ..
}
| Self::CopyObj {
dest_addr: a,
src_addr: b,
..
}
| Self::StoreObj {
dest_addr: a,
value: b,
..
} => {
f(OperandRole::Use, *a);
f(OperandRole::Use, *b);
}
Self::Select {
dest,
condition: a,
true_val: b,
false_val: c,
..
}
| Self::CmpXchg {
dest,
addr: a,
expected: b,
desired: c,
..
}
| Self::VectorTernary {
dest,
first: a,
second: b,
third: c,
..
}
| Self::AtomicPairStoreConditional {
status: dest,
addr: a,
first_value: b,
second_value: c,
..
} => {
f(OperandRole::Def, *dest);
f(OperandRole::Use, *a);
f(OperandRole::Use, *b);
f(OperandRole::Use, *c);
}
Self::StoreElement {
array: a,
index: b,
value: c,
..
}
| Self::VectorMaskedStore {
addr: a,
value: b,
mask: c,
..
}
| Self::VectorPackStore {
addr: a,
value: b,
mask: c,
..
}
| Self::InitBlk {
dest_addr: a,
value: b,
size: c,
..
}
| Self::CopyBlk {
dest_addr: a,
src_addr: b,
size: c,
..
} => {
f(OperandRole::Use, *a);
f(OperandRole::Use, *b);
f(OperandRole::Use, *c);
}
Self::VectorScatter {
base: a,
indices: b,
value: c,
mask: d,
..
} => {
f(OperandRole::Use, *a);
f(OperandRole::Use, *b);
f(OperandRole::Use, *c);
f(OperandRole::Use, *d);
}
Self::AtomicCmpXchg {
old,
success,
addr,
expected,
desired,
..
} => {
f(OperandRole::Def, *old);
if let Some(success_v) = success {
f(OperandRole::Def, *success_v);
}
f(OperandRole::Use, *addr);
f(OperandRole::Use, *expected);
f(OperandRole::Use, *desired);
}
Self::AtomicPairLoad {
first,
second,
addr,
..
} => {
f(OperandRole::Def, *first);
f(OperandRole::Def, *second);
f(OperandRole::Use, *addr);
}
Self::AtomicPairCmpXchg {
old_first,
old_second,
addr,
expected_first,
expected_second,
desired_first,
desired_second,
..
} => {
f(OperandRole::Def, *old_first);
f(OperandRole::Def, *old_second);
f(OperandRole::Use, *addr);
f(OperandRole::Use, *expected_first);
f(OperandRole::Use, *expected_second);
f(OperandRole::Use, *desired_first);
f(OperandRole::Use, *desired_second);
}
Self::VectorPredicatedUnary {
dest,
value,
mask,
passthrough,
..
}
| Self::VectorMaskedLoad {
dest,
addr: value,
mask,
passthrough,
..
}
| Self::VectorPack {
dest,
value,
mask,
passthrough,
..
}
| Self::VectorPackLoad {
dest,
addr: value,
mask,
passthrough,
..
} => {
f(OperandRole::Def, *dest);
f(OperandRole::Use, *value);
f(OperandRole::Use, *mask);
if let Some(passthrough_v) = passthrough {
f(OperandRole::Use, *passthrough_v);
}
}
Self::VectorPredicatedBinary {
dest,
left,
right,
mask,
passthrough,
..
}
| Self::VectorGather {
dest,
base: left,
indices: right,
mask,
passthrough,
..
} => {
f(OperandRole::Def, *dest);
f(OperandRole::Use, *left);
f(OperandRole::Use, *right);
f(OperandRole::Use, *mask);
if let Some(passthrough_v) = passthrough {
f(OperandRole::Use, *passthrough_v);
}
}
Self::VectorPredicatedTernary {
dest,
first,
second,
third,
mask,
passthrough,
..
} => {
f(OperandRole::Def, *dest);
f(OperandRole::Use, *first);
f(OperandRole::Use, *second);
f(OperandRole::Use, *third);
f(OperandRole::Use, *mask);
if let Some(passthrough_v) = passthrough {
f(OperandRole::Use, *passthrough_v);
}
}
Self::VectorFaultingLoad {
dest,
fault,
addr,
mask,
passthrough,
..
} => {
f(OperandRole::Def, *dest);
if let Some(fault_v) = fault {
f(OperandRole::Def, *fault_v);
}
f(OperandRole::Use, *addr);
if let Some(mask_v) = mask {
f(OperandRole::Use, *mask_v);
}
if let Some(passthrough_v) = passthrough {
f(OperandRole::Use, *passthrough_v);
}
}
Self::VectorSegmentLoad {
dests, base, mask, ..
} => {
for item in dests {
f(OperandRole::Def, *item);
}
f(OperandRole::Use, *base);
if let Some(mask_v) = mask {
f(OperandRole::Use, *mask_v);
}
}
Self::VectorSegmentStore {
base, values, mask, ..
} => {
f(OperandRole::Use, *base);
for item in values {
f(OperandRole::Use, *item);
}
if let Some(mask_v) = mask {
f(OperandRole::Use, *mask_v);
}
}
Self::VectorShuffle {
dest, left, right, ..
} => {
f(OperandRole::Def, *dest);
f(OperandRole::Use, *left);
if let Some(right_v) = right {
f(OperandRole::Use, *right_v);
}
}
Self::NativeOpaque(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::NativeIntrinsic(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::SystemOp(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::ComputeOp(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::BcdAdjust(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorCrypto(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::TileOp(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorPermute(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorMultiplyAdd(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorPackNarrow(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorNarrowSaturate(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorPredicateWhile(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorPredicateBreak(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorComplexAdd(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorCountAdjust(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorExtendInLane(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorElementCount(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
}
Self::VectorSveAddressGen(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::FlagAdjust(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorStructLoadReplicate(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorSmeMisc(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorPredicateOp(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorSveCompute(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorReverseChunks(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorMatrixMulAcc(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorSmeOuterProduct(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorPredicateGen(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorFpHelper(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorSvePermute(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorTernaryLogic(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorDotProduct(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorMultiSad(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorIntDotProduct(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorStringCompare(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorBitfield(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorIntersect(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorShuffleBits(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorConditionalMove(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorHorizontalMinPos(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorComplexMul(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorClassify(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::VectorHorizontalReduce(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::BlockString(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::WideCompareExchange(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::FpTranscendental(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::FpuControl(data) => {
for item in &data.outputs {
f(OperandRole::Def, *item);
}
for item in &data.inputs {
f(OperandRole::Use, *item);
}
}
Self::NewObj { dest, args, .. } => {
f(OperandRole::Def, *dest);
for item in args {
f(OperandRole::Use, *item);
}
}
Self::Call { dest, args, .. } | Self::CallVirt { dest, args, .. } => {
if let Some(dest_v) = dest {
f(OperandRole::Def, *dest_v);
}
for item in args {
f(OperandRole::Use, *item);
}
}
Self::CallIndirect {
dest, fptr, args, ..
} => {
if let Some(dest_v) = dest {
f(OperandRole::Def, *dest_v);
}
f(OperandRole::Use, *fptr);
for item in args {
f(OperandRole::Use, *item);
}
}
Self::Return { value } => {
if let Some(value_v) = value {
f(OperandRole::Use, *value_v);
}
}
Self::Phi { dest, operands } => {
f(OperandRole::Def, *dest);
for (_, item) in operands {
f(OperandRole::Use, *item);
}
}
Self::Jump { .. }
| Self::Rethrow
| Self::EndFinally
| Self::Leave { .. }
| Self::Nop
| Self::Break
| Self::Constrained { .. }
| Self::Volatile
| Self::Unaligned { .. }
| Self::TailPrefix
| Self::Readonly
| Self::Fence { .. }
| Self::InterruptReturn
| Self::VectorZeroUpper { .. }
| Self::Unreachable => {}
}
}
#[allow(clippy::match_same_arms)] pub fn visit_operands_mut<F>(&mut self, mut f: F)
where
F: FnMut(OperandRole, &mut SsaVarId),
{
match self {
Self::PtrAdd {
dest, base, index, ..
} => {
f(OperandRole::Def, dest);
f(OperandRole::Use, base);
if let Some(index) = index {
f(OperandRole::Use, index);
}
}
Self::Const { dest, .. }
| Self::LoadStaticField { dest, .. }
| Self::LoadStaticFieldAddr { dest, .. }
| Self::SizeOf { dest, .. }
| Self::LoadToken { dest, .. }
| Self::LoadFunctionPtr { dest, .. }
| Self::LoadArg { dest, .. }
| Self::LoadLocal { dest, .. }
| Self::LoadArgAddr { dest, .. }
| Self::LoadLocalAddr { dest, .. } => f(OperandRole::Def, dest),
Self::ComputeFlags { dest, inputs } => {
f(OperandRole::Def, dest);
for input in inputs {
f(OperandRole::Use, input);
}
}
Self::CallClobber { outputs } => {
for output in outputs {
f(OperandRole::Def, output);
}
}
Self::Add {
dest,
left,
right,
flags,
..
}
| Self::AddOvf {
dest,
left,
right,
flags,
..
}
| Self::Sub {
dest,
left,
right,
flags,
..
}
| Self::SubOvf {
dest,
left,
right,
flags,
..
}
| Self::Mul {
dest,
left,
right,
flags,
..
}
| Self::MulOvf {
dest,
left,
right,
flags,
..
}
| Self::Div {
dest,
left,
right,
flags,
..
}
| Self::Rem {
dest,
left,
right,
flags,
..
}
| Self::And {
dest,
left,
right,
flags,
..
}
| Self::Or {
dest,
left,
right,
flags,
..
}
| Self::Xor {
dest,
left,
right,
flags,
..
} => {
f(OperandRole::Def, dest);
if let Some(flags_v) = flags {
f(OperandRole::FlagsDef, flags_v);
}
f(OperandRole::Use, left);
f(OperandRole::Use, right);
}
Self::Neg {
dest,
operand,
flags,
..
}
| Self::Not {
dest,
operand,
flags,
..
} => {
f(OperandRole::Def, dest);
if let Some(flags_v) = flags {
f(OperandRole::FlagsDef, flags_v);
}
f(OperandRole::Use, operand);
}
Self::Shl {
dest,
value,
amount,
flags,
..
}
| Self::Shr {
dest,
value,
amount,
flags,
..
} => {
f(OperandRole::Def, dest);
if let Some(flags_v) = flags {
f(OperandRole::FlagsDef, flags_v);
}
f(OperandRole::Use, value);
f(OperandRole::Use, amount);
}
Self::Rol {
dest,
value,
amount,
..
}
| Self::Ror {
dest,
value,
amount,
..
}
| Self::Rcl {
dest,
value,
amount,
..
}
| Self::Rcr {
dest,
value,
amount,
..
} => {
f(OperandRole::Def, dest);
f(OperandRole::Use, value);
f(OperandRole::Use, amount);
}
Self::WideMul {
low,
high,
left,
right,
..
} => {
f(OperandRole::Def, low);
f(OperandRole::Def, high);
f(OperandRole::Use, left);
f(OperandRole::Use, right);
}
Self::WideDiv {
quotient,
remainder,
high,
low,
divisor,
..
} => {
f(OperandRole::Def, quotient);
f(OperandRole::Def, remainder);
f(OperandRole::Use, high);
f(OperandRole::Use, low);
f(OperandRole::Use, divisor);
}
Self::Ceq {
dest, left, right, ..
}
| Self::Clt {
dest, left, right, ..
}
| Self::Cgt {
dest, left, right, ..
}
| Self::BoolAnd {
dest, left, right, ..
}
| Self::BoolOr {
dest, left, right, ..
}
| Self::BoolXor {
dest, left, right, ..
}
| Self::VectorBinary {
dest, left, right, ..
}
| Self::VectorCompare {
dest, left, right, ..
}
| Self::VectorMaskBinary {
dest, left, right, ..
} => {
f(OperandRole::Def, dest);
f(OperandRole::Use, left);
f(OperandRole::Use, right);
}
Self::FloatCompareFlags {
flags, left, right, ..
} => {
f(OperandRole::FlagsDef, flags);
f(OperandRole::Use, left);
f(OperandRole::Use, right);
}
Self::BranchCmp { left, right, .. } => {
f(OperandRole::Use, left);
f(OperandRole::Use, right);
}
Self::BoolNot { dest, value, .. }
| Self::Copy {
dest, src: value, ..
}
| Self::IntConv {
dest,
operand: value,
..
}
| Self::IntToPtr {
dest,
operand: value,
..
}
| Self::PtrToInt {
dest,
operand: value,
..
}
| Self::IntToFloat {
dest,
operand: value,
..
}
| Self::FloatToInt {
dest,
operand: value,
..
}
| Self::FloatConv {
dest,
operand: value,
..
}
| Self::Bitcast {
dest,
operand: value,
..
}
| Self::Ckfinite {
dest,
operand: value,
..
}
| Self::FpClassify {
dest,
operand: value,
..
}
| Self::BSwap {
dest, src: value, ..
}
| Self::BRev {
dest, src: value, ..
}
| Self::BitScanForward {
dest, src: value, ..
}
| Self::BitScanReverse {
dest, src: value, ..
}
| Self::Popcount {
dest, src: value, ..
}
| Self::Parity {
dest, src: value, ..
}
| Self::LoadField {
dest,
object: value,
..
}
| Self::LoadFieldAddr {
dest,
object: value,
..
}
| Self::ArrayLength {
dest, array: value, ..
}
| Self::LoadIndirect {
dest, addr: value, ..
}
| Self::AtomicLoad {
dest, addr: value, ..
}
| Self::VectorUnary { dest, value, .. }
| Self::VectorSplat { dest, value, .. }
| Self::VectorCast { dest, value, .. }
| Self::VectorReinterpret { dest, value, .. }
| Self::VectorLoad {
dest, addr: value, ..
}
| Self::VectorBroadcastLoad {
dest, addr: value, ..
}
| Self::VectorExtract {
dest,
vector: value,
..
}
| Self::VectorMaskUnary {
dest, mask: value, ..
}
| Self::VectorReduce { dest, value, .. }
| Self::VectorBitmask { dest, value, .. }
| Self::NewArr {
dest,
length: value,
..
}
| Self::Box { dest, value, .. }
| Self::LoadVirtFunctionPtr {
dest,
object: value,
..
}
| Self::LocalAlloc {
dest, size: value, ..
}
| Self::CastClass {
dest,
object: value,
..
}
| Self::IsInst {
dest,
object: value,
..
}
| Self::Unbox {
dest,
object: value,
..
}
| Self::UnboxAny {
dest,
object: value,
..
}
| Self::LoadObj {
dest,
src_addr: value,
..
}
| Self::ReadFlags {
dest, flags: value, ..
} => {
f(OperandRole::Def, dest);
f(OperandRole::Use, value);
}
Self::Branch {
condition: value, ..
}
| Self::Switch { value, .. }
| Self::StoreStaticField { value, .. }
| Self::Pop { value }
| Self::Throw { exception: value }
| Self::EndFilter { result: value }
| Self::InitObj {
dest_addr: value, ..
}
| Self::IndirectBranch { target: value, .. }
| Self::BranchFlags { flags: value, .. } => f(OperandRole::Use, value),
Self::LoadElement {
dest,
array: a,
index: b,
..
}
| Self::LoadElementAddr {
dest,
array: a,
index: b,
..
}
| Self::AtomicRmw {
dest,
addr: a,
value: b,
..
}
| Self::AtomicExchange {
dest,
addr: a,
value: b,
..
}
| Self::AtomicLockRmw {
dest,
addr: a,
value: b,
..
}
| Self::AtomicStoreConditional {
status: dest,
addr: a,
value: b,
..
}
| Self::VectorInsert {
dest,
vector: a,
value: b,
..
} => {
f(OperandRole::Def, dest);
f(OperandRole::Use, a);
f(OperandRole::Use, b);
}
Self::StoreField {
object: a,
value: b,
..
}
| Self::StoreIndirect {
addr: a, value: b, ..
}
| Self::AtomicStore {
addr: a, value: b, ..
}
| Self::VectorStore {
addr: a, value: b, ..
}
| Self::CopyObj {
dest_addr: a,
src_addr: b,
..
}
| Self::StoreObj {
dest_addr: a,
value: b,
..
} => {
f(OperandRole::Use, a);
f(OperandRole::Use, b);
}
Self::Select {
dest,
condition: a,
true_val: b,
false_val: c,
..
}
| Self::CmpXchg {
dest,
addr: a,
expected: b,
desired: c,
..
}
| Self::VectorTernary {
dest,
first: a,
second: b,
third: c,
..
}
| Self::AtomicPairStoreConditional {
status: dest,
addr: a,
first_value: b,
second_value: c,
..
} => {
f(OperandRole::Def, dest);
f(OperandRole::Use, a);
f(OperandRole::Use, b);
f(OperandRole::Use, c);
}
Self::StoreElement {
array: a,
index: b,
value: c,
..
}
| Self::VectorMaskedStore {
addr: a,
value: b,
mask: c,
..
}
| Self::VectorPackStore {
addr: a,
value: b,
mask: c,
..
}
| Self::InitBlk {
dest_addr: a,
value: b,
size: c,
..
}
| Self::CopyBlk {
dest_addr: a,
src_addr: b,
size: c,
..
} => {
f(OperandRole::Use, a);
f(OperandRole::Use, b);
f(OperandRole::Use, c);
}
Self::VectorScatter {
base: a,
indices: b,
value: c,
mask: d,
..
} => {
f(OperandRole::Use, a);
f(OperandRole::Use, b);
f(OperandRole::Use, c);
f(OperandRole::Use, d);
}
Self::AtomicCmpXchg {
old,
success,
addr,
expected,
desired,
..
} => {
f(OperandRole::Def, old);
if let Some(success_v) = success {
f(OperandRole::Def, success_v);
}
f(OperandRole::Use, addr);
f(OperandRole::Use, expected);
f(OperandRole::Use, desired);
}
Self::AtomicPairLoad {
first,
second,
addr,
..
} => {
f(OperandRole::Def, first);
f(OperandRole::Def, second);
f(OperandRole::Use, addr);
}
Self::AtomicPairCmpXchg {
old_first,
old_second,
addr,
expected_first,
expected_second,
desired_first,
desired_second,
..
} => {
f(OperandRole::Def, old_first);
f(OperandRole::Def, old_second);
f(OperandRole::Use, addr);
f(OperandRole::Use, expected_first);
f(OperandRole::Use, expected_second);
f(OperandRole::Use, desired_first);
f(OperandRole::Use, desired_second);
}
Self::VectorPredicatedUnary {
dest,
value,
mask,
passthrough,
..
}
| Self::VectorMaskedLoad {
dest,
addr: value,
mask,
passthrough,
..
}
| Self::VectorPack {
dest,
value,
mask,
passthrough,
..
}
| Self::VectorPackLoad {
dest,
addr: value,
mask,
passthrough,
..
} => {
f(OperandRole::Def, dest);
f(OperandRole::Use, value);
f(OperandRole::Use, mask);
if let Some(passthrough_v) = passthrough {
f(OperandRole::Use, passthrough_v);
}
}
Self::VectorPredicatedBinary {
dest,
left,
right,
mask,
passthrough,
..
}
| Self::VectorGather {
dest,
base: left,
indices: right,
mask,
passthrough,
..
} => {
f(OperandRole::Def, dest);
f(OperandRole::Use, left);
f(OperandRole::Use, right);
f(OperandRole::Use, mask);
if let Some(passthrough_v) = passthrough {
f(OperandRole::Use, passthrough_v);
}
}
Self::VectorPredicatedTernary {
dest,
first,
second,
third,
mask,
passthrough,
..
} => {
f(OperandRole::Def, dest);
f(OperandRole::Use, first);
f(OperandRole::Use, second);
f(OperandRole::Use, third);
f(OperandRole::Use, mask);
if let Some(passthrough_v) = passthrough {
f(OperandRole::Use, passthrough_v);
}
}
Self::VectorFaultingLoad {
dest,
fault,
addr,
mask,
passthrough,
..
} => {
f(OperandRole::Def, dest);
if let Some(fault_v) = fault {
f(OperandRole::Def, fault_v);
}
f(OperandRole::Use, addr);
if let Some(mask_v) = mask {
f(OperandRole::Use, mask_v);
}
if let Some(passthrough_v) = passthrough {
f(OperandRole::Use, passthrough_v);
}
}
Self::VectorSegmentLoad {
dests, base, mask, ..
} => {
for item in dests {
f(OperandRole::Def, item);
}
f(OperandRole::Use, base);
if let Some(mask_v) = mask {
f(OperandRole::Use, mask_v);
}
}
Self::VectorSegmentStore {
base, values, mask, ..
} => {
f(OperandRole::Use, base);
for item in values {
f(OperandRole::Use, item);
}
if let Some(mask_v) = mask {
f(OperandRole::Use, mask_v);
}
}
Self::VectorShuffle {
dest, left, right, ..
} => {
f(OperandRole::Def, dest);
f(OperandRole::Use, left);
if let Some(right_v) = right {
f(OperandRole::Use, right_v);
}
}
Self::NativeOpaque(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::NativeIntrinsic(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::SystemOp(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::ComputeOp(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::BcdAdjust(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorCrypto(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::TileOp(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorPermute(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorMultiplyAdd(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorPackNarrow(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorNarrowSaturate(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorPredicateWhile(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorPredicateBreak(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorComplexAdd(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorCountAdjust(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorExtendInLane(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorElementCount(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
}
Self::VectorSveAddressGen(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::FlagAdjust(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorStructLoadReplicate(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorSmeMisc(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorPredicateOp(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorSveCompute(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorReverseChunks(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorMatrixMulAcc(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorSmeOuterProduct(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorPredicateGen(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorFpHelper(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorSvePermute(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorTernaryLogic(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorDotProduct(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorMultiSad(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorIntDotProduct(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorStringCompare(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorBitfield(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorIntersect(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorShuffleBits(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorConditionalMove(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorHorizontalMinPos(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorComplexMul(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorClassify(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::VectorHorizontalReduce(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::BlockString(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::WideCompareExchange(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::FpTranscendental(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::FpuControl(data) => {
for item in &mut data.outputs {
f(OperandRole::Def, item);
}
for item in &mut data.inputs {
f(OperandRole::Use, item);
}
}
Self::NewObj { dest, args, .. } => {
f(OperandRole::Def, dest);
for item in args {
f(OperandRole::Use, item);
}
}
Self::Call { dest, args, .. } | Self::CallVirt { dest, args, .. } => {
if let Some(dest_v) = dest {
f(OperandRole::Def, dest_v);
}
for item in args {
f(OperandRole::Use, item);
}
}
Self::CallIndirect {
dest, fptr, args, ..
} => {
if let Some(dest_v) = dest {
f(OperandRole::Def, dest_v);
}
f(OperandRole::Use, fptr);
for item in args {
f(OperandRole::Use, item);
}
}
Self::Return { value } => {
if let Some(value_v) = value {
f(OperandRole::Use, value_v);
}
}
Self::Phi { dest, operands } => {
f(OperandRole::Def, dest);
for (_, item) in operands {
f(OperandRole::Use, item);
}
}
Self::Jump { .. }
| Self::Rethrow
| Self::EndFinally
| Self::Leave { .. }
| Self::Nop
| Self::Break
| Self::Constrained { .. }
| Self::Volatile
| Self::Unaligned { .. }
| Self::TailPrefix
| Self::Readonly
| Self::Fence { .. }
| Self::InterruptReturn
| Self::VectorZeroUpper { .. }
| Self::Unreachable => {}
}
}
pub fn for_each_use<F>(&self, mut f: F)
where
F: FnMut(SsaVarId),
{
self.visit_operands(|role, var| {
if matches!(role, OperandRole::Use) {
f(var);
}
});
}
#[must_use]
pub fn uses(&self) -> Vec<SsaVarId> {
let mut uses = Vec::new();
self.for_each_use(|var| uses.push(var));
uses
}
#[must_use]
pub fn use_count(&self) -> usize {
let mut count = 0usize;
self.for_each_use(|_| count = count.saturating_add(1));
count
}
#[must_use]
pub fn uses_var(&self, var: SsaVarId) -> bool {
let mut found = false;
self.for_each_use(|used| {
if used == var {
found = true;
}
});
found
}
#[must_use]
pub const fn is_terminator(&self) -> bool {
matches!(
self,
Self::Jump { .. }
| Self::Branch { .. }
| Self::BranchCmp { .. }
| Self::BranchFlags { .. }
| Self::IndirectBranch { .. }
| Self::Switch { .. }
| Self::Return { .. }
| Self::Throw { .. }
| Self::Rethrow
| Self::Leave { .. }
| Self::EndFinally
| Self::EndFilter { .. }
| Self::InterruptReturn
| Self::Unreachable
)
}
#[must_use]
pub const fn may_throw(&self) -> bool {
matches!(
self,
Self::Div { .. }
| Self::Rem { .. }
| Self::FloatCompareFlags {
signaling: true,
..
}
| Self::WideDiv { .. }
| Self::AddOvf { .. }
| Self::SubOvf { .. }
| Self::MulOvf { .. }
| Self::IntConv {
overflow_check: true,
..
}
| Self::FloatToInt {
overflow_check: true,
..
}
| Self::LoadField { .. }
| Self::StoreField { .. }
| Self::LoadStaticField { .. }
| Self::StoreStaticField { .. }
| Self::LoadElement { .. }
| Self::StoreElement { .. }
| Self::LoadElementAddr { .. }
| Self::LoadIndirect { .. }
| Self::StoreIndirect { .. }
| Self::LoadObj { .. }
| Self::StoreObj { .. }
| Self::InitObj { .. }
| Self::CopyObj { .. }
| Self::InitBlk { .. }
| Self::CopyBlk { .. }
| Self::NewObj { .. }
| Self::NewArr { .. }
| Self::CastClass { .. }
| Self::Unbox { .. }
| Self::UnboxAny { .. }
| Self::Call { .. }
| Self::CallVirt { .. }
| Self::CallIndirect { .. }
| Self::Throw { .. }
| Self::Rethrow
| Self::Break
| Self::Ckfinite { .. }
| Self::CmpXchg { .. }
| Self::WideCompareExchange { .. }
| Self::AtomicRmw { .. }
| Self::AtomicLoad { .. }
| Self::AtomicStore { .. }
| Self::AtomicPairLoad { .. }
| Self::AtomicPairStoreConditional { .. }
| Self::AtomicExchange { .. }
| Self::AtomicLockRmw { .. }
| Self::AtomicStoreConditional { .. }
| Self::AtomicCmpXchg { .. }
| Self::AtomicPairCmpXchg { .. }
| Self::VectorLoad { .. }
| Self::VectorMaskedLoad { .. }
| Self::VectorBroadcastLoad { .. }
| Self::VectorGather { .. }
| Self::VectorFaultingLoad { .. }
| Self::VectorSegmentLoad { .. }
| Self::VectorPackLoad { .. }
| Self::VectorStructLoadReplicate(_)
| Self::VectorStore { .. }
| Self::VectorMaskedStore { .. }
| Self::VectorScatter { .. }
| Self::VectorSegmentStore { .. }
| Self::VectorPackStore { .. }
) || matches!(self, Self::NativeOpaque(data) if data.effects.may_throw)
|| matches!(self, Self::NativeIntrinsic(data) if data.effects.may_throw)
|| matches!(self, Self::SystemOp(data) if data.kind.effects().may_throw)
|| matches!(self, Self::ComputeOp(data) if data.kind.effects().may_throw)
|| matches!(self, Self::BcdAdjust(data) if data.kind.effects().may_throw)
|| matches!(self, Self::VectorCrypto(data) if data.kind.effects().may_throw)
|| matches!(self, Self::TileOp(data) if data.kind.effects().may_throw)
|| matches!(self, Self::BlockString(data) if data.kind.effects().may_throw)
}
#[must_use]
pub const fn effects(&self) -> SsaEffects {
match self {
Self::LoadField { .. }
| Self::LoadStaticField { .. }
| Self::LoadElement { .. }
| Self::LoadIndirect { .. }
| Self::LoadObj { .. }
| Self::VectorLoad { .. }
| Self::VectorMaskedLoad { .. }
| Self::VectorBroadcastLoad { .. }
| Self::VectorGather { .. }
| Self::VectorFaultingLoad { .. }
| Self::VectorSegmentLoad { .. }
| Self::VectorPackLoad { .. }
| Self::VectorStructLoadReplicate(_) => {
SsaEffects::new(SsaEffectKind::Read, self.may_throw())
.with_trap(TrapClass::MemoryFault)
}
Self::StoreField { .. }
| Self::StoreStaticField { .. }
| Self::StoreElement { .. }
| Self::StoreIndirect { .. }
| Self::StoreObj { .. }
| Self::InitObj { .. }
| Self::VectorStore { .. }
| Self::VectorMaskedStore { .. }
| Self::VectorScatter { .. }
| Self::VectorSegmentStore { .. }
| Self::VectorPackStore { .. } => {
SsaEffects::new(SsaEffectKind::Write, self.may_throw())
.with_trap(TrapClass::MemoryFault)
}
Self::CopyBlk { .. } | Self::InitBlk { .. } | Self::CopyObj { .. } => {
SsaEffects::new(SsaEffectKind::ReadWrite, self.may_throw())
.with_trap(TrapClass::MemoryFault)
}
Self::CmpXchg { .. } | Self::AtomicRmw { .. } => {
SsaEffects::new(SsaEffectKind::Atomic, self.may_throw())
.atomic_ordering(AtomicOrdering::SeqCst)
.with_trap(TrapClass::MemoryFault)
}
Self::AtomicLoad {
ordering, volatile, ..
}
| Self::AtomicStore {
ordering, volatile, ..
}
| Self::AtomicPairLoad {
ordering, volatile, ..
}
| Self::AtomicExchange {
ordering, volatile, ..
}
| Self::AtomicLockRmw {
ordering, volatile, ..
} => {
let effects = SsaEffects::new(SsaEffectKind::Atomic, self.may_throw())
.atomic_ordering(*ordering)
.with_trap(TrapClass::MemoryFault);
if *volatile {
effects.volatile()
} else {
effects
}
}
Self::AtomicCmpXchg {
success_ordering,
volatile,
..
}
| Self::AtomicStoreConditional {
success_ordering,
volatile,
..
}
| Self::AtomicPairStoreConditional {
success_ordering,
volatile,
..
}
| Self::AtomicPairCmpXchg {
success_ordering,
volatile,
..
} => {
let effects = SsaEffects::new(SsaEffectKind::Atomic, self.may_throw())
.atomic_ordering(*success_ordering)
.with_trap(TrapClass::MemoryFault);
if *volatile {
effects.volatile()
} else {
effects
}
}
Self::Fence { kind } => {
SsaEffects::new(SsaEffectKind::Fence, false).fence_ordering(kind.ordering())
}
Self::NativeOpaque(data) => data.effects,
Self::NativeIntrinsic(data) => data.effects,
Self::SystemOp(data) => data.kind.effects(),
Self::ComputeOp(data) => data.kind.effects(),
Self::BcdAdjust(data) => data.kind.effects(),
Self::VectorCrypto(data) => data.kind.effects(),
Self::TileOp(data) => data.kind.effects(),
Self::VectorPermute(_)
| Self::VectorMultiplyAdd(_)
| Self::VectorPackNarrow(_)
| Self::VectorNarrowSaturate(_)
| Self::VectorPredicateWhile(_)
| Self::VectorPredicateBreak(_)
| Self::VectorComplexAdd(_)
| Self::VectorCountAdjust(_)
| Self::VectorExtendInLane(_)
| Self::VectorElementCount(_)
| Self::VectorSveAddressGen(_)
| Self::FlagAdjust(_)
| Self::VectorSmeMisc(_)
| Self::VectorSveCompute(_)
| Self::VectorReverseChunks(_)
| Self::VectorMatrixMulAcc(_)
| Self::VectorSmeOuterProduct(_)
| Self::VectorPredicateGen(_)
| Self::VectorFpHelper(_)
| Self::VectorSvePermute(_)
| Self::VectorTernaryLogic(_)
| Self::VectorDotProduct(_)
| Self::VectorMultiSad(_)
| Self::VectorIntDotProduct(_)
| Self::VectorStringCompare(_)
| Self::VectorBitfield(_)
| Self::VectorIntersect(_)
| Self::VectorShuffleBits(_)
| Self::VectorConditionalMove(_)
| Self::VectorHorizontalMinPos(_)
| Self::VectorComplexMul(_)
| Self::VectorClassify(_)
| Self::VectorHorizontalReduce(_) => SsaEffects::new(SsaEffectKind::Pure, false),
Self::VectorPredicateOp(data) => match data.op {
PredicateOpKind::SetFirstFault | PredicateOpKind::WriteFirstFault => {
SsaEffects::new(SsaEffectKind::Opaque, false)
}
_ => SsaEffects::new(SsaEffectKind::Pure, false),
},
Self::BlockString(data) => data.kind.effects(),
Self::WideCompareExchange { .. } => {
SsaEffects::new(SsaEffectKind::Atomic, self.may_throw())
.atomic_ordering(AtomicOrdering::SeqCst)
.with_trap(TrapClass::MemoryFault)
}
Self::FpTranscendental { .. } => SsaEffects::new(SsaEffectKind::Pure, false),
Self::FpuControl { .. } => SsaEffects::new(SsaEffectKind::Opaque, false),
Self::FloatCompareFlags { signaling, .. } => {
if *signaling {
SsaEffects::new(SsaEffectKind::Pure, true).with_trap(TrapClass::Unknown)
} else {
SsaEffects::new(SsaEffectKind::Pure, false)
}
}
Self::Call { .. } | Self::CallVirt { .. } | Self::CallIndirect { .. } => {
SsaEffects::new(SsaEffectKind::Call, true).with_control(ControlEffect::Call)
}
Self::Jump { .. }
| Self::Branch { .. }
| Self::BranchCmp { .. }
| Self::BranchFlags { .. }
| Self::IndirectBranch { .. }
| Self::Switch { .. } => SsaEffects::new(SsaEffectKind::Opaque, false)
.with_control(ControlEffect::Terminator),
Self::Return { .. } | Self::Leave { .. } => {
SsaEffects::new(SsaEffectKind::Opaque, false).with_control(ControlEffect::Return)
}
Self::NewObj { .. }
| Self::NewArr { .. }
| Self::CastClass { .. }
| Self::Unbox { .. }
| Self::UnboxAny { .. }
| Self::Box { .. }
| Self::LocalAlloc { .. } => SsaEffects::new(SsaEffectKind::Opaque, self.may_throw()),
Self::Throw { .. } | Self::Rethrow => SsaEffects::new(SsaEffectKind::Opaque, true)
.with_trap(TrapClass::UserThrow)
.with_control(ControlEffect::Throw),
Self::EndFinally
| Self::EndFilter { .. }
| Self::InterruptReturn
| Self::Unreachable => SsaEffects::new(SsaEffectKind::Opaque, self.may_throw())
.with_control(ControlEffect::Terminator),
Self::Break => SsaEffects::new(SsaEffectKind::Opaque, self.may_throw())
.with_trap(TrapClass::IllegalInstruction),
Self::Constrained { .. }
| Self::Volatile
| Self::Unaligned { .. }
| Self::TailPrefix
| Self::Readonly => SsaEffects::new(SsaEffectKind::Opaque, self.may_throw()),
Self::VectorZeroUpper { .. } => SsaEffects::new(SsaEffectKind::Opaque, false),
Self::Add { .. }
| Self::Sub { .. }
| Self::Mul { .. }
| Self::WideMul { .. }
| Self::Div { .. }
| Self::Rem { .. }
| Self::WideDiv { .. }
| Self::AddOvf { .. }
| Self::SubOvf { .. }
| Self::MulOvf { .. }
| Self::And { .. }
| Self::Or { .. }
| Self::Xor { .. }
| Self::Neg { .. }
| Self::Not { .. }
| Self::Shl { .. }
| Self::Shr { .. }
| Self::Rol { .. }
| Self::Ror { .. }
| Self::Ceq { .. }
| Self::Cgt { .. }
| Self::Clt { .. }
| Self::BoolAnd { .. }
| Self::BoolOr { .. }
| Self::BoolXor { .. }
| Self::BoolNot { .. }
| Self::BSwap { .. }
| Self::BRev { .. }
| Self::BitScanForward { .. }
| Self::BitScanReverse { .. }
| Self::Popcount { .. }
| Self::Parity { .. }
| Self::Bitcast { .. }
| Self::IntConv { .. }
| Self::IntToPtr { .. }
| Self::PtrToInt { .. }
| Self::IntToFloat { .. }
| Self::FloatToInt { .. }
| Self::FloatConv { .. }
| Self::Ckfinite { .. }
| Self::FpClassify { .. }
| Self::Const { .. }
| Self::Copy { .. }
| Self::Select { .. }
| Self::SizeOf { .. }
| Self::ReadFlags { .. }
| Self::ComputeFlags { .. }
| Self::CallClobber { .. }
| Self::Phi { .. }
| Self::Nop
| Self::Pop { .. }
| Self::LoadArg { .. }
| Self::LoadArgAddr { .. }
| Self::LoadLocalAddr { .. }
| Self::LoadFieldAddr { .. }
| Self::LoadStaticFieldAddr { .. }
| Self::LoadElementAddr { .. }
| Self::PtrAdd { .. }
| Self::LoadFunctionPtr { .. }
| Self::LoadToken { .. }
| Self::VectorUnary { .. }
| Self::VectorBinary { .. }
| Self::VectorTernary { .. }
| Self::VectorPredicatedUnary { .. }
| Self::VectorPredicatedBinary { .. }
| Self::VectorPredicatedTernary { .. }
| Self::VectorCompare { .. }
| Self::VectorCast { .. }
| Self::VectorReinterpret { .. }
| Self::VectorExtract { .. }
| Self::VectorInsert { .. }
| Self::VectorSplat { .. }
| Self::VectorShuffle { .. }
| Self::VectorPack { .. }
| Self::VectorReduce { .. }
| Self::VectorBitmask { .. }
| Self::VectorMaskUnary { .. }
| Self::VectorMaskBinary { .. } => {
SsaEffects::new(SsaEffectKind::Pure, self.may_throw())
}
Self::LoadLocal { .. }
| Self::IsInst { .. }
| Self::ArrayLength { .. }
| Self::LoadVirtFunctionPtr { .. } => {
SsaEffects::new(SsaEffectKind::Read, self.may_throw())
}
Self::Rcl { .. } | Self::Rcr { .. } => {
SsaEffects::new(SsaEffectKind::Opaque, self.may_throw())
} }
}
#[must_use]
pub const fn class(&self) -> SsaOpClass {
match self {
Self::Nop
| Self::Phi { .. }
| Self::Copy { .. }
| Self::Pop { .. }
| Self::CallClobber { .. } => SsaOpClass::Synthetic,
Self::BoolAnd { .. }
| Self::BoolOr { .. }
| Self::BoolXor { .. }
| Self::BoolNot { .. } => SsaOpClass::Boolean,
Self::FlagAdjust(_) => SsaOpClass::Flags,
Self::ReadFlags { .. } | Self::BranchFlags { .. } | Self::ComputeFlags { .. } => {
SsaOpClass::Flags
}
Self::VectorUnary { .. }
| Self::VectorBinary { .. }
| Self::VectorTernary { .. }
| Self::VectorPredicatedUnary { .. }
| Self::VectorPredicatedBinary { .. }
| Self::VectorPredicatedTernary { .. }
| Self::VectorCompare { .. }
| Self::VectorLoad { .. }
| Self::VectorStore { .. }
| Self::VectorMaskedLoad { .. }
| Self::VectorMaskedStore { .. }
| Self::VectorBroadcastLoad { .. }
| Self::VectorGather { .. }
| Self::VectorFaultingLoad { .. }
| Self::VectorSegmentLoad { .. }
| Self::VectorScatter { .. }
| Self::VectorSegmentStore { .. }
| Self::VectorPackLoad { .. }
| Self::VectorPackStore { .. }
| Self::VectorExtract { .. }
| Self::VectorInsert { .. }
| Self::VectorShuffle { .. }
| Self::VectorSplat { .. }
| Self::VectorCast { .. }
| Self::VectorReinterpret { .. }
| Self::VectorPack { .. }
| Self::VectorZeroUpper { .. }
| Self::VectorMaskUnary { .. }
| Self::VectorMaskBinary { .. }
| Self::VectorReduce { .. }
| Self::VectorBitmask { .. } => SsaOpClass::Vector,
Self::LoadField { .. }
| Self::StoreField { .. }
| Self::LoadStaticField { .. }
| Self::StoreStaticField { .. }
| Self::LoadFieldAddr { .. }
| Self::LoadStaticFieldAddr { .. }
| Self::LoadElement { .. }
| Self::StoreElement { .. }
| Self::LoadElementAddr { .. }
| Self::PtrAdd { .. }
| Self::ArrayLength { .. }
| Self::LoadIndirect { .. }
| Self::StoreIndirect { .. }
| Self::NewObj { .. }
| Self::NewArr { .. }
| Self::Box { .. }
| Self::Unbox { .. }
| Self::UnboxAny { .. }
| Self::LocalAlloc { .. }
| Self::InitBlk { .. }
| Self::CopyBlk { .. }
| Self::InitObj { .. }
| Self::CopyObj { .. }
| Self::LoadObj { .. }
| Self::StoreObj { .. }
| Self::Fence { .. } => SsaOpClass::Memory,
Self::CmpXchg { .. }
| Self::AtomicRmw { .. }
| Self::AtomicLoad { .. }
| Self::AtomicStore { .. }
| Self::AtomicPairLoad { .. }
| Self::AtomicPairStoreConditional { .. }
| Self::AtomicExchange { .. }
| Self::AtomicLockRmw { .. }
| Self::AtomicStoreConditional { .. }
| Self::AtomicCmpXchg { .. }
| Self::AtomicPairCmpXchg { .. } => SsaOpClass::Atomic,
Self::Call { .. }
| Self::CallVirt { .. }
| Self::CallIndirect { .. }
| Self::LoadFunctionPtr { .. }
| Self::LoadVirtFunctionPtr { .. } => SsaOpClass::Call,
Self::Jump { .. }
| Self::Branch { .. }
| Self::BranchCmp { .. }
| Self::IndirectBranch { .. }
| Self::Switch { .. }
| Self::Return { .. }
| Self::Throw { .. }
| Self::Rethrow
| Self::EndFinally
| Self::EndFilter { .. }
| Self::InterruptReturn
| Self::Unreachable
| Self::Leave { .. }
| Self::Break => SsaOpClass::Control,
Self::NativeOpaque(_) => SsaOpClass::NativeOpaque,
Self::NativeIntrinsic(_) => SsaOpClass::NativeIntrinsic,
Self::SystemOp(_) => SsaOpClass::Call,
Self::ComputeOp(_) => SsaOpClass::Scalar,
Self::BcdAdjust(_) => SsaOpClass::Scalar,
Self::VectorCrypto(_) => SsaOpClass::Vector,
Self::TileOp(_) => SsaOpClass::Vector,
Self::VectorPermute(_) => SsaOpClass::Vector,
Self::VectorMultiplyAdd(_) => SsaOpClass::Vector,
Self::VectorPackNarrow(_) => SsaOpClass::Vector,
Self::VectorNarrowSaturate(_) => SsaOpClass::Vector,
Self::VectorPredicateWhile(_) => SsaOpClass::Vector,
Self::VectorPredicateBreak(_) => SsaOpClass::Vector,
Self::VectorComplexAdd(_) => SsaOpClass::Vector,
Self::VectorCountAdjust(_) => SsaOpClass::Vector,
Self::VectorExtendInLane(_) => SsaOpClass::Vector,
Self::VectorElementCount(_) => SsaOpClass::Vector,
Self::VectorSveAddressGen(_) => SsaOpClass::Vector,
Self::VectorStructLoadReplicate(_) => SsaOpClass::Vector,
Self::VectorSmeMisc(_) => SsaOpClass::Vector,
Self::VectorPredicateOp(_) => SsaOpClass::Vector,
Self::VectorSveCompute(_) => SsaOpClass::Vector,
Self::VectorReverseChunks(_) => SsaOpClass::Vector,
Self::VectorMatrixMulAcc(_) => SsaOpClass::Vector,
Self::VectorSmeOuterProduct(_) => SsaOpClass::Vector,
Self::VectorPredicateGen(_) => SsaOpClass::Vector,
Self::VectorFpHelper(_) => SsaOpClass::Vector,
Self::VectorSvePermute(_) => SsaOpClass::Vector,
Self::VectorTernaryLogic(_) => SsaOpClass::Vector,
Self::VectorDotProduct(_) => SsaOpClass::Vector,
Self::VectorMultiSad(_) => SsaOpClass::Vector,
Self::VectorIntDotProduct(_) => SsaOpClass::Vector,
Self::VectorStringCompare(_) => SsaOpClass::Vector,
Self::VectorBitfield(_) => SsaOpClass::Vector,
Self::VectorIntersect(_) => SsaOpClass::Vector,
Self::VectorShuffleBits(_) => SsaOpClass::Vector,
Self::VectorConditionalMove(_) => SsaOpClass::Vector,
Self::VectorHorizontalMinPos(_) => SsaOpClass::Vector,
Self::VectorComplexMul(_) => SsaOpClass::Vector,
Self::VectorClassify(_) => SsaOpClass::Vector,
Self::VectorHorizontalReduce(_) => SsaOpClass::Vector,
Self::BlockString(_) => SsaOpClass::Memory,
Self::WideCompareExchange { .. } => SsaOpClass::Atomic,
Self::FpTranscendental { .. } | Self::FpuControl { .. } => SsaOpClass::NativeOpaque,
Self::WideMul { .. } | Self::WideDiv { .. } => SsaOpClass::WideArithmetic,
Self::FloatCompareFlags { .. } => SsaOpClass::Scalar,
Self::Constrained { .. }
| Self::Volatile
| Self::Unaligned { .. }
| Self::TailPrefix
| Self::Readonly => SsaOpClass::Prefix,
Self::Add { flags: Some(_), .. }
| Self::AddOvf { flags: Some(_), .. }
| Self::Sub { flags: Some(_), .. }
| Self::SubOvf { flags: Some(_), .. }
| Self::Mul { flags: Some(_), .. }
| Self::MulOvf { flags: Some(_), .. }
| Self::Div { flags: Some(_), .. }
| Self::Rem { flags: Some(_), .. }
| Self::Neg { flags: Some(_), .. }
| Self::And { flags: Some(_), .. }
| Self::Or { flags: Some(_), .. }
| Self::Xor { flags: Some(_), .. }
| Self::Not { flags: Some(_), .. }
| Self::Shl { flags: Some(_), .. }
| Self::Shr { flags: Some(_), .. } => SsaOpClass::Flags,
Self::Const { .. }
| Self::Add { .. }
| Self::AddOvf { .. }
| Self::Sub { .. }
| Self::SubOvf { .. }
| Self::Mul { .. }
| Self::MulOvf { .. }
| Self::Div { .. }
| Self::Rem { .. }
| Self::Neg { .. }
| Self::And { .. }
| Self::Or { .. }
| Self::Xor { .. }
| Self::Not { .. }
| Self::Shl { .. }
| Self::Shr { .. }
| Self::Ceq { .. }
| Self::Clt { .. }
| Self::Cgt { .. }
| Self::Rol { .. }
| Self::Ror { .. }
| Self::Rcl { .. }
| Self::Rcr { .. }
| Self::BSwap { .. }
| Self::BRev { .. }
| Self::BitScanForward { .. }
| Self::BitScanReverse { .. }
| Self::Popcount { .. }
| Self::Parity { .. }
| Self::Ckfinite { .. }
| Self::FpClassify { .. }
| Self::IntConv { .. }
| Self::IntToPtr { .. }
| Self::PtrToInt { .. }
| Self::IntToFloat { .. }
| Self::FloatToInt { .. }
| Self::FloatConv { .. }
| Self::Bitcast { .. }
| Self::Select { .. }
| Self::CastClass { .. }
| Self::IsInst { .. }
| Self::SizeOf { .. }
| Self::LoadToken { .. }
| Self::LoadArg { .. }
| Self::LoadLocal { .. }
| Self::LoadArgAddr { .. }
| Self::LoadLocalAddr { .. } => SsaOpClass::Scalar,
}
}
#[must_use]
pub const fn arith_signedness(&self) -> Option<Signedness> {
match self {
Self::AddOvf { unsigned, .. }
| Self::SubOvf { unsigned, .. }
| Self::MulOvf { unsigned, .. }
| Self::WideMul { unsigned, .. }
| Self::Div { unsigned, .. }
| Self::WideDiv { unsigned, .. }
| Self::Rem { unsigned, .. }
| Self::Shr { unsigned, .. }
| Self::Clt { unsigned, .. }
| Self::Cgt { unsigned, .. }
| Self::IntConv { unsigned, .. }
| Self::IntToFloat { unsigned, .. }
| Self::FloatToInt { unsigned, .. }
| Self::BranchCmp { unsigned, .. }
| Self::VectorCompare { unsigned, .. } => Some(Signedness::from_unsigned(*unsigned)),
_ => None,
}
}
#[must_use]
pub const fn compare_kind(&self) -> Option<CmpKind> {
match self {
Self::Ceq { .. } => Some(CmpKind::Eq),
Self::Clt { .. } => Some(CmpKind::Lt),
Self::Cgt { .. } => Some(CmpKind::Gt),
Self::BranchCmp { cmp, .. } => Some(*cmp),
Self::VectorCompare { kind, .. } => match kind {
VectorCompareKind::Eq => Some(CmpKind::Eq),
VectorCompareKind::Ne => Some(CmpKind::Ne),
VectorCompareKind::Lt => Some(CmpKind::Lt),
VectorCompareKind::Le => Some(CmpKind::Le),
VectorCompareKind::Gt => Some(CmpKind::Gt),
VectorCompareKind::Ge => Some(CmpKind::Ge),
VectorCompareKind::Unordered
| VectorCompareKind::Ordered
| VectorCompareKind::NotLt
| VectorCompareKind::NotLe
| VectorCompareKind::NotGe
| VectorCompareKind::NotGt
| VectorCompareKind::AlwaysTrue
| VectorCompareKind::AlwaysFalse => None,
},
_ => None,
}
}
#[must_use]
pub const fn memory_effect(&self) -> Option<MemoryEffect<'_, T>> {
match self {
Self::LoadIndirect {
addr, value_type, ..
}
| Self::AtomicLoad {
addr, value_type, ..
} => Some(MemoryEffect {
addr: *addr,
reads: true,
writes: false,
value_type: Some(value_type),
}),
Self::StoreIndirect {
addr, value_type, ..
}
| Self::AtomicStore {
addr, value_type, ..
}
| Self::AtomicStoreConditional {
addr, value_type, ..
} => Some(MemoryEffect {
addr: *addr,
reads: false,
writes: true,
value_type: Some(value_type),
}),
Self::CmpXchg { addr, .. }
| Self::AtomicRmw { addr, .. }
| Self::AtomicExchange { addr, .. }
| Self::AtomicLockRmw { addr, .. }
| Self::AtomicCmpXchg { addr, .. }
| Self::AtomicPairCmpXchg { addr, .. } => Some(MemoryEffect {
addr: *addr,
reads: true,
writes: true,
value_type: None,
}),
Self::AtomicPairLoad { addr, .. }
| Self::VectorLoad { addr, .. }
| Self::VectorMaskedLoad { addr, .. }
| Self::VectorBroadcastLoad { addr, .. }
| Self::VectorFaultingLoad { addr, .. }
| Self::VectorPackLoad { addr, .. } => Some(MemoryEffect {
addr: *addr,
reads: true,
writes: false,
value_type: None,
}),
Self::AtomicPairStoreConditional { addr, .. }
| Self::VectorStore { addr, .. }
| Self::VectorMaskedStore { addr, .. }
| Self::VectorPackStore { addr, .. } => Some(MemoryEffect {
addr: *addr,
reads: false,
writes: true,
value_type: None,
}),
Self::InitBlk { dest_addr, .. }
| Self::InitObj { dest_addr, .. }
| Self::StoreObj { dest_addr, .. } => Some(MemoryEffect {
addr: *dest_addr,
reads: false,
writes: true,
value_type: None,
}),
Self::LoadObj { src_addr, .. } => Some(MemoryEffect {
addr: *src_addr,
reads: true,
writes: false,
value_type: None,
}),
_ => None,
}
}
#[must_use]
pub const fn similarity_class(&self) -> SsaSimilarityClass {
match self {
Self::Nop
| Self::Phi { .. }
| Self::Copy { .. }
| Self::Pop { .. }
| Self::CallClobber { .. } => SsaSimilarityClass::Synthetic,
Self::Const { .. } | Self::SizeOf { .. } | Self::LoadToken { .. } => {
SsaSimilarityClass::Constant
}
Self::Add { flags: Some(_), .. }
| Self::AddOvf { flags: Some(_), .. }
| Self::Sub { flags: Some(_), .. }
| Self::SubOvf { flags: Some(_), .. }
| Self::Mul { flags: Some(_), .. }
| Self::MulOvf { flags: Some(_), .. }
| Self::Div { flags: Some(_), .. }
| Self::Rem { flags: Some(_), .. }
| Self::Neg { flags: Some(_), .. }
| Self::And { flags: Some(_), .. }
| Self::Or { flags: Some(_), .. }
| Self::Xor { flags: Some(_), .. }
| Self::Not { flags: Some(_), .. }
| Self::Shl { flags: Some(_), .. }
| Self::Shr { flags: Some(_), .. }
| Self::ReadFlags { .. }
| Self::BranchFlags { .. }
| Self::ComputeFlags { .. }
| Self::FlagAdjust(_) => SsaSimilarityClass::Flags,
Self::Add { .. }
| Self::AddOvf { .. }
| Self::Sub { .. }
| Self::SubOvf { .. }
| Self::Mul { .. }
| Self::MulOvf { .. }
| Self::Div { .. }
| Self::Rem { .. }
| Self::FloatCompareFlags { .. }
| Self::Neg { .. } => SsaSimilarityClass::Arithmetic,
Self::And { .. }
| Self::Or { .. }
| Self::Xor { .. }
| Self::Not { .. }
| Self::BSwap { .. }
| Self::BRev { .. }
| Self::BitScanForward { .. }
| Self::BitScanReverse { .. }
| Self::Popcount { .. }
| Self::Parity { .. } => SsaSimilarityClass::Bitwise,
Self::Shl { .. }
| Self::Shr { .. }
| Self::Rol { .. }
| Self::Ror { .. }
| Self::Rcl { .. }
| Self::Rcr { .. } => SsaSimilarityClass::ShiftRotate,
Self::Ceq { .. } | Self::Clt { .. } | Self::Cgt { .. } => SsaSimilarityClass::Compare,
Self::BoolAnd { .. }
| Self::BoolOr { .. }
| Self::BoolXor { .. }
| Self::BoolNot { .. } => SsaSimilarityClass::Boolean,
Self::Select { .. } => SsaSimilarityClass::Select,
Self::IntConv { .. }
| Self::IntToPtr { .. }
| Self::PtrToInt { .. }
| Self::IntToFloat { .. }
| Self::FloatToInt { .. }
| Self::FloatConv { .. }
| Self::Bitcast { .. }
| Self::Ckfinite { .. }
| Self::FpClassify { .. }
| Self::CastClass { .. }
| Self::IsInst { .. }
| Self::Box { .. }
| Self::Unbox { .. }
| Self::UnboxAny { .. } => SsaSimilarityClass::Conversion,
Self::LoadArg { .. }
| Self::LoadLocal { .. }
| Self::LoadArgAddr { .. }
| Self::LoadLocalAddr { .. }
| Self::LoadFunctionPtr { .. }
| Self::LoadVirtFunctionPtr { .. } => SsaSimilarityClass::TypeFlow,
Self::LoadField { .. }
| Self::LoadStaticField { .. }
| Self::LoadFieldAddr { .. }
| Self::LoadStaticFieldAddr { .. }
| Self::LoadElement { .. }
| Self::LoadElementAddr { .. }
| Self::PtrAdd { .. }
| Self::ArrayLength { .. }
| Self::LoadIndirect { .. }
| Self::LoadObj { .. }
| Self::VectorLoad { .. }
| Self::VectorMaskedLoad { .. }
| Self::VectorBroadcastLoad { .. }
| Self::VectorGather { .. }
| Self::VectorFaultingLoad { .. }
| Self::VectorSegmentLoad { .. }
| Self::VectorPackLoad { .. } => SsaSimilarityClass::MemoryRead,
Self::StoreField { .. }
| Self::StoreStaticField { .. }
| Self::StoreElement { .. }
| Self::StoreIndirect { .. }
| Self::StoreObj { .. }
| Self::InitObj { .. }
| Self::VectorStore { .. }
| Self::VectorMaskedStore { .. }
| Self::VectorScatter { .. }
| Self::VectorSegmentStore { .. }
| Self::VectorPackStore { .. } => SsaSimilarityClass::MemoryWrite,
Self::CopyBlk { .. } | Self::InitBlk { .. } | Self::CopyObj { .. } => {
SsaSimilarityClass::MemoryReadWrite
}
Self::NewObj { .. } | Self::NewArr { .. } | Self::LocalAlloc { .. } => {
SsaSimilarityClass::Allocation
}
Self::CmpXchg { .. }
| Self::AtomicRmw { .. }
| Self::AtomicLoad { .. }
| Self::AtomicStore { .. }
| Self::AtomicPairLoad { .. }
| Self::AtomicPairStoreConditional { .. }
| Self::AtomicExchange { .. }
| Self::AtomicLockRmw { .. }
| Self::AtomicStoreConditional { .. }
| Self::AtomicCmpXchg { .. }
| Self::AtomicPairCmpXchg { .. } => SsaSimilarityClass::Atomic,
Self::Fence { .. } => SsaSimilarityClass::Fence,
Self::Call { .. } | Self::CallVirt { .. } | Self::CallIndirect { .. } => {
SsaSimilarityClass::Call
}
Self::Jump { .. }
| Self::Branch { .. }
| Self::BranchCmp { .. }
| Self::IndirectBranch { .. }
| Self::Switch { .. }
| Self::Return { .. }
| Self::Throw { .. }
| Self::Rethrow
| Self::EndFinally
| Self::EndFilter { .. }
| Self::InterruptReturn
| Self::Unreachable
| Self::Leave { .. }
| Self::Break => SsaSimilarityClass::Control,
Self::VectorUnary { .. }
| Self::VectorBinary { .. }
| Self::VectorTernary { .. }
| Self::VectorPredicatedUnary { .. }
| Self::VectorPredicatedBinary { .. }
| Self::VectorPredicatedTernary { .. }
| Self::VectorCompare { .. }
| Self::VectorExtract { .. }
| Self::VectorInsert { .. }
| Self::VectorShuffle { .. }
| Self::VectorSplat { .. }
| Self::VectorCast { .. }
| Self::VectorReinterpret { .. }
| Self::VectorPack { .. }
| Self::VectorZeroUpper { .. }
| Self::VectorMaskUnary { .. }
| Self::VectorMaskBinary { .. }
| Self::VectorReduce { .. }
| Self::VectorCrypto(_)
| Self::TileOp(_)
| Self::VectorPermute(_)
| Self::VectorMultiplyAdd(_)
| Self::VectorPackNarrow(_)
| Self::VectorNarrowSaturate(_)
| Self::VectorPredicateWhile(_)
| Self::VectorPredicateBreak(_)
| Self::VectorComplexAdd(_)
| Self::VectorCountAdjust(_)
| Self::VectorExtendInLane(_)
| Self::VectorElementCount(_)
| Self::VectorSveAddressGen(_)
| Self::VectorStructLoadReplicate(_)
| Self::VectorSmeMisc(_)
| Self::VectorPredicateOp(_)
| Self::VectorSveCompute(_)
| Self::VectorReverseChunks(_)
| Self::VectorMatrixMulAcc(_)
| Self::VectorSmeOuterProduct(_)
| Self::VectorPredicateGen(_)
| Self::VectorFpHelper(_)
| Self::VectorSvePermute(_)
| Self::VectorTernaryLogic(_)
| Self::VectorDotProduct(_)
| Self::VectorMultiSad(_)
| Self::VectorIntDotProduct(_)
| Self::VectorStringCompare(_)
| Self::VectorBitfield(_)
| Self::VectorIntersect(_)
| Self::VectorShuffleBits(_)
| Self::VectorConditionalMove(_)
| Self::VectorHorizontalMinPos(_)
| Self::VectorComplexMul(_)
| Self::VectorClassify(_)
| Self::VectorHorizontalReduce(_)
| Self::VectorBitmask { .. } => SsaSimilarityClass::Vector,
Self::WideMul { .. } | Self::WideDiv { .. } => SsaSimilarityClass::WideArithmetic,
Self::NativeOpaque(_) | Self::NativeIntrinsic(_) => SsaSimilarityClass::NativeOpaque,
Self::SystemOp(_) => SsaSimilarityClass::Call,
Self::ComputeOp(data) => match data.kind {
ComputeKind::BitDeposit | ComputeKind::BitExtract | ComputeKind::PointerAuth(_) => {
SsaSimilarityClass::Bitwise
}
ComputeKind::Checksum | ComputeKind::MipsDspAccumulate => {
SsaSimilarityClass::Arithmetic
}
ComputeKind::Random { .. } => SsaSimilarityClass::Call,
},
Self::BcdAdjust(_) => SsaSimilarityClass::Arithmetic,
Self::BlockString(data) => match data.kind {
BlockStringKind::Load => SsaSimilarityClass::MemoryRead,
BlockStringKind::Compare | BlockStringKind::Scan => {
SsaSimilarityClass::MemoryReadWrite
}
},
Self::WideCompareExchange { .. } => SsaSimilarityClass::Atomic,
Self::FpTranscendental { .. } | Self::FpuControl { .. } => {
SsaSimilarityClass::NativeOpaque
}
Self::Constrained { .. }
| Self::Volatile
| Self::Unaligned { .. }
| Self::TailPrefix
| Self::Readonly => SsaSimilarityClass::Prefix,
}
}
#[must_use]
pub const fn opcode_name(&self) -> &'static str {
match self {
Self::Const { .. } => "const",
Self::Add { .. } => "add",
Self::AddOvf { .. } => "add.ovf",
Self::Sub { .. } => "sub",
Self::SubOvf { .. } => "sub.ovf",
Self::Mul { .. } => "mul",
Self::MulOvf { .. } => "mul.ovf",
Self::WideMul { .. } => "wide.mul",
Self::Div { .. } => "div",
Self::Rem { .. } => "rem",
Self::WideDiv { .. } => "wide.div",
Self::Neg { .. } => "neg",
Self::And { .. } => "and",
Self::Or { .. } => "or",
Self::Xor { .. } => "xor",
Self::Not { .. } => "not",
Self::Shl { .. } => "shl",
Self::Shr { .. } => "shr",
Self::Rol { .. } => "rol",
Self::Ror { .. } => "ror",
Self::Rcl { .. } => "rcl",
Self::Rcr { .. } => "rcr",
Self::BSwap { .. } => "bswap",
Self::BRev { .. } => "brev",
Self::BitScanForward { .. } => "bsf",
Self::BitScanReverse { .. } => "bsr",
Self::Popcount { .. } => "popcount",
Self::Parity { .. } => "parity",
Self::Ceq { .. } => "ceq",
Self::Clt { .. } => "clt",
Self::Cgt { .. } => "cgt",
Self::BoolAnd { .. } => "bool.and",
Self::BoolOr { .. } => "bool.or",
Self::BoolXor { .. } => "bool.xor",
Self::BoolNot { .. } => "bool.not",
Self::IntConv { .. } => "conv",
Self::IntToPtr { .. } => "inttoptr",
Self::PtrToInt { .. } => "ptrtoint",
Self::IntToFloat { .. } => "inttofloat",
Self::FloatToInt { .. } => "floattoint",
Self::FloatConv { .. } => "fconv",
Self::Bitcast { .. } => "bitcast",
Self::Select { .. } => "select",
Self::ReadFlags { .. } => "readflags",
Self::ComputeFlags { .. } => "flags.compute",
Self::CallClobber { .. } => "call.clobber",
Self::VectorUnary { .. } => "vector.unary",
Self::VectorBinary { .. } => "vector.binary",
Self::VectorTernary { .. } => "vector.ternary",
Self::VectorPredicatedUnary { .. } => "vector.predicated.unary",
Self::VectorPredicatedBinary { .. } => "vector.predicated.binary",
Self::VectorPredicatedTernary { .. } => "vector.predicated.ternary",
Self::VectorCompare { .. } => "vector.compare",
Self::VectorLoad { .. } => "vector.load",
Self::VectorStore { .. } => "vector.store",
Self::VectorMaskedLoad { .. } => "vector.masked.load",
Self::VectorMaskedStore { .. } => "vector.masked.store",
Self::VectorBroadcastLoad { .. } => "vector.broadcast.load",
Self::VectorGather { .. } => "vector.gather",
Self::VectorFaultingLoad { .. } => "vector.faulting.load",
Self::VectorSegmentLoad { .. } => "vector.segment.load",
Self::VectorScatter { .. } => "vector.scatter",
Self::VectorSegmentStore { .. } => "vector.segment.store",
Self::VectorPackLoad { .. } => "vector.pack.load",
Self::VectorPackStore { .. } => "vector.pack.store",
Self::VectorExtract { .. } => "vector.extract",
Self::VectorInsert { .. } => "vector.insert",
Self::VectorSplat { .. } => "vector.splat",
Self::VectorShuffle { .. } => "vector.shuffle",
Self::VectorCast { .. } => "vector.cast",
Self::VectorReinterpret { .. } => "vector.reinterpret",
Self::VectorPack { .. } => "vector.pack",
Self::VectorZeroUpper { .. } => "vector.zero.upper",
Self::VectorMaskUnary { .. } => "vector.mask.unary",
Self::VectorMaskBinary { .. } => "vector.mask.binary",
Self::VectorReduce { .. } => "vector.reduce",
Self::VectorBitmask { .. } => "vector.bitmask",
Self::Jump { .. } => "jump",
Self::Branch { .. } => "branch",
Self::BranchCmp { .. } => "branch.cmp",
Self::BranchFlags { .. } => "branch.flags",
Self::IndirectBranch { .. } => "branch.indirect",
Self::Switch { .. } => "switch",
Self::Return { .. } => "return",
Self::LoadField { .. } => "load.field",
Self::StoreField { .. } => "store.field",
Self::LoadStaticField { .. } => "load.static.field",
Self::StoreStaticField { .. } => "store.static.field",
Self::LoadFieldAddr { .. } => "load.field.addr",
Self::LoadStaticFieldAddr { .. } => "load.static.field.addr",
Self::LoadElement { .. } => "load.element",
Self::StoreElement { .. } => "store.element",
Self::LoadElementAddr { .. } => "load.element.addr",
Self::PtrAdd { .. } => "ptradd",
Self::ArrayLength { .. } => "array.length",
Self::LoadIndirect { .. } => "load.indirect",
Self::StoreIndirect { .. } => "store.indirect",
Self::NewObj { .. } => "new.obj",
Self::NewArr { .. } => "new.arr",
Self::CastClass { .. } => "cast.class",
Self::IsInst { .. } => "is.inst",
Self::Box { .. } => "box",
Self::Unbox { .. } => "unbox",
Self::UnboxAny { .. } => "unbox.any",
Self::SizeOf { .. } => "sizeof",
Self::LoadToken { .. } => "load.token",
Self::Call { .. } => "call",
Self::CallVirt { .. } => "call.virt",
Self::CallIndirect { .. } => "call.indirect",
Self::LoadFunctionPtr { .. } => "load.function.ptr",
Self::LoadVirtFunctionPtr { .. } => "load.virt.function.ptr",
Self::LoadArg { .. } => "load.arg",
Self::LoadLocal { .. } => "load.local",
Self::LoadArgAddr { .. } => "load.arg.addr",
Self::LoadLocalAddr { .. } => "load.local.addr",
Self::Copy { .. } => "copy",
Self::Pop { .. } => "pop",
Self::Throw { .. } => "throw",
Self::Rethrow => "rethrow",
Self::EndFinally => "end.finally",
Self::EndFilter { .. } => "end.filter",
Self::InterruptReturn => "interrupt.return",
Self::Unreachable => "unreachable",
Self::Leave { .. } => "leave",
Self::InitBlk { .. } => "init.blk",
Self::CopyBlk { .. } => "copy.blk",
Self::Fence { .. } => "fence",
Self::FloatCompareFlags { .. } => "float.compare.flags",
Self::NativeOpaque(_) => "native.opaque",
Self::NativeIntrinsic(_) => "native.intrinsic",
Self::SystemOp(data) => data.kind.kind_str(),
Self::ComputeOp(data) => data.kind.kind_str(),
Self::BcdAdjust(data) => data.kind.kind_str(),
Self::VectorCrypto(data) => data.kind.kind_str(),
Self::TileOp(data) => data.kind.kind_str(),
Self::VectorPermute(_) => "vector.permute",
Self::VectorMultiplyAdd(data) => data.kind.kind_str(),
Self::VectorPackNarrow(data) => {
if data.unsigned {
"vector.pack.narrow.u"
} else {
"vector.pack.narrow.s"
}
}
Self::VectorNarrowSaturate(data) => {
if data.unsigned_dst {
"vector.narrow.saturate.u"
} else {
"vector.narrow.saturate.s"
}
}
Self::VectorPredicateWhile(_) => "vector.while",
Self::VectorPredicateBreak(_) => "vector.break",
Self::VectorComplexAdd(_) => "vector.cadd",
Self::VectorCountAdjust(_) => "vector.countadj",
Self::VectorExtendInLane(_) => "vector.xtl",
Self::VectorElementCount(_) => "vector.cnt",
Self::VectorSveAddressGen(_) => "vector.adr",
Self::FlagAdjust(_) => "flags.adjust",
Self::VectorStructLoadReplicate(_) => "vector.ldNr",
Self::VectorSmeMisc(_) => "vector.sme.misc",
Self::VectorPredicateOp(_) => "vector.predop",
Self::VectorSveCompute(_) => "vector.sve.compute",
Self::VectorReverseChunks(_) => "vector.revchunks",
Self::VectorMatrixMulAcc(_) => "vector.mmla",
Self::VectorSmeOuterProduct(_) => "vector.sme.mopa",
Self::VectorPredicateGen(_) => "vector.pgen",
Self::VectorFpHelper(_) => "vector.fphelper",
Self::VectorSvePermute(_) => "vector.sve.perm",
Self::VectorTernaryLogic(_) => "vector.ternlog",
Self::VectorDotProduct(_) => "vector.dotproduct",
Self::VectorMultiSad(_) => "vector.mpsadbw",
Self::VectorIntDotProduct(_) => "vector.intdot",
Self::VectorStringCompare(_) => "vector.pcmpstr",
Self::VectorBitfield(_) => "vector.bitfield",
Self::VectorIntersect(_) => "vector.p2intersect",
Self::VectorShuffleBits(_) => "vector.shufbitqmb",
Self::VectorConditionalMove(_) => "vector.condmove",
Self::VectorHorizontalMinPos(_) => "vector.phminposuw",
Self::VectorComplexMul(data) => data.kind.kind_str(),
Self::VectorClassify(_) => "vector.fpclass",
Self::VectorHorizontalReduce(_) => "vector.hreduce",
Self::BlockString(data) => data.kind.kind_str(),
Self::WideCompareExchange(data) => {
if data.wide {
"atomic.cmpxchg16b"
} else {
"atomic.cmpxchg8b"
}
}
Self::FpTranscendental { .. } => "fp.transcendental",
Self::FpuControl { .. } => "fpu.control",
Self::CmpXchg { .. } => "cmpxchg",
Self::AtomicRmw { .. } => "atomic.rmw",
Self::AtomicLoad { .. } => "atomic.load",
Self::AtomicStore { .. } => "atomic.store",
Self::AtomicPairLoad { .. } => "atomic.pair.load",
Self::AtomicPairStoreConditional { .. } => "atomic.pair.store.conditional",
Self::AtomicExchange { .. } => "atomic.exchange",
Self::AtomicLockRmw { .. } => "atomic.lock.rmw",
Self::AtomicStoreConditional { .. } => "atomic.store.conditional",
Self::AtomicCmpXchg { .. } => "atomic.cmpxchg",
Self::AtomicPairCmpXchg { .. } => "atomic.pair.cmpxchg",
Self::InitObj { .. } => "init.obj",
Self::CopyObj { .. } => "copy.obj",
Self::LoadObj { .. } => "load.obj",
Self::StoreObj { .. } => "store.obj",
Self::Nop => "nop",
Self::Break => "break",
Self::Ckfinite { .. } => "ckfinite",
Self::FpClassify { .. } => "fpclassify",
Self::LocalAlloc { .. } => "local.alloc",
Self::Constrained { .. } => "constrained",
Self::Volatile => "volatile",
Self::Unaligned { .. } => "unaligned",
Self::TailPrefix => "tail",
Self::Readonly => "readonly",
Self::Phi { .. } => "phi",
}
}
#[must_use]
pub fn feature_token(&self) -> SsaFeatureToken {
SsaFeatureToken {
opcode: self.opcode_name(),
op_class: self.class(),
similarity_class: self.similarity_class(),
effect_kind: self.effects().kind,
def_count: self.defs().count(),
use_count: self.use_count(),
may_throw: self.may_throw(),
}
}
#[must_use]
pub const fn is_pure(&self) -> bool {
self.effects().is_pure()
}
pub fn replace_uses(&mut self, old_var: SsaVarId, new_var: SsaVarId) -> usize {
let mut count: usize = 0;
self.visit_operands_mut(|role, var| {
if matches!(role, OperandRole::Use) && *var == old_var {
*var = new_var;
count = count.saturating_add(1);
}
});
count
}
pub fn replace_uses_with<F>(&mut self, mut lookup: F) -> usize
where
F: FnMut(SsaVarId) -> Option<SsaVarId>,
{
let mut count: usize = 0;
self.visit_operands_mut(|role, var| {
if matches!(role, OperandRole::Use) {
if let Some(new_var) = lookup(*var) {
*var = new_var;
count = count.saturating_add(1);
}
}
});
count
}
pub fn remap_branch_targets<F>(&mut self, remap: F)
where
F: Fn(usize) -> Option<usize>,
{
match self {
Self::Jump { target } | Self::Leave { target } => {
if let Some(new_target) = remap(*target) {
*target = new_target;
}
}
Self::Branch {
true_target,
false_target,
..
}
| Self::BranchCmp {
true_target,
false_target,
..
}
| Self::BranchFlags {
true_target,
false_target,
..
} => {
if let Some(new_target) = remap(*true_target) {
*true_target = new_target;
}
if let Some(new_target) = remap(*false_target) {
*false_target = new_target;
}
}
Self::Switch {
targets, default, ..
} => {
for target in targets.iter_mut() {
if let Some(new_target) = remap(*target) {
*target = new_target;
}
}
if let Some(new_target) = remap(*default) {
*default = new_target;
}
}
Self::IndirectBranch {
resolved_targets, ..
} => {
for target in resolved_targets.iter_mut() {
if let Some(new_target) = remap(*target) {
*target = new_target;
}
}
}
_ => {}
}
}
#[must_use]
pub fn successors(&self) -> Vec<usize> {
match self {
Self::Jump { target } | Self::Leave { target } => vec![*target],
Self::Branch {
true_target,
false_target,
..
}
| Self::BranchCmp {
true_target,
false_target,
..
}
| Self::BranchFlags {
true_target,
false_target,
..
} => vec![*true_target, *false_target],
Self::Switch {
targets, default, ..
} => {
let mut succs = targets.clone();
succs.push(*default);
succs
}
Self::IndirectBranch {
resolved_targets, ..
} => resolved_targets.clone(),
_ => vec![],
}
}
pub fn for_each_successor<F>(&self, mut f: F)
where
F: FnMut(usize),
{
match self {
Self::Jump { target } | Self::Leave { target } => f(*target),
Self::Branch {
true_target,
false_target,
..
}
| Self::BranchCmp {
true_target,
false_target,
..
}
| Self::BranchFlags {
true_target,
false_target,
..
} => {
f(*true_target);
f(*false_target);
}
Self::Switch {
targets, default, ..
} => {
for target in targets {
f(*target);
}
f(*default);
}
Self::IndirectBranch {
resolved_targets, ..
} => {
for target in resolved_targets {
f(*target);
}
}
_ => {}
}
}
#[must_use]
pub fn has_successor(&self, block: usize) -> bool {
match self {
Self::Jump { target } | Self::Leave { target } => *target == block,
Self::Branch {
true_target,
false_target,
..
}
| Self::BranchCmp {
true_target,
false_target,
..
}
| Self::BranchFlags {
true_target,
false_target,
..
} => *true_target == block || *false_target == block,
Self::Switch {
targets, default, ..
} => *default == block || targets.contains(&block),
Self::IndirectBranch {
resolved_targets, ..
} => resolved_targets.contains(&block),
_ => false,
}
}
pub fn redirect_target(&mut self, old_target: usize, new_target: usize) -> bool {
if old_target == new_target {
return false;
}
match self {
Self::Jump { target } | Self::Leave { target } if *target == old_target => {
*target = new_target;
true
}
Self::Branch {
true_target,
false_target,
..
}
| Self::BranchCmp {
true_target,
false_target,
..
}
| Self::BranchFlags {
true_target,
false_target,
..
} => {
let mut changed = false;
if *true_target == old_target {
*true_target = new_target;
changed = true;
}
if *false_target == old_target {
*false_target = new_target;
changed = true;
}
changed
}
Self::Switch {
targets, default, ..
} => {
let mut changed = false;
if *default == old_target {
*default = new_target;
changed = true;
}
for target in targets.iter_mut() {
if *target == old_target {
*target = new_target;
changed = true;
}
}
changed
}
Self::IndirectBranch {
resolved_targets, ..
} => {
let mut changed = false;
for target in resolved_targets.iter_mut() {
if *target == old_target {
*target = new_target;
changed = true;
}
}
changed
}
_ => false,
}
}
#[must_use]
pub fn remap_variables<F>(&self, remap: F) -> Self
where
F: Fn(SsaVarId) -> Option<SsaVarId>,
{
let mut out = self.clone();
out.visit_operands_mut(|_role, var| {
if let Some(new) = remap(*var) {
*var = new;
}
});
out
}
#[must_use]
pub fn as_binary_op(&self) -> Option<BinaryOpInfo> {
match *self {
Self::Add {
dest,
left,
right,
flags,
} => Some(BinaryOpInfo {
kind: BinaryOpKind::Add,
dest,
left,
right,
unsigned: false,
flags,
}),
Self::AddOvf {
dest,
left,
right,
unsigned,
flags,
} => Some(BinaryOpInfo {
kind: BinaryOpKind::AddOvf,
dest,
left,
right,
unsigned,
flags,
}),
Self::Sub {
dest,
left,
right,
flags,
} => Some(BinaryOpInfo {
kind: BinaryOpKind::Sub,
dest,
left,
right,
unsigned: false,
flags,
}),
Self::SubOvf {
dest,
left,
right,
unsigned,
flags,
} => Some(BinaryOpInfo {
kind: BinaryOpKind::SubOvf,
dest,
left,
right,
unsigned,
flags,
}),
Self::Mul {
dest,
left,
right,
flags,
} => Some(BinaryOpInfo {
kind: BinaryOpKind::Mul,
dest,
left,
right,
unsigned: false,
flags,
}),
Self::MulOvf {
dest,
left,
right,
unsigned,
flags,
} => Some(BinaryOpInfo {
kind: BinaryOpKind::MulOvf,
dest,
left,
right,
unsigned,
flags,
}),
Self::Div {
dest,
left,
right,
unsigned,
flags,
} => Some(BinaryOpInfo {
kind: BinaryOpKind::Div,
dest,
left,
right,
unsigned,
flags,
}),
Self::Rem {
dest,
left,
right,
unsigned,
flags,
} => Some(BinaryOpInfo {
kind: BinaryOpKind::Rem,
dest,
left,
right,
unsigned,
flags,
}),
Self::And {
dest,
left,
right,
flags,
} => Some(BinaryOpInfo {
kind: BinaryOpKind::And,
dest,
left,
right,
unsigned: false,
flags,
}),
Self::Or {
dest,
left,
right,
flags,
} => Some(BinaryOpInfo {
kind: BinaryOpKind::Or,
dest,
left,
right,
unsigned: false,
flags,
}),
Self::Xor {
dest,
left,
right,
flags,
} => Some(BinaryOpInfo {
kind: BinaryOpKind::Xor,
dest,
left,
right,
unsigned: false,
flags,
}),
Self::Shl {
dest,
value,
amount,
flags,
} => Some(BinaryOpInfo {
kind: BinaryOpKind::Shl,
dest,
left: value,
right: amount,
unsigned: false,
flags,
}),
Self::Shr {
dest,
value,
amount,
unsigned,
flags,
} => Some(BinaryOpInfo {
kind: BinaryOpKind::Shr,
dest,
left: value,
right: amount,
unsigned,
flags,
}),
Self::Ceq { dest, left, right } => Some(BinaryOpInfo {
kind: BinaryOpKind::Ceq,
dest,
left,
right,
unsigned: false,
flags: None,
}),
Self::Clt {
dest,
left,
right,
unsigned,
} => Some(BinaryOpInfo {
kind: BinaryOpKind::Clt,
dest,
left,
right,
unsigned,
flags: None,
}),
Self::Cgt {
dest,
left,
right,
unsigned,
} => Some(BinaryOpInfo {
kind: BinaryOpKind::Cgt,
dest,
left,
right,
unsigned,
flags: None,
}),
Self::Rol {
dest,
value,
amount,
} => Some(BinaryOpInfo {
kind: BinaryOpKind::Rol,
dest,
left: value,
right: amount,
unsigned: false,
flags: None,
}),
Self::Ror {
dest,
value,
amount,
} => Some(BinaryOpInfo {
kind: BinaryOpKind::Ror,
dest,
left: value,
right: amount,
unsigned: false,
flags: None,
}),
Self::Rcl {
dest,
value,
amount,
} => Some(BinaryOpInfo {
kind: BinaryOpKind::Rcl,
dest,
left: value,
right: amount,
unsigned: false,
flags: None,
}),
Self::Rcr {
dest,
value,
amount,
} => Some(BinaryOpInfo {
kind: BinaryOpKind::Rcr,
dest,
left: value,
right: amount,
unsigned: false,
flags: None,
}),
_ => None,
}
}
#[must_use]
pub fn as_unary_op(&self) -> Option<UnaryOpInfo> {
match *self {
Self::Neg { dest, operand, .. } => Some(UnaryOpInfo {
kind: UnaryOpKind::Neg,
dest,
operand,
}),
Self::Not { dest, operand, .. } => Some(UnaryOpInfo {
kind: UnaryOpKind::Not,
dest,
operand,
}),
Self::Ckfinite { dest, operand } => Some(UnaryOpInfo {
kind: UnaryOpKind::Ckfinite,
dest,
operand,
}),
Self::BSwap { dest, src } => Some(UnaryOpInfo {
kind: UnaryOpKind::BSwap,
dest,
operand: src,
}),
Self::BRev { dest, src } => Some(UnaryOpInfo {
kind: UnaryOpKind::BRev,
dest,
operand: src,
}),
Self::BitScanForward { dest, src } => Some(UnaryOpInfo {
kind: UnaryOpKind::BitScanForward,
dest,
operand: src,
}),
Self::BitScanReverse { dest, src } => Some(UnaryOpInfo {
kind: UnaryOpKind::BitScanReverse,
dest,
operand: src,
}),
Self::Popcount { dest, src } => Some(UnaryOpInfo {
kind: UnaryOpKind::Popcount,
dest,
operand: src,
}),
Self::Parity { dest, src } => Some(UnaryOpInfo {
kind: UnaryOpKind::Parity,
dest,
operand: src,
}),
_ => None,
}
}
#[must_use]
pub fn stack_effect(&self) -> (u32, u32) {
match self {
Self::PtrAdd { index, .. } => (1u32.saturating_add(u32::from(index.is_some())), 1),
Self::ComputeFlags { inputs, .. } => {
#[allow(clippy::cast_possible_truncation)]
let pops = inputs.len() as u32;
(pops, 1)
}
Self::CallClobber { outputs } => {
#[allow(clippy::cast_possible_truncation)]
let pushes = outputs.len() as u32;
(0, pushes)
}
Self::Add { .. }
| Self::Sub { .. }
| Self::Mul { .. }
| Self::Div { .. }
| Self::Rem { .. }
| Self::AddOvf { .. }
| Self::SubOvf { .. }
| Self::MulOvf { .. }
| Self::FloatCompareFlags { .. }
| Self::And { .. }
| Self::Or { .. }
| Self::Xor { .. }
| Self::Shl { .. }
| Self::Shr { .. }
| Self::Rol { .. }
| Self::Ror { .. }
| Self::Rcl { .. }
| Self::Rcr { .. }
| Self::Ceq { .. }
| Self::Clt { .. }
| Self::Cgt { .. }
| Self::BoolAnd { .. }
| Self::BoolOr { .. }
| Self::BoolXor { .. }
| Self::LoadElement { .. }
| Self::LoadElementAddr { .. }
| Self::VectorBinary { .. }
| Self::VectorCompare { .. }
| Self::VectorMaskBinary { .. }
| Self::VectorPredicatedUnary {
passthrough: None, ..
}
| Self::VectorPack {
passthrough: None, ..
}
| Self::VectorPackLoad {
passthrough: None, ..
} => (2, 1),
Self::Select { .. }
| Self::CmpXchg { .. }
| Self::VectorTernary { .. }
| Self::VectorPredicatedUnary {
passthrough: Some(_),
..
}
| Self::VectorPredicatedBinary {
passthrough: None, ..
}
| Self::VectorPack {
passthrough: Some(_),
..
}
| Self::VectorPackLoad {
passthrough: Some(_),
..
} => (3, 1),
Self::VectorPredicatedBinary {
passthrough: Some(_),
..
}
| Self::VectorPredicatedTernary {
passthrough: None, ..
} => (4, 1),
Self::VectorPredicatedTernary {
passthrough: Some(_),
..
} => (5, 1),
Self::AtomicCmpXchg { success, .. } => {
(3, 1_u32.saturating_add(u32::from(success.is_some())))
}
Self::AtomicPairCmpXchg { .. } => (5, 2),
Self::WideMul { .. } => (2, 2),
Self::WideDiv { .. } => (3, 2),
Self::AtomicRmw { .. }
| Self::AtomicExchange { .. }
| Self::AtomicLockRmw { .. }
| Self::AtomicStoreConditional { .. } => (2, 1),
Self::AtomicPairStoreConditional { .. } => (3, 1),
Self::Return { value } => {
if value.is_some() {
(1, 0) } else {
(0, 0) }
}
Self::Jump { .. }
| Self::Rethrow
| Self::Leave { .. }
| Self::EndFinally
| Self::Copy { .. }
| Self::Nop
| Self::Break
| Self::Constrained { .. }
| Self::Volatile
| Self::Unaligned { .. }
| Self::TailPrefix
| Self::Readonly
| Self::Phi { .. }
| Self::Fence { .. }
| Self::InterruptReturn
| Self::VectorZeroUpper { .. }
| Self::Unreachable => (0, 0),
Self::Branch { .. }
| Self::IndirectBranch { .. }
| Self::Switch { .. }
| Self::Throw { .. }
| Self::EndFilter { .. }
| Self::Pop { .. }
| Self::StoreStaticField { .. }
| Self::InitObj { .. }
| Self::BranchFlags { .. } => (1, 0),
Self::BranchCmp { .. }
| Self::StoreField { .. }
| Self::StoreIndirect { .. }
| Self::AtomicStore { .. }
| Self::StoreObj { .. }
| Self::CopyObj { .. }
| Self::VectorStore { .. } => (2, 0),
Self::VectorMaskedStore { .. } | Self::VectorPackStore { .. } => (3, 0),
Self::VectorScatter { .. } => (4, 0),
Self::VectorSegmentStore { values, mask, .. } => {
#[allow(clippy::cast_possible_truncation)]
let pops = values.len() as u32;
(
pops.saturating_add(1_u32.saturating_add(u32::from(mask.is_some()))),
0,
)
}
Self::StoreElement { .. } | Self::InitBlk { .. } | Self::CopyBlk { .. } => (3, 0),
Self::LoadStaticField { .. }
| Self::LoadStaticFieldAddr { .. }
| Self::SizeOf { .. }
| Self::LoadToken { .. }
| Self::LoadArg { .. }
| Self::LoadLocal { .. }
| Self::LoadArgAddr { .. }
| Self::LoadLocalAddr { .. }
| Self::LoadFunctionPtr { .. }
| Self::Const { .. } => (0, 1),
Self::Neg { .. }
| Self::Not { .. }
| Self::IntConv { .. }
| Self::IntToPtr { .. }
| Self::PtrToInt { .. }
| Self::IntToFloat { .. }
| Self::FloatToInt { .. }
| Self::FloatConv { .. }
| Self::Bitcast { .. }
| Self::Ckfinite { .. }
| Self::FpClassify { .. }
| Self::BSwap { .. }
| Self::BRev { .. }
| Self::BitScanForward { .. }
| Self::BitScanReverse { .. }
| Self::Popcount { .. }
| Self::Parity { .. }
| Self::LoadField { .. }
| Self::LoadFieldAddr { .. }
| Self::ArrayLength { .. }
| Self::NewArr { .. }
| Self::LoadIndirect { .. }
| Self::AtomicLoad { .. }
| Self::LoadObj { .. }
| Self::Box { .. }
| Self::Unbox { .. }
| Self::UnboxAny { .. }
| Self::CastClass { .. }
| Self::IsInst { .. }
| Self::LoadVirtFunctionPtr { .. }
| Self::LocalAlloc { .. }
| Self::ReadFlags { .. }
| Self::BoolNot { .. }
| Self::VectorUnary { .. }
| Self::VectorLoad { .. }
| Self::VectorBroadcastLoad { .. }
| Self::VectorExtract { .. }
| Self::VectorSplat { .. }
| Self::VectorShuffle { right: None, .. }
| Self::VectorCast { .. }
| Self::VectorReinterpret { .. }
| Self::VectorMaskUnary { .. }
| Self::VectorReduce { .. }
| Self::VectorBitmask { .. } => (1, 1),
Self::AtomicPairLoad { .. } => (1, 2),
Self::VectorInsert { .. } | Self::VectorShuffle { right: Some(_), .. } => (2, 1),
Self::VectorMaskedLoad {
passthrough: None, ..
} => (2, 1),
Self::VectorMaskedLoad {
passthrough: Some(_),
..
} => (3, 1),
Self::VectorGather {
passthrough: None, ..
} => (3, 1),
Self::VectorGather {
passthrough: Some(_),
..
} => (4, 1),
Self::VectorFaultingLoad {
mask, passthrough, ..
} => (
1_u32
.saturating_add(u32::from(mask.is_some()))
.saturating_add(u32::from(passthrough.is_some())),
1,
),
Self::VectorSegmentLoad { dests, mask, .. } => {
#[allow(clippy::cast_possible_truncation)]
let pushes = dests.len() as u32;
(1_u32.saturating_add(u32::from(mask.is_some())), pushes)
}
Self::Call { dest, args, .. } | Self::CallVirt { dest, args, .. } => {
#[allow(clippy::cast_possible_truncation)]
let pops = args.len() as u32;
let pushes = u32::from(dest.is_some());
(pops, pushes)
}
Self::CallIndirect { dest, args, .. } => {
#[allow(clippy::cast_possible_truncation)]
let pops = (args.len() as u32).saturating_add(1);
let pushes = u32::from(dest.is_some());
(pops, pushes)
}
Self::NewObj { args, .. } => {
#[allow(clippy::cast_possible_truncation)]
let pops = args.len() as u32;
(pops, 1)
}
Self::NativeOpaque(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::NativeIntrinsic(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::SystemOp(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::ComputeOp(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::BcdAdjust(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorCrypto(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::TileOp(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorPermute(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorMultiplyAdd(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorPackNarrow(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorNarrowSaturate(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorPredicateWhile(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorPredicateBreak(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorComplexAdd(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorCountAdjust(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorExtendInLane(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorElementCount(data) => {
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(0, pushes)
}
Self::VectorSveAddressGen(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::FlagAdjust(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorStructLoadReplicate(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorSmeMisc(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorPredicateOp(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorSveCompute(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorReverseChunks(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorMatrixMulAcc(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorSmeOuterProduct(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorPredicateGen(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorFpHelper(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorSvePermute(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorTernaryLogic(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorDotProduct(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorMultiSad(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorIntDotProduct(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorStringCompare(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorBitfield(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorIntersect(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorShuffleBits(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorConditionalMove(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorHorizontalMinPos(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorComplexMul(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorClassify(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::VectorHorizontalReduce(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::BlockString(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::WideCompareExchange(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::FpTranscendental(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
Self::FpuControl(data) => {
#[allow(clippy::cast_possible_truncation)]
let pops = data.inputs.len() as u32;
#[allow(clippy::cast_possible_truncation)]
let pushes = data.outputs.len() as u32;
(pops, pushes)
}
}
}
}
impl<T: Target> SsaOp<T> {
#[must_use]
pub fn infer_result_type(&self) -> Option<T::Type> {
match self {
Self::Const { value, .. } => T::result_type_for_const(value),
Self::IntConv { target, .. }
| Self::IntToPtr { target, .. }
| Self::PtrToInt { target, .. }
| Self::IntToFloat { target, .. }
| Self::FloatToInt { target, .. }
| Self::FloatConv { target, .. }
| Self::Bitcast { target, .. } => Some(target.clone()),
Self::Ceq { .. }
| Self::Clt { .. }
| Self::Cgt { .. }
| Self::BoolAnd { .. }
| Self::BoolOr { .. }
| Self::BoolXor { .. }
| Self::BoolNot { .. } => T::comparison_result_type(),
Self::Add { .. }
| Self::Sub { .. }
| Self::Mul { .. }
| Self::Div { .. }
| Self::Rem { .. }
| Self::And { .. }
| Self::Or { .. }
| Self::Xor { .. }
| Self::Shl { .. }
| Self::Shr { .. }
| Self::Rol { .. }
| Self::Ror { .. }
| Self::Rcl { .. }
| Self::Rcr { .. }
| Self::BSwap { .. }
| Self::BRev { .. }
| Self::Neg { .. }
| Self::Not { .. }
| Self::AddOvf { .. }
| Self::SubOvf { .. }
| Self::MulOvf { .. }
| Self::SizeOf { .. } => T::arithmetic_result_type(),
Self::UnboxAny { value_type, .. } | Self::LoadObj { value_type, .. } => {
T::value_type_from_ref(value_type)
}
Self::LoadField { .. }
| Self::LoadStaticField { .. }
| Self::Call { dest: Some(_), .. }
| Self::CallVirt { dest: Some(_), .. }
| Self::CallIndirect { dest: Some(_), .. }
| Self::LoadArg { .. }
| Self::LoadLocal { .. } => None,
Self::Box { .. }
| Self::NewObj { .. }
| Self::NewArr { .. }
| Self::CastClass { .. }
| Self::IsInst { .. } => T::object_result_type(),
Self::ArrayLength { .. }
| Self::LocalAlloc { .. }
| Self::BitScanForward { .. }
| Self::BitScanReverse { .. }
| Self::Popcount { .. } => T::native_int_result_type(),
Self::Ckfinite { .. } => T::ckfinite_result_type(),
Self::FpClassify { .. } => T::native_int_result_type(),
Self::Parity { .. } | Self::ReadFlags { .. } | Self::ComputeFlags { .. } => {
T::comparison_result_type()
}
Self::VectorLoad { vector_type, .. }
| Self::VectorMaskedLoad { vector_type, .. }
| Self::VectorBroadcastLoad { vector_type, .. }
| Self::VectorGather { vector_type, .. }
| Self::VectorFaultingLoad { vector_type, .. }
| Self::VectorSegmentLoad { vector_type, .. }
| Self::VectorSplat { vector_type, .. }
| Self::VectorPack { vector_type, .. }
| Self::VectorPackLoad { vector_type, .. }
| Self::VectorCast {
target_type: vector_type,
..
}
| Self::VectorReinterpret {
target_type: vector_type,
..
} => Some(vector_type.clone()),
Self::LoadFunctionPtr { .. } | Self::LoadVirtFunctionPtr { .. } => {
T::function_ptr_result_type()
}
Self::LoadElement { elem_type, .. } => Some(elem_type.clone()),
Self::LoadIndirect { value_type, .. } => Some(value_type.clone()),
Self::LoadToken { .. } => None,
Self::Unbox { value_type, .. } => T::byref_value_type_from_ref(value_type),
Self::LoadElementAddr { elem_type, .. } => T::byref_class_type_from_ref(elem_type),
Self::PtrAdd { result_type, .. } => Some(result_type.clone()),
Self::LoadFieldAddr { .. }
| Self::LoadStaticFieldAddr { .. }
| Self::LoadArgAddr { .. }
| Self::LoadLocalAddr { .. } => None,
_ => None,
}
}
}
impl<T: Target> fmt::Display for SsaOp<T>
where
T::TypeRef: fmt::Display,
T::MethodRef: fmt::Display,
T::FieldRef: fmt::Display,
T::SigRef: fmt::Display,
T::Type: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Const { dest, value } => write!(f, "{dest} = {value}"),
Self::Add {
dest,
left,
right,
flags,
} => {
if let Some(flags) = flags {
write!(f, "{dest} = add {left}, {right} flags={flags}")
} else {
write!(f, "{dest} = add {left}, {right}")
}
}
Self::AddOvf {
dest,
left,
right,
unsigned,
flags,
} => {
let suffix = if *unsigned { ".un" } else { "" };
if let Some(flags) = flags {
write!(f, "{dest} = add.ovf{suffix} {left}, {right} flags={flags}")
} else {
write!(f, "{dest} = add.ovf{suffix} {left}, {right}")
}
}
Self::Sub {
dest,
left,
right,
flags,
} => {
if let Some(flags) = flags {
write!(f, "{dest} = sub {left}, {right} flags={flags}")
} else {
write!(f, "{dest} = sub {left}, {right}")
}
}
Self::SubOvf {
dest,
left,
right,
unsigned,
flags,
} => {
let suffix = if *unsigned { ".un" } else { "" };
if let Some(flags) = flags {
write!(f, "{dest} = sub.ovf{suffix} {left}, {right} flags={flags}")
} else {
write!(f, "{dest} = sub.ovf{suffix} {left}, {right}")
}
}
Self::Mul {
dest,
left,
right,
flags,
} => {
if let Some(flags) = flags {
write!(f, "{dest} = mul {left}, {right} flags={flags}")
} else {
write!(f, "{dest} = mul {left}, {right}")
}
}
Self::MulOvf {
dest,
left,
right,
unsigned,
flags,
} => {
let suffix = if *unsigned { ".un" } else { "" };
if let Some(flags) = flags {
write!(f, "{dest} = mul.ovf{suffix} {left}, {right} flags={flags}")
} else {
write!(f, "{dest} = mul.ovf{suffix} {left}, {right}")
}
}
Self::WideMul {
low,
high,
left,
right,
unsigned,
} => {
let suffix = if *unsigned { ".un" } else { "" };
write!(f, "{low}, {high} = widemul{suffix} {left}, {right}")
}
Self::Div {
dest,
left,
right,
unsigned,
flags,
} => {
let suffix = if *unsigned { ".un" } else { "" };
if let Some(flags) = flags {
write!(f, "{dest} = div{suffix} {left}, {right} flags={flags}")
} else {
write!(f, "{dest} = div{suffix} {left}, {right}")
}
}
Self::Rem {
dest,
left,
right,
unsigned,
flags,
} => {
let suffix = if *unsigned { ".un" } else { "" };
if let Some(flags) = flags {
write!(f, "{dest} = rem{suffix} {left}, {right} flags={flags}")
} else {
write!(f, "{dest} = rem{suffix} {left}, {right}")
}
}
Self::FloatCompareFlags {
flags,
left,
right,
signaling,
} => {
let suffix = if *signaling { ".signaling" } else { "" };
write!(f, "{flags} = fcmp.flags{suffix} {left}, {right}")
}
Self::WideDiv {
quotient,
remainder,
high,
low,
divisor,
unsigned,
} => {
let suffix = if *unsigned { ".un" } else { "" };
write!(
f,
"{quotient}, {remainder} = widediv{suffix} {high}:{low}, {divisor}"
)
}
Self::Neg {
dest,
operand,
flags,
} => {
if let Some(flags) = flags {
write!(f, "{dest} = neg {operand} flags={flags}")
} else {
write!(f, "{dest} = neg {operand}")
}
}
Self::And {
dest,
left,
right,
flags,
} => {
if let Some(flags) = flags {
write!(f, "{dest} = and {left}, {right} flags={flags}")
} else {
write!(f, "{dest} = and {left}, {right}")
}
}
Self::Or {
dest,
left,
right,
flags,
} => {
if let Some(flags) = flags {
write!(f, "{dest} = or {left}, {right} flags={flags}")
} else {
write!(f, "{dest} = or {left}, {right}")
}
}
Self::Xor {
dest,
left,
right,
flags,
} => {
if let Some(flags) = flags {
write!(f, "{dest} = xor {left}, {right} flags={flags}")
} else {
write!(f, "{dest} = xor {left}, {right}")
}
}
Self::Not {
dest,
operand,
flags,
} => {
if let Some(flags) = flags {
write!(f, "{dest} = not {operand} flags={flags}")
} else {
write!(f, "{dest} = not {operand}")
}
}
Self::Shl {
dest,
value,
amount,
flags,
} => {
if let Some(flags) = flags {
write!(f, "{dest} = shl {value}, {amount} flags={flags}")
} else {
write!(f, "{dest} = shl {value}, {amount}")
}
}
Self::Shr {
dest,
value,
amount,
unsigned,
flags,
} => {
let suffix = if *unsigned { ".un" } else { "" };
if let Some(flags) = flags {
write!(f, "{dest} = shr{suffix} {value}, {amount} flags={flags}")
} else {
write!(f, "{dest} = shr{suffix} {value}, {amount}")
}
}
Self::Rol {
dest,
value,
amount,
} => write!(f, "{dest} = rol {value}, {amount}"),
Self::Ror {
dest,
value,
amount,
} => write!(f, "{dest} = ror {value}, {amount}"),
Self::Rcl {
dest,
value,
amount,
} => write!(f, "{dest} = rcl {value}, {amount}"),
Self::Rcr {
dest,
value,
amount,
} => write!(f, "{dest} = rcr {value}, {amount}"),
Self::BSwap { dest, src } => write!(f, "{dest} = bswap {src}"),
Self::BRev { dest, src } => write!(f, "{dest} = brev {src}"),
Self::BitScanForward { dest, src } => write!(f, "{dest} = bsf {src}"),
Self::BitScanReverse { dest, src } => write!(f, "{dest} = bsr {src}"),
Self::Popcount { dest, src } => write!(f, "{dest} = popcnt {src}"),
Self::Parity { dest, src } => write!(f, "{dest} = parity {src}"),
Self::Ceq { dest, left, right } => write!(f, "{dest} = ceq {left}, {right}"),
Self::Clt {
dest,
left,
right,
unsigned,
} => {
let suffix = if *unsigned { ".un" } else { "" };
write!(f, "{dest} = clt{suffix} {left}, {right}")
}
Self::Cgt {
dest,
left,
right,
unsigned,
} => {
let suffix = if *unsigned { ".un" } else { "" };
write!(f, "{dest} = cgt{suffix} {left}, {right}")
}
Self::BoolAnd { dest, left, right } => {
write!(f, "{dest} = bool.and {left}, {right}")
}
Self::BoolOr { dest, left, right } => {
write!(f, "{dest} = bool.or {left}, {right}")
}
Self::BoolXor { dest, left, right } => {
write!(f, "{dest} = bool.xor {left}, {right}")
}
Self::BoolNot { dest, value } => write!(f, "{dest} = bool.not {value}"),
Self::IntConv {
dest,
operand,
target,
..
} => write!(f, "{dest} = conv.{target} {operand}"),
Self::IntToPtr {
dest,
operand,
target,
} => write!(f, "{dest} = inttoptr.{target} {operand}"),
Self::PtrToInt {
dest,
operand,
target,
} => write!(f, "{dest} = ptrtoint.{target} {operand}"),
Self::IntToFloat {
dest,
operand,
target,
..
} => write!(f, "{dest} = inttofloat.{target} {operand}"),
Self::FloatToInt {
dest,
operand,
target,
..
} => write!(f, "{dest} = floattoint.{target} {operand}"),
Self::FloatConv {
dest,
operand,
target,
} => write!(f, "{dest} = fconv.{target} {operand}"),
Self::Bitcast {
dest,
operand,
target,
} => write!(f, "{dest} = bitcast.{target} {operand}"),
Self::ReadFlags { dest, flags, mask } => {
write!(f, "{dest} = readflags {flags}, {mask}")
}
Self::ComputeFlags { dest, inputs } => {
write!(f, "{dest} = flags.compute")?;
for (i, input) in inputs.iter().enumerate() {
write!(f, "{} {input}", if i == 0 { "" } else { "," })?;
}
Ok(())
}
Self::CallClobber { outputs } => {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = call.clobber")
}
Self::VectorUnary {
dest, value, kind, ..
} => {
write!(f, "{dest} = vunary.{kind:?} {value}")
}
Self::VectorBinary {
dest,
left,
right,
kind,
..
} => write!(f, "{dest} = vbinary.{kind:?} {left}, {right}"),
Self::VectorTernary {
dest,
first,
second,
third,
kind,
} => write!(f, "{dest} = vternary.{kind:?} {first}, {second}, {third}"),
Self::VectorPredicatedUnary {
dest,
value,
mask,
passthrough,
kind,
mode,
} => {
if let Some(passthrough) = passthrough {
write!(
f,
"{dest} = vunary.pred.{kind:?}.{mode:?} {value}, {mask}, {passthrough}"
)
} else {
write!(f, "{dest} = vunary.pred.{kind:?}.{mode:?} {value}, {mask}")
}
}
Self::VectorPredicatedBinary {
dest,
left,
right,
mask,
passthrough,
kind,
mode,
} => {
if let Some(passthrough) = passthrough {
write!(
f,
"{dest} = vbinary.pred.{kind:?}.{mode:?} {left}, {right}, {mask}, {passthrough}"
)
} else {
write!(
f,
"{dest} = vbinary.pred.{kind:?}.{mode:?} {left}, {right}, {mask}"
)
}
}
Self::VectorPredicatedTernary {
dest,
first,
second,
third,
mask,
passthrough,
kind,
mode,
} => {
if let Some(passthrough) = passthrough {
write!(
f,
"{dest} = vternary.pred.{kind:?}.{mode:?} {first}, {second}, {third}, {mask}, {passthrough}"
)
} else {
write!(
f,
"{dest} = vternary.pred.{kind:?}.{mode:?} {first}, {second}, {third}, {mask}"
)
}
}
Self::VectorCompare {
dest,
left,
right,
kind,
unsigned,
} => {
let suffix = if *unsigned { ".un" } else { "" };
write!(f, "{dest} = vcmp.{kind:?}{suffix} {left}, {right}")
}
Self::VectorLoad {
dest,
addr,
vector_type,
} => write!(f, "{dest} = vload.{vector_type} {addr}"),
Self::VectorStore {
addr,
value,
vector_type,
} => write!(f, "vstore.{vector_type} {addr}, {value}"),
Self::VectorMaskedLoad {
dest,
addr,
mask,
passthrough,
vector_type,
mode,
} => {
if let Some(passthrough) = passthrough {
write!(
f,
"{dest} = vload.masked.{mode:?}.{vector_type} {addr}, {mask}, {passthrough}"
)
} else {
write!(
f,
"{dest} = vload.masked.{mode:?}.{vector_type} {addr}, {mask}"
)
}
}
Self::VectorMaskedStore {
addr,
value,
mask,
vector_type,
} => write!(f, "vstore.masked.{vector_type} {addr}, {value}, {mask}"),
Self::VectorBroadcastLoad {
dest,
addr,
vector_type,
} => write!(f, "{dest} = vbroadcast.load.{vector_type} {addr}"),
Self::VectorGather {
dest,
base,
indices,
mask,
passthrough,
vector_type,
mode,
} => {
if let Some(passthrough) = passthrough {
write!(
f,
"{dest} = vgather.{mode:?}.{vector_type} {base}, {indices}, {mask}, {passthrough}"
)
} else {
write!(
f,
"{dest} = vgather.{mode:?}.{vector_type} {base}, {indices}, {mask}"
)
}
}
Self::VectorFaultingLoad {
dest,
fault,
addr,
mask,
passthrough,
vector_type,
fault_mode,
mask_mode,
} => match (fault, mask, passthrough) {
(Some(fault), Some(mask), Some(passthrough)) => write!(
f,
"{dest}, {fault} = vload.faulting.{fault_mode:?}.{mask_mode:?}.{vector_type} {addr}, {mask}, {passthrough}"
),
(Some(fault), Some(mask), None) => write!(
f,
"{dest}, {fault} = vload.faulting.{fault_mode:?}.{mask_mode:?}.{vector_type} {addr}, {mask}"
),
(Some(fault), None, Some(passthrough)) => write!(
f,
"{dest}, {fault} = vload.faulting.{fault_mode:?}.{mask_mode:?}.{vector_type} {addr}, {passthrough}"
),
(Some(fault), None, None) => write!(
f,
"{dest}, {fault} = vload.faulting.{fault_mode:?}.{mask_mode:?}.{vector_type} {addr}"
),
(None, Some(mask), Some(passthrough)) => write!(
f,
"{dest} = vload.faulting.{fault_mode:?}.{mask_mode:?}.{vector_type} {addr}, {mask}, {passthrough}"
),
(None, Some(mask), None) => write!(
f,
"{dest} = vload.faulting.{fault_mode:?}.{mask_mode:?}.{vector_type} {addr}, {mask}"
),
(None, None, Some(passthrough)) => write!(
f,
"{dest} = vload.faulting.{fault_mode:?}.{mask_mode:?}.{vector_type} {addr}, {passthrough}"
),
(None, None, None) => write!(
f,
"{dest} = vload.faulting.{fault_mode:?}.{mask_mode:?}.{vector_type} {addr}"
),
},
Self::VectorSegmentLoad {
dests,
base,
mask,
vector_type,
segments,
layout,
} => {
if let Some(mask) = mask {
write!(
f,
"{dests:?} = vload.segment.{layout:?}.{segments}.{vector_type} {base}, {mask}"
)
} else {
write!(
f,
"{dests:?} = vload.segment.{layout:?}.{segments}.{vector_type} {base}"
)
}
}
Self::VectorScatter {
base,
indices,
value,
mask,
vector_type,
} => write!(
f,
"vscatter.{vector_type} {base}, {indices}, {value}, {mask}"
),
Self::VectorSegmentStore {
base,
values,
mask,
vector_type,
segments,
layout,
} => {
if let Some(mask) = mask {
write!(
f,
"vstore.segment.{layout:?}.{segments}.{vector_type} {base}, {values:?}, {mask}"
)
} else {
write!(
f,
"vstore.segment.{layout:?}.{segments}.{vector_type} {base}, {values:?}"
)
}
}
Self::VectorExtract { dest, vector, lane } => {
write!(f, "{dest} = vextract {vector}, {lane}")
}
Self::VectorInsert {
dest,
vector,
lane,
value,
} => write!(f, "{dest} = vinsert {vector}, {lane}, {value}"),
Self::VectorSplat {
dest,
value,
vector_type,
} => write!(f, "{dest} = vsplat.{vector_type} {value}"),
Self::VectorShuffle {
dest, left, right, ..
} => {
if let Some(right) = right {
write!(f, "{dest} = vshuffle {left}, {right}")
} else {
write!(f, "{dest} = vshuffle {left}")
}
}
Self::VectorCast {
dest,
value,
target_type,
kind,
} => write!(f, "{dest} = vcast.{kind:?}.{target_type} {value}"),
Self::VectorReinterpret {
dest,
value,
target_type,
} => write!(f, "{dest} = vreinterpret.{target_type} {value}"),
Self::VectorPack {
dest,
value,
mask,
passthrough,
vector_type,
element_bits,
kind,
mode,
} => {
if let Some(passthrough) = passthrough {
write!(
f,
"{dest} = vpack.{kind:?}.{mode:?}.e{element_bits}.{vector_type} {value}, {mask}, {passthrough}"
)
} else {
write!(
f,
"{dest} = vpack.{kind:?}.{mode:?}.e{element_bits}.{vector_type} {value}, {mask}"
)
}
}
Self::VectorPackLoad {
dest,
addr,
mask,
passthrough,
vector_type,
element_bits,
kind,
mode,
} => {
if let Some(passthrough) = passthrough {
write!(
f,
"{dest} = vpack.load.{kind:?}.{mode:?}.e{element_bits}.{vector_type} [{addr}], {mask}, {passthrough}"
)
} else {
write!(
f,
"{dest} = vpack.load.{kind:?}.{mode:?}.e{element_bits}.{vector_type} [{addr}], {mask}"
)
}
}
Self::VectorPackStore {
addr,
value,
mask,
vector_type,
element_bits,
kind,
} => write!(
f,
"vpack.store.{kind:?}.e{element_bits}.{vector_type} [{addr}], {value}, {mask}"
),
Self::VectorZeroUpper { all } => {
let suffix = if *all { "all" } else { "upper" };
write!(f, "vzero.{suffix}")
}
Self::VectorMaskUnary { dest, mask, kind } => {
write!(f, "{dest} = vmask.unary.{kind:?} {mask}")
}
Self::VectorMaskBinary {
dest,
left,
right,
kind,
} => write!(f, "{dest} = vmask.binary.{kind:?} {left}, {right}"),
Self::VectorReduce { dest, value, kind } => {
write!(f, "{dest} = vreduce.{kind:?} {value}")
}
Self::VectorBitmask { dest, value, kind } => {
write!(f, "{dest} = vbitmask.{kind:?} {value}")
}
Self::Select {
dest,
condition,
true_val,
false_val,
} => write!(f, "{dest} = select {condition}, {true_val}, {false_val}"),
Self::Jump { target } => write!(f, "jump B{target}"),
Self::Branch {
condition,
true_target,
false_target,
} => write!(f, "branch {condition}, B{true_target}, B{false_target}"),
Self::BranchCmp {
left,
right,
cmp,
unsigned,
true_target,
false_target,
} => {
let suffix = if *unsigned { ".un" } else { "" };
write!(
f,
"branchcmp{suffix} {left} {cmp} {right}, B{true_target}, B{false_target}"
)
}
Self::BranchFlags {
flags: _,
condition,
true_target,
false_target,
} => {
write!(f, "branchflags {condition} B{true_target}, B{false_target}")
}
Self::Switch {
value,
targets,
default,
} => {
write!(f, "switch {value}, [")?;
for (i, t) in targets.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "B{t}")?;
}
write!(f, "], B{default}")
}
Self::IndirectBranch {
target,
resolved_targets,
} => {
write!(f, "branch.indirect {target}")?;
if !resolved_targets.is_empty() {
write!(f, " [")?;
for (i, t) in resolved_targets.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "B{t}")?;
}
write!(f, "]")?;
}
Ok(())
}
Self::Return { value: Some(v) } => write!(f, "ret {v}"),
Self::Return { value: None } => write!(f, "ret"),
Self::LoadField {
dest,
object,
field,
} => {
write!(f, "{dest} = ldfld {field}, {object}")
}
Self::StoreField {
object,
field,
value,
} => write!(f, "stfld {field}, {object}, {value}"),
Self::LoadStaticField { dest, field } => write!(f, "{dest} = ldsfld {field}"),
Self::StoreStaticField { field, value } => write!(f, "stsfld {field}, {value}"),
Self::LoadFieldAddr {
dest,
object,
field,
} => {
write!(f, "{dest} = ldflda {field}, {object}")
}
Self::LoadStaticFieldAddr { dest, field } => write!(f, "{dest} = ldsflda {field}"),
Self::LoadElement {
dest,
array,
index,
elem_type,
} => write!(f, "{dest} = ldelem.{elem_type} {array}[{index}]"),
Self::StoreElement {
array,
index,
value,
elem_type,
} => write!(f, "stelem.{elem_type} {array}[{index}], {value}"),
Self::LoadElementAddr {
dest, array, index, ..
} => write!(f, "{dest} = ldelema {array}[{index}]"),
Self::PtrAdd {
dest,
base,
index,
stride,
offset,
..
} => {
write!(f, "{dest} = ptradd {base}")?;
if let Some(index) = index {
write!(f, " + {index}*{stride}")?;
}
if *offset != 0 {
write!(f, " + {offset}")?;
}
Ok(())
}
Self::ArrayLength { dest, array } => write!(f, "{dest} = ldlen {array}"),
Self::LoadIndirect {
dest,
addr,
value_type,
} => write!(f, "{dest} = ldind.{value_type} {addr}"),
Self::StoreIndirect {
addr,
value,
value_type,
} => write!(f, "stind.{value_type} {addr}, {value}"),
Self::NewObj { dest, ctor, args } => {
write!(f, "{dest} = newobj {ctor}(")?;
for (i, arg) in args.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{arg}")?;
}
write!(f, ")")
}
Self::NewArr {
dest,
elem_type,
length,
} => write!(f, "{dest} = newarr {elem_type}[{length}]"),
Self::CastClass {
dest,
object,
target_type,
} => write!(f, "{dest} = castclass {target_type}, {object}"),
Self::IsInst {
dest,
object,
target_type,
} => write!(f, "{dest} = isinst {target_type}, {object}"),
Self::Box {
dest,
value,
value_type,
} => write!(f, "{dest} = box {value_type}, {value}"),
Self::Unbox {
dest,
object,
value_type,
} => write!(f, "{dest} = unbox {value_type}, {object}"),
Self::UnboxAny {
dest,
object,
value_type,
} => write!(f, "{dest} = unbox.any {value_type}, {object}"),
Self::SizeOf { dest, value_type } => write!(f, "{dest} = sizeof {value_type}"),
Self::LoadToken { dest, token } => write!(f, "{dest} = ldtoken {token}"),
Self::Call { dest, method, args } => {
if let Some(d) = dest {
write!(f, "{d} = ")?;
}
write!(f, "call {method}(")?;
for (i, arg) in args.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{arg}")?;
}
write!(f, ")")
}
Self::CallVirt { dest, method, args } => {
if let Some(d) = dest {
write!(f, "{d} = ")?;
}
write!(f, "callvirt {method}(")?;
for (i, arg) in args.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{arg}")?;
}
write!(f, ")")
}
Self::CallIndirect {
dest, fptr, args, ..
} => {
if let Some(d) = dest {
write!(f, "{d} = ")?;
}
write!(f, "calli {fptr}(")?;
for (i, arg) in args.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{arg}")?;
}
write!(f, ")")
}
Self::LoadFunctionPtr { dest, method } => write!(f, "{dest} = ldftn {method}"),
Self::LoadVirtFunctionPtr {
dest,
object,
method,
} => write!(f, "{dest} = ldvirtftn {method}, {object}"),
Self::LoadArg { dest, arg_index } => write!(f, "{dest} = ldarg {arg_index}"),
Self::LoadLocal { dest, local_index } => write!(f, "{dest} = ldloc {local_index}"),
Self::LoadArgAddr { dest, arg_index } => write!(f, "{dest} = ldarga {arg_index}"),
Self::LoadLocalAddr { dest, local_index } => {
write!(f, "{dest} = ldloca {local_index}")
}
Self::Copy { dest, src } => write!(f, "{dest} = {src}"),
Self::Pop { value } => write!(f, "pop {value}"),
Self::Throw { exception } => write!(f, "throw {exception}"),
Self::Rethrow => write!(f, "rethrow"),
Self::EndFinally => write!(f, "endfinally"),
Self::InterruptReturn => write!(f, "iret"),
Self::Unreachable => write!(f, "unreachable"),
Self::EndFilter { result } => write!(f, "endfilter {result}"),
Self::Leave { target } => write!(f, "leave B{target}"),
Self::InitBlk {
dest_addr,
value,
size,
} => write!(f, "initblk {dest_addr}, {value}, {size}"),
Self::CopyBlk {
dest_addr,
src_addr,
size,
} => write!(f, "cpblk {dest_addr}, {src_addr}, {size}"),
Self::Fence { kind } => write!(f, "fence {kind}"),
Self::NativeOpaque(data) => {
let NativeOpaqueData {
mnemonic,
metadata,
outputs,
inputs,
clobbers,
effects,
} = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "native.opaque {mnemonic}")?;
if !inputs.is_empty() {
write!(f, " ")?;
for (i, input) in inputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{input}")?;
}
}
if let Some(metadata) = metadata {
if let Some(architecture) = &metadata.architecture {
write!(f, " arch={architecture}")?;
}
if let Some(address) = metadata.address {
write!(f, " addr=0x{address:x}")?;
}
if !metadata.raw_bytes.is_empty() {
write!(f, " bytes={}", metadata.raw_bytes.len())?;
}
}
if !clobbers.is_empty() {
write!(f, " clobbers={}", clobbers.len())?;
}
write!(f, " effects={:?}", effects.kind)
}
Self::NativeIntrinsic(data) => {
let NativeIntrinsicData {
id,
mnemonic,
metadata,
outputs,
inputs,
clobbers,
effects,
} = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "native.intrinsic.{id:?} {mnemonic}")?;
if !inputs.is_empty() {
write!(f, " ")?;
for (i, input) in inputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{input}")?;
}
}
if let Some(metadata) = metadata {
if let Some(architecture) = &metadata.architecture {
write!(f, " arch={architecture}")?;
}
if let Some(address) = metadata.address {
write!(f, " addr=0x{address:x}")?;
}
if !metadata.raw_bytes.is_empty() {
write!(f, " bytes={}", metadata.raw_bytes.len())?;
}
}
if !clobbers.is_empty() {
write!(f, " clobbers={}", clobbers.len())?;
}
write!(f, " effects={:?}", effects.kind)
}
Self::SystemOp(data) => {
let NativeKindedData {
kind,
mnemonic,
metadata,
outputs,
inputs,
clobbers,
} = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "{} {mnemonic}", kind.kind_str())?;
if !inputs.is_empty() {
write!(f, " ")?;
for (i, input) in inputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{input}")?;
}
}
if let Some(metadata) = metadata {
if let Some(architecture) = &metadata.architecture {
write!(f, " arch={architecture}")?;
}
if let Some(address) = metadata.address {
write!(f, " addr=0x{address:x}")?;
}
if !metadata.raw_bytes.is_empty() {
write!(f, " bytes={}", metadata.raw_bytes.len())?;
}
}
if !clobbers.is_empty() {
write!(f, " clobbers={}", clobbers.len())?;
}
write!(f, " effects={:?}", kind.effects().kind)
}
Self::ComputeOp(data) => {
let NativeKindedData {
kind,
mnemonic,
metadata,
outputs,
inputs,
clobbers,
} = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "{} {mnemonic}", kind.kind_str())?;
if !inputs.is_empty() {
write!(f, " ")?;
for (i, input) in inputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{input}")?;
}
}
if let Some(metadata) = metadata {
if let Some(architecture) = &metadata.architecture {
write!(f, " arch={architecture}")?;
}
if let Some(address) = metadata.address {
write!(f, " addr=0x{address:x}")?;
}
if !metadata.raw_bytes.is_empty() {
write!(f, " bytes={}", metadata.raw_bytes.len())?;
}
}
if !clobbers.is_empty() {
write!(f, " clobbers={}", clobbers.len())?;
}
write!(f, " effects={:?}", kind.effects().kind)
}
Self::BcdAdjust(data) => {
let BcdAdjustData {
kind,
base,
mnemonic,
metadata,
outputs,
inputs,
clobbers,
} = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "{} {mnemonic}", kind.kind_str())?;
if matches!(
kind,
BcdAdjustKind::AsciiMulAdjust | BcdAdjustKind::AsciiDivAdjust
) {
write!(f, " base={base}")?;
}
if !inputs.is_empty() {
write!(f, " ")?;
for (i, input) in inputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{input}")?;
}
}
if let Some(metadata) = metadata {
if let Some(architecture) = &metadata.architecture {
write!(f, " arch={architecture}")?;
}
if let Some(address) = metadata.address {
write!(f, " addr=0x{address:x}")?;
}
if !metadata.raw_bytes.is_empty() {
write!(f, " bytes={}", metadata.raw_bytes.len())?;
}
}
if !clobbers.is_empty() {
write!(f, " clobbers={}", clobbers.len())?;
}
write!(f, " effects={:?}", kind.effects().kind)
}
Self::VectorCrypto(data) => {
let KindedVecData {
kind,
outputs,
inputs,
} = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "{}", kind.kind_str())?;
if !inputs.is_empty() {
write!(f, " ")?;
for (i, input) in inputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{input}")?;
}
}
write!(f, " effects={:?}", kind.effects().kind)
}
Self::TileOp(data) => {
let KindedVecData {
kind,
outputs,
inputs,
} = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "{}", kind.kind_str())?;
if !inputs.is_empty() {
write!(f, " ")?;
for (i, input) in inputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{input}")?;
}
}
write!(f, " effects={:?}", kind.effects().kind)
}
Self::VectorPermute(data) => {
let VectorPermuteData { outputs, inputs } = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "vector.permute")?;
if !inputs.is_empty() {
write!(f, " ")?;
for (i, input) in inputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{input}")?;
}
}
Ok(())
}
Self::VectorMultiplyAdd(data) => {
let KindedVecData {
kind,
outputs,
inputs,
} = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "{}", kind.kind_str())?;
if !inputs.is_empty() {
write!(f, " ")?;
for (i, input) in inputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{input}")?;
}
}
Ok(())
}
Self::VectorSvePermute(data) => {
let KindedVecData { outputs, inputs, .. } = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "vector.sve.perm")?;
if !inputs.is_empty() {
write!(f, " ")?;
for (i, input) in inputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{input}")?;
}
}
Ok(())
}
Self::VectorFpHelper(data) => {
let KindedVecData { outputs, inputs, .. } = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "vector.fphelper")?;
if !inputs.is_empty() {
write!(f, " ")?;
for (i, input) in inputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{input}")?;
}
}
Ok(())
}
Self::VectorPredicateGen(data) => {
let KindedVecData { outputs, inputs, .. } = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "vector.pgen")?;
if !inputs.is_empty() {
write!(f, " ")?;
for (i, input) in inputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{input}")?;
}
}
Ok(())
}
Self::VectorSmeOuterProduct(data) => {
let VectorSmeOuterProductData { outputs, inputs, .. } = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "vector.sme.mopa")?;
if !inputs.is_empty() {
write!(f, " ")?;
for (i, input) in inputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{input}")?;
}
}
Ok(())
}
Self::VectorMatrixMulAcc(data) => {
let VectorMatrixMulAccData { outputs, inputs, .. } = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "vector.mmla")?;
if !inputs.is_empty() {
write!(f, " ")?;
for (i, input) in inputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{input}")?;
}
}
Ok(())
}
Self::VectorReverseChunks(data) => {
let VectorReverseChunksData { outputs, inputs, .. } = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "vector.revchunks")?;
if !inputs.is_empty() {
write!(f, " ")?;
for (i, input) in inputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{input}")?;
}
}
Ok(())
}
Self::VectorCountAdjust(data) => {
let VectorCountAdjustData { outputs, inputs, .. } = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "vector.countadj")?;
if !inputs.is_empty() {
write!(f, " ")?;
for (i, input) in inputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{input}")?;
}
}
Ok(())
}
Self::VectorExtendInLane(data) => {
let VectorExtendInLaneData {
signed,
source_bits,
element_bits,
outputs,
inputs,
} = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
let kind = if *signed { "sxt" } else { "uxt" };
write!(f, "vector.{kind} i{source_bits}->i{element_bits}")?;
for input in inputs {
write!(f, " {input}")?;
}
Ok(())
}
Self::VectorElementCount(data) => {
let VectorElementCountData {
element_bits,
multiplier,
outputs,
} = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "vector.cnt e{element_bits} x{multiplier}")?;
Ok(())
}
Self::VectorSveAddressGen(data) => {
let VectorSveAddressGenData {
signed_extend,
shift,
outputs,
inputs,
} = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
let ext = match signed_extend {
Some(true) => "sxtw",
Some(false) => "uxtw",
None => "lsl",
};
write!(f, "vector.adr {ext} #{shift}")?;
for input in inputs {
write!(f, " {input}")?;
}
Ok(())
}
Self::FlagAdjust(data) => {
let KindedVecData { outputs, inputs, .. } = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "flags.adjust")?;
if !inputs.is_empty() {
write!(f, " ")?;
for (i, input) in inputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{input}")?;
}
}
Ok(())
}
Self::VectorStructLoadReplicate(data) => {
let VectorStructLoadReplicateData { outputs, inputs, .. } = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "vector.ldNr")?;
if !inputs.is_empty() {
write!(f, " ")?;
for (i, input) in inputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{input}")?;
}
}
Ok(())
}
Self::VectorSmeMisc(data) => {
let VectorSmeMiscData { outputs, inputs, .. } = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "vector.sme.misc")?;
if !inputs.is_empty() {
write!(f, " ")?;
for (i, input) in inputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{input}")?;
}
}
Ok(())
}
Self::VectorPredicateOp(data) => {
let VectorPredicateOpData { outputs, inputs, .. } = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "vector.predop")?;
if !inputs.is_empty() {
write!(f, " ")?;
for (i, input) in inputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{input}")?;
}
}
Ok(())
}
Self::VectorSveCompute(data) => {
let VectorSveComputeData { outputs, inputs, .. } = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "vector.sve.compute")?;
if !inputs.is_empty() {
write!(f, " ")?;
for (i, input) in inputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{input}")?;
}
}
Ok(())
}
Self::VectorComplexAdd(data) => {
let VectorComplexAddData { outputs, inputs, .. } = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "vector.cadd")?;
if !inputs.is_empty() {
write!(f, " ")?;
for (i, input) in inputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{input}")?;
}
}
Ok(())
}
Self::VectorPredicateBreak(data) => {
let VectorPredicateBreakData { outputs, inputs, .. } = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "vector.break")?;
if !inputs.is_empty() {
write!(f, " ")?;
for (i, input) in inputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{input}")?;
}
}
Ok(())
}
Self::VectorPredicateWhile(data) => {
let VectorPredicateWhileData { outputs, inputs, .. } = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "vector.while")?;
if !inputs.is_empty() {
write!(f, " ")?;
for (i, input) in inputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{input}")?;
}
}
Ok(())
}
Self::VectorNarrowSaturate(data) => {
let VectorNarrowSaturateData {
unsigned_dst,
outputs,
inputs,
..
} = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(
f,
"{}",
if *unsigned_dst {
"vector.narrow.saturate.u"
} else {
"vector.narrow.saturate.s"
}
)?;
if !inputs.is_empty() {
write!(f, " ")?;
for (i, input) in inputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{input}")?;
}
}
Ok(())
}
Self::VectorPackNarrow(data) => {
let VectorPackNarrowData {
unsigned,
outputs,
inputs,
} = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(
f,
"{}",
if *unsigned {
"vector.pack.narrow.u"
} else {
"vector.pack.narrow.s"
}
)?;
if !inputs.is_empty() {
write!(f, " ")?;
for (i, input) in inputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{input}")?;
}
}
Ok(())
}
Self::VectorTernaryLogic(data) => {
let VecImm8Data {
imm8,
outputs,
inputs,
} = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "vector.ternlog")?;
for input in inputs {
write!(f, " {input}")?;
}
write!(f, " imm={imm8:#04x}")
}
Self::VectorDotProduct(data) => {
let VectorDotProductData {
imm8,
element_bits,
outputs,
inputs,
} = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "vector.dotproduct.{element_bits}")?;
for input in inputs {
write!(f, " {input}")?;
}
write!(f, " imm={imm8:#04x}")
}
Self::VectorMultiSad(data) => {
let VecImm8Data {
imm8,
outputs,
inputs,
} = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "vector.mpsadbw")?;
for input in inputs {
write!(f, " {input}")?;
}
write!(f, " imm={imm8:#04x}")
}
Self::VectorIntDotProduct(data) => {
let VectorIntDotProductData {
signed_a,
signed_b,
source_bits,
dest_bits,
outputs,
inputs,
} = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "vector.intdot")?;
for input in inputs {
write!(f, " {input}")?;
}
write!(
f,
" s_a={signed_a} s_b={signed_b} src={source_bits} dst={dest_bits}"
)
}
Self::VectorStringCompare(data) => {
let VectorStringCompareData {
imm8,
explicit_length,
result_index,
outputs,
inputs,
} = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(
f,
"vector.pcmp{}str{}",
if *explicit_length { "e" } else { "i" },
if *result_index { "i" } else { "m" }
)?;
for input in inputs {
write!(f, " {input}")?;
}
write!(f, " imm={imm8:#04x}")
}
Self::VectorBitfield(data) => {
let VectorBitfieldData {
insert,
index,
length,
outputs,
inputs,
} = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(
f,
"vector.{}",
if *insert { "insertq" } else { "extrq" }
)?;
for input in inputs {
write!(f, " {input}")?;
}
write!(f, " index={index} length={length}")
}
Self::VectorIntersect(data) => {
let VectorIntersectData { outputs, inputs } = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "vector.p2intersect")?;
for input in inputs {
write!(f, " {input}")?;
}
Ok(())
}
Self::VectorShuffleBits(data) => {
let VectorShuffleBitsData { outputs, inputs } = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "vector.shufbitqmb")?;
for input in inputs {
write!(f, " {input}")?;
}
Ok(())
}
Self::VectorConditionalMove(data) => {
let VectorConditionalMoveData {
condition,
outputs,
inputs,
} = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "vector.condmove.{}", condition.kind_str())?;
for input in inputs {
write!(f, " {input}")?;
}
Ok(())
}
Self::VectorHorizontalMinPos(data) => {
let VectorHorizontalMinPosData { outputs, inputs } = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "vector.phminposuw")?;
for input in inputs {
write!(f, " {input}")?;
}
Ok(())
}
Self::VectorComplexMul(data) => {
let KindedVecData {
kind,
outputs,
inputs,
} = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "{}", kind.kind_str())?;
for input in inputs {
write!(f, " {input}")?;
}
Ok(())
}
Self::VectorClassify(data) => {
let VecImm8Data {
imm8,
outputs,
inputs,
} = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "vector.fpclass")?;
for input in inputs {
write!(f, " {input}")?;
}
write!(f, " imm={imm8:#04x}")
}
Self::VectorHorizontalReduce(data) => {
let VectorHorizontalReduceData {
subtract,
source_bits,
dest_bits,
outputs,
inputs,
..
} = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(
f,
"vector.hreduce.{} {}->{}",
if *subtract { "sub" } else { "add" },
source_bits,
dest_bits
)?;
for input in inputs {
write!(f, " {input}")?;
}
Ok(())
}
Self::BlockString(data) => {
let BlockStringOpData {
kind,
prefix: _,
element_bits: _,
mnemonic,
metadata,
outputs,
inputs,
clobbers,
} = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(f, "{} {mnemonic}", kind.kind_str())?;
if !inputs.is_empty() {
write!(f, " ")?;
for (i, input) in inputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{input}")?;
}
}
if let Some(metadata) = metadata {
if let Some(architecture) = &metadata.architecture {
write!(f, " arch={architecture}")?;
}
if let Some(address) = metadata.address {
write!(f, " addr=0x{address:x}")?;
}
if !metadata.raw_bytes.is_empty() {
write!(f, " bytes={}", metadata.raw_bytes.len())?;
}
}
if !clobbers.is_empty() {
write!(f, " clobbers={}", clobbers.len())?;
}
write!(f, " effects={:?}", kind.effects().kind)
}
Self::WideCompareExchange(data) => {
let WideCmpXchgData {
wide,
mnemonic,
metadata,
outputs,
inputs,
clobbers,
} = data.as_ref();
if !outputs.is_empty() {
for (i, output) in outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{output}")?;
}
write!(f, " = ")?;
}
write!(
f,
"{} {mnemonic}",
if *wide {
"atomic.cmpxchg16b"
} else {
"atomic.cmpxchg8b"
}
)?;
if !inputs.is_empty() {
write!(f, " ")?;
for (i, input) in inputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{input}")?;
}
}
if let Some(metadata) = metadata {
if let Some(architecture) = &metadata.architecture {
write!(f, " arch={architecture}")?;
}
if let Some(address) = metadata.address {
write!(f, " addr=0x{address:x}")?;
}
if !metadata.raw_bytes.is_empty() {
write!(f, " bytes={}", metadata.raw_bytes.len())?;
}
}
if !clobbers.is_empty() {
write!(f, " clobbers={}", clobbers.len())?;
}
write!(f, " effects=Atomic")
}
Self::FpTranscendental(data) => {
for (i, dest) in data.outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{dest}")?;
}
if !data.outputs.is_empty() {
write!(f, " = ")?;
}
write!(f, "fp.transcendental.{:?}", data.kind)?;
for arg in &data.inputs {
write!(f, " {arg}")?;
}
Ok(())
}
Self::FpuControl(data) => {
for (i, dest) in data.outputs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{dest}")?;
}
if !data.outputs.is_empty() {
write!(f, " = ")?;
}
write!(f, "fpu.control.{:?}", data.kind)?;
for arg in &data.inputs {
write!(f, " {arg}")?;
}
Ok(())
}
Self::CmpXchg {
dest,
addr,
expected,
desired,
} => write!(f, "{dest} = cmpxchg {addr}, {expected}, {desired}"),
Self::AtomicRmw {
dest,
addr,
value,
op,
} => write!(f, "{dest} = atomicrmw.{op} {addr}, {value}"),
Self::AtomicLoad {
dest,
addr,
value_type,
ordering,
width,
volatile,
} => {
let volatile = if *volatile { ".volatile" } else { "" };
write!(
f,
"{dest} = atomicload{volatile}.{ordering}.{width} {value_type}, {addr}"
)
}
Self::AtomicStore {
addr,
value,
value_type,
ordering,
width,
volatile,
} => {
let volatile = if *volatile { ".volatile" } else { "" };
write!(
f,
"atomicstore{volatile}.{ordering}.{width} {value_type}, {addr}, {value}"
)
}
Self::AtomicStoreConditional {
status,
addr,
value,
value_type,
success_ordering,
failure_ordering,
width,
volatile,
} => {
let volatile = if *volatile { ".volatile" } else { "" };
write!(
f,
"{status} = atomicstore.conditional{volatile}.{success_ordering}/{failure_ordering}.{width} {value_type}, {addr}, {value}"
)
}
Self::AtomicPairLoad {
first,
second,
addr,
first_type,
second_type,
ordering,
width,
volatile,
} => {
let volatile = if *volatile { ".volatile" } else { "" };
write!(
f,
"{first}, {second} = atomicload.pair{volatile}.{ordering}.{width} {first_type}/{second_type}, {addr}"
)
}
Self::AtomicPairStoreConditional {
status,
addr,
first_value,
second_value,
first_type,
second_type,
success_ordering,
failure_ordering,
width,
volatile,
} => {
let volatile = if *volatile { ".volatile" } else { "" };
write!(
f,
"{status} = atomicstore.conditional.pair{volatile}.{success_ordering}/{failure_ordering}.{width} {first_type}/{second_type}, {addr}, {first_value}, {second_value}"
)
}
Self::AtomicExchange {
dest,
addr,
value,
ordering,
width,
volatile,
} => {
let volatile = if *volatile { ".volatile" } else { "" };
write!(
f,
"{dest} = atomicxchg{volatile}.{ordering}.{width} {addr}, {value}"
)
}
Self::AtomicLockRmw {
dest,
addr,
value,
op,
ordering,
width,
volatile,
} => {
let volatile = if *volatile { ".volatile" } else { "" };
write!(
f,
"{dest} = lock.atomicrmw{volatile}.{op}.{ordering}.{width} {addr}, {value}"
)
}
Self::AtomicCmpXchg {
old,
success,
addr,
expected,
desired,
success_ordering,
failure_ordering,
width,
weak,
volatile,
} => {
write!(f, "{old}")?;
if let Some(success) = success {
write!(f, ", {success}")?;
}
let weak = if *weak { ".weak" } else { "" };
let volatile = if *volatile { ".volatile" } else { "" };
write!(
f,
" = cmpxchg{weak}{volatile}.{success_ordering}/{failure_ordering}.{width} {addr}, {expected}, {desired}"
)
}
Self::AtomicPairCmpXchg {
old_first,
old_second,
addr,
expected_first,
expected_second,
desired_first,
desired_second,
success_ordering,
failure_ordering,
width,
weak,
volatile,
} => {
let weak = if *weak { ".weak" } else { "" };
let volatile = if *volatile { ".volatile" } else { "" };
write!(
f,
"{old_first}, {old_second} = cmpxchg.pair{weak}{volatile}.{success_ordering}/{failure_ordering}.{width} {addr}, {expected_first}, {expected_second}, {desired_first}, {desired_second}"
)
}
Self::InitObj {
dest_addr,
value_type,
} => write!(f, "initobj {value_type}, {dest_addr}"),
Self::CopyObj {
dest_addr,
src_addr,
value_type,
} => write!(f, "cpobj {value_type}, {dest_addr}, {src_addr}"),
Self::LoadObj {
dest,
src_addr,
value_type,
} => write!(f, "{dest} = ldobj {value_type}, {src_addr}"),
Self::StoreObj {
dest_addr,
value,
value_type,
} => write!(f, "stobj {value_type}, {dest_addr}, {value}"),
Self::LocalAlloc { dest, size } => write!(f, "{dest} = localloc {size}"),
Self::Constrained { constraint_type } => {
write!(f, "constrained. {constraint_type}")
}
Self::Volatile => write!(f, "volatile."),
Self::Unaligned { alignment } => write!(f, "unaligned. {alignment}"),
Self::TailPrefix => write!(f, "tail."),
Self::Readonly => write!(f, "readonly."),
Self::Ckfinite { dest, operand } => write!(f, "{dest} = ckfinite {operand}"),
Self::FpClassify { dest, operand } => write!(f, "{dest} = fpclassify {operand}"),
Self::Nop => write!(f, "nop"),
Self::Break => write!(f, "break"),
Self::Phi { dest, operands } => {
write!(f, "{dest} = phi(")?;
for (i, (block, var)) in operands.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "B{block}: {var}")?;
}
write!(f, ")")
}
}
}
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use super::*;
use crate::{
ir::{value::ConstValue, variable::SsaVarId},
testing::{MockTarget, MockType},
};
#[test]
fn is_pure_classifies_calls_and_arith() {
let add: SsaOp<MockTarget> = SsaOp::Add {
dest: SsaVarId::from_index(0),
left: SsaVarId::from_index(1),
right: SsaVarId::from_index(2),
flags: None,
};
assert!(add.is_pure());
let const_op: SsaOp<MockTarget> = SsaOp::Const {
dest: SsaVarId::from_index(3),
value: ConstValue::I32(42),
};
assert!(const_op.is_pure());
let call: SsaOp<MockTarget> = SsaOp::Call {
dest: Some(SsaVarId::from_index(4)),
method: 0xAB,
args: vec![],
};
assert!(!call.is_pure());
}
#[test]
fn uses_lists_operands() {
let v1 = SsaVarId::from_index(0);
let v2 = SsaVarId::from_index(1);
let dest = SsaVarId::from_index(2);
let op: SsaOp<MockTarget> = SsaOp::Add {
dest,
left: v1,
right: v2,
flags: None,
};
let uses = op.uses();
assert_eq!(uses.len(), 2);
assert!(uses.contains(&v1));
assert!(uses.contains(&v2));
let const_op: SsaOp<MockTarget> = SsaOp::Const {
dest,
value: ConstValue::I32(42),
};
assert!(const_op.uses().is_empty());
}
#[test]
fn rotate_ops_dest_and_uses() {
let d = SsaVarId::from_index(0);
let v = SsaVarId::from_index(1);
let a = SsaVarId::from_index(2);
let rol: SsaOp<MockTarget> = SsaOp::Rol {
dest: d,
value: v,
amount: a,
};
assert_eq!(rol.dest(), Some(d));
let uses = rol.uses();
assert_eq!(uses.len(), 2);
assert!(uses.contains(&v));
assert!(uses.contains(&a));
let ror: SsaOp<MockTarget> = SsaOp::Ror {
dest: d,
value: v,
amount: a,
};
assert_eq!(ror.dest(), Some(d));
assert!(ror.uses().contains(&v));
}
#[test]
fn bit_manip_ops_dest_and_uses() {
let d = SsaVarId::from_index(0);
let s = SsaVarId::from_index(1);
let bswap: SsaOp<MockTarget> = SsaOp::BSwap { dest: d, src: s };
assert_eq!(bswap.dest(), Some(d));
assert_eq!(bswap.uses(), vec![s]);
let brev: SsaOp<MockTarget> = SsaOp::BRev { dest: d, src: s };
assert_eq!(brev.dest(), Some(d));
assert_eq!(brev.uses(), vec![s]);
let bsf: SsaOp<MockTarget> = SsaOp::BitScanForward { dest: d, src: s };
assert_eq!(bsf.dest(), Some(d));
assert_eq!(bsf.uses(), vec![s]);
let bsr: SsaOp<MockTarget> = SsaOp::BitScanReverse { dest: d, src: s };
assert_eq!(bsr.dest(), Some(d));
assert_eq!(bsr.uses(), vec![s]);
let popcnt: SsaOp<MockTarget> = SsaOp::Popcount { dest: d, src: s };
assert_eq!(popcnt.dest(), Some(d));
assert_eq!(popcnt.uses(), vec![s]);
let parity: SsaOp<MockTarget> = SsaOp::Parity { dest: d, src: s };
assert_eq!(parity.dest(), Some(d));
assert_eq!(parity.uses(), vec![s]);
}
#[test]
fn select_dest_and_uses() {
let d = SsaVarId::from_index(0);
let c = SsaVarId::from_index(1);
let t = SsaVarId::from_index(2);
let f = SsaVarId::from_index(3);
let op: SsaOp<MockTarget> = SsaOp::Select {
dest: d,
condition: c,
true_val: t,
false_val: f,
};
assert_eq!(op.dest(), Some(d));
assert_eq!(op.uses().len(), 3);
assert!(op.uses().contains(&c));
assert!(op.uses().contains(&t));
assert!(op.uses().contains(&f));
}
#[test]
fn atomic_ops_dest_and_uses() {
let d = SsaVarId::from_index(0);
let a = SsaVarId::from_index(1);
let e = SsaVarId::from_index(2);
let v = SsaVarId::from_index(3);
let op: SsaOp<MockTarget> = SsaOp::CmpXchg {
dest: d,
addr: a,
expected: e,
desired: v,
};
assert_eq!(op.dest(), Some(d));
assert_eq!(op.uses().len(), 3);
let op2: SsaOp<MockTarget> = SsaOp::AtomicRmw {
dest: d,
addr: a,
value: v,
op: AtomicRmwOp::Xchg,
};
assert_eq!(op2.dest(), Some(d));
assert_eq!(op2.uses().len(), 2);
}
#[test]
fn native_atomic_ops_report_defs_uses_and_effects() {
let old = SsaVarId::from_index(0);
let success = SsaVarId::from_index(1);
let addr = SsaVarId::from_index(2);
let expected = SsaVarId::from_index(3);
let desired = SsaVarId::from_index(4);
let cmpxchg: SsaOp<MockTarget> = SsaOp::AtomicCmpXchg {
old,
success: Some(success),
addr,
expected,
desired,
success_ordering: AtomicOrdering::SeqCst,
failure_ordering: AtomicOrdering::Acquire,
width: AtomicAccessWidth::Bits32,
weak: false,
volatile: false,
};
assert_eq!(cmpxchg.dest(), Some(old));
assert_eq!(cmpxchg.defs().collect::<Vec<_>>(), vec![old, success]);
assert_eq!(cmpxchg.uses(), vec![addr, expected, desired]);
assert_eq!(cmpxchg.stack_effect(), (3, 2));
let cmpxchg_effects = cmpxchg.effects();
assert_eq!(cmpxchg_effects.kind, SsaEffectKind::Atomic);
assert_eq!(
cmpxchg_effects.memory_semantics,
MemoryAccessSemantics::Atomic
);
assert_eq!(cmpxchg_effects.ordering, Some(AtomicOrdering::SeqCst));
assert_eq!(cmpxchg_effects.trap, TrapClass::MemoryFault);
assert!(!cmpxchg.is_pure());
let xchg: SsaOp<MockTarget> = SsaOp::AtomicExchange {
dest: old,
addr,
value: desired,
ordering: AtomicOrdering::AcqRel,
width: AtomicAccessWidth::Bits32,
volatile: true,
};
assert_eq!(xchg.defs().collect::<Vec<_>>(), vec![old]);
assert_eq!(xchg.uses(), vec![addr, desired]);
assert_eq!(xchg.stack_effect(), (2, 1));
assert_eq!(
xchg.effects().memory_semantics,
MemoryAccessSemantics::Atomic
);
assert!(xchg.effects().volatile);
assert_eq!(
format!("{xchg}"),
"v0 = atomicxchg.volatile.acqrel.i32 v2, v4"
);
}
#[test]
fn boolean_ops_are_pure_and_remappable() {
let dest = SsaVarId::from_index(0);
let left = SsaVarId::from_index(1);
let right = SsaVarId::from_index(2);
let replacement = SsaVarId::from_index(9);
let op: SsaOp<MockTarget> = SsaOp::BoolAnd { dest, left, right };
assert_eq!(op.dest(), Some(dest));
assert_eq!(op.uses(), vec![left, right]);
assert_eq!(op.stack_effect(), (2, 1));
assert!(op.is_pure());
assert_eq!(format!("{op}"), "v0 = bool.and v1, v2");
let remapped = op.remap_variables(|var| (var == right).then_some(replacement));
assert_eq!(remapped.uses(), vec![left, replacement]);
let not: SsaOp<MockTarget> = SsaOp::BoolNot { dest, value: left };
assert_eq!(not.uses(), vec![left]);
assert_eq!(not.stack_effect(), (1, 1));
assert_eq!(format!("{not}"), "v0 = bool.not v1");
}
#[test]
fn wide_arithmetic_ops_report_secondary_defs() {
let low = SsaVarId::from_index(0);
let high = SsaVarId::from_index(1);
let left = SsaVarId::from_index(2);
let right = SsaVarId::from_index(3);
let mul: SsaOp<MockTarget> = SsaOp::WideMul {
low,
high,
left,
right,
unsigned: true,
};
assert_eq!(mul.dest(), Some(low));
assert_eq!(mul.defs().collect::<Vec<_>>(), vec![low, high]);
assert_eq!(mul.uses(), vec![left, right]);
assert_eq!(mul.stack_effect(), (2, 2));
assert_eq!(format!("{mul}"), "v0, v1 = widemul.un v2, v3");
let quotient = SsaVarId::from_index(4);
let remainder = SsaVarId::from_index(5);
let divisor = SsaVarId::from_index(6);
let div: SsaOp<MockTarget> = SsaOp::WideDiv {
quotient,
remainder,
high,
low,
divisor,
unsigned: false,
};
assert_eq!(div.defs().collect::<Vec<_>>(), vec![quotient, remainder]);
assert_eq!(div.uses(), vec![high, low, divisor]);
assert_eq!(div.stack_effect(), (3, 2));
assert!(div.may_throw());
assert_eq!(format!("{div}"), "v4, v5 = widediv v1:v0, v6");
}
#[test]
fn expanded_vector_ops_report_uses_effects_and_stack_shape() {
let dest = SsaVarId::from_index(0);
let addr = SsaVarId::from_index(1);
let mask = SsaVarId::from_index(2);
let passthrough = SsaVarId::from_index(3);
let indices = SsaVarId::from_index(4);
let masked_load: SsaOp<MockTarget> = SsaOp::VectorMaskedLoad {
dest,
addr,
mask,
passthrough: Some(passthrough),
vector_type: MockType::V4I32,
mode: VectorMaskMode::Merge,
};
assert_eq!(masked_load.uses(), vec![addr, mask, passthrough]);
assert_eq!(masked_load.stack_effect(), (3, 1));
assert_eq!(masked_load.effects().kind, SsaEffectKind::Read);
let scatter: SsaOp<MockTarget> = SsaOp::VectorScatter {
base: addr,
indices,
value: dest,
mask,
vector_type: MockType::V4I32,
};
assert_eq!(scatter.uses(), vec![addr, indices, dest, mask]);
assert_eq!(scatter.stack_effect(), (4, 0));
assert_eq!(scatter.effects().kind, SsaEffectKind::Write);
let fault = SsaVarId::from_index(5);
let faulting_load: SsaOp<MockTarget> = SsaOp::VectorFaultingLoad {
dest,
fault: Some(fault),
addr,
mask: Some(mask),
passthrough: Some(passthrough),
vector_type: MockType::V4I32,
fault_mode: VectorFaultMode::FaultOnlyFirst,
mask_mode: VectorMaskMode::Merge,
};
assert_eq!(faulting_load.defs().collect::<Vec<_>>(), vec![dest, fault]);
assert_eq!(faulting_load.uses(), vec![addr, mask, passthrough]);
assert_eq!(faulting_load.stack_effect(), (3, 1));
assert_eq!(faulting_load.effects().kind, SsaEffectKind::Read);
let second_dest = SsaVarId::from_index(6);
let segment_load: SsaOp<MockTarget> = SsaOp::VectorSegmentLoad {
dests: vec![dest, second_dest],
base: addr,
mask: Some(mask),
vector_type: MockType::V4I32,
segments: 2,
layout: VectorSegmentLayout::Interleaved,
};
assert_eq!(segment_load.dest(), Some(dest));
assert_eq!(
segment_load.defs().collect::<Vec<_>>(),
vec![dest, second_dest]
);
assert_eq!(segment_load.uses(), vec![addr, mask]);
assert_eq!(segment_load.stack_effect(), (2, 2));
assert_eq!(segment_load.effects().kind, SsaEffectKind::Read);
let segment_store: SsaOp<MockTarget> = SsaOp::VectorSegmentStore {
base: addr,
values: vec![dest, second_dest],
mask: Some(mask),
vector_type: MockType::V4I32,
segments: 2,
layout: VectorSegmentLayout::Interleaved,
};
assert_eq!(segment_store.dest(), None);
assert_eq!(segment_store.uses(), vec![addr, dest, second_dest, mask]);
assert_eq!(segment_store.stack_effect(), (4, 0));
assert_eq!(segment_store.effects().kind, SsaEffectKind::Write);
let bitmask: SsaOp<MockTarget> = SsaOp::VectorBitmask {
dest,
value: passthrough,
kind: VectorBitmaskKind::LaneMostSignificantBits,
};
assert_eq!(bitmask.uses(), vec![passthrough]);
assert_eq!(bitmask.stack_effect(), (1, 1));
assert!(bitmask.is_pure());
}
#[test]
fn fence_and_iret_no_dest_no_uses() {
let fence: SsaOp<MockTarget> = SsaOp::Fence {
kind: FenceKind::Full,
};
assert_eq!(fence.dest(), None);
assert!(fence.uses().is_empty());
let iret: SsaOp<MockTarget> = SsaOp::InterruptReturn;
assert_eq!(iret.dest(), None);
assert!(iret.uses().is_empty());
}
#[test]
fn new_pure_ops_classification() {
let d = SsaVarId::from_index(0);
let v = SsaVarId::from_index(1);
let a = SsaVarId::from_index(2);
assert!(SsaOp::<MockTarget>::Rol {
dest: d,
value: v,
amount: a
}
.is_pure());
assert!(SsaOp::<MockTarget>::Ror {
dest: d,
value: v,
amount: a
}
.is_pure());
assert!(!SsaOp::<MockTarget>::Rcl {
dest: d,
value: v,
amount: a
}
.is_pure());
assert!(!SsaOp::<MockTarget>::Rcr {
dest: d,
value: v,
amount: a
}
.is_pure());
assert!(SsaOp::<MockTarget>::BSwap { dest: d, src: v }.is_pure());
assert!(SsaOp::<MockTarget>::BRev { dest: d, src: v }.is_pure());
assert!(SsaOp::<MockTarget>::BitScanForward { dest: d, src: v }.is_pure());
assert!(SsaOp::<MockTarget>::BitScanReverse { dest: d, src: v }.is_pure());
assert!(SsaOp::<MockTarget>::Popcount { dest: d, src: v }.is_pure());
assert!(SsaOp::<MockTarget>::Parity { dest: d, src: v }.is_pure());
assert!(SsaOp::<MockTarget>::Select {
dest: d,
condition: v,
true_val: a,
false_val: d,
}
.is_pure());
}
#[test]
fn new_impure_ops_classification() {
assert!(!SsaOp::<MockTarget>::Fence {
kind: FenceKind::Full,
}
.is_pure());
assert!(!SsaOp::<MockTarget>::InterruptReturn.is_pure());
assert!(!SsaOp::<MockTarget>::CmpXchg {
dest: SsaVarId::from_index(0),
addr: SsaVarId::from_index(1),
expected: SsaVarId::from_index(2),
desired: SsaVarId::from_index(3),
}
.is_pure());
assert!(!SsaOp::<MockTarget>::AtomicRmw {
dest: SsaVarId::from_index(0),
addr: SsaVarId::from_index(1),
value: SsaVarId::from_index(2),
op: AtomicRmwOp::Add,
}
.is_pure());
}
#[test]
fn interrupt_return_is_terminator() {
assert!(SsaOp::<MockTarget>::InterruptReturn.is_terminator());
}
#[test]
fn atomic_ops_may_throw() {
assert!(SsaOp::<MockTarget>::CmpXchg {
dest: SsaVarId::from_index(0),
addr: SsaVarId::from_index(1),
expected: SsaVarId::from_index(2),
desired: SsaVarId::from_index(3),
}
.may_throw());
assert!(SsaOp::<MockTarget>::AtomicRmw {
dest: SsaVarId::from_index(0),
addr: SsaVarId::from_index(1),
value: SsaVarId::from_index(2),
op: AtomicRmwOp::Xchg,
}
.may_throw());
}
#[test]
fn new_pure_ops_no_throw() {
let d = SsaVarId::from_index(0);
let v = SsaVarId::from_index(1);
assert!(!SsaOp::<MockTarget>::Rol {
dest: d,
value: v,
amount: v
}
.may_throw());
assert!(!SsaOp::<MockTarget>::BSwap { dest: d, src: v }.may_throw());
assert!(!SsaOp::<MockTarget>::Select {
dest: d,
condition: v,
true_val: v,
false_val: v,
}
.may_throw());
}
#[test]
fn as_binary_op_rotations() {
let d = SsaVarId::from_index(0);
let v = SsaVarId::from_index(1);
let a = SsaVarId::from_index(2);
let rol = SsaOp::<MockTarget>::Rol {
dest: d,
value: v,
amount: a,
};
let info = rol.as_binary_op().unwrap();
assert_eq!(info.kind, BinaryOpKind::Rol);
assert_eq!(info.dest, d);
assert_eq!(info.left, v);
assert_eq!(info.right, a);
let ror = SsaOp::<MockTarget>::Ror {
dest: d,
value: v,
amount: a,
};
let info = ror.as_binary_op().unwrap();
assert_eq!(info.kind, BinaryOpKind::Ror);
}
#[test]
fn as_unary_op_bit_manip() {
let d = SsaVarId::from_index(0);
let s = SsaVarId::from_index(1);
let bswap = SsaOp::<MockTarget>::BSwap { dest: d, src: s };
let info = bswap.as_unary_op().unwrap();
assert_eq!(info.kind, UnaryOpKind::BSwap);
assert_eq!(info.dest, d);
assert_eq!(info.operand, s);
let brev = SsaOp::<MockTarget>::BRev { dest: d, src: s };
assert_eq!(brev.as_unary_op().unwrap().kind, UnaryOpKind::BRev);
let bsf = SsaOp::<MockTarget>::BitScanForward { dest: d, src: s };
assert_eq!(bsf.as_unary_op().unwrap().kind, UnaryOpKind::BitScanForward);
let bsr = SsaOp::<MockTarget>::BitScanReverse { dest: d, src: s };
assert_eq!(bsr.as_unary_op().unwrap().kind, UnaryOpKind::BitScanReverse);
let popcnt = SsaOp::<MockTarget>::Popcount { dest: d, src: s };
assert_eq!(popcnt.as_unary_op().unwrap().kind, UnaryOpKind::Popcount);
let parity = SsaOp::<MockTarget>::Parity { dest: d, src: s };
assert_eq!(parity.as_unary_op().unwrap().kind, UnaryOpKind::Parity);
}
#[test]
fn stack_effect_new_ops() {
let d = SsaVarId::from_index(0);
let v = SsaVarId::from_index(1);
let a = SsaVarId::from_index(2);
assert_eq!(
SsaOp::<MockTarget>::Rol {
dest: d,
value: v,
amount: a
}
.stack_effect(),
(2, 1)
);
assert_eq!(
SsaOp::<MockTarget>::Ror {
dest: d,
value: v,
amount: a
}
.stack_effect(),
(2, 1)
);
assert_eq!(
SsaOp::<MockTarget>::BSwap { dest: d, src: v }.stack_effect(),
(1, 1)
);
assert_eq!(
SsaOp::<MockTarget>::Popcount { dest: d, src: v }.stack_effect(),
(1, 1)
);
assert_eq!(
SsaOp::<MockTarget>::Select {
dest: d,
condition: v,
true_val: a,
false_val: d,
}
.stack_effect(),
(3, 1)
);
assert_eq!(
SsaOp::<MockTarget>::CmpXchg {
dest: d,
addr: v,
expected: a,
desired: d,
}
.stack_effect(),
(3, 1)
);
assert_eq!(
SsaOp::<MockTarget>::AtomicRmw {
dest: d,
addr: v,
value: a,
op: AtomicRmwOp::Add,
}
.stack_effect(),
(2, 1)
);
assert_eq!(
SsaOp::<MockTarget>::Fence {
kind: FenceKind::SeqCst
}
.stack_effect(),
(0, 0)
);
assert_eq!(SsaOp::<MockTarget>::InterruptReturn.stack_effect(), (0, 0));
}
#[test]
fn replace_uses_new_ops() {
let d = SsaVarId::from_index(0);
let old = SsaVarId::from_index(1);
let new = SsaVarId::from_index(99);
let other = SsaVarId::from_index(2);
let mut op: SsaOp<MockTarget> = SsaOp::Rol {
dest: d,
value: old,
amount: other,
};
assert_eq!(op.replace_uses(old, new), 1);
assert_eq!(op.uses(), vec![new, other]);
let mut op2: SsaOp<MockTarget> = SsaOp::BSwap { dest: d, src: old };
assert_eq!(op2.replace_uses(old, new), 1);
assert_eq!(op2.uses(), vec![new]);
let mut op3: SsaOp<MockTarget> = SsaOp::Select {
dest: d,
condition: old,
true_val: other,
false_val: old,
};
assert_eq!(op3.replace_uses(old, new), 2);
assert_eq!(op3.uses(), vec![new, other, new]);
let mut op4: SsaOp<MockTarget> = SsaOp::CmpXchg {
dest: d,
addr: old,
expected: other,
desired: old,
};
assert_eq!(op4.replace_uses(old, new), 2);
let mut op5: SsaOp<MockTarget> = SsaOp::AtomicRmw {
dest: d,
addr: old,
value: other,
op: AtomicRmwOp::Xor,
};
assert_eq!(op5.replace_uses(old, new), 1);
}
#[test]
fn set_dest_new_ops() {
let d = SsaVarId::from_index(0);
let new_d = SsaVarId::from_index(99);
let v = SsaVarId::from_index(1);
let a = SsaVarId::from_index(2);
let mut rol: SsaOp<MockTarget> = SsaOp::Rol {
dest: d,
value: v,
amount: a,
};
assert!(rol.set_dest(new_d));
assert_eq!(rol.dest(), Some(new_d));
let mut bswap: SsaOp<MockTarget> = SsaOp::BSwap { dest: d, src: v };
assert!(bswap.set_dest(new_d));
assert_eq!(bswap.dest(), Some(new_d));
let mut select: SsaOp<MockTarget> = SsaOp::Select {
dest: d,
condition: v,
true_val: a,
false_val: d,
};
assert!(select.set_dest(new_d));
assert_eq!(select.dest(), Some(new_d));
}
#[test]
fn set_dest_fails_for_no_dest_ops() {
assert!(!SsaOp::<MockTarget>::Fence {
kind: FenceKind::Full
}
.set_dest(SsaVarId::from_index(0)));
assert!(!SsaOp::<MockTarget>::InterruptReturn.set_dest(SsaVarId::from_index(0)));
}
#[test]
fn remap_variables_new_ops() {
let d0 = SsaVarId::from_index(0);
let d99 = SsaVarId::from_index(99);
let v1 = SsaVarId::from_index(1);
let v55 = SsaVarId::from_index(55);
let a2 = SsaVarId::from_index(2);
let mut map = HashMap::new();
map.insert(d0, d99);
map.insert(v1, v55);
let remap = |v: SsaVarId| map.get(&v).copied();
let rol = SsaOp::<MockTarget>::Rol {
dest: d0,
value: v1,
amount: a2,
};
let remapped = rol.remap_variables(remap);
assert_eq!(remapped.dest(), Some(d99));
assert!(remapped.uses().contains(&v55));
assert!(remapped.uses().contains(&a2));
}
#[test]
fn fence_kind_display() {
assert_eq!(format!("{}", FenceKind::Full), "full");
assert_eq!(format!("{}", FenceKind::Acquire), "acquire");
assert_eq!(format!("{}", FenceKind::Release), "release");
assert_eq!(format!("{}", FenceKind::AcqRel), "acqrel");
assert_eq!(format!("{}", FenceKind::SeqCst), "seqcst");
}
#[test]
fn atomic_rmw_op_display() {
assert_eq!(format!("{}", AtomicRmwOp::Xchg), "xchg");
assert_eq!(format!("{}", AtomicRmwOp::Add), "add");
assert_eq!(format!("{}", AtomicRmwOp::Sub), "sub");
assert_eq!(format!("{}", AtomicRmwOp::And), "and");
assert_eq!(format!("{}", AtomicRmwOp::Or), "or");
assert_eq!(format!("{}", AtomicRmwOp::Xor), "xor");
assert_eq!(format!("{}", AtomicRmwOp::Min), "min");
assert_eq!(format!("{}", AtomicRmwOp::Max), "max");
}
#[test]
fn flags_mask_constants() {
assert_ne!(FlagsMask::CARRY, FlagsMask::ZERO);
assert_ne!(FlagsMask::CARRY, FlagsMask::OVERFLOW);
assert_eq!(FlagsMask::CARRY.bits(), 1 << 0);
assert_eq!(FlagsMask::ZERO.bits(), 1 << 3);
assert_eq!(FlagsMask::OVERFLOW.bits(), 1 << 5);
assert!(FlagsMask::from_bits(0).is_empty());
assert!(!FlagsMask::CARRY.is_empty());
assert!(FlagsMask::x86_status().contains(FlagsMask::ADJUST));
assert!(FlagsMask::x86_status().contains(FlagsMask::OVERFLOW));
assert_eq!(
FlagsMask::from_flag_bit(NativeFlagBit::Carry),
FlagsMask::CARRY
);
assert_eq!(FlagsMask::CARRY.union(FlagsMask::ZERO).bits(), 0b1001);
}
#[test]
fn flags_mask_display() {
assert_eq!(format!("{}", FlagsMask::CARRY), "CF");
assert_eq!(
format!(
"{}",
FlagsMask::from_bits(FlagsMask::CARRY.bits() | FlagsMask::ZERO.bits())
),
"CF,ZF"
);
assert_eq!(format!("{}", FlagsMask::from_bits(0)), "none");
}
#[test]
fn flag_condition_display() {
assert_eq!(format!("{}", FlagCondition::Carry), "carry");
assert_eq!(format!("{}", FlagCondition::NotCarry), "not_carry");
assert_eq!(format!("{}", FlagCondition::Zero), "zero");
assert_eq!(format!("{}", FlagCondition::NotZero), "not_zero");
assert_eq!(format!("{}", FlagCondition::Overflow), "overflow");
assert_eq!(format!("{}", FlagCondition::NotOverflow), "not_overflow");
assert_eq!(format!("{}", FlagCondition::Negative), "negative");
assert_eq!(format!("{}", FlagCondition::Positive), "positive");
assert_eq!(format!("{}", FlagCondition::ParityEven), "parity_even");
assert_eq!(format!("{}", FlagCondition::ParityOdd), "parity_odd");
}
#[test]
fn flag_condition_variants_are_distinct() {
assert_ne!(FlagCondition::Carry, FlagCondition::Zero);
assert_ne!(FlagCondition::Overflow, FlagCondition::NotOverflow);
assert_ne!(FlagCondition::Negative, FlagCondition::Positive);
assert_ne!(FlagCondition::ParityEven, FlagCondition::ParityOdd);
}
#[test]
fn flag_condition_required_flags() {
assert_eq!(FlagCondition::Carry.required_flags(), FlagsMask::CARRY);
assert_eq!(FlagCondition::NotZero.required_flags(), FlagsMask::ZERO);
assert_eq!(
FlagCondition::NotOverflow.required_flags(),
FlagsMask::OVERFLOW
);
assert_eq!(FlagCondition::Positive.required_flags(), FlagsMask::SIGN);
assert_eq!(FlagCondition::ParityOdd.required_flags(), FlagsMask::PARITY);
}
#[test]
fn flag_producer_semantics_classify_defined_and_undefined_flags() {
assert!(FlagProducerSemantics::X86Arithmetic
.defined_mask()
.contains(FlagsMask::x86_status()));
assert!(FlagProducerSemantics::X86Logical
.defined_mask()
.contains(FlagsMask::CARRY.union(FlagsMask::OVERFLOW)));
assert!(!FlagProducerSemantics::X86Multiply
.defined_mask()
.contains(FlagsMask::ZERO));
assert!(FlagProducerSemantics::AArch64Arithmetic
.defined_mask()
.contains(FlagsMask::SIGN.union(FlagsMask::ZERO)));
let logical_writes = FlagProducerSemantics::X86Logical.writes();
assert!(logical_writes.contains(&FlagWrite::undefined(NativeFlagBit::Adjust)));
assert!(logical_writes.contains(&FlagWrite::cleared(NativeFlagBit::Carry)));
}
#[test]
fn flags_dest_returns_flags_on_flag_setting_ops() {
let d = SsaVarId::from_index(0);
let v = SsaVarId::from_index(1);
let flags_var = SsaVarId::from_index(99);
let add: SsaOp<MockTarget> = SsaOp::Add {
dest: d,
left: v,
right: v,
flags: Some(flags_var),
};
assert_eq!(add.flags_dest(), Some(flags_var));
let sub: SsaOp<MockTarget> = SsaOp::Sub {
dest: d,
left: v,
right: v,
flags: Some(flags_var),
};
assert_eq!(sub.flags_dest(), Some(flags_var));
let _and: SsaOp<MockTarget> = SsaOp::And {
dest: d,
left: v,
right: v,
flags: Some(flags_var),
};
}
#[test]
fn flags_dest_is_none_when_no_flags_set() {
let d = SsaVarId::from_index(0);
let v = SsaVarId::from_index(1);
let add: SsaOp<MockTarget> = SsaOp::Add {
dest: d,
left: v,
right: v,
flags: None,
};
assert_eq!(add.flags_dest(), None);
let mul: SsaOp<MockTarget> = SsaOp::Mul {
dest: d,
left: v,
right: v,
flags: None,
};
assert_eq!(mul.flags_dest(), None);
}
#[test]
fn flags_dest_is_none_for_non_flag_ops() {
let d = SsaVarId::from_index(0);
let v = SsaVarId::from_index(1);
let select: SsaOp<MockTarget> = SsaOp::Select {
dest: d,
condition: v,
true_val: v,
false_val: v,
};
assert_eq!(select.flags_dest(), None);
let call: SsaOp<MockTarget> = SsaOp::Call {
dest: Some(d),
method: 0,
args: vec![],
};
assert_eq!(call.flags_dest(), None);
}
#[test]
fn read_flags_dest_and_uses() {
let d = SsaVarId::from_index(0);
let flags_var = SsaVarId::from_index(1);
let op: SsaOp<MockTarget> = SsaOp::ReadFlags {
dest: d,
flags: flags_var,
mask: FlagsMask::ZERO,
};
assert_eq!(op.dest(), Some(d));
assert_eq!(op.uses(), vec![flags_var]);
assert!(op.is_pure());
assert!(!op.is_terminator());
assert!(!op.may_throw());
}
#[test]
fn read_flags_stack_effect() {
let d = SsaVarId::from_index(0);
let f = SsaVarId::from_index(1);
let op: SsaOp<MockTarget> = SsaOp::ReadFlags {
dest: d,
flags: f,
mask: FlagsMask::CARRY,
};
assert_eq!(op.stack_effect(), (1, 1));
}
#[test]
fn read_flags_replace_uses() {
let d = SsaVarId::from_index(0);
let old = SsaVarId::from_index(1);
let new = SsaVarId::from_index(99);
let mut op: SsaOp<MockTarget> = SsaOp::ReadFlags {
dest: d,
flags: old,
mask: FlagsMask::SIGN,
};
assert_eq!(op.replace_uses(old, new), 1);
assert_eq!(op.uses(), vec![new]);
}
#[test]
fn read_flags_remap_variables() {
let d0 = SsaVarId::from_index(0);
let d99 = SsaVarId::from_index(99);
let f1 = SsaVarId::from_index(1);
let f55 = SsaVarId::from_index(55);
let op: SsaOp<MockTarget> = SsaOp::ReadFlags {
dest: d0,
flags: f1,
mask: FlagsMask::OVERFLOW,
};
let remapped = op.remap_variables(|v| {
if v == d0 {
Some(d99)
} else if v == f1 {
Some(f55)
} else {
None
}
});
assert_eq!(remapped.dest(), Some(d99));
assert_eq!(remapped.uses(), vec![f55]);
}
#[test]
fn read_flags_display() {
let d = SsaVarId::from_index(0);
let f = SsaVarId::from_index(1);
let op: SsaOp<MockTarget> = SsaOp::ReadFlags {
dest: d,
flags: f,
mask: FlagsMask::ZERO,
};
assert_eq!(format!("{op}"), "v0 = readflags v1, ZF");
}
#[test]
fn branch_flags_is_terminator_with_successors() {
let f = SsaVarId::from_index(0);
let op: SsaOp<MockTarget> = SsaOp::BranchFlags {
flags: f,
condition: FlagCondition::Zero,
true_target: 1,
false_target: 2,
};
assert!(op.is_terminator());
assert!(!op.is_pure());
assert!(!op.may_throw());
assert_eq!(op.dest(), None);
assert_eq!(op.uses(), vec![f]);
assert_eq!(op.successors(), vec![1, 2]);
}
#[test]
fn branch_flags_stack_effect() {
let f = SsaVarId::from_index(0);
let op: SsaOp<MockTarget> = SsaOp::BranchFlags {
flags: f,
condition: FlagCondition::Carry,
true_target: 1,
false_target: 2,
};
assert_eq!(op.stack_effect(), (1, 0));
}
#[test]
fn branch_flags_redirect_target() {
let f = SsaVarId::from_index(0);
let mut op: SsaOp<MockTarget> = SsaOp::BranchFlags {
flags: f,
condition: FlagCondition::NotZero,
true_target: 3,
false_target: 5,
};
assert!(op.redirect_target(3, 7));
assert_eq!(op.successors(), vec![7, 5]);
assert!(!op.redirect_target(99, 42)); }
#[test]
fn branch_flags_remap_targets() {
let f = SsaVarId::from_index(0);
let mut op: SsaOp<MockTarget> = SsaOp::BranchFlags {
flags: f,
condition: FlagCondition::Overflow,
true_target: 2,
false_target: 4,
};
op.remap_branch_targets(|t| {
if t == 2 {
Some(10)
} else if t == 4 {
Some(20)
} else {
None
}
});
assert_eq!(op.successors(), vec![10, 20]);
}
#[test]
fn branch_flags_replace_uses() {
let old = SsaVarId::from_index(1);
let new = SsaVarId::from_index(99);
let mut op: SsaOp<MockTarget> = SsaOp::BranchFlags {
flags: old,
condition: FlagCondition::Positive,
true_target: 1,
false_target: 2,
};
assert_eq!(op.replace_uses(old, new), 1);
assert_eq!(op.uses(), vec![new]);
}
#[test]
fn branch_flags_display() {
let f = SsaVarId::from_index(0);
let op: SsaOp<MockTarget> = SsaOp::BranchFlags {
flags: f,
condition: FlagCondition::Carry,
true_target: 3,
false_target: 7,
};
assert_eq!(format!("{op}"), "branchflags carry B3, B7");
}
#[test]
fn unreachable_is_terminator_with_no_successors() {
assert!(SsaOp::<MockTarget>::Unreachable.is_terminator());
assert!(!SsaOp::<MockTarget>::Unreachable.is_pure());
assert!(!SsaOp::<MockTarget>::Unreachable.may_throw());
assert_eq!(SsaOp::<MockTarget>::Unreachable.dest(), None);
assert!(SsaOp::<MockTarget>::Unreachable.uses().is_empty());
assert!(SsaOp::<MockTarget>::Unreachable.successors().is_empty());
}
#[test]
fn unreachable_stack_effect() {
assert_eq!(SsaOp::<MockTarget>::Unreachable.stack_effect(), (0, 0));
}
#[test]
fn unreachable_display() {
assert_eq!(
format!("{}", SsaOp::<MockTarget>::Unreachable),
"unreachable"
);
}
#[test]
fn unreachable_no_variable_remap() {
let op = SsaOp::<MockTarget>::Unreachable;
let remapped = op.remap_variables(|_| unreachable!());
assert_eq!(remapped, SsaOp::Unreachable);
}
#[test]
fn flag_setting_op_has_two_defs() {
let d = SsaVarId::from_index(0);
let f = SsaVarId::from_index(99);
let v = SsaVarId::from_index(1);
let op: SsaOp<MockTarget> = SsaOp::Add {
dest: d,
left: v,
right: v,
flags: Some(f),
};
assert_eq!(op.dest(), Some(d));
assert_eq!(op.flags_dest(), Some(f));
assert!(op.is_pure());
}
#[test]
fn flag_setting_op_remap_remaps_flags() {
let d0 = SsaVarId::from_index(0);
let d99 = SsaVarId::from_index(99);
let f1 = SsaVarId::from_index(1);
let f55 = SsaVarId::from_index(55);
let op: SsaOp<MockTarget> = SsaOp::Add {
dest: d0,
left: d0,
right: d0,
flags: Some(f1),
};
let remapped = op.remap_variables(|v| {
if v == d0 {
Some(d99)
} else if v == f1 {
Some(f55)
} else {
None
}
});
assert_eq!(remapped.dest(), Some(d99));
assert_eq!(remapped.flags_dest(), Some(f55));
}
#[test]
fn flag_setting_op_display_shows_flags() {
let d = SsaVarId::from_index(0);
let f = SsaVarId::from_index(99);
let v = SsaVarId::from_index(1);
let with_flags: SsaOp<MockTarget> = SsaOp::Add {
dest: d,
left: v,
right: v,
flags: Some(f),
};
assert_eq!(format!("{with_flags}"), "v0 = add v1, v1 flags=v99");
let without_flags: SsaOp<MockTarget> = SsaOp::Add {
dest: d,
left: v,
right: v,
flags: None,
};
assert_eq!(format!("{without_flags}"), "v0 = add v1, v1");
}
#[test]
fn effect_summaries_classify_pure_memory_atomic_and_call_ops() {
let d = SsaVarId::from_index(0);
let a = SsaVarId::from_index(1);
let b = SsaVarId::from_index(2);
assert!(SsaOp::<MockTarget>::Add {
dest: d,
left: a,
right: b,
flags: None,
}
.effects()
.is_pure());
let load = SsaOp::<MockTarget>::LoadIndirect {
dest: d,
addr: a,
value_type: MockType::I32,
}
.effects();
assert_eq!(load.kind, SsaEffectKind::Read);
assert_eq!(load.trap, TrapClass::MemoryFault);
assert!(load.reads_memory());
assert!(!load.writes_memory());
let store = SsaOp::<MockTarget>::StoreIndirect {
addr: a,
value: b,
value_type: MockType::I32,
}
.effects();
assert_eq!(store.kind, SsaEffectKind::Write);
assert!(store.writes_memory());
let atomic = SsaOp::<MockTarget>::AtomicRmw {
dest: d,
addr: a,
value: b,
op: AtomicRmwOp::Add,
}
.effects();
assert_eq!(atomic.kind, SsaEffectKind::Atomic);
assert_eq!(atomic.memory_semantics, MemoryAccessSemantics::Atomic);
assert_eq!(atomic.ordering, Some(AtomicOrdering::SeqCst));
assert!(atomic.reads_memory());
assert!(atomic.writes_memory());
assert!(!atomic.removable_when_unused());
let struct_load_replicate = SsaOp::<MockTarget>::VectorStructLoadReplicate(Box::new(
VectorStructLoadReplicateData {
count: 2,
element_bits: 32,
outputs: vec![d],
inputs: vec![a],
},
))
.effects();
assert_eq!(struct_load_replicate.kind, SsaEffectKind::Read);
assert_eq!(struct_load_replicate.trap, TrapClass::MemoryFault);
assert!(struct_load_replicate.reads_memory());
assert!(!struct_load_replicate.writes_memory());
assert!(
!struct_load_replicate.is_pure(),
"ld2r/ld3r/ld4r is a memory load and must never be pure"
);
assert!(
!struct_load_replicate.removable_when_unused(),
"a faulting load must not be removable just because its dests are unused"
);
let setffr = SsaOp::<MockTarget>::VectorPredicateOp(Box::new(VectorPredicateOpData {
op: PredicateOpKind::SetFirstFault,
element_bits: 32,
outputs: vec![],
inputs: vec![],
}))
.effects();
assert!(
!setffr.removable_when_unused(),
"setffr writes the first-fault register and must not be DCE'd"
);
let count_active =
SsaOp::<MockTarget>::VectorPredicateOp(Box::new(VectorPredicateOpData {
op: PredicateOpKind::CountActive,
element_bits: 32,
outputs: vec![d],
inputs: vec![a],
}))
.effects();
assert!(count_active.is_pure(), "cntp computes a value and is pure");
let barrier = SsaOp::<MockTarget>::SystemOp(Box::new(NativeKindedData {
kind: SystemOpKind::Barrier,
mnemonic: "dmb".into(),
metadata: None,
clobbers: vec![],
outputs: vec![],
inputs: vec![],
}))
.effects();
assert_eq!(
barrier.kind,
SsaEffectKind::Fence,
"a memory barrier must classify as a fence, not a write"
);
assert_eq!(barrier.ordering, Some(AtomicOrdering::SeqCst));
let branch = SsaOp::<MockTarget>::Branch {
condition: a,
true_target: 1,
false_target: 2,
}
.effects();
assert_eq!(branch.control, ControlEffect::Terminator);
let fence = SsaOp::<MockTarget>::Fence {
kind: FenceKind::Acquire,
}
.effects();
assert_eq!(fence.memory_semantics, MemoryAccessSemantics::Fence);
assert_eq!(fence.ordering, Some(AtomicOrdering::Acquire));
assert_eq!(
SsaOp::<MockTarget>::Call {
dest: Some(d),
method: 1,
args: vec![a],
}
.effects()
.kind,
SsaEffectKind::Call
);
}
#[test]
fn system_op_kinds_never_declare_a_block_ending_control_effect() {
let all = [
SystemOpKind::CpuId,
SystemOpKind::Timestamp { aux: false },
SystemOpKind::Timestamp { aux: true },
SystemOpKind::ReadSysReg {
namespace: SysRegNamespace::X86Msr,
},
SystemOpKind::WriteSysReg {
namespace: SysRegNamespace::Arm64System,
},
SystemOpKind::ReadPerfCounter,
SystemOpKind::SystemCall,
SystemOpKind::SystemReturn,
SystemOpKind::Trap { vector: None },
SystemOpKind::Trap { vector: Some(0x80) },
SystemOpKind::InterruptReturn,
SystemOpKind::CacheMaintenance,
SystemOpKind::TlbMaintenance,
SystemOpKind::Barrier,
SystemOpKind::Privileged,
SystemOpKind::Hypervisor,
SystemOpKind::HardwareEngine,
SystemOpKind::Transaction(SystemTransactionKind::Start),
SystemOpKind::Transaction(SystemTransactionKind::Commit),
SystemOpKind::Transaction(SystemTransactionKind::Cancel),
SystemOpKind::Transaction(SystemTransactionKind::Test),
];
for kind in all {
let control = kind.effects().control;
assert!(
!matches!(
control,
ControlEffect::Terminator | ControlEffect::Return | ControlEffect::Throw
),
"SystemOpKind {kind:?} declares block-ending control {control:?} but \
SsaOp::SystemOp is not a terminator",
);
}
assert_eq!(
SystemOpKind::InterruptReturn.effects().control,
ControlEffect::Call,
);
assert!(!SsaOp::<MockTarget>::SystemOp(Box::new(NativeKindedData {
kind: SystemOpKind::InterruptReturn,
mnemonic: String::from("iret"),
metadata: None,
outputs: Vec::new(),
inputs: Vec::new(),
clobbers: Vec::new(),
}))
.is_terminator());
}
#[test]
fn op_class_groups_native_scalar_vector_memory_and_control_ops() {
let d = SsaVarId::from_index(0);
let a = SsaVarId::from_index(1);
let b = SsaVarId::from_index(2);
assert_eq!(
SsaOp::<MockTarget>::Add {
dest: d,
left: a,
right: b,
flags: None,
}
.class(),
SsaOpClass::Scalar
);
assert_eq!(
SsaOp::<MockTarget>::Add {
dest: d,
left: a,
right: b,
flags: Some(SsaVarId::from_index(3)),
}
.class(),
SsaOpClass::Flags
);
assert_eq!(
SsaOp::<MockTarget>::VectorBinary {
dest: d,
left: a,
right: b,
kind: VectorBinaryKind::Add,
element: VectorElement::default(),
}
.class(),
SsaOpClass::Vector
);
assert_eq!(
SsaOp::<MockTarget>::AtomicRmw {
dest: d,
addr: a,
value: b,
op: AtomicRmwOp::Add,
}
.class(),
SsaOpClass::Atomic
);
assert_eq!(
SsaOp::<MockTarget>::Branch {
condition: a,
true_target: 1,
false_target: 2,
}
.class(),
SsaOpClass::Control
);
assert_eq!(
SsaOp::<MockTarget>::NativeOpaque(Box::new(NativeOpaqueData {
mnemonic: "ud2".to_string(),
metadata: None,
outputs: Vec::new(),
inputs: Vec::new(),
clobbers: Vec::new(),
effects: SsaEffects::new(SsaEffectKind::Opaque, true),
}))
.class(),
SsaOpClass::NativeOpaque
);
assert_eq!(
SsaOp::<MockTarget>::NativeIntrinsic(Box::new(NativeIntrinsicData {
id: NativeIntrinsicId::Rdtsc,
mnemonic: "rdtsc".to_string(),
metadata: None,
outputs: vec![d],
inputs: Vec::new(),
clobbers: Vec::new(),
effects: SsaEffects::new(SsaEffectKind::Opaque, false),
}))
.class(),
SsaOpClass::NativeIntrinsic
);
}
fn visitor_battery() -> Vec<SsaOp<MockTarget>> {
let v: Vec<SsaVarId> = (0..8).map(SsaVarId::from_index).collect();
vec![
SsaOp::Const {
dest: v[0],
value: ConstValue::I32(7),
},
SsaOp::Add {
dest: v[0],
left: v[1],
right: v[2],
flags: Some(v[3]),
},
SsaOp::Neg {
dest: v[0],
operand: v[1],
flags: None,
},
SsaOp::Shr {
dest: v[0],
value: v[1],
amount: v[2],
unsigned: true,
flags: Some(v[3]),
},
SsaOp::WideMul {
low: v[0],
high: v[1],
left: v[2],
right: v[3],
unsigned: false,
},
SsaOp::WideDiv {
quotient: v[0],
remainder: v[1],
high: v[2],
low: v[3],
divisor: v[4],
unsigned: false,
},
SsaOp::FloatCompareFlags {
flags: v[0],
left: v[1],
right: v[2],
signaling: false,
},
SsaOp::Select {
dest: v[0],
condition: v[1],
true_val: v[2],
false_val: v[3],
},
SsaOp::StoreIndirect {
addr: v[0],
value: v[1],
value_type: MockType::I32,
},
SsaOp::AtomicCmpXchg {
old: v[0],
success: Some(v[1]),
addr: v[2],
expected: v[3],
desired: v[4],
success_ordering: AtomicOrdering::SeqCst,
failure_ordering: AtomicOrdering::Relaxed,
width: AtomicAccessWidth::Bits64,
weak: false,
volatile: false,
},
SsaOp::AtomicPairLoad {
first: v[0],
second: v[1],
addr: v[2],
first_type: MockType::I64,
second_type: MockType::I64,
ordering: AtomicOrdering::Acquire,
width: AtomicAccessWidth::Bits128,
volatile: false,
},
SsaOp::Call {
dest: Some(v[0]),
method: 3,
args: vec![v[1], v[2], v[3]],
},
SsaOp::CallIndirect {
dest: None,
fptr: v[0],
signature: 0,
args: vec![v[1]],
},
SsaOp::Return { value: Some(v[0]) },
SsaOp::Return { value: None },
SsaOp::Phi {
dest: v[0],
operands: vec![(0, v[1]), (1, v[2])],
},
SsaOp::NativeOpaque(Box::new(NativeOpaqueData {
mnemonic: "ud2".to_string(),
metadata: None,
outputs: vec![v[0], v[1]],
inputs: vec![v[2], v[3]],
clobbers: Vec::new(),
effects: SsaEffects::new(SsaEffectKind::Opaque, true),
})),
SsaOp::NativeIntrinsic(Box::new(NativeIntrinsicData {
id: NativeIntrinsicId::Cpuid,
mnemonic: "cpuid".to_string(),
metadata: None,
outputs: vec![v[0], v[1]],
inputs: vec![v[2]],
clobbers: Vec::new(),
effects: SsaEffects::new(SsaEffectKind::Opaque, false),
})),
SsaOp::BcdAdjust(Box::new(BcdAdjustData {
kind: BcdAdjustKind::AsciiMulAdjust,
base: 10,
mnemonic: "aam".to_string(),
metadata: None,
outputs: vec![v[0], v[1]],
inputs: vec![v[2]],
clobbers: Vec::new(),
})),
SsaOp::VectorDotProduct(Box::new(VectorDotProductData {
imm8: 0xff,
element_bits: 32,
outputs: vec![v[0]],
inputs: vec![v[1], v[2]],
})),
SsaOp::VectorMultiSad(Box::new(VecImm8Data {
imm8: 0x05,
outputs: vec![v[0]],
inputs: vec![v[1], v[2]],
})),
SsaOp::VectorStringCompare(Box::new(VectorStringCompareData {
imm8: 0x0c,
explicit_length: true,
result_index: true,
outputs: vec![v[0], v[1]],
inputs: vec![v[2], v[3]],
})),
SsaOp::VectorHorizontalMinPos(Box::new(VectorHorizontalMinPosData {
outputs: vec![v[0]],
inputs: vec![v[1]],
})),
SsaOp::VectorConditionalMove(Box::new(VectorConditionalMoveData {
condition: ByteMoveCondition::Negative,
outputs: vec![v[0]],
inputs: vec![v[1], v[2]],
})),
SsaOp::VectorIntersect(Box::new(VectorIntersectData {
outputs: vec![v[0], v[1]],
inputs: vec![v[2], v[3]],
})),
SsaOp::VectorShuffleBits(Box::new(VectorShuffleBitsData {
outputs: vec![v[0]],
inputs: vec![v[1], v[2]],
})),
SsaOp::VectorBitfield(Box::new(VectorBitfieldData {
insert: false,
index: 4,
length: 8,
outputs: vec![v[0]],
inputs: vec![v[1]],
})),
SsaOp::Jump { target: 4 },
SsaOp::Nop,
]
}
#[test]
fn vector_bitfield_defs_uses_effects_and_remap() {
let a = SsaVarId::from_index(0);
let b = SsaVarId::from_index(1);
let c = SsaVarId::from_index(2);
let op: SsaOp<MockTarget> = SsaOp::VectorBitfield(Box::new(VectorBitfieldData {
insert: true,
index: 16,
length: 8,
outputs: vec![a],
inputs: vec![b, c],
}));
assert_eq!(op.defs().collect::<Vec<_>>(), vec![a]);
assert_eq!(op.uses(), vec![b, c]);
assert_eq!(op.effects().kind, SsaEffectKind::Pure);
let remapped = op.remap_variables(|x| Some(SsaVarId::from_index(x.index() + 10)));
let SsaOp::VectorBitfield(data) = &remapped else {
unreachable!("remap must preserve the VectorBitfield variant")
};
assert!(data.insert);
assert_eq!(data.index, 16);
assert_eq!(data.length, 8);
assert_eq!(data.outputs, vec![SsaVarId::from_index(10)]);
assert_eq!(
data.inputs,
vec![SsaVarId::from_index(11), SsaVarId::from_index(12)]
);
}
#[test]
fn vector_horizontal_minpos_defs_uses_effects_and_remap() {
let a = SsaVarId::from_index(0);
let b = SsaVarId::from_index(1);
let op: SsaOp<MockTarget> =
SsaOp::VectorHorizontalMinPos(Box::new(VectorHorizontalMinPosData {
outputs: vec![a],
inputs: vec![b],
}));
assert_eq!(op.defs().collect::<Vec<_>>(), vec![a]);
assert_eq!(op.uses(), vec![b]);
assert_eq!(op.effects().kind, SsaEffectKind::Pure);
let remapped = op.remap_variables(|x| Some(SsaVarId::from_index(x.index() + 10)));
let SsaOp::VectorHorizontalMinPos(data) = &remapped else {
unreachable!("remap must preserve the VectorHorizontalMinPos variant")
};
assert_eq!(data.outputs, vec![SsaVarId::from_index(10)]);
assert_eq!(data.inputs, vec![SsaVarId::from_index(11)]);
}
#[test]
fn vector_string_compare_defs_uses_effects_and_remap() {
let v: Vec<SsaVarId> = (0..4).map(SsaVarId::from_index).collect();
let op: SsaOp<MockTarget> = SsaOp::VectorStringCompare(Box::new(VectorStringCompareData {
imm8: 0x0c,
explicit_length: false,
result_index: false,
outputs: vec![v[0], v[1]],
inputs: vec![v[2], v[3]],
}));
assert_eq!(op.defs().collect::<Vec<_>>(), vec![v[0], v[1]]);
assert_eq!(op.uses(), vec![v[2], v[3]]);
assert_eq!(op.effects().kind, SsaEffectKind::Pure);
let remapped = op.remap_variables(|x| Some(SsaVarId::from_index(x.index() + 10)));
let SsaOp::VectorStringCompare(data) = &remapped else {
unreachable!("remap must preserve the VectorStringCompare variant")
};
assert_eq!(data.imm8, 0x0c);
assert!(!data.explicit_length);
assert!(!data.result_index);
assert_eq!(
data.outputs,
vec![SsaVarId::from_index(10), SsaVarId::from_index(11)]
);
assert_eq!(
data.inputs,
vec![SsaVarId::from_index(12), SsaVarId::from_index(13)]
);
}
#[test]
fn vector_multi_sad_defs_uses_effects_and_remap() {
let a = SsaVarId::from_index(0);
let b = SsaVarId::from_index(1);
let c = SsaVarId::from_index(2);
let op: SsaOp<MockTarget> = SsaOp::VectorMultiSad(Box::new(VecImm8Data {
imm8: 0x05,
outputs: vec![a],
inputs: vec![b, c],
}));
assert_eq!(op.defs().collect::<Vec<_>>(), vec![a]);
assert_eq!(op.uses(), vec![b, c]);
assert_eq!(op.effects().kind, SsaEffectKind::Pure);
let remapped = op.remap_variables(|v| match v {
v if v == a => Some(SsaVarId::from_index(10)),
v if v == b => Some(SsaVarId::from_index(11)),
v if v == c => Some(SsaVarId::from_index(12)),
_ => None,
});
let SsaOp::VectorMultiSad(data) = &remapped else {
unreachable!("remap must preserve the VectorMultiSad variant")
};
assert_eq!(data.imm8, 0x05);
assert_eq!(data.outputs, vec![SsaVarId::from_index(10)]);
assert_eq!(
data.inputs,
vec![SsaVarId::from_index(11), SsaVarId::from_index(12)]
);
}
#[test]
fn vector_dot_product_defs_uses_effects_and_remap() {
let a = SsaVarId::from_index(0);
let b = SsaVarId::from_index(1);
let c = SsaVarId::from_index(2);
let op: SsaOp<MockTarget> = SsaOp::VectorDotProduct(Box::new(VectorDotProductData {
imm8: 0x31,
element_bits: 64,
outputs: vec![a],
inputs: vec![b, c],
}));
assert_eq!(op.defs().collect::<Vec<_>>(), vec![a]);
assert_eq!(op.uses(), vec![b, c]);
assert_eq!(op.effects().kind, SsaEffectKind::Pure);
let remapped = op.remap_variables(|v| match v {
v if v == a => Some(SsaVarId::from_index(10)),
v if v == b => Some(SsaVarId::from_index(11)),
v if v == c => Some(SsaVarId::from_index(12)),
_ => None,
});
let SsaOp::VectorDotProduct(data) = &remapped else {
unreachable!("remap must preserve the VectorDotProduct variant")
};
assert_eq!(data.imm8, 0x31);
assert_eq!(data.element_bits, 64);
assert_eq!(data.outputs, vec![SsaVarId::from_index(10)]);
assert_eq!(
data.inputs,
vec![SsaVarId::from_index(11), SsaVarId::from_index(12)]
);
}
#[test]
fn bcd_adjust_defs_uses_effects_and_remap() {
let a = SsaVarId::from_index(0);
let b = SsaVarId::from_index(1);
let c = SsaVarId::from_index(2);
let op: SsaOp<MockTarget> = SsaOp::BcdAdjust(Box::new(BcdAdjustData {
kind: BcdAdjustKind::AsciiDivAdjust,
base: 16,
mnemonic: "aad".to_string(),
metadata: None,
outputs: vec![a, b],
inputs: vec![c],
clobbers: Vec::new(),
}));
assert_eq!(op.defs().collect::<Vec<_>>(), vec![a, b]);
assert_eq!(op.uses(), vec![c]);
assert_eq!(op.effects().kind, SsaEffectKind::Pure);
assert!(!op.effects().may_throw);
let remapped = op.remap_variables(|v| match v {
v if v == a => Some(SsaVarId::from_index(10)),
v if v == b => Some(SsaVarId::from_index(11)),
v if v == c => Some(SsaVarId::from_index(12)),
_ => None,
});
let SsaOp::BcdAdjust(data) = &remapped else {
unreachable!("remap must preserve the BcdAdjust variant")
};
assert_eq!(data.kind, BcdAdjustKind::AsciiDivAdjust);
assert_eq!(data.base, 16);
assert_eq!(
data.outputs,
vec![SsaVarId::from_index(10), SsaVarId::from_index(11)]
);
assert_eq!(data.inputs, vec![SsaVarId::from_index(12)]);
}
#[allow(dead_code)]
fn op_variant_exhaustiveness_sentinel(op: SsaOp<MockTarget>) {
match op {
SsaOp::Const { .. } => {}
SsaOp::Add { .. } => {}
SsaOp::AddOvf { .. } => {}
SsaOp::Sub { .. } => {}
SsaOp::SubOvf { .. } => {}
SsaOp::Mul { .. } => {}
SsaOp::MulOvf { .. } => {}
SsaOp::WideMul { .. } => {}
SsaOp::Div { .. } => {}
SsaOp::Rem { .. } => {}
SsaOp::FloatCompareFlags { .. } => {}
SsaOp::WideDiv { .. } => {}
SsaOp::Neg { .. } => {}
SsaOp::And { .. } => {}
SsaOp::Or { .. } => {}
SsaOp::Xor { .. } => {}
SsaOp::Not { .. } => {}
SsaOp::Shl { .. } => {}
SsaOp::Shr { .. } => {}
SsaOp::Rol { .. } => {}
SsaOp::Ror { .. } => {}
SsaOp::Rcl { .. } => {}
SsaOp::Rcr { .. } => {}
SsaOp::BSwap { .. } => {}
SsaOp::BRev { .. } => {}
SsaOp::BitScanForward { .. } => {}
SsaOp::BitScanReverse { .. } => {}
SsaOp::Popcount { .. } => {}
SsaOp::Parity { .. } => {}
SsaOp::Ceq { .. } => {}
SsaOp::Clt { .. } => {}
SsaOp::Cgt { .. } => {}
SsaOp::BoolAnd { .. } => {}
SsaOp::BoolOr { .. } => {}
SsaOp::BoolXor { .. } => {}
SsaOp::BoolNot { .. } => {}
SsaOp::IntConv { .. } => {}
SsaOp::IntToPtr { .. } => {}
SsaOp::PtrToInt { .. } => {}
SsaOp::IntToFloat { .. } => {}
SsaOp::FloatToInt { .. } => {}
SsaOp::FloatConv { .. } => {}
SsaOp::Bitcast { .. } => {}
SsaOp::Select { .. } => {}
SsaOp::ReadFlags { .. } => {}
SsaOp::VectorUnary { .. } => {}
SsaOp::VectorBinary { .. } => {}
SsaOp::VectorTernary { .. } => {}
SsaOp::VectorPredicatedUnary { .. } => {}
SsaOp::VectorPredicatedBinary { .. } => {}
SsaOp::VectorPredicatedTernary { .. } => {}
SsaOp::VectorCompare { .. } => {}
SsaOp::VectorLoad { .. } => {}
SsaOp::VectorStore { .. } => {}
SsaOp::VectorMaskedLoad { .. } => {}
SsaOp::VectorMaskedStore { .. } => {}
SsaOp::VectorBroadcastLoad { .. } => {}
SsaOp::VectorGather { .. } => {}
SsaOp::VectorFaultingLoad { .. } => {}
SsaOp::VectorSegmentLoad { .. } => {}
SsaOp::VectorScatter { .. } => {}
SsaOp::VectorSegmentStore { .. } => {}
SsaOp::VectorExtract { .. } => {}
SsaOp::VectorInsert { .. } => {}
SsaOp::VectorSplat { .. } => {}
SsaOp::VectorShuffle { .. } => {}
SsaOp::VectorCast { .. } => {}
SsaOp::VectorReinterpret { .. } => {}
SsaOp::VectorPack { .. } => {}
SsaOp::VectorPackLoad { .. } => {}
SsaOp::VectorPackStore { .. } => {}
SsaOp::VectorZeroUpper { .. } => {}
SsaOp::VectorMaskUnary { .. } => {}
SsaOp::VectorMaskBinary { .. } => {}
SsaOp::VectorReduce { .. } => {}
SsaOp::VectorBitmask { .. } => {}
SsaOp::Jump { .. } => {}
SsaOp::Branch { .. } => {}
SsaOp::BranchCmp { .. } => {}
SsaOp::BranchFlags { .. } => {}
SsaOp::Switch { .. } => {}
SsaOp::IndirectBranch { .. } => {}
SsaOp::Return { .. } => {}
SsaOp::LoadField { .. } => {}
SsaOp::StoreField { .. } => {}
SsaOp::LoadStaticField { .. } => {}
SsaOp::StoreStaticField { .. } => {}
SsaOp::LoadFieldAddr { .. } => {}
SsaOp::LoadStaticFieldAddr { .. } => {}
SsaOp::LoadElement { .. } => {}
SsaOp::StoreElement { .. } => {}
SsaOp::LoadElementAddr { .. } => {}
SsaOp::PtrAdd { .. } => {}
SsaOp::ArrayLength { .. } => {}
SsaOp::LoadIndirect { .. } => {}
SsaOp::StoreIndirect { .. } => {}
SsaOp::NewObj { .. } => {}
SsaOp::NewArr { .. } => {}
SsaOp::CastClass { .. } => {}
SsaOp::IsInst { .. } => {}
SsaOp::Box { .. } => {}
SsaOp::Unbox { .. } => {}
SsaOp::UnboxAny { .. } => {}
SsaOp::SizeOf { .. } => {}
SsaOp::LoadToken { .. } => {}
SsaOp::Call { .. } => {}
SsaOp::CallVirt { .. } => {}
SsaOp::CallIndirect { .. } => {}
SsaOp::LoadFunctionPtr { .. } => {}
SsaOp::LoadVirtFunctionPtr { .. } => {}
SsaOp::LoadArg { .. } => {}
SsaOp::LoadLocal { .. } => {}
SsaOp::LoadArgAddr { .. } => {}
SsaOp::LoadLocalAddr { .. } => {}
SsaOp::Copy { .. } => {}
SsaOp::Pop { .. } => {}
SsaOp::Throw { .. } => {}
SsaOp::Rethrow => {}
SsaOp::EndFinally => {}
SsaOp::EndFilter { .. } => {}
SsaOp::InterruptReturn => {}
SsaOp::Unreachable => {}
SsaOp::Leave { .. } => {}
SsaOp::InitBlk { .. } => {}
SsaOp::CopyBlk { .. } => {}
SsaOp::Fence { .. } => {}
SsaOp::NativeOpaque(_) => {}
SsaOp::NativeIntrinsic(_) => {}
SsaOp::SystemOp(_) => {}
SsaOp::ComputeOp(_) => {}
SsaOp::BcdAdjust(_) => {}
SsaOp::VectorCrypto(_) => {}
SsaOp::TileOp(_) => {}
SsaOp::VectorPermute(_) => {}
SsaOp::VectorMultiplyAdd(_) => {}
SsaOp::VectorPackNarrow(_) => {}
SsaOp::VectorNarrowSaturate(_) => {}
SsaOp::VectorPredicateWhile(_) => {}
SsaOp::VectorPredicateBreak(_) => {}
SsaOp::VectorComplexAdd(_) => {}
SsaOp::VectorCountAdjust(_) => {}
SsaOp::VectorExtendInLane(_) => {}
SsaOp::VectorElementCount(_) => {}
SsaOp::VectorSveAddressGen(_) => {}
SsaOp::FlagAdjust(_) => {}
SsaOp::VectorStructLoadReplicate(_) => {}
SsaOp::VectorSmeMisc(_) => {}
SsaOp::VectorPredicateOp(_) => {}
SsaOp::VectorSveCompute(_) => {}
SsaOp::VectorReverseChunks(_) => {}
SsaOp::VectorMatrixMulAcc(_) => {}
SsaOp::VectorSmeOuterProduct(_) => {}
SsaOp::VectorPredicateGen(_) => {}
SsaOp::VectorFpHelper(_) => {}
SsaOp::VectorSvePermute(_) => {}
SsaOp::VectorTernaryLogic(_) => {}
SsaOp::VectorDotProduct(_) => {}
SsaOp::VectorMultiSad(_) => {}
SsaOp::VectorIntDotProduct(_) => {}
SsaOp::VectorStringCompare(_) => {}
SsaOp::VectorBitfield(_) => {}
SsaOp::VectorIntersect(_) => {}
SsaOp::VectorShuffleBits(_) => {}
SsaOp::VectorConditionalMove(_) => {}
SsaOp::VectorHorizontalMinPos(_) => {}
SsaOp::VectorComplexMul(_) => {}
SsaOp::VectorClassify(_) => {}
SsaOp::VectorHorizontalReduce(_) => {}
SsaOp::BlockString(_) => {}
SsaOp::WideCompareExchange(_) => {}
SsaOp::ComputeFlags { .. } => {}
SsaOp::CallClobber { .. } => {}
SsaOp::CmpXchg { .. } => {}
SsaOp::AtomicRmw { .. } => {}
SsaOp::AtomicLoad { .. } => {}
SsaOp::AtomicStore { .. } => {}
SsaOp::AtomicStoreConditional { .. } => {}
SsaOp::AtomicPairLoad { .. } => {}
SsaOp::AtomicPairStoreConditional { .. } => {}
SsaOp::AtomicExchange { .. } => {}
SsaOp::AtomicLockRmw { .. } => {}
SsaOp::AtomicCmpXchg { .. } => {}
SsaOp::AtomicPairCmpXchg { .. } => {}
SsaOp::InitObj { .. } => {}
SsaOp::CopyObj { .. } => {}
SsaOp::LoadObj { .. } => {}
SsaOp::StoreObj { .. } => {}
SsaOp::Nop => {}
SsaOp::Break => {}
SsaOp::Ckfinite { .. } => {}
SsaOp::FpClassify { .. } => {}
SsaOp::FpTranscendental(_) => {}
SsaOp::FpuControl(_) => {}
SsaOp::LocalAlloc { .. } => {}
SsaOp::Constrained { .. } => {}
SsaOp::Volatile => {}
SsaOp::Unaligned { .. } => {}
SsaOp::TailPrefix => {}
SsaOp::Readonly => {}
SsaOp::Phi { .. } => {}
}
}
fn all_sample_ops() -> Vec<SsaOp<MockTarget>> {
let sv = SsaVarId::from_index(1);
vec![
SsaOp::Const {
dest: sv,
value: ConstValue::I32(0),
},
SsaOp::Add {
dest: sv,
left: sv,
right: sv,
flags: Some(sv),
},
SsaOp::AddOvf {
dest: sv,
left: sv,
right: sv,
unsigned: false,
flags: Some(sv),
},
SsaOp::Sub {
dest: sv,
left: sv,
right: sv,
flags: Some(sv),
},
SsaOp::SubOvf {
dest: sv,
left: sv,
right: sv,
unsigned: false,
flags: Some(sv),
},
SsaOp::Mul {
dest: sv,
left: sv,
right: sv,
flags: Some(sv),
},
SsaOp::MulOvf {
dest: sv,
left: sv,
right: sv,
unsigned: false,
flags: Some(sv),
},
SsaOp::WideMul {
low: sv,
high: sv,
left: sv,
right: sv,
unsigned: false,
},
SsaOp::Div {
dest: sv,
left: sv,
right: sv,
unsigned: false,
flags: Some(sv),
},
SsaOp::Rem {
dest: sv,
left: sv,
right: sv,
unsigned: false,
flags: Some(sv),
},
SsaOp::FloatCompareFlags {
flags: sv,
left: sv,
right: sv,
signaling: false,
},
SsaOp::WideDiv {
quotient: sv,
remainder: sv,
high: sv,
low: sv,
divisor: sv,
unsigned: false,
},
SsaOp::Neg {
dest: sv,
operand: sv,
flags: Some(sv),
},
SsaOp::And {
dest: sv,
left: sv,
right: sv,
flags: Some(sv),
},
SsaOp::Or {
dest: sv,
left: sv,
right: sv,
flags: Some(sv),
},
SsaOp::Xor {
dest: sv,
left: sv,
right: sv,
flags: Some(sv),
},
SsaOp::Not {
dest: sv,
operand: sv,
flags: Some(sv),
},
SsaOp::Shl {
dest: sv,
value: sv,
amount: sv,
flags: Some(sv),
},
SsaOp::Shr {
dest: sv,
value: sv,
amount: sv,
unsigned: false,
flags: Some(sv),
},
SsaOp::Rol {
dest: sv,
value: sv,
amount: sv,
},
SsaOp::Ror {
dest: sv,
value: sv,
amount: sv,
},
SsaOp::Rcl {
dest: sv,
value: sv,
amount: sv,
},
SsaOp::Rcr {
dest: sv,
value: sv,
amount: sv,
},
SsaOp::BSwap { dest: sv, src: sv },
SsaOp::BRev { dest: sv, src: sv },
SsaOp::BitScanForward { dest: sv, src: sv },
SsaOp::BitScanReverse { dest: sv, src: sv },
SsaOp::Popcount { dest: sv, src: sv },
SsaOp::Parity { dest: sv, src: sv },
SsaOp::Ceq {
dest: sv,
left: sv,
right: sv,
},
SsaOp::Clt {
dest: sv,
left: sv,
right: sv,
unsigned: false,
},
SsaOp::Cgt {
dest: sv,
left: sv,
right: sv,
unsigned: false,
},
SsaOp::BoolAnd {
dest: sv,
left: sv,
right: sv,
},
SsaOp::BoolOr {
dest: sv,
left: sv,
right: sv,
},
SsaOp::BoolXor {
dest: sv,
left: sv,
right: sv,
},
SsaOp::BoolNot {
dest: sv,
value: sv,
},
SsaOp::IntConv {
dest: sv,
operand: sv,
target: MockType::I32,
overflow_check: false,
unsigned: false,
},
SsaOp::IntToPtr {
dest: sv,
operand: sv,
target: MockType::I32,
},
SsaOp::PtrToInt {
dest: sv,
operand: sv,
target: MockType::I32,
},
SsaOp::IntToFloat {
dest: sv,
operand: sv,
target: MockType::I32,
unsigned: false,
},
SsaOp::FloatToInt {
dest: sv,
operand: sv,
target: MockType::I32,
overflow_check: false,
unsigned: false,
},
SsaOp::FloatConv {
dest: sv,
operand: sv,
target: MockType::I32,
},
SsaOp::Bitcast {
dest: sv,
operand: sv,
target: MockType::I32,
},
SsaOp::Select {
dest: sv,
condition: sv,
true_val: sv,
false_val: sv,
},
SsaOp::ReadFlags {
dest: sv,
flags: sv,
mask: FlagsMask::from_bits(0),
},
SsaOp::VectorUnary {
dest: sv,
value: sv,
kind: VectorUnaryKind::Neg,
element: VectorElement {
kind: VectorElementKind::Integer,
bits: 32,
scalar: false,
},
},
SsaOp::VectorBinary {
dest: sv,
left: sv,
right: sv,
kind: VectorBinaryKind::Add,
element: VectorElement {
kind: VectorElementKind::Integer,
bits: 32,
scalar: false,
},
},
SsaOp::VectorTernary {
dest: sv,
first: sv,
second: sv,
third: sv,
kind: VectorTernaryKind::Fma,
},
SsaOp::VectorPredicatedUnary {
dest: sv,
value: sv,
mask: sv,
passthrough: Some(sv),
kind: VectorUnaryKind::Neg,
mode: VectorMaskMode::Merge,
},
SsaOp::VectorPredicatedBinary {
dest: sv,
left: sv,
right: sv,
mask: sv,
passthrough: Some(sv),
kind: VectorBinaryKind::Add,
mode: VectorMaskMode::Merge,
},
SsaOp::VectorPredicatedTernary {
dest: sv,
first: sv,
second: sv,
third: sv,
mask: sv,
passthrough: Some(sv),
kind: VectorTernaryKind::Fma,
mode: VectorMaskMode::Merge,
},
SsaOp::VectorCompare {
dest: sv,
left: sv,
right: sv,
kind: VectorCompareKind::Eq,
unsigned: false,
},
SsaOp::VectorLoad {
dest: sv,
addr: sv,
vector_type: MockType::I32,
},
SsaOp::VectorStore {
addr: sv,
value: sv,
vector_type: MockType::I32,
},
SsaOp::VectorMaskedLoad {
dest: sv,
addr: sv,
mask: sv,
passthrough: Some(sv),
vector_type: MockType::I32,
mode: VectorMaskMode::Merge,
},
SsaOp::VectorMaskedStore {
addr: sv,
value: sv,
mask: sv,
vector_type: MockType::I32,
},
SsaOp::VectorBroadcastLoad {
dest: sv,
addr: sv,
vector_type: MockType::I32,
},
SsaOp::VectorGather {
dest: sv,
base: sv,
indices: sv,
mask: sv,
passthrough: Some(sv),
vector_type: MockType::I32,
mode: VectorMaskMode::Merge,
},
SsaOp::VectorFaultingLoad {
dest: sv,
fault: Some(sv),
addr: sv,
mask: Some(sv),
passthrough: Some(sv),
vector_type: MockType::I32,
fault_mode: VectorFaultMode::Normal,
mask_mode: VectorMaskMode::Merge,
},
SsaOp::VectorSegmentLoad {
dests: vec![sv],
base: sv,
mask: Some(sv),
vector_type: MockType::I32,
segments: 0,
layout: VectorSegmentLayout::Interleaved,
},
SsaOp::VectorScatter {
base: sv,
indices: sv,
value: sv,
mask: sv,
vector_type: MockType::I32,
},
SsaOp::VectorSegmentStore {
base: sv,
values: vec![sv],
mask: Some(sv),
vector_type: MockType::I32,
segments: 0,
layout: VectorSegmentLayout::Interleaved,
},
SsaOp::VectorExtract {
dest: sv,
vector: sv,
lane: 0,
},
SsaOp::VectorInsert {
dest: sv,
vector: sv,
lane: 0,
value: sv,
},
SsaOp::VectorSplat {
dest: sv,
value: sv,
vector_type: MockType::I32,
},
SsaOp::VectorShuffle {
dest: sv,
left: sv,
right: Some(sv),
mask: VectorShuffleMask::new(vec![crate::target::VectorShuffleLane::Zero]),
},
SsaOp::VectorCast {
dest: sv,
value: sv,
target_type: MockType::I32,
kind: VectorCastKind::Signed,
},
SsaOp::VectorReinterpret {
dest: sv,
value: sv,
target_type: MockType::I32,
},
SsaOp::VectorPack {
dest: sv,
value: sv,
mask: sv,
passthrough: Some(sv),
vector_type: MockType::I32,
element_bits: 0,
kind: VectorPackKind::Compress,
mode: VectorMaskMode::Merge,
},
SsaOp::VectorPackLoad {
dest: sv,
addr: sv,
mask: sv,
passthrough: Some(sv),
vector_type: MockType::I32,
element_bits: 0,
kind: VectorPackKind::Compress,
mode: VectorMaskMode::Merge,
},
SsaOp::VectorPackStore {
addr: sv,
value: sv,
mask: sv,
vector_type: MockType::I32,
element_bits: 0,
kind: VectorPackKind::Compress,
},
SsaOp::VectorZeroUpper { all: false },
SsaOp::VectorMaskUnary {
dest: sv,
mask: sv,
kind: VectorMaskUnaryKind::Not,
},
SsaOp::VectorMaskBinary {
dest: sv,
left: sv,
right: sv,
kind: VectorMaskBinaryKind::And,
},
SsaOp::VectorReduce {
dest: sv,
value: sv,
kind: VectorReduceKind::Add,
},
SsaOp::VectorBitmask {
dest: sv,
value: sv,
kind: VectorBitmaskKind::LaneMostSignificantBits,
},
SsaOp::Jump { target: 0 },
SsaOp::Branch {
condition: sv,
true_target: 0,
false_target: 0,
},
SsaOp::BranchCmp {
left: sv,
right: sv,
cmp: CmpKind::Eq,
unsigned: false,
true_target: 0,
false_target: 0,
},
SsaOp::BranchFlags {
flags: sv,
condition: FlagCondition::Carry,
true_target: 0,
false_target: 0,
},
SsaOp::Switch {
value: sv,
targets: vec![0usize],
default: 0,
},
SsaOp::IndirectBranch {
target: sv,
resolved_targets: vec![0usize],
},
SsaOp::Return { value: Some(sv) },
SsaOp::LoadField {
dest: sv,
object: sv,
field: 0u32,
},
SsaOp::StoreField {
object: sv,
field: 0u32,
value: sv,
},
SsaOp::LoadStaticField {
dest: sv,
field: 0u32,
},
SsaOp::StoreStaticField {
field: 0u32,
value: sv,
},
SsaOp::LoadFieldAddr {
dest: sv,
object: sv,
field: 0u32,
},
SsaOp::LoadStaticFieldAddr {
dest: sv,
field: 0u32,
},
SsaOp::LoadElement {
dest: sv,
array: sv,
index: sv,
elem_type: MockType::I32,
},
SsaOp::StoreElement {
array: sv,
index: sv,
value: sv,
elem_type: MockType::I32,
},
SsaOp::LoadElementAddr {
dest: sv,
array: sv,
index: sv,
elem_type: 0u32,
},
SsaOp::PtrAdd {
dest: sv,
base: sv,
index: Some(sv),
stride: 4,
offset: 8,
result_type: MockType::I64,
},
SsaOp::ArrayLength {
dest: sv,
array: sv,
},
SsaOp::LoadIndirect {
dest: sv,
addr: sv,
value_type: MockType::I32,
},
SsaOp::StoreIndirect {
addr: sv,
value: sv,
value_type: MockType::I32,
},
SsaOp::NewObj {
dest: sv,
ctor: 0u32,
args: vec![sv],
},
SsaOp::NewArr {
dest: sv,
elem_type: 0u32,
length: sv,
},
SsaOp::CastClass {
dest: sv,
object: sv,
target_type: 0u32,
},
SsaOp::IsInst {
dest: sv,
object: sv,
target_type: 0u32,
},
SsaOp::Box {
dest: sv,
value: sv,
value_type: 0u32,
},
SsaOp::Unbox {
dest: sv,
object: sv,
value_type: 0u32,
},
SsaOp::UnboxAny {
dest: sv,
object: sv,
value_type: 0u32,
},
SsaOp::SizeOf {
dest: sv,
value_type: 0u32,
},
SsaOp::LoadToken {
dest: sv,
token: 0u32,
},
SsaOp::Call {
dest: Some(sv),
method: 0u32,
args: vec![sv],
},
SsaOp::CallVirt {
dest: Some(sv),
method: 0u32,
args: vec![sv],
},
SsaOp::CallIndirect {
dest: Some(sv),
fptr: sv,
signature: 0u32,
args: vec![sv],
},
SsaOp::LoadFunctionPtr {
dest: sv,
method: 0u32,
},
SsaOp::LoadVirtFunctionPtr {
dest: sv,
object: sv,
method: 0u32,
},
SsaOp::LoadArg {
dest: sv,
arg_index: 0,
},
SsaOp::LoadLocal {
dest: sv,
local_index: 0,
},
SsaOp::LoadArgAddr {
dest: sv,
arg_index: 0,
},
SsaOp::LoadLocalAddr {
dest: sv,
local_index: 0,
},
SsaOp::Copy { dest: sv, src: sv },
SsaOp::Pop { value: sv },
SsaOp::Throw { exception: sv },
SsaOp::Rethrow,
SsaOp::EndFinally,
SsaOp::EndFilter { result: sv },
SsaOp::InterruptReturn,
SsaOp::Unreachable,
SsaOp::Leave { target: 0 },
SsaOp::InitBlk {
dest_addr: sv,
value: sv,
size: sv,
},
SsaOp::CopyBlk {
dest_addr: sv,
src_addr: sv,
size: sv,
},
SsaOp::Fence {
kind: FenceKind::Full,
},
SsaOp::NativeOpaque(Box::new(NativeOpaqueData {
mnemonic: String::new(),
metadata: None,
outputs: vec![sv],
inputs: vec![sv],
clobbers: Vec::new(),
effects: SsaEffects::new(SsaEffectKind::Opaque, false),
})),
SsaOp::NativeIntrinsic(Box::new(NativeIntrinsicData {
id: NativeIntrinsicId::Cpuid,
mnemonic: String::new(),
metadata: None,
outputs: vec![sv],
inputs: vec![sv],
clobbers: Vec::new(),
effects: SsaEffects::new(SsaEffectKind::Opaque, false),
})),
SsaOp::SystemOp(Box::new(NativeKindedData {
kind: SystemOpKind::CpuId,
mnemonic: String::new(),
metadata: None,
outputs: vec![sv],
inputs: vec![sv],
clobbers: Vec::new(),
})),
SsaOp::ComputeOp(Box::new(NativeKindedData {
kind: ComputeKind::BitDeposit,
mnemonic: String::new(),
metadata: None,
outputs: vec![sv],
inputs: vec![sv],
clobbers: Vec::new(),
})),
SsaOp::BcdAdjust(Box::new(BcdAdjustData {
kind: BcdAdjustKind::DecimalAddAdjust,
base: 0,
mnemonic: String::new(),
metadata: None,
outputs: vec![sv],
inputs: vec![sv],
clobbers: Vec::new(),
})),
SsaOp::VectorCrypto(Box::new(KindedVecData {
kind: VectorCryptoKind::AesEncrypt,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::TileOp(Box::new(KindedVecData {
kind: TileOpKind::Zero,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorPermute(Box::new(VectorPermuteData {
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorMultiplyAdd(Box::new(KindedVecData {
kind: VectorMaddKind::MultiplyAddS16,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorPackNarrow(Box::new(VectorPackNarrowData {
unsigned: false,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorNarrowSaturate(Box::new(VectorNarrowSaturateData {
signed_src: false,
unsigned_dst: false,
rounding: false,
shift: 0,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorPredicateWhile(Box::new(VectorPredicateWhileData {
kind: VectorCompareKind::Eq,
unsigned: false,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorPredicateBreak(Box::new(VectorPredicateBreakData {
after: false,
pair: false,
propagate: false,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorComplexAdd(Box::new(VectorComplexAddData {
rotate_270: false,
saturate: false,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorCountAdjust(Box::new(VectorCountAdjustData {
decrement: false,
saturate: false,
signed: false,
by_predicate: false,
element_bits: 0,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorExtendInLane(Box::new(VectorExtendInLaneData {
signed: false,
source_bits: 0,
element_bits: 0,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorElementCount(Box::new(VectorElementCountData {
element_bits: 0,
multiplier: 0,
outputs: vec![sv],
})),
SsaOp::VectorSveAddressGen(Box::new(VectorSveAddressGenData {
signed_extend: Some(false),
shift: 0,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::FlagAdjust(Box::new(KindedVecData {
kind: FlagAdjustKind::InvertCarry,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorStructLoadReplicate(Box::new(VectorStructLoadReplicateData {
count: 0,
element_bits: 0,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorSmeMisc(Box::new(VectorSmeMiscData {
op: SmeMiscKind::AddHorizontal,
element_bits: 0,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorPredicateOp(Box::new(VectorPredicateOpData {
op: PredicateOpKind::CountActive,
element_bits: 0,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorSveCompute(Box::new(VectorSveComputeData {
op: SveComputeKind::AddCarryBottom,
element_bits: 0,
rotation: 0,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorReverseChunks(Box::new(VectorReverseChunksData {
chunk_bits: 0,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorMatrixMulAcc(Box::new(VectorMatrixMulAccData {
signed_a: false,
signed_b: false,
float: false,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorSmeOuterProduct(Box::new(VectorSmeOuterProductData {
subtract: false,
signed_a: false,
signed_b: false,
float: false,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorPredicateGen(Box::new(KindedVecData {
kind: PredicateGenKind::True,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorFpHelper(Box::new(KindedVecData {
kind: FpHelperKind::ReciprocalExponent,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorSvePermute(Box::new(KindedVecData {
kind: SvePermuteKind::Index,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorTernaryLogic(Box::new(VecImm8Data {
imm8: 0,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorDotProduct(Box::new(VectorDotProductData {
imm8: 0,
element_bits: 0,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorMultiSad(Box::new(VecImm8Data {
imm8: 0,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorIntDotProduct(Box::new(VectorIntDotProductData {
signed_a: false,
signed_b: false,
source_bits: 0,
dest_bits: 0,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorStringCompare(Box::new(VectorStringCompareData {
imm8: 0,
explicit_length: false,
result_index: false,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorBitfield(Box::new(VectorBitfieldData {
insert: false,
index: 0,
length: 0,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorIntersect(Box::new(VectorIntersectData {
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorShuffleBits(Box::new(VectorShuffleBitsData {
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorConditionalMove(Box::new(VectorConditionalMoveData {
condition: ByteMoveCondition::Zero,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorHorizontalMinPos(Box::new(VectorHorizontalMinPosData {
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorComplexMul(Box::new(KindedVecData {
kind: ComplexMulKind::Multiply,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorClassify(Box::new(VecImm8Data {
imm8: 0,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::VectorHorizontalReduce(Box::new(VectorHorizontalReduceData {
subtract: false,
unsigned: false,
source_bits: 0,
dest_bits: 0,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::BlockString(Box::new(BlockStringOpData {
kind: BlockStringKind::Compare,
prefix: BlockStringPrefix::Repeat,
element_bits: 0,
mnemonic: String::new(),
metadata: None,
outputs: vec![sv],
inputs: vec![sv],
clobbers: Vec::new(),
})),
SsaOp::WideCompareExchange(Box::new(WideCmpXchgData {
wide: false,
mnemonic: String::new(),
metadata: None,
outputs: vec![sv],
inputs: vec![sv],
clobbers: Vec::new(),
})),
SsaOp::ComputeFlags {
dest: sv,
inputs: vec![sv],
},
SsaOp::CallClobber { outputs: vec![sv] },
SsaOp::CmpXchg {
dest: sv,
addr: sv,
expected: sv,
desired: sv,
},
SsaOp::AtomicRmw {
dest: sv,
addr: sv,
value: sv,
op: AtomicRmwOp::Xchg,
},
SsaOp::AtomicLoad {
dest: sv,
addr: sv,
value_type: MockType::I32,
ordering: AtomicOrdering::Relaxed,
width: AtomicAccessWidth::Bits8,
volatile: false,
},
SsaOp::AtomicStore {
addr: sv,
value: sv,
value_type: MockType::I32,
ordering: AtomicOrdering::Relaxed,
width: AtomicAccessWidth::Bits8,
volatile: false,
},
SsaOp::AtomicStoreConditional {
status: sv,
addr: sv,
value: sv,
value_type: MockType::I32,
success_ordering: AtomicOrdering::Relaxed,
failure_ordering: AtomicOrdering::Relaxed,
width: AtomicAccessWidth::Bits8,
volatile: false,
},
SsaOp::AtomicPairLoad {
first: sv,
second: sv,
addr: sv,
first_type: MockType::I32,
second_type: MockType::I32,
ordering: AtomicOrdering::Relaxed,
width: AtomicAccessWidth::Bits8,
volatile: false,
},
SsaOp::AtomicPairStoreConditional {
status: sv,
addr: sv,
first_value: sv,
second_value: sv,
first_type: MockType::I32,
second_type: MockType::I32,
success_ordering: AtomicOrdering::Relaxed,
failure_ordering: AtomicOrdering::Relaxed,
width: AtomicAccessWidth::Bits8,
volatile: false,
},
SsaOp::AtomicExchange {
dest: sv,
addr: sv,
value: sv,
ordering: AtomicOrdering::Relaxed,
width: AtomicAccessWidth::Bits8,
volatile: false,
},
SsaOp::AtomicLockRmw {
dest: sv,
addr: sv,
value: sv,
op: AtomicRmwOp::Xchg,
ordering: AtomicOrdering::Relaxed,
width: AtomicAccessWidth::Bits8,
volatile: false,
},
SsaOp::AtomicCmpXchg {
old: sv,
success: Some(sv),
addr: sv,
expected: sv,
desired: sv,
success_ordering: AtomicOrdering::Relaxed,
failure_ordering: AtomicOrdering::Relaxed,
width: AtomicAccessWidth::Bits8,
weak: false,
volatile: false,
},
SsaOp::AtomicPairCmpXchg {
old_first: sv,
old_second: sv,
addr: sv,
expected_first: sv,
expected_second: sv,
desired_first: sv,
desired_second: sv,
success_ordering: AtomicOrdering::Relaxed,
failure_ordering: AtomicOrdering::Relaxed,
width: AtomicAccessWidth::Bits8,
weak: false,
volatile: false,
},
SsaOp::InitObj {
dest_addr: sv,
value_type: 0u32,
},
SsaOp::CopyObj {
dest_addr: sv,
src_addr: sv,
value_type: 0u32,
},
SsaOp::LoadObj {
dest: sv,
src_addr: sv,
value_type: 0u32,
},
SsaOp::StoreObj {
dest_addr: sv,
value: sv,
value_type: 0u32,
},
SsaOp::Nop,
SsaOp::Break,
SsaOp::Ckfinite {
dest: sv,
operand: sv,
},
SsaOp::FpClassify {
dest: sv,
operand: sv,
},
SsaOp::FpTranscendental(Box::new(KindedVecData {
kind: TranscendentalKind::Sin,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::FpuControl(Box::new(KindedVecData {
kind: FpuControlKind::LoadControlWord,
outputs: vec![sv],
inputs: vec![sv],
})),
SsaOp::LocalAlloc { dest: sv, size: sv },
SsaOp::Constrained {
constraint_type: 0u32,
},
SsaOp::Volatile,
SsaOp::Unaligned { alignment: 0 },
SsaOp::TailPrefix,
SsaOp::Readonly,
SsaOp::Phi {
dest: sv,
operands: vec![(0usize, sv)],
},
]
}
#[test]
fn all_variants_visitor_defs_uses_and_opcode_are_consistent() {
let ops = all_sample_ops();
assert_eq!(
ops.len(),
200,
"every SsaOp variant must have exactly one sample"
);
let mut names = std::collections::HashSet::new();
for op in &ops {
let mut visitor_defs = Vec::new();
let mut visitor_uses = Vec::new();
let mut first_def = None;
op.visit_operands(|role, var| match role {
OperandRole::Def => {
if first_def.is_none() {
first_def = Some(var);
}
visitor_defs.push(var);
}
OperandRole::FlagsDef => visitor_defs.push(var),
OperandRole::Use => visitor_uses.push(var),
});
assert_eq!(
visitor_defs,
op.defs().collect::<Vec<_>>(),
"defs mismatch for {op}"
);
assert_eq!(visitor_uses, op.uses(), "uses mismatch for {op}");
assert_eq!(op.dest(), first_def, "dest is not the first def for {op}");
assert!(
names.insert(op.opcode_name()),
"opcode_name {:?} is not unique",
op.opcode_name()
);
}
assert_eq!(names.len(), 200, "opcode_name must be unique per variant");
}
#[test]
fn classification_methods_are_self_consistent() {
for op in all_sample_ops() {
let eff = op.effects();
assert_eq!(
op.is_pure(),
eff.kind == SsaEffectKind::Pure && !eff.may_throw,
"is_pure() disagrees with effects() for {op}"
);
assert_eq!(
eff.may_throw,
op.may_throw(),
"effects().may_throw disagrees with may_throw() for {op}"
);
}
}
#[test]
fn visit_operands_agrees_with_defs_and_uses() {
for op in visitor_battery() {
let mut visited_defs = Vec::new();
let mut visited_uses = Vec::new();
op.visit_operands(|role, var| match role {
OperandRole::Def | OperandRole::FlagsDef => visited_defs.push(var),
OperandRole::Use => visited_uses.push(var),
});
let defs: Vec<SsaVarId> = op.defs().collect();
assert_eq!(visited_defs, defs, "defs mismatch for {op}");
assert_eq!(visited_uses, op.uses(), "uses mismatch for {op}");
if matches!(op, SsaOp::FloatCompareFlags { .. }) {
assert_eq!(op.dest(), None);
} else {
assert_eq!(op.dest(), defs.first().copied(), "dest mismatch for {op}");
}
}
}
#[test]
fn replace_def_covers_native_intrinsic_outputs() {
let old = SsaVarId::from_index(1);
let new = SsaVarId::from_index(9);
let mut op: SsaOp<MockTarget> = SsaOp::NativeIntrinsic(Box::new(NativeIntrinsicData {
id: NativeIntrinsicId::Rdtsc,
mnemonic: "rdtsc".to_string(),
metadata: None,
outputs: vec![SsaVarId::from_index(0), old],
inputs: vec![old],
clobbers: Vec::new(),
effects: SsaEffects::new(SsaEffectKind::Opaque, false),
}));
assert!(op.replace_def(old, new));
let SsaOp::NativeIntrinsic(data) = &op else {
unreachable!()
};
assert_eq!(data.outputs, vec![SsaVarId::from_index(0), new]);
assert_eq!(data.inputs, vec![old], "uses must stay untouched");
}
#[test]
fn system_op_defs_uses_effects_and_remap() {
let a = SsaVarId::from_index(1);
let b = SsaVarId::from_index(2);
let op: SsaOp<MockTarget> = SsaOp::SystemOp(Box::new(NativeKindedData {
kind: SystemOpKind::ReadSysReg {
namespace: SysRegNamespace::X86Msr,
},
mnemonic: "rdmsr".to_string(),
metadata: None,
outputs: vec![a],
inputs: vec![b],
clobbers: Vec::new(),
}));
assert_eq!(op.defs().collect::<Vec<_>>(), vec![a]);
assert!(op.uses().contains(&b), "input must appear in uses");
assert_eq!(op.effects().kind, SsaEffectKind::Read);
let remapped = op.remap_variables(|v| match v {
v if v == a => Some(SsaVarId::from_index(10)),
v if v == b => Some(SsaVarId::from_index(20)),
_ => None,
});
let SsaOp::SystemOp(data) = &remapped else {
unreachable!("remap must preserve the SystemOp variant")
};
assert_eq!(data.outputs, vec![SsaVarId::from_index(10)]);
assert_eq!(data.inputs, vec![SsaVarId::from_index(20)]);
assert_eq!(
data.kind,
SystemOpKind::ReadSysReg {
namespace: SysRegNamespace::X86Msr
},
"kind must survive remap"
);
}
#[test]
fn compute_op_defs_uses_effects_and_remap() {
let a = SsaVarId::from_index(1);
let b = SsaVarId::from_index(2);
let pdep: SsaOp<MockTarget> = SsaOp::ComputeOp(Box::new(NativeKindedData {
kind: ComputeKind::BitDeposit,
mnemonic: "pdep".to_string(),
metadata: None,
outputs: vec![a],
inputs: vec![b],
clobbers: Vec::new(),
}));
assert_eq!(pdep.defs().collect::<Vec<_>>(), vec![a]);
assert!(pdep.uses().contains(&b));
assert_eq!(pdep.effects().kind, SsaEffectKind::Pure);
let rdrand: SsaOp<MockTarget> = SsaOp::ComputeOp(Box::new(NativeKindedData {
kind: ComputeKind::Random {
from_entropy: false,
},
mnemonic: "rdrand".to_string(),
metadata: None,
outputs: vec![a],
inputs: Vec::new(),
clobbers: Vec::new(),
}));
assert_eq!(rdrand.effects().kind, SsaEffectKind::Read);
let remapped = pdep.remap_variables(|v| match v {
v if v == a => Some(SsaVarId::from_index(10)),
v if v == b => Some(SsaVarId::from_index(20)),
_ => None,
});
let SsaOp::ComputeOp(data) = &remapped else {
unreachable!("remap must preserve the ComputeOp variant")
};
assert_eq!(data.outputs, vec![SsaVarId::from_index(10)]);
assert_eq!(data.inputs, vec![SsaVarId::from_index(20)]);
assert_eq!(data.kind, ComputeKind::BitDeposit);
}
#[test]
fn block_string_defs_uses_effects_and_remap() {
let a = SsaVarId::from_index(1);
let b = SsaVarId::from_index(2);
let cmps: SsaOp<MockTarget> = SsaOp::BlockString(Box::new(BlockStringOpData {
kind: BlockStringKind::Compare,
prefix: BlockStringPrefix::RepeatEqual,
element_bits: 8,
mnemonic: "repe cmps".to_string(),
metadata: None,
outputs: vec![a],
inputs: vec![b],
clobbers: Vec::new(),
}));
assert_eq!(cmps.defs().collect::<Vec<_>>(), vec![a]);
assert!(cmps.uses().contains(&b));
assert_eq!(cmps.effects().kind, SsaEffectKind::ReadWrite);
let lods: SsaOp<MockTarget> = SsaOp::BlockString(Box::new(BlockStringOpData {
kind: BlockStringKind::Load,
prefix: BlockStringPrefix::Repeat,
element_bits: 32,
mnemonic: "rep lods".to_string(),
metadata: None,
outputs: vec![a],
inputs: Vec::new(),
clobbers: Vec::new(),
}));
assert_eq!(lods.effects().kind, SsaEffectKind::Read);
let remapped = cmps.remap_variables(|v| match v {
v if v == a => Some(SsaVarId::from_index(10)),
v if v == b => Some(SsaVarId::from_index(20)),
_ => None,
});
let SsaOp::BlockString(data) = &remapped else {
unreachable!("remap must preserve the BlockString variant")
};
assert_eq!(data.outputs, vec![SsaVarId::from_index(10)]);
assert_eq!(data.inputs, vec![SsaVarId::from_index(20)]);
assert_eq!(data.prefix, BlockStringPrefix::RepeatEqual);
assert_eq!(data.element_bits, 8);
}
#[test]
fn wide_compare_exchange_defs_uses_effects_and_remap() {
let a = SsaVarId::from_index(1);
let b = SsaVarId::from_index(2);
let op: SsaOp<MockTarget> = SsaOp::WideCompareExchange(Box::new(WideCmpXchgData {
wide: true,
mnemonic: "cmpxchg16b".to_string(),
metadata: None,
outputs: vec![a],
inputs: vec![b],
clobbers: Vec::new(),
}));
assert_eq!(op.defs().collect::<Vec<_>>(), vec![a]);
assert!(op.uses().contains(&b));
assert_eq!(op.effects().kind, SsaEffectKind::Atomic);
assert_eq!(op.effects().ordering, Some(AtomicOrdering::SeqCst));
let remapped = op.remap_variables(|v| match v {
v if v == a => Some(SsaVarId::from_index(10)),
v if v == b => Some(SsaVarId::from_index(20)),
_ => None,
});
let SsaOp::WideCompareExchange(data) = &remapped else {
unreachable!("remap must preserve the WideCompareExchange variant")
};
assert_eq!(data.outputs, vec![SsaVarId::from_index(10)]);
assert_eq!(data.inputs, vec![SsaVarId::from_index(20)]);
assert!(data.wide, "wide flag must survive remap");
}
#[test]
fn compute_flags_defs_uses_effects_and_remap() {
let dest = SsaVarId::from_index(1);
let a = SsaVarId::from_index(2);
let b = SsaVarId::from_index(3);
let op: SsaOp<MockTarget> = SsaOp::ComputeFlags {
dest,
inputs: vec![a, b],
};
assert_eq!(op.defs().collect::<Vec<_>>(), vec![dest]);
assert!(op.uses().contains(&a) && op.uses().contains(&b));
assert_eq!(op.effects().kind, SsaEffectKind::Pure);
let remapped = op.remap_variables(|v| match v {
v if v == dest => Some(SsaVarId::from_index(11)),
v if v == a => Some(SsaVarId::from_index(12)),
v if v == b => Some(SsaVarId::from_index(13)),
_ => None,
});
let SsaOp::ComputeFlags { dest, inputs } = &remapped else {
unreachable!("remap must preserve the ComputeFlags variant")
};
assert_eq!(*dest, SsaVarId::from_index(11));
assert_eq!(
inputs,
&vec![SsaVarId::from_index(12), SsaVarId::from_index(13)]
);
}
#[test]
fn call_clobber_defs_uses_effects_and_remap() {
let a = SsaVarId::from_index(1);
let b = SsaVarId::from_index(2);
let op: SsaOp<MockTarget> = SsaOp::CallClobber {
outputs: vec![a, b],
};
assert_eq!(op.defs().collect::<Vec<_>>(), vec![a, b]);
assert!(op.uses().is_empty());
assert_eq!(op.effects().kind, SsaEffectKind::Pure);
let remapped = op.remap_variables(|v| match v {
v if v == a => Some(SsaVarId::from_index(11)),
v if v == b => Some(SsaVarId::from_index(12)),
_ => None,
});
let SsaOp::CallClobber { outputs } = &remapped else {
unreachable!("remap must preserve the CallClobber variant")
};
assert_eq!(
outputs,
&vec![SsaVarId::from_index(11), SsaVarId::from_index(12)]
);
}
#[test]
fn payload_accessors_report_signedness_compare_and_memory() {
let d = SsaVarId::from_index(0);
let a = SsaVarId::from_index(1);
let b = SsaVarId::from_index(2);
let udiv: SsaOp<MockTarget> = SsaOp::Div {
dest: d,
left: a,
right: b,
unsigned: true,
flags: None,
};
assert_eq!(udiv.arith_signedness(), Some(Signedness::Unsigned));
assert_eq!(udiv.compare_kind(), None);
assert!(udiv.memory_effect().is_none());
let clt: SsaOp<MockTarget> = SsaOp::Clt {
dest: d,
left: a,
right: b,
unsigned: false,
};
assert_eq!(clt.arith_signedness(), Some(Signedness::Signed));
assert_eq!(clt.compare_kind(), Some(CmpKind::Lt));
let branch_cmp: SsaOp<MockTarget> = SsaOp::BranchCmp {
left: a,
right: b,
cmp: CmpKind::Ge,
unsigned: true,
true_target: 1,
false_target: 2,
};
assert_eq!(branch_cmp.compare_kind(), Some(CmpKind::Ge));
assert_eq!(branch_cmp.arith_signedness(), Some(Signedness::Unsigned));
let add: SsaOp<MockTarget> = SsaOp::Add {
dest: d,
left: a,
right: b,
flags: None,
};
assert_eq!(add.arith_signedness(), None);
assert_eq!(add.compare_kind(), None);
assert!(add.memory_effect().is_none());
let load: SsaOp<MockTarget> = SsaOp::LoadIndirect {
dest: d,
addr: a,
value_type: MockType::I32,
};
let effect = load.memory_effect().expect("load has a memory effect");
assert_eq!(effect.addr, a);
assert!(effect.reads);
assert!(!effect.writes);
assert_eq!(effect.value_type, Some(&MockType::I32));
let rmw: SsaOp<MockTarget> = SsaOp::AtomicRmw {
dest: d,
addr: a,
value: b,
op: AtomicRmwOp::Add,
};
let effect = rmw.memory_effect().expect("rmw has a memory effect");
assert!(effect.reads);
assert!(effect.writes);
assert_eq!(effect.value_type, None);
}
#[test]
fn similarity_class_groups_feature_extraction_families() {
let d = SsaVarId::from_index(0);
let a = SsaVarId::from_index(1);
let b = SsaVarId::from_index(2);
assert_eq!(
SsaOp::<MockTarget>::Const {
dest: d,
value: ConstValue::I32(1),
}
.similarity_class(),
SsaSimilarityClass::Constant
);
assert_eq!(
SsaOp::<MockTarget>::Add {
dest: d,
left: a,
right: b,
flags: None,
}
.similarity_class(),
SsaSimilarityClass::Arithmetic
);
assert_eq!(
SsaOp::<MockTarget>::Add {
dest: d,
left: a,
right: b,
flags: Some(SsaVarId::from_index(3)),
}
.similarity_class(),
SsaSimilarityClass::Flags
);
assert_eq!(
SsaOp::<MockTarget>::Xor {
dest: d,
left: a,
right: b,
flags: None,
}
.similarity_class(),
SsaSimilarityClass::Bitwise
);
assert_eq!(
SsaOp::<MockTarget>::Ceq {
dest: d,
left: a,
right: b,
}
.similarity_class(),
SsaSimilarityClass::Compare
);
assert_eq!(
SsaOp::<MockTarget>::VectorFaultingLoad {
dest: d,
fault: None,
addr: a,
mask: None,
passthrough: None,
vector_type: MockType::V4I32,
fault_mode: VectorFaultMode::Normal,
mask_mode: VectorMaskMode::Zero,
}
.similarity_class(),
SsaSimilarityClass::MemoryRead
);
assert_eq!(
SsaOp::<MockTarget>::VectorBinary {
dest: d,
left: a,
right: b,
kind: VectorBinaryKind::Add,
element: VectorElement::default(),
}
.similarity_class(),
SsaSimilarityClass::Vector
);
assert_eq!(
SsaOp::<MockTarget>::AtomicExchange {
dest: d,
addr: a,
value: b,
ordering: AtomicOrdering::SeqCst,
width: AtomicAccessWidth::Bits32,
volatile: false,
}
.similarity_class(),
SsaSimilarityClass::Atomic
);
assert_eq!(
SsaOp::<MockTarget>::Fence {
kind: FenceKind::SeqCst,
}
.similarity_class(),
SsaSimilarityClass::Fence
);
assert_eq!(
SsaOp::<MockTarget>::NativeOpaque(Box::new(NativeOpaqueData {
mnemonic: "ud2".to_string(),
metadata: None,
outputs: Vec::new(),
inputs: Vec::new(),
clobbers: Vec::new(),
effects: SsaEffects::new(SsaEffectKind::Opaque, true),
}))
.similarity_class(),
SsaSimilarityClass::NativeOpaque
);
}
#[test]
fn feature_token_serializes_stable_target_generic_shape() {
let d = SsaVarId::from_index(0);
let a = SsaVarId::from_index(1);
let b = SsaVarId::from_index(2);
let op: SsaOp<MockTarget> = SsaOp::AtomicCmpXchg {
old: d,
success: Some(SsaVarId::from_index(3)),
addr: a,
expected: b,
desired: d,
success_ordering: AtomicOrdering::SeqCst,
failure_ordering: AtomicOrdering::Acquire,
width: AtomicAccessWidth::Bits32,
weak: false,
volatile: true,
};
let token = op.feature_token();
assert_eq!(token.opcode, "atomic.cmpxchg");
assert_eq!(token.op_class, SsaOpClass::Atomic);
assert_eq!(token.similarity_class, SsaSimilarityClass::Atomic);
assert_eq!(token.effect_kind, SsaEffectKind::Atomic);
assert_eq!(token.def_count, 2);
assert_eq!(token.use_count, 3);
assert!(token.may_throw);
assert_eq!(
token.to_string(),
"op=atomic.cmpxchg;class=Atomic;sim=Atomic;effect=Atomic;defs=2;uses=3;throw=true"
);
}
#[test]
fn native_register_aliases_track_subregister_overlap() {
let rax = NativeRegister::new("x86_64", "gpr", "rax", "rax", 0, 64).unwrap();
let eax = NativeRegister::new("x86_64", "gpr", "rax", "eax", 0, 32).unwrap();
let ah = NativeRegister::new("x86_64", "gpr", "rax", "ah", 8, 8).unwrap();
let rbx = NativeRegister::new("x86_64", "gpr", "rbx", "rbx", 0, 64).unwrap();
let q0 = NativeRegister::new("aarch64", "simd", "v0", "q0", 0, 128).unwrap();
assert!(rax.aliases(&eax));
assert!(eax.aliases(&ah));
assert!(!rax.aliases(&rbx));
assert!(!rax.aliases(&q0));
assert!(NativeRegister::new("x86_64", "gpr", "rax", "al", 0, 0).is_none());
}
#[test]
fn native_state_accesses_classify_implicit_machine_state() {
let rflags = NativeStateAccess::implicit_read_write(
NativeStateLocation::Flags("rflags".to_string()),
Some(64),
)
.unwrap();
assert!(rflags.reads());
assert!(rflags.writes());
assert!(rflags.implicit);
let vl = NativeStateAccess::implicit_read(NativeStateLocation::VectorLength, None).unwrap();
assert!(vl.reads());
assert!(!vl.writes());
assert!(
NativeStateAccess::implicit_write(NativeStateLocation::StackPointer, Some(0)).is_none()
);
}
#[test]
fn native_clobbers_expose_structured_machine_state_categories() {
let rax = NativeRegister::new("x86_64", "gpr", "rax", "rax", 0, 64).unwrap();
let reg = NativeClobber::MachineState(
NativeStateAccess::implicit_read_write(NativeStateLocation::Register(rax), Some(64))
.unwrap(),
);
let flags = NativeClobber::Flags("eflags".to_string());
let memory = NativeClobber::MachineState(
NativeStateAccess::implicit_write(NativeStateLocation::Memory("io".to_string()), None)
.unwrap(),
);
assert!(reg.touches_registers());
assert!(!reg.touches_memory());
assert!(flags.touches_flags());
assert!(memory.touches_memory());
}
#[test]
fn native_opaque_tracks_outputs_inputs_and_effects() {
let out0 = SsaVarId::from_index(0);
let out1 = SsaVarId::from_index(1);
let in0 = SsaVarId::from_index(2);
let in1 = SsaVarId::from_index(3);
let op: SsaOp<MockTarget> = SsaOp::NativeOpaque(Box::new(NativeOpaqueData {
mnemonic: "mulx".to_string(),
metadata: Some(NativeInstructionMetadata::new(
Some("x86_64".to_string()),
Some(0x1000),
vec![0xc4, 0xe2, 0xfb, 0xf6],
)),
outputs: vec![out0, out1],
inputs: vec![in0, in1],
clobbers: vec![NativeClobber::Flags("eflags".to_string())],
effects: SsaEffects::new(SsaEffectKind::ReadWrite, true),
}));
assert_eq!(op.dest(), Some(out0));
assert_eq!(op.defs().collect::<Vec<_>>(), vec![out0, out1]);
assert_eq!(op.uses(), vec![in0, in1]);
assert_eq!(op.stack_effect(), (2, 2));
assert_eq!(
op.effects(),
SsaEffects::new(SsaEffectKind::ReadWrite, true)
);
assert!(op.may_throw());
assert!(!op.is_pure());
}
#[test]
fn native_opaque_rewrites_defs_and_uses_separately() {
let out0 = SsaVarId::from_index(0);
let out1 = SsaVarId::from_index(1);
let new_out = SsaVarId::from_index(9);
let input = SsaVarId::from_index(2);
let new_input = SsaVarId::from_index(10);
let mut op: SsaOp<MockTarget> = SsaOp::NativeOpaque(Box::new(NativeOpaqueData {
mnemonic: "opaque".to_string(),
metadata: None,
outputs: vec![out0, out1],
inputs: vec![input],
clobbers: Vec::new(),
effects: SsaEffects::pure(),
}));
assert!(op.replace_def(out1, new_out));
assert_eq!(op.replace_uses(input, new_input), 1);
assert_eq!(op.defs().collect::<Vec<_>>(), vec![out0, new_out]);
assert_eq!(op.uses(), vec![new_input]);
let remapped = op.remap_variables(|var| {
if var == out0 {
Some(SsaVarId::from_index(20))
} else if var == new_input {
Some(SsaVarId::from_index(30))
} else {
None
}
});
assert_eq!(
remapped.defs().collect::<Vec<_>>(),
vec![SsaVarId::from_index(20), new_out]
);
assert_eq!(remapped.uses(), vec![SsaVarId::from_index(30)]);
}
}
#[cfg(test)]
mod size_guards {
use super::*;
use crate::{
ir::{instruction::SsaInstruction, value::ConstValue},
testing::MockTarget,
};
#[test]
fn core_ir_types_stay_compact() {
assert!(
std::mem::size_of::<Option<SsaVarId>>() <= 4,
"Option<SsaVarId> grew to {} bytes; SsaVarId lost its niche",
std::mem::size_of::<Option<SsaVarId>>()
);
assert!(
std::mem::size_of::<ConstValue<MockTarget>>() <= 24,
"ConstValue grew to {} bytes; box the new heap-bearing arm",
std::mem::size_of::<ConstValue<MockTarget>>()
);
assert!(
std::mem::size_of::<SsaOp<MockTarget>>() <= 40,
"SsaOp grew to {} bytes; box the new fat variant's payload",
std::mem::size_of::<SsaOp<MockTarget>>()
);
assert!(
std::mem::size_of::<SsaInstruction<MockTarget>>() <= 48,
"SsaInstruction grew to {} bytes",
std::mem::size_of::<SsaInstruction<MockTarget>>()
);
}
}