This repository has been archived on 2026-09-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
OxideJS/crates/oxide_bytecode/src/opcode.rs
T

347 lines
12 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 字节码操作码表与指令编解码。
//!
//! [`OpCode`] 由 `define_opcodes!` 宏从单张表生成;[`Instr`] 是 4 字节指令
//! (opcode + rd + a + b,imm16/offset16 复用 a、b 两字节)。本模块提供
//! 编译器发射(`encode*`)与 VM 解码(`opcode` / `rd` / `a` / `b` 等)全套函数。
use std::fmt;
/// Generate the `OpCode` enum plus its `TryFrom<u8>` and `Display` impls from a
/// single table. Each row is `VARIANT = 0xXX => "DISPLAY"` — the byte value is
/// the enum discriminant, the `TryFrom` match arm, and the `Display` name come
/// from the same row, so the three tables can never drift. A new opcode is one
/// row.
///
/// Grouping comments (`// -- Arithmetic --`) are stripped by the lexer before
/// macro expansion, so they can be interleaved freely between rows.
macro_rules! define_opcodes {
( $( $name:ident = $val:literal => $disp:literal ),+ $(,)? ) => {
/// OpCode for register-based bytecode VM.
///
/// Organized in groups of 16 for readability. Implemented opcodes have
/// emitter support in the compiler; placeholder opcodes are reserved
/// for future phases (IC, profiling, parallelization).
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(non_camel_case_types)]
pub enum OpCode {
$( $name = $val, )+
}
impl TryFrom<u8> for OpCode {
type Error = ();
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
$( $val => Ok(OpCode::$name), )+
_ => Err(()),
}
}
}
impl fmt::Display for OpCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match self {
$( OpCode::$name => $disp, )+
};
write!(f, "{name}")
}
}
};
}
define_opcodes! {
// ── Arithmetic (0x00-0x0F) ──
ADD = 0x00 => "ADD",
SUB = 0x01 => "SUB",
MUL = 0x02 => "MUL",
DIV = 0x03 => "DIV",
MOD = 0x04 => "MOD",
NEG = 0x05 => "NEG",
COMPOUND_ADD = 0x06 => "COMPOUND_ADD",
COMPOUND_SUB = 0x07 => "COMPOUND_SUB",
COMPOUND_MUL = 0x08 => "COMPOUND_MUL",
COMPOUND_DIV = 0x09 => "COMPOUND_DIV",
COMPOUND_MOD = 0x0A => "COMPOUND_MOD",
COMPOUND_EXP = 0x0B => "COMPOUND_EXP",
MOV = 0x0C => "MOV", // rd=dst, a=src(寄存器复制,D-07,区间拆分搬值)
SPILL = 0x0D => "SPILL", // rd=src, ext=[slot u16](溢出写 VM spill 栈,D-06)
UNSPILL = 0x0E => "UNSPILL", // rd=dst, ext=[slot u16](从 VM spill 栈恢复,D-06)
// -- Comparison (0x10-0x1F) --
EQ = 0x10 => "EQ",
NEQ = 0x11 => "NEQ",
LT = 0x12 => "LT",
GT = 0x13 => "GT",
LTE = 0x14 => "LTE",
GTE = 0x15 => "GTE",
IN = 0x16 => "IN",
NOT = 0x17 => "NOT",
AND = 0x18 => "AND",
OR = 0x19 => "OR",
STRICT_EQ = 0x1A => "STRICT_EQ",
STRICT_NEQ = 0x1C => "STRICT_NEQ",
UNARY_PLUS = 0x1D => "UNARY_PLUS",
// -- Control Flow (0x20-0x2F) --
JMP = 0x20 => "JMP",
JMP_IF_FALSE = 0x21 => "JMP_IF_FALSE",
JMP_IF_TRUE = 0x22 => "JMP_IF_TRUE",
FOR_OF_INIT = 0x23 => "FOR_OF_INIT",
FOR_OF_NEXT = 0x24 => "FOR_OF_NEXT",
// -- Update (0x25-0x28) --
INC_PRE = 0x25 => "INC_PRE",
INC_POST = 0x26 => "INC_POST",
DEC_PRE = 0x27 => "DEC_PRE",
DEC_POST = 0x28 => "DEC_POST",
FOR_IN_INIT = 0x29 => "FOR_IN_INIT",
FOR_IN_NEXT = 0x2A => "FOR_IN_NEXT",
FOR_IN_DONE = 0x2B => "FOR_IN_DONE",
SWITCH_TABLE = 0x2C => "SWITCH_TABLE",
FOR_IN_CLEANUP = 0x2D => "FOR_IN_CLEANUP",
// -- Exception (0x2E-0x2F, 0x33-0x35) --
THROW = 0x2E => "THROW",
TRY_BEGIN = 0x2F => "TRY_BEGIN",
TRY_END = 0x33 => "TRY_END",
TRY_FINALLY_BEGIN = 0x34 => "TRY_FINALLY_BEGIN",
TRY_FINALLY_END = 0x35 => "TRY_FINALLY_END",
FOR_OF_DONE = 0x36 => "FOR_OF_DONE",
FOR_OF_CLOSE = 0x37 => "FOR_OF_CLOSE",
// -- Template Literal (0x38) --
TEMPLATE_STR = 0x38 => "TEMPLATE_STR",
// -- Small Language Features (0x39-0x3B) --
DELETE_PROP_STATIC = 0x39 => "DELETE_PROP_STATIC",
DELETE_PROP_DYNAMIC = 0x3A => "DELETE_PROP_DYNAMIC",
INSTANCEOF = 0x3B => "INSTANCEOF",
MAKE_CELL = 0x3C => "MAKE_CELL",
CELL_GET = 0x3D => "CELL_GET",
REST_OBJECT = 0x3E => "REST_OBJECT",
CELL_SET = 0x3F => "CELL_SET",
// -- Variable (0x30-0x32) --
LOAD_VAR = 0x30 => "LOAD_VAR",
STORE_VAR = 0x31 => "STORE_VAR",
LOAD_CONST = 0x32 => "LOAD_CONST",
// -- Call (0x40-0x4F) --
CALL = 0x40 => "CALL",
RETURN = 0x41 => "RETURN",
CALL_NATIVE = 0x42 => "CALL_NATIVE",
NEW_EXPRESSION = 0x43 => "NEW_EXPRESSION",
SUPER_CALL = 0x44 => "SUPER_CALL",
SUPER_GET_PROP = 0x45 => "SUPER_GET_PROP",
SUPER_STATIC_GET_PROP = 0x46 => "SUPER_STATIC_GET_PROP",
SET_HOME_OBJECT = 0x47 => "SET_HOME_OBJECT",
DEFINE_ACCESSOR = 0x48 => "DEFINE_ACCESSOR",
LOAD_UPVALUE = 0x49 => "LOAD_UPVALUE",
CREATE_CLOSURE = 0x4A => "CREATE_CLOSURE",
CREATE_REGEXP = 0x4B => "CREATE_REGEXP",
STORE_UPVALUE = 0x4C => "STORE_UPVALUE",
// ── Object Property (0x50-0x5F) ──
IC_GET_PROP = 0x50 => "IC_GET_PROP",
IC_SET_PROP = 0x51 => "IC_SET_PROP",
GET_PROP = 0x52 => "GET_PROP",
GET_PROP_DYNAMIC = 0x53 => "GET_PROP_DYNAMIC",
SET_PROP = 0x54 => "SET_PROP",
SET_PROP_DYNAMIC = 0x55 => "SET_PROP_DYNAMIC",
NEW_OBJECT = 0x56 => "NEW_OBJECT",
NEW_ARRAY = 0x57 => "NEW_ARRAY",
SET_ELEM = 0x58 => "SET_ELEM",
// -- Member Update (0x59-0x62) --
MEMBER_INC = 0x59 => "MEMBER_INC",
MEMBER_DEC = 0x5A => "MEMBER_DEC",
DYN_MEMBER_INC = 0x5B => "DYN_MEMBER_INC",
DYN_MEMBER_DEC = 0x5C => "DYN_MEMBER_DEC",
COMPOUND_MEMBER_ADD = 0x5D => "COMPOUND_MEMBER_ADD",
COMPOUND_MEMBER_SUB = 0x5E => "COMPOUND_MEMBER_SUB",
COMPOUND_MEMBER_MUL = 0x5F => "COMPOUND_MEMBER_MUL",
COMPOUND_MEMBER_DIV = 0x60 => "COMPOUND_MEMBER_DIV",
COMPOUND_MEMBER_MOD = 0x61 => "COMPOUND_MEMBER_MOD",
COMPOUND_MEMBER_EXP = 0x62 => "COMPOUND_MEMBER_EXP",
// -- Profiling - placeholders (0x63-0x6F) --
PROFILE_TYPE = 0x63 => "PROFILE_TYPE",
PROFILE_SHAPE = 0x64 => "PROFILE_SHAPE",
PROFILE_BRANCH = 0x65 => "PROFILE_BRANCH",
PROFILE_CALL = 0x66 => "PROFILE_CALL",
// ── Parallel — placeholders (0x70-0x7F) ──
FORK = 0x70 => "FORK",
JOIN = 0x71 => "JOIN",
GET_PRIVATE = 0x72 => "GET_PRIVATE",
SET_PRIVATE = 0x73 => "SET_PRIVATE",
INIT_PRIVATE = 0x74 => "INIT_PRIVATE",
PRIVATE_BRAND_IN = 0x75 => "PRIVATE_BRAND_IN",
// -- Bitwise (0x80-0x8F) --
BIT_AND = 0x80 => "BIT_AND",
BIT_OR = 0x81 => "BIT_OR",
BIT_XOR = 0x82 => "BIT_XOR",
SHL = 0x83 => "SHL",
SHR = 0x84 => "SHR",
USHR = 0x85 => "USHR",
BIT_NOT = 0x86 => "BIT_NOT",
COMPOUND_AND = 0x88 => "COMPOUND_AND",
COMPOUND_OR = 0x89 => "COMPOUND_OR",
COMPOUND_XOR = 0x8A => "COMPOUND_XOR",
COMPOUND_SHL = 0x8B => "COMPOUND_SHL",
COMPOUND_SHR = 0x8C => "COMPOUND_SHR",
COMPOUND_USHR = 0x8D => "COMPOUND_USHR",
NULLISH = 0x8E => "NULLISH",
JMP_IF_NULLISH = 0x8F => "JMP_IF_NULLISH",
// ── Misc (0xF0-0xFF) ──
NOP = 0xF0 => "NOP",
HALT = 0xF1 => "HALT",
TYPEOF = 0xF2 => "TYPEOF",
VOID = 0xF3 => "VOID",
}
impl OpCode {
/// 是否需要 IC 扩展字(inline cache 元数据跟随指令之后)。
pub fn has_ic_ext_words(&self) -> bool {
matches!(
self,
OpCode::IC_GET_PROP
| OpCode::IC_SET_PROP
| OpCode::MEMBER_INC
| OpCode::MEMBER_DEC
| OpCode::COMPOUND_MEMBER_ADD
| OpCode::COMPOUND_MEMBER_SUB
| OpCode::COMPOUND_MEMBER_MUL
| OpCode::COMPOUND_MEMBER_DIV
| OpCode::COMPOUND_MEMBER_MOD
| OpCode::COMPOUND_MEMBER_EXP
)
}
}
/// 4-byte instruction.
///
/// Layout: `[opcode: u8] [rd: u8] [a: u8] [b: u8]`
/// - `rd` — destination register
/// - `a` — first source register, or imm16 low byte
/// - `b` — second source register, or imm16 high byte
pub type Instr = u32;
/// 把操作码与三个操作数字节编码为一条 [`Instr`]。
pub fn encode(op: OpCode, rd: u8, a: u8, b: u8) -> Instr {
((b as Instr) << 24) | ((a as Instr) << 16) | ((rd as Instr) << 8) | (op as Instr)
}
/// 解码指令的低 8 位操作码;未知字节返回 [`OpCode::NOP`]。
pub fn opcode(instr: Instr) -> OpCode {
OpCode::try_from((instr & 0xFF) as u8).unwrap_or(OpCode::NOP)
}
/// 解码目标寄存器(bits 8-15)。
pub fn rd(instr: Instr) -> u8 {
((instr >> 8) & 0xFF) as u8
}
/// 解码第一个操作数(bits 16-23)。
pub fn a(instr: Instr) -> u8 {
((instr >> 16) & 0xFF) as u8
}
/// 解码第二个操作数(bits 24-31)。
pub fn b(instr: Instr) -> u8 {
((instr >> 24) & 0xFF) as u8
}
/// 解码 16 位立即数(bits 16-31,由 a、b 两字节拼成)。
pub fn imm16(instr: Instr) -> u16 {
((instr >> 16) & 0xFFFF) as u16
}
/// 解码 16 位有符号跳转偏移(bits 16-31)。
pub fn offset16(instr: Instr) -> i16 {
((instr >> 16) & 0xFFFF) as i16
}
/// 发射无条件跳转指令(`JMP`,偏移以 16 位补码编码)。
pub fn encode_jmp(offset: i16) -> Instr {
let lo = (offset as u16 & 0xFF) as u8;
let hi = ((offset as u16 >> 8) & 0xFF) as u8;
encode(OpCode::JMP, 0, lo, hi)
}
/// 发射 `JMP_IF_FALSE`:当 `rd` 寄存器为 falsy 时跳转。
pub fn encode_jmp_if_false(rd: u8, offset: i16) -> Instr {
let lo = (offset as u16 & 0xFF) as u8;
let hi = ((offset as u16 >> 8) & 0xFF) as u8;
encode(OpCode::JMP_IF_FALSE, rd, lo, hi)
}
/// 发射 `JMP_IF_TRUE`:当 `rd` 寄存器为 truthy 时跳转。
pub fn encode_jmp_if_true(rd: u8, offset: i16) -> Instr {
let lo = (offset as u16 & 0xFF) as u8;
let hi = ((offset as u16 >> 8) & 0xFF) as u8;
encode(OpCode::JMP_IF_TRUE, rd, lo, hi)
}
/// 发射 `JMP_IF_NULLISH`:当 `rd` 为 `null` / `undefined` 时跳转(`??` 短路)。
pub fn encode_jmp_if_nullish(rd: u8, offset: i16) -> Instr {
let lo = (offset as u16 & 0xFF) as u8;
let hi = ((offset as u16 >> 8) & 0xFF) as u8;
encode(OpCode::JMP_IF_NULLISH, rd, lo, hi)
}
/// 发射 `TRY_BEGIN`:标记 try 块开始,偏移指向 catch 处理器。
pub fn encode_try_begin(offset: i16) -> Instr {
let lo = (offset as u16 & 0xFF) as u8;
let hi = ((offset as u16 >> 8) & 0xFF) as u8;
encode(OpCode::TRY_BEGIN, 0, lo, hi)
}
/// 发射 `TRY_FINALLY_BEGIN`:标记 try/finally 块开始,偏移指向 finally 处理器。
pub fn encode_try_finally_begin(offset: i16) -> Instr {
let lo = (offset as u16 & 0xFF) as u8;
let hi = ((offset as u16 >> 8) & 0xFF) as u8;
encode(OpCode::TRY_FINALLY_BEGIN, 0, lo, hi)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn opcode_table_is_byte_identical() {
for byte in 0u8..=u8::MAX {
if let Ok(op) = OpCode::try_from(byte) {
assert_eq!(op as u8, byte, "discriminant mismatch for 0x{byte:02X}");
assert!(!op.to_string().is_empty(), "empty Display for 0x{byte:02X}");
}
}
assert_eq!(OpCode::ADD as u8, 0x00);
assert_eq!(OpCode::STRICT_EQ as u8, 0x1A);
assert_eq!(OpCode::JMP_IF_NULLISH as u8, 0x8F);
assert_eq!(OpCode::VOID as u8, 0xF3);
assert_eq!(OpCode::MOV as u8, 0x0C);
assert_eq!(OpCode::SPILL as u8, 0x0D);
assert_eq!(OpCode::UNSPILL as u8, 0x0E);
assert_eq!(OpCode::ADD.to_string(), "ADD");
assert_eq!(OpCode::COMPOUND_MEMBER_EXP.to_string(), "COMPOUND_MEMBER_EXP");
assert_eq!(OpCode::MOV.to_string(), "MOV");
assert_eq!(OpCode::SPILL.to_string(), "SPILL");
assert_eq!(OpCode::UNSPILL.to_string(), "UNSPILL");
assert!(OpCode::try_from(0x1B).is_err());
assert!(OpCode::try_from(0xFF).is_err());
let ic_count = (0u8..=u8::MAX)
.filter_map(|b| OpCode::try_from(b).ok())
.filter(OpCode::has_ic_ext_words)
.count();
assert_eq!(ic_count, 10);
}
}