///|
priv enum MemoryEffect {
None
Read
Write
Unknown
}
///|
priv struct OpcodeSemantics {
memory : MemoryEffect
may_trap : Bool
other_observable_effect : Bool
}
///|
fn pure_opcode_semantics() -> OpcodeSemantics {
{ memory: None, may_trap: false, other_observable_effect: false }
}
///|
fn trapping_opcode_semantics() -> OpcodeSemantics {
{ memory: None, may_trap: true, other_observable_effect: false }
}
///|
fn memory_read_opcode_semantics() -> OpcodeSemantics {
{ memory: Read, may_trap: true, other_observable_effect: false }
}
///|
fn memory_write_opcode_semantics() -> OpcodeSemantics {
{ memory: Write, may_trap: true, other_observable_effect: false }
}
///|
fn unknown_opcode_semantics() -> OpcodeSemantics {
{ memory: Unknown, may_trap: true, other_observable_effect: true }
}
///|
fn IntBinaryOp::semantics(self : IntBinaryOp) -> OpcodeSemantics {
match self {
SignedDiv | UnsignedDiv | SignedRem | UnsignedRem =>
trapping_opcode_semantics()
Add
| Sub
| Mul
| UnsignedMulHigh
| SignedMulHigh
| And
| Or
| Xor
| ShiftLeft
| SignedShiftRight
| UnsignedShiftRight
| RotateLeft
| RotateRight => pure_opcode_semantics()
}
}
///|
fn IntUnaryOp::semantics(self : IntUnaryOp) -> OpcodeSemantics {
match self {
Not | CountLeadingZeros | CountTrailingZeros | PopulationCount =>
pure_opcode_semantics()
}
}
///|
fn FloatBinaryOp::semantics(self : FloatBinaryOp) -> OpcodeSemantics {
match self {
Add | Sub | Mul | Div | Min | Max => pure_opcode_semantics()
}
}
///|
fn FloatUnaryOp::semantics(self : FloatUnaryOp) -> OpcodeSemantics {
match self {
Neg | Abs | Sqrt | Ceil | Floor | Trunc | Nearest => pure_opcode_semantics()
}
}
///|
fn ConversionOp::semantics(self : ConversionOp) -> OpcodeSemantics {
match self {
FloatToSignedInt | FloatToUnsignedInt => trapping_opcode_semantics()
IntReduce
| SignedExtend
| UnsignedExtend
| FloatPromote
| FloatDemote
| FloatToSignedIntSaturating
| FloatToUnsignedIntSaturating
| SignedIntToFloat
| UnsignedIntToFloat
| Bitcast => pure_opcode_semantics()
}
}
///|
fn ScalarOp::semantics(self : ScalarOp) -> OpcodeSemantics {
match self {
IntBinary(op) => op.semantics()
IntUnary(op) => op.semantics()
FloatBinary(op) => op.semantics()
FloatUnary(op) => op.semantics()
Convert(op) => op.semantics()
IntConst(_)
| FloatConst32(_)
| FloatConst64(_)
| IntCompare(_)
| FloatCompare(_)
| SignExtendFrom(_)
| Select
| Copy => pure_opcode_semantics()
}
}
///|
fn MemoryOp::semantics(self : MemoryOp) -> OpcodeSemantics {
match self {
Load(_) | LoadNarrow(_, _, _) => memory_read_opcode_semantics()
Store(_) | StoreNarrow(_) => memory_write_opcode_semantics()
Vector(LoadExtend(_, _) | LoadSplat(_) | LoadZero(_) | LoadLane(_, _)) =>
memory_read_opcode_semantics()
Vector(StoreLane(_, _)) => memory_write_opcode_semantics()
}
}
///|
fn CallOp::semantics(self : CallOp) -> OpcodeSemantics {
match self {
Direct(_, _) | Pointer(_, _) => unknown_opcode_semantics()
}
}
///|
fn VectorOp::semantics(self : VectorOp) -> OpcodeSemantics {
match self {
Const(_)
| Splat(_)
| ExtractLane(_, _, _)
| ReplaceLane(_, _)
| Shuffle(_)
| Swizzle
| Bitwise(_)
| Predicate(_)
| IntUnary(_, _)
| IntBinary(_, _)
| IntShift(_, _)
| IntCompare(_, _)
| Narrow(_, _)
| FloatUnary(_, _)
| FloatBinary(_, _)
| FloatCompare(_, _)
| Convert(_)
| Relaxed(_) => pure_opcode_semantics()
}
}
///|
fn Opcode::semantics(self : Opcode) -> OpcodeSemantics {
match self {
Scalar(op) => op.semantics()
Call(op) => op.semantics()
Memory(op) => op.semantics()
Ext(_, _) => unknown_opcode_semantics()
Vector(op) => op.semantics()
}
}
///|
fn MemoryEffect::reads_memory(self : MemoryEffect) -> Bool {
match self {
Read | Unknown => true
None | Write => false
}
}
///|
fn MemoryEffect::may_write_memory(self : MemoryEffect) -> Bool {
match self {
Write | Unknown => true
None | Read => false
}
}
///|
fn OpcodeSemantics::must_preserve_if_unused(self : OpcodeSemantics) -> Bool {
self.may_trap ||
self.other_observable_effect ||
self.memory.may_write_memory()
}