slonik/src/ir/expr.rs

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,
}