use std::fmt;
use super::*;
use crate::ir::variable::SsaVarId;
#[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>,
pub reverse: bool,
}
#[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,
}