slonik/src/ir/stmt.rs

137 lines
4.6 KiB
Rust

//! Ordered block-local statements for Slonik IR.
//!
//! In the lift IR every value-producing operation is a statement anchored in a
//! block. This ensures memory order is always explicit in the statement stream
//! rather than floating in a sea of pure expressions.
use crate::ir::{BinaryOp, Block, CastOp, FloatCC, IntCC, MemSize, UnaryOp, Value};
use cranelift_entity::EntityList;
/// A compact list of SSA values.
pub type ValueList = EntityList<Value>;
/// A target block together with the SSA arguments passed to that block.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct BlockCall {
/// The destination block.
pub block: Block,
/// The arguments passed to the destination block.
pub args: ValueList,
}
/// An ordered statement inside a basic block.
///
/// Variants that carry a `result` field produce exactly one SSA value.
/// The `result` value's [`ValueDef`] points back to this statement.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum StmtData {
// ------------------------------------------------------------------ //
// Value-producing statements //
// ------------------------------------------------------------------ //
/// Signed integer literal. Result type determines the width.
IConst { imm: i64, result: Value },
/// 32-bit float literal stored as raw IEEE-754 bits.
F32Const { bits: u32, result: Value },
/// 64-bit float literal stored as raw IEEE-754 bits.
F64Const { bits: u64, result: Value },
/// Boolean literal.
BConst { value: bool, result: Value },
/// Unary operation.
Unary { op: UnaryOp, arg: Value, result: Value },
/// Binary operation.
Binary { op: BinaryOp, lhs: Value, rhs: Value, result: Value },
/// Cast or conversion.
Cast { op: CastOp, arg: Value, result: Value },
/// Integer comparison. Result type is always `bool`.
Icmp { cc: IntCC, lhs: Value, rhs: Value, result: Value },
/// Floating-point comparison. Result type is always `bool`.
Fcmp { cc: FloatCC, lhs: Value, rhs: Value, result: Value },
/// Conditional select. `cond` must be `bool`.
Select {
cond: Value,
if_true: Value,
if_false: Value,
result: Value,
},
/// Memory load. Anchored in the statement stream to preserve memory order.
Load { addr: Value, size: MemSize, result: Value },
// ------------------------------------------------------------------ //
// Side-effecting statements (no result value) //
// ------------------------------------------------------------------ //
/// Memory store.
Store { addr: Value, value: Value, size: MemSize },
/// Effectful call.
Call { callee: Value, args: ValueList },
// ------------------------------------------------------------------ //
// Terminators //
// ------------------------------------------------------------------ //
/// Unconditional jump.
Jump { dst: BlockCall },
/// Conditional branch.
BrIf {
cond: Value,
then_dst: BlockCall,
else_dst: BlockCall,
},
/// Return from the current body.
Return { values: ValueList },
}
impl StmtData {
/// Returns the result value produced by this statement, if any.
pub const fn result(self) -> Option<Value> {
match self {
Self::IConst { result, .. }
| Self::F32Const { result, .. }
| Self::F64Const { result, .. }
| Self::BConst { result, .. }
| Self::Unary { result, .. }
| Self::Binary { result, .. }
| Self::Cast { result, .. }
| Self::Icmp { result, .. }
| Self::Fcmp { result, .. }
| Self::Select { result, .. }
| Self::Load { result, .. } => Some(result),
Self::Store { .. }
| Self::Call { .. }
| Self::Jump { .. }
| Self::BrIf { .. }
| Self::Return { .. } => None,
}
}
/// Returns whether this statement is a block terminator.
pub const fn is_terminator(self) -> bool {
matches!(self, Self::Jump { .. } | Self::BrIf { .. } | Self::Return { .. })
}
/// Returns whether this statement may read memory.
pub const fn may_read_memory(self) -> bool {
matches!(self, Self::Load { .. } | Self::Call { .. })
}
/// Returns whether this statement may write memory.
pub const fn may_write_memory(self) -> bool {
matches!(self, Self::Store { .. } | Self::Call { .. })
}
}