2026-03-24 13:40:36 +00:00

537 lines
16 KiB
Rust

use crate::opcode::OpCode;
/// Specification for a single OpCode.
/// All JMP/JMP_IF_* immediate are u32 absolute offsets from function start.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct OpcodeSpec {
pub name: &'static str,
pub imm_bytes: u8, // immediate payload size (decode)
pub pops: u16, // slots popped
pub pushes: u16, // slots pushed
pub is_branch: bool, // has a control-flow target
pub is_terminator: bool, // ends basic block: JMP/RET/TRAP/HALT
pub may_trap: bool, // runtime trap possible
/// Marks this opcode as a VM safepoint. Used by GC/scheduler policies.
pub is_safepoint: bool,
}
pub trait OpCodeSpecExt {
fn spec(&self) -> OpcodeSpec;
}
impl OpCodeSpecExt for OpCode {
fn spec(&self) -> OpcodeSpec {
match self {
OpCode::Nop => OpcodeSpec {
name: "NOP",
imm_bytes: 0,
pops: 0,
pushes: 0,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::Halt => OpcodeSpec {
name: "HALT",
imm_bytes: 0,
pops: 0,
pushes: 0,
is_branch: false,
is_terminator: true,
may_trap: false,
is_safepoint: false,
},
OpCode::Jmp => OpcodeSpec {
name: "JMP",
imm_bytes: 4,
pops: 0,
pushes: 0,
is_branch: true,
is_terminator: true,
may_trap: false,
is_safepoint: false,
},
OpCode::JmpIfFalse => OpcodeSpec {
name: "JMP_IF_FALSE",
imm_bytes: 4,
pops: 1,
pushes: 0,
is_branch: true,
is_terminator: false,
may_trap: true,
is_safepoint: false,
},
OpCode::JmpIfTrue => OpcodeSpec {
name: "JMP_IF_TRUE",
imm_bytes: 4,
pops: 1,
pushes: 0,
is_branch: true,
is_terminator: false,
may_trap: true,
is_safepoint: false,
},
OpCode::Trap => OpcodeSpec {
name: "TRAP",
imm_bytes: 0,
pops: 0,
pushes: 0,
is_branch: false,
is_terminator: true,
may_trap: true,
is_safepoint: false,
},
OpCode::PushConst => OpcodeSpec {
name: "PUSH_CONST",
imm_bytes: 4,
pops: 0,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::Pop => OpcodeSpec {
name: "POP",
imm_bytes: 0,
pops: 1,
pushes: 0,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::PopN => OpcodeSpec {
name: "POP_N",
imm_bytes: 4,
pops: 0,
pushes: 0,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::Dup => OpcodeSpec {
name: "DUP",
imm_bytes: 0,
pops: 1,
pushes: 2,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::Swap => OpcodeSpec {
name: "SWAP",
imm_bytes: 0,
pops: 2,
pushes: 2,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::PushI64 => OpcodeSpec {
name: "PUSH_I64",
imm_bytes: 8,
pops: 0,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::PushF64 => OpcodeSpec {
name: "PUSH_F64",
imm_bytes: 8,
pops: 0,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::PushBool => OpcodeSpec {
name: "PUSH_BOOL",
imm_bytes: 1,
pops: 0,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::PushI32 => OpcodeSpec {
name: "PUSH_I32",
imm_bytes: 4,
pops: 0,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::PushBounded => OpcodeSpec {
name: "PUSH_BOUNDED",
imm_bytes: 4,
pops: 0,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: true,
is_safepoint: false,
},
OpCode::Add => OpcodeSpec {
name: "ADD",
imm_bytes: 0,
pops: 2,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: true,
is_safepoint: false,
},
OpCode::Sub => OpcodeSpec {
name: "SUB",
imm_bytes: 0,
pops: 2,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: true,
is_safepoint: false,
},
OpCode::Mul => OpcodeSpec {
name: "MUL",
imm_bytes: 0,
pops: 2,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: true,
is_safepoint: false,
},
OpCode::Div => OpcodeSpec {
name: "DIV",
imm_bytes: 0,
pops: 2,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: true,
is_safepoint: false,
},
OpCode::Mod => OpcodeSpec {
name: "MOD",
imm_bytes: 0,
pops: 2,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: true,
is_safepoint: false,
},
OpCode::BoundToInt => OpcodeSpec {
name: "BOUND_TO_INT",
imm_bytes: 0,
pops: 1,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::IntToBoundChecked => OpcodeSpec {
name: "INT_TO_BOUND_CHECKED",
imm_bytes: 0,
pops: 1,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: true,
is_safepoint: false,
},
OpCode::Eq => OpcodeSpec {
name: "EQ",
imm_bytes: 0,
pops: 2,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::Neq => OpcodeSpec {
name: "NEQ",
imm_bytes: 0,
pops: 2,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::Lt => OpcodeSpec {
name: "LT",
imm_bytes: 0,
pops: 2,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::Gt => OpcodeSpec {
name: "GT",
imm_bytes: 0,
pops: 2,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::And => OpcodeSpec {
name: "AND",
imm_bytes: 0,
pops: 2,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::Or => OpcodeSpec {
name: "OR",
imm_bytes: 0,
pops: 2,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::Not => OpcodeSpec {
name: "NOT",
imm_bytes: 0,
pops: 1,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::BitAnd => OpcodeSpec {
name: "BIT_AND",
imm_bytes: 0,
pops: 2,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::BitOr => OpcodeSpec {
name: "BIT_OR",
imm_bytes: 0,
pops: 2,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::BitXor => OpcodeSpec {
name: "BIT_XOR",
imm_bytes: 0,
pops: 2,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::Shl => OpcodeSpec {
name: "SHL",
imm_bytes: 0,
pops: 2,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::Shr => OpcodeSpec {
name: "SHR",
imm_bytes: 0,
pops: 2,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::Lte => OpcodeSpec {
name: "LTE",
imm_bytes: 0,
pops: 2,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::Gte => OpcodeSpec {
name: "GTE",
imm_bytes: 0,
pops: 2,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::Neg => OpcodeSpec {
name: "NEG",
imm_bytes: 0,
pops: 1,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::GetGlobal => OpcodeSpec {
name: "GET_GLOBAL",
imm_bytes: 4,
pops: 0,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::SetGlobal => OpcodeSpec {
name: "SET_GLOBAL",
imm_bytes: 4,
pops: 1,
pushes: 0,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::GetLocal => OpcodeSpec {
name: "GET_LOCAL",
imm_bytes: 4,
pops: 0,
pushes: 1,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::SetLocal => OpcodeSpec {
name: "SET_LOCAL",
imm_bytes: 4,
pops: 1,
pushes: 0,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::Call => OpcodeSpec {
name: "CALL",
imm_bytes: 4,
pops: 0,
pushes: 0,
is_branch: false,
is_terminator: false,
may_trap: true,
is_safepoint: false,
},
OpCode::Ret => OpcodeSpec {
name: "RET",
imm_bytes: 0,
pops: 0,
pushes: 0,
is_branch: false,
is_terminator: true,
may_trap: false,
is_safepoint: false,
},
OpCode::PushScope => OpcodeSpec {
name: "PUSH_SCOPE",
imm_bytes: 0,
pops: 0,
pushes: 0,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::PopScope => OpcodeSpec {
name: "POP_SCOPE",
imm_bytes: 0,
pops: 0,
pushes: 0,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: false,
},
OpCode::Syscall => OpcodeSpec {
name: "SYSCALL",
imm_bytes: 4,
pops: 0,
pushes: 0,
is_branch: false,
is_terminator: false,
may_trap: true,
is_safepoint: false,
},
OpCode::FrameSync => OpcodeSpec {
name: "FRAME_SYNC",
imm_bytes: 0,
pops: 0,
pushes: 0,
is_branch: false,
is_terminator: false,
may_trap: false,
is_safepoint: true,
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
// Infer the numeric range from the TryFrom<u16> mapping used in opcode.rs tests
// by scanning a plausible range (0..1024) and keeping all successful decodes.
#[test]
fn every_opcode_has_spec_and_imm_defined() {
let mut count = 0usize;
for val in 0u16..=1023u16 {
if let Ok(op) = OpCode::try_from(val) {
let spec = op.spec();
// Access all fields to ensure they are present and not optimized away
let _ = (
spec.name,
spec.imm_bytes,
spec.pops,
spec.pushes,
spec.is_branch,
spec.is_terminator,
spec.may_trap,
);
count += 1;
}
}
assert!(count > 0, "No opcodes were found via OpCode::try_from");
}
}