396 lines
8.7 KiB
Rust
396 lines
8.7 KiB
Rust
//! Pure expression nodes and SSA value definitions for Slonik IR.
|
|
|
|
use core::fmt;
|
|
|
|
use crate::ir::{Block, Expr, Type, Value};
|
|
|
|
/// Memory access width in bytes.
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
|
pub enum MemSize {
|
|
S1,
|
|
S2,
|
|
S4,
|
|
S8,
|
|
S16,
|
|
}
|
|
|
|
impl MemSize {
|
|
/// Returns the access width in bytes.
|
|
pub const fn bytes(self) -> u8 {
|
|
match self {
|
|
Self::S1 => 1,
|
|
Self::S2 => 2,
|
|
Self::S4 => 4,
|
|
Self::S8 => 8,
|
|
Self::S16 => 16,
|
|
}
|
|
}
|
|
|
|
/// Returns the access width in bits.
|
|
pub const fn bits(self) -> u16 {
|
|
(self.bytes() as u16) * 8
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for MemSize {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "{}B", self.bytes())
|
|
}
|
|
}
|
|
|
|
/// Integer comparison condition codes.
|
|
///
|
|
/// These are used by [`ExprData::Icmp`].
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
|
pub enum IntCC {
|
|
Eq,
|
|
Ne,
|
|
|
|
Ult,
|
|
Ule,
|
|
Ugt,
|
|
Uge,
|
|
|
|
Slt,
|
|
Sle,
|
|
Sgt,
|
|
Sge,
|
|
}
|
|
|
|
impl IntCC {
|
|
/// Returns the logical inverse of this condition code.
|
|
pub const fn invert(self) -> Self {
|
|
match self {
|
|
Self::Eq => Self::Ne,
|
|
Self::Ne => Self::Eq,
|
|
|
|
Self::Ult => Self::Uge,
|
|
Self::Ule => Self::Ugt,
|
|
Self::Ugt => Self::Ule,
|
|
Self::Uge => Self::Ult,
|
|
|
|
Self::Slt => Self::Sge,
|
|
Self::Sle => Self::Sgt,
|
|
Self::Sgt => Self::Sle,
|
|
Self::Sge => Self::Slt,
|
|
}
|
|
}
|
|
|
|
/// Returns the condition code obtained by swapping the operands.
|
|
pub const fn swap_args(self) -> Self {
|
|
match self {
|
|
Self::Eq => Self::Eq,
|
|
Self::Ne => Self::Ne,
|
|
|
|
Self::Ult => Self::Ugt,
|
|
Self::Ule => Self::Uge,
|
|
Self::Ugt => Self::Ult,
|
|
Self::Uge => Self::Ule,
|
|
|
|
Self::Slt => Self::Sgt,
|
|
Self::Sle => Self::Sge,
|
|
Self::Sgt => Self::Slt,
|
|
Self::Sge => Self::Sle,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for IntCC {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let s = match self {
|
|
Self::Eq => "eq",
|
|
Self::Ne => "ne",
|
|
|
|
Self::Ult => "ult",
|
|
Self::Ule => "ule",
|
|
Self::Ugt => "ugt",
|
|
Self::Uge => "uge",
|
|
|
|
Self::Slt => "slt",
|
|
Self::Sle => "sle",
|
|
Self::Sgt => "sgt",
|
|
Self::Sge => "sge",
|
|
};
|
|
|
|
f.write_str(s)
|
|
}
|
|
}
|
|
|
|
/// Floating-point comparison condition codes.
|
|
///
|
|
/// These are used by [`ExprData::Fcmp`].
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
|
pub enum FloatCC {
|
|
Eq,
|
|
Ne,
|
|
Lt,
|
|
Le,
|
|
Gt,
|
|
Ge,
|
|
Ordered,
|
|
Unordered,
|
|
}
|
|
|
|
impl FloatCC {
|
|
/// Returns the logical inverse of this condition code.
|
|
pub const fn invert(self) -> Self {
|
|
match self {
|
|
Self::Eq => Self::Ne,
|
|
Self::Ne => Self::Eq,
|
|
Self::Lt => Self::Ge,
|
|
Self::Le => Self::Gt,
|
|
Self::Gt => Self::Le,
|
|
Self::Ge => Self::Lt,
|
|
Self::Ordered => Self::Unordered,
|
|
Self::Unordered => Self::Ordered,
|
|
}
|
|
}
|
|
|
|
/// Returns the condition code obtained by swapping the operands.
|
|
pub const fn swap_args(self) -> Self {
|
|
match self {
|
|
Self::Eq => Self::Eq,
|
|
Self::Ne => Self::Ne,
|
|
Self::Lt => Self::Gt,
|
|
Self::Le => Self::Ge,
|
|
Self::Gt => Self::Lt,
|
|
Self::Ge => Self::Le,
|
|
Self::Ordered => Self::Ordered,
|
|
Self::Unordered => Self::Unordered,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for FloatCC {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let s = match self {
|
|
Self::Eq => "eq",
|
|
Self::Ne => "ne",
|
|
Self::Lt => "lt",
|
|
Self::Le => "le",
|
|
Self::Gt => "gt",
|
|
Self::Ge => "ge",
|
|
Self::Ordered => "ord",
|
|
Self::Unordered => "uno",
|
|
};
|
|
|
|
f.write_str(s)
|
|
}
|
|
}
|
|
|
|
/// Unary expression operators.
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
|
pub enum UnaryOp {
|
|
/// Integer or bitvector negation.
|
|
Neg,
|
|
|
|
/// Bitwise not.
|
|
Not,
|
|
|
|
/// Floating-point negation.
|
|
FNeg,
|
|
}
|
|
|
|
impl fmt::Display for UnaryOp {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let s = match self {
|
|
Self::Neg => "neg",
|
|
Self::Not => "not",
|
|
Self::FNeg => "fneg",
|
|
};
|
|
|
|
f.write_str(s)
|
|
}
|
|
}
|
|
|
|
/// Binary expression operators.
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
|
pub enum BinaryOp {
|
|
IAdd,
|
|
ISub,
|
|
IMul,
|
|
UDiv,
|
|
SDiv,
|
|
URem,
|
|
SRem,
|
|
|
|
And,
|
|
Or,
|
|
Xor,
|
|
|
|
Shl,
|
|
LShr,
|
|
AShr,
|
|
|
|
FAdd,
|
|
FSub,
|
|
FMul,
|
|
FDiv,
|
|
}
|
|
|
|
impl BinaryOp {
|
|
/// Returns whether this binary operator is commutative.
|
|
pub const fn is_commutative(self) -> bool {
|
|
matches!(
|
|
self,
|
|
Self::IAdd | Self::IMul | Self::And | Self::Or | Self::Xor | Self::FAdd | Self::FMul
|
|
)
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for BinaryOp {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let s = match self {
|
|
Self::IAdd => "iadd",
|
|
Self::ISub => "isub",
|
|
Self::IMul => "imul",
|
|
Self::UDiv => "udiv",
|
|
Self::SDiv => "sdiv",
|
|
Self::URem => "urem",
|
|
Self::SRem => "srem",
|
|
|
|
Self::And => "and",
|
|
Self::Or => "or",
|
|
Self::Xor => "xor",
|
|
|
|
Self::Shl => "shl",
|
|
Self::LShr => "lshr",
|
|
Self::AShr => "ashr",
|
|
|
|
Self::FAdd => "fadd",
|
|
Self::FSub => "fsub",
|
|
Self::FMul => "fmul",
|
|
Self::FDiv => "fdiv",
|
|
};
|
|
|
|
f.write_str(s)
|
|
}
|
|
}
|
|
|
|
/// Cast and conversion operators.
|
|
///
|
|
/// The source type is taken from the operand value.
|
|
/// The destination type is taken from the result value.
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
|
pub enum CastOp {
|
|
/// Zero-extension.
|
|
Zext,
|
|
|
|
/// Sign-extension.
|
|
Sext,
|
|
|
|
/// Truncation.
|
|
Trunc,
|
|
|
|
/// Bit-preserving reinterpretation.
|
|
Bitcast,
|
|
|
|
/// Integer-to-pointer conversion.
|
|
IntToPtr,
|
|
|
|
/// Pointer-to-integer conversion.
|
|
PtrToInt,
|
|
}
|
|
|
|
impl fmt::Display for CastOp {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let s = match self {
|
|
Self::Zext => "zext",
|
|
Self::Sext => "sext",
|
|
Self::Trunc => "trunc",
|
|
Self::Bitcast => "bitcast",
|
|
Self::IntToPtr => "inttoptr",
|
|
Self::PtrToInt => "ptrtoint",
|
|
};
|
|
|
|
f.write_str(s)
|
|
}
|
|
}
|
|
|
|
/// A pure value-producing expression node.
|
|
///
|
|
/// Every expression defines exactly one SSA value.
|
|
/// The type of that value is stored in the corresponding [`ValueData`].
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub enum ExprData {
|
|
/// A signed integer literal.
|
|
///
|
|
/// The result type determines the intended width.
|
|
IConst { imm: i64 },
|
|
|
|
/// A 32-bit floating-point literal stored as raw IEEE-754 bits.
|
|
F32Const { bits: u32 },
|
|
|
|
/// A 64-bit floating-point literal stored as raw IEEE-754 bits.
|
|
F64Const { bits: u64 },
|
|
|
|
/// A boolean literal.
|
|
BConst { value: bool },
|
|
|
|
/// A unary operation.
|
|
Unary { op: UnaryOp, arg: Value },
|
|
|
|
/// A binary operation.
|
|
Binary {
|
|
op: BinaryOp,
|
|
lhs: Value,
|
|
rhs: Value,
|
|
},
|
|
|
|
/// A cast or conversion.
|
|
Cast { op: CastOp, arg: Value },
|
|
|
|
/// An integer comparison.
|
|
///
|
|
/// The result type is expected to be `bool`.
|
|
Icmp { cc: IntCC, lhs: Value, rhs: Value },
|
|
|
|
/// A floating-point comparison.
|
|
///
|
|
/// The result type is expected to be `bool`.
|
|
Fcmp { cc: FloatCC, lhs: Value, rhs: Value },
|
|
|
|
/// A conditional select.
|
|
///
|
|
/// `cond` must be a boolean value.
|
|
Select {
|
|
cond: Value,
|
|
if_true: Value,
|
|
if_false: Value,
|
|
},
|
|
|
|
/// A memory load.
|
|
Load { addr: Value, size: MemSize },
|
|
}
|
|
|
|
impl ExprData {
|
|
/// Returns whether this expression may observe memory.
|
|
pub const fn may_read_memory(&self) -> bool {
|
|
matches!(self, Self::Load { .. })
|
|
}
|
|
|
|
/// Returns whether this expression may trap.
|
|
pub const fn may_trap(&self) -> bool {
|
|
matches!(self, Self::Load { .. })
|
|
}
|
|
}
|
|
|
|
/// The definition site of an SSA value.
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
pub enum ValueDef {
|
|
/// A value defined by an expression.
|
|
Expr(Expr),
|
|
|
|
/// A block parameter at position `index`.
|
|
Param(Block, u16),
|
|
}
|
|
|
|
/// Metadata attached to an SSA value.
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
pub struct ValueData {
|
|
/// The semantic type of the value.
|
|
pub ty: Type,
|
|
|
|
/// The definition site of the value.
|
|
pub def: ValueDef,
|
|
}
|