refactor(ir): 拆分 oxide_ir 为独立 crate
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{CompileCtx, Compiler, FunctionBodyContext};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::{Class, ClassElement, MethodDefinitionKind, PropertyKey};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::compiler::{CompileCtx, Compiler};
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_parser::{ClassElement, PropertyKey};
|
||||
|
||||
impl Compiler {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{CompileCtx, Compiler, FunctionBodyContext};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::{ClassElement, MethodDefinitionKind, PropertyKey};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||
|
||||
use crate::emit_ctx::{LabelCtx, ScopeCtx};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::lower::lower;
|
||||
use crate::ir::operand::{LabelId, Operand};
|
||||
use crate::ir::IRFunction;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::lower::lower;
|
||||
use oxide_ir::operand::{LabelId, Operand};
|
||||
use oxide_ir::IRFunction;
|
||||
use crate::symbol_table::{Binding, SymbolTable};
|
||||
use oxide_bytecode::module::CompiledModule;
|
||||
use oxide_bytecode::module::UpvalueCapture;
|
||||
@@ -464,7 +464,7 @@ impl CompileCtx {
|
||||
|
||||
/// 组装 IRFunction(两出口共用),take 走编译产物状态。
|
||||
/// `parent_ctx` 用于补全 upvalue_captures 的 enclosing_reg(父符号表在父 emit 完成后完整)。
|
||||
fn assemble_ir(&mut self, param_layout: crate::ir::ParamLayout, parent_ctx: Option<&CompileCtx>) -> IRFunction {
|
||||
fn assemble_ir(&mut self, param_layout: oxide_ir::ParamLayout, parent_ctx: Option<&CompileCtx>) -> IRFunction {
|
||||
let upvalue_captures = self
|
||||
.current_upvalue_captures
|
||||
.iter()
|
||||
@@ -716,7 +716,7 @@ impl Compiler {
|
||||
}
|
||||
|
||||
let ir = ctx.assemble_ir(
|
||||
crate::ir::ParamLayout {
|
||||
oxide_ir::ParamLayout {
|
||||
base: param_base as u32,
|
||||
count: param_specs.len() as u32,
|
||||
},
|
||||
@@ -889,7 +889,7 @@ impl Compiler {
|
||||
crate::compiler_debug!("compile: done, {} instructions, {} constants", ctx.insts.len(), ctx.constants.len());
|
||||
|
||||
let ir = ctx.assemble_ir(
|
||||
crate::ir::ParamLayout {
|
||||
oxide_ir::ParamLayout {
|
||||
base: ctx.scopes.builtin_reg_map.len() as u32,
|
||||
count: 0,
|
||||
},
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
//! `CompileCtx`.
|
||||
|
||||
use crate::compiler::LabelScope;
|
||||
use crate::ir::operand::LabelId;
|
||||
use oxide_ir::operand::LabelId;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
|
||||
/// Jump-target / labeled-statement resolution state.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::AssignmentOperator;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::Expression;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{CompileCtx, Compiler, ParamSpec};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_parser::{Class, Expression, Statement};
|
||||
|
||||
impl Compiler {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
|
||||
impl Compiler {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{is_int_literal, CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::Expression;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::Expression;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
impl Compiler {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::{Expression, ObjectPropertyKind, PropertyKey, PropertyKind};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{is_side_effect_free, BinaryOperator, CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::{ChainElement, Expression, LogicalOperator, SimpleAssignmentTarget, UnaryOperator, UpdateOperator};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
impl Compiler {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
|
||||
impl Compiler {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
|
||||
impl Compiler {
|
||||
|
||||
@@ -1,295 +0,0 @@
|
||||
//! IR 指令结构 + 构造 API。
|
||||
//!
|
||||
//! `ext` 内部是裸 u32(lowering 直拼字节码扩展字)。扩展字的**数量与语义值编码**
|
||||
//! 全部由 `inst_*` 构造 API 保证——emit 代码不直接触碰 `ext` 字段。
|
||||
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use super::operand::{LabelId, Operand};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Inst {
|
||||
pub op: OpCode,
|
||||
pub rd: Operand,
|
||||
pub a: Operand,
|
||||
pub b: Operand,
|
||||
pub ext: SmallVec<[u32; 4]>,
|
||||
}
|
||||
|
||||
impl Inst {
|
||||
/// 基础构造:ext 为空。
|
||||
pub fn new(op: OpCode, rd: Operand, a: Operand, b: Operand) -> Self {
|
||||
Self {
|
||||
op,
|
||||
rd,
|
||||
a,
|
||||
b,
|
||||
ext: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn with_ext(op: OpCode, rd: Operand, a: Operand, b: Operand, ext: &[u32]) -> Self {
|
||||
Self {
|
||||
op,
|
||||
rd,
|
||||
a,
|
||||
b,
|
||||
ext: SmallVec::from_slice(ext),
|
||||
}
|
||||
}
|
||||
|
||||
// ── IC 系:ext = [0, 0, 0](shape/slot/proto 占位字,VM 运行时 patch)──
|
||||
|
||||
pub fn ic_get(dst: Operand, key: Operand) -> Self {
|
||||
Self::with_ext(OpCode::IC_GET_PROP, Operand::None, dst, key, &[0, 0, 0])
|
||||
}
|
||||
|
||||
pub fn ic_set(obj: Operand, value: Operand, key: Operand) -> Self {
|
||||
Self::with_ext(OpCode::IC_SET_PROP, obj, value, key, &[0, 0, 0])
|
||||
}
|
||||
|
||||
pub fn member_inc(obj: Operand, val: Operand, key: Operand) -> Self {
|
||||
Self::with_ext(OpCode::MEMBER_INC, obj, val, key, &[0, 0, 0])
|
||||
}
|
||||
|
||||
pub fn member_dec(obj: Operand, val: Operand, key: Operand) -> Self {
|
||||
Self::with_ext(OpCode::MEMBER_DEC, obj, val, key, &[0, 0, 0])
|
||||
}
|
||||
|
||||
pub fn compound_member_add(obj: Operand, val: Operand, key: Operand) -> Self {
|
||||
Self::with_ext(OpCode::COMPOUND_MEMBER_ADD, obj, val, key, &[0, 0, 0])
|
||||
}
|
||||
|
||||
pub fn compound_member_sub(obj: Operand, val: Operand, key: Operand) -> Self {
|
||||
Self::with_ext(OpCode::COMPOUND_MEMBER_SUB, obj, val, key, &[0, 0, 0])
|
||||
}
|
||||
|
||||
pub fn compound_member_mul(obj: Operand, val: Operand, key: Operand) -> Self {
|
||||
Self::with_ext(OpCode::COMPOUND_MEMBER_MUL, obj, val, key, &[0, 0, 0])
|
||||
}
|
||||
|
||||
pub fn compound_member_div(obj: Operand, val: Operand, key: Operand) -> Self {
|
||||
Self::with_ext(OpCode::COMPOUND_MEMBER_DIV, obj, val, key, &[0, 0, 0])
|
||||
}
|
||||
|
||||
pub fn compound_member_mod(obj: Operand, val: Operand, key: Operand) -> Self {
|
||||
Self::with_ext(OpCode::COMPOUND_MEMBER_MOD, obj, val, key, &[0, 0, 0])
|
||||
}
|
||||
|
||||
pub fn compound_member_exp(obj: Operand, val: Operand, key: Operand) -> Self {
|
||||
Self::with_ext(OpCode::COMPOUND_MEMBER_EXP, obj, val, key, &[0, 0, 0])
|
||||
}
|
||||
|
||||
// ── Call 系:ext = [nargs] ──
|
||||
|
||||
pub fn call(callee: Operand, this: Operand, first_arg: Operand, nargs: u8) -> Self {
|
||||
Self::with_ext(OpCode::CALL, callee, this, first_arg, &[nargs as u32])
|
||||
}
|
||||
|
||||
pub fn call_native(callee: Operand, this: Operand, first_arg: Operand, nargs: u8) -> Self {
|
||||
Self::with_ext(OpCode::CALL_NATIVE, callee, this, first_arg, &[nargs as u32])
|
||||
}
|
||||
|
||||
pub fn new_expression(result: Operand, constructor: Operand, first_arg: Operand, nargs: u8) -> Self {
|
||||
Self::with_ext(OpCode::NEW_EXPRESSION, result, constructor, first_arg, &[nargs as u32])
|
||||
}
|
||||
|
||||
pub fn super_call(result: Operand, first_arg: Operand, nargs: u8) -> Self {
|
||||
Self::with_ext(OpCode::SUPER_CALL, result, first_arg, Operand::None, &[nargs as u32])
|
||||
}
|
||||
|
||||
// ── 其他带 ext 字 ──
|
||||
|
||||
pub fn define_accessor(home: Operand, get: Operand, set: Operand, key_idx: u32) -> Self {
|
||||
Self::with_ext(OpCode::DEFINE_ACCESSOR, home, get, set, &[key_idx])
|
||||
}
|
||||
|
||||
pub fn delete_prop_static(obj: Operand, const_idx: u32) -> Self {
|
||||
Self::with_ext(OpCode::DELETE_PROP_STATIC, obj, obj, Operand::None, &[const_idx])
|
||||
}
|
||||
|
||||
pub fn rest_object(rest: Operand, src: Operand, excluded_idx: u32) -> Self {
|
||||
Self::with_ext(OpCode::REST_OBJECT, rest, src, Operand::None, &[excluded_idx])
|
||||
}
|
||||
|
||||
/// TEMPLATE_STR:变长 ext。首字打包 `(segment_count<<16) | total_len_hint`,
|
||||
/// 后续每 quasi 一项 `quasi_const_idx & 0x7FFF_FFFF`,其后若跟表达式再一项 `0x8000_0000 | expr_reg`。
|
||||
pub fn template_str(dst: Operand, segment_count: u32, total_len_hint: u16, parts: &[u32]) -> Self {
|
||||
let mut ext = SmallVec::with_capacity(2 + parts.len());
|
||||
ext.push(((segment_count & 0xFFFF) << 16) | (total_len_hint as u32 & 0xFFFF));
|
||||
ext.extend_from_slice(parts);
|
||||
Self {
|
||||
op: OpCode::TEMPLATE_STR,
|
||||
rd: dst,
|
||||
a: Operand::None,
|
||||
b: Operand::None,
|
||||
ext,
|
||||
}
|
||||
}
|
||||
|
||||
// ── 无 ext:立即数/索引指令(拆字是 lowering 职责)──
|
||||
|
||||
pub fn load_const(dst: Operand, idx: u16) -> Self {
|
||||
Self::new(OpCode::LOAD_CONST, dst, Operand::Const(idx), Operand::None)
|
||||
}
|
||||
|
||||
pub fn create_closure(dst: Operand, sub_idx: u16) -> Self {
|
||||
Self::new(OpCode::CREATE_CLOSURE, dst, Operand::Imm(sub_idx), Operand::None)
|
||||
}
|
||||
|
||||
// ── 跳转族:label 放 b 槽,offset 计算是 lowering 职责 ──
|
||||
|
||||
pub fn jmp(label: LabelId) -> Self {
|
||||
Self::new(OpCode::JMP, Operand::None, Operand::None, Operand::Label(label))
|
||||
}
|
||||
|
||||
pub fn jmp_if_false(cond_reg: u8, label: LabelId) -> Self {
|
||||
Self::new(
|
||||
OpCode::JMP_IF_FALSE,
|
||||
Operand::Reg(cond_reg as u32),
|
||||
Operand::None,
|
||||
Operand::Label(label),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn jmp_if_true(cond_reg: u8, label: LabelId) -> Self {
|
||||
Self::new(
|
||||
OpCode::JMP_IF_TRUE,
|
||||
Operand::Reg(cond_reg as u32),
|
||||
Operand::None,
|
||||
Operand::Label(label),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn jmp_if_nullish(cond_reg: u8, label: LabelId) -> Self {
|
||||
Self::new(
|
||||
OpCode::JMP_IF_NULLISH,
|
||||
Operand::Reg(cond_reg as u32),
|
||||
Operand::None,
|
||||
Operand::Label(label),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn try_begin(label: LabelId) -> Self {
|
||||
Self::new(OpCode::TRY_BEGIN, Operand::None, Operand::None, Operand::Label(label))
|
||||
}
|
||||
|
||||
pub fn try_finally_begin(label: LabelId) -> Self {
|
||||
Self::new(OpCode::TRY_FINALLY_BEGIN, Operand::None, Operand::None, Operand::Label(label))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn inst_new_has_empty_ext() {
|
||||
let inst = Inst::new(OpCode::ADD, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2));
|
||||
assert!(inst.ext.is_empty());
|
||||
assert_eq!(inst.op, OpCode::ADD);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ic_instructions_carry_three_zero_ext_words() {
|
||||
let insts = [
|
||||
Inst::ic_get(Operand::Reg(1), Operand::Reg(2)),
|
||||
Inst::ic_set(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)),
|
||||
Inst::member_inc(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)),
|
||||
Inst::member_dec(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)),
|
||||
Inst::compound_member_add(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)),
|
||||
Inst::compound_member_sub(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)),
|
||||
Inst::compound_member_mul(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)),
|
||||
Inst::compound_member_div(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)),
|
||||
Inst::compound_member_mod(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)),
|
||||
Inst::compound_member_exp(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)),
|
||||
];
|
||||
for inst in &insts {
|
||||
assert_eq!(inst.ext.as_slice(), &[0, 0, 0], "IC op {} must carry 3 zero ext words", inst.op);
|
||||
assert!(inst.ext.len() == 3);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn call_instructions_carry_nargs() {
|
||||
let call = Inst::call(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2), 3);
|
||||
assert_eq!(call.ext.as_slice(), &[3]);
|
||||
assert_eq!(call.rd, Operand::Reg(0));
|
||||
assert_eq!(call.a, Operand::Reg(1));
|
||||
assert_eq!(call.b, Operand::Reg(2));
|
||||
|
||||
let native = Inst::call_native(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2), 0);
|
||||
assert_eq!(native.ext.as_slice(), &[0]);
|
||||
|
||||
let new_expr = Inst::new_expression(Operand::Reg(3), Operand::Reg(0), Operand::Reg(1), 2);
|
||||
assert_eq!(new_expr.ext.as_slice(), &[2]);
|
||||
assert_eq!(new_expr.rd, Operand::Reg(3));
|
||||
assert_eq!(new_expr.a, Operand::Reg(0));
|
||||
|
||||
let super_call = Inst::super_call(Operand::Reg(3), Operand::Reg(1), 1);
|
||||
assert_eq!(super_call.ext.as_slice(), &[1]);
|
||||
assert_eq!(super_call.rd, Operand::Reg(3));
|
||||
assert_eq!(super_call.a, Operand::Reg(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_ext_word_instructions() {
|
||||
let accessor = Inst::define_accessor(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2), 42);
|
||||
assert_eq!(accessor.ext.as_slice(), &[42]);
|
||||
assert_eq!(accessor.rd, Operand::Reg(0));
|
||||
assert_eq!(accessor.a, Operand::Reg(1));
|
||||
assert_eq!(accessor.b, Operand::Reg(2));
|
||||
|
||||
let rest = Inst::rest_object(Operand::Reg(0), Operand::Reg(1), 7);
|
||||
assert_eq!(rest.ext.as_slice(), &[7]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_const_and_create_closure_keep_semantic_operands() {
|
||||
let lc = Inst::load_const(Operand::Reg(4), 300);
|
||||
assert_eq!(lc.a, Operand::Const(300));
|
||||
assert_eq!(lc.b, Operand::None);
|
||||
assert!(lc.ext.is_empty());
|
||||
|
||||
let cc = Inst::create_closure(Operand::Reg(4), 5);
|
||||
assert_eq!(cc.a, Operand::Imm(5));
|
||||
assert_eq!(cc.b, Operand::None);
|
||||
assert!(cc.ext.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn jump_family_puts_label_in_b_slot() {
|
||||
let jmp = Inst::jmp(9);
|
||||
assert_eq!(jmp.b, Operand::Label(9));
|
||||
assert_eq!(jmp.rd, Operand::None);
|
||||
|
||||
let cond = Inst::jmp_if_false(3, 9);
|
||||
assert_eq!(cond.rd, Operand::Reg(3));
|
||||
assert_eq!(cond.b, Operand::Label(9));
|
||||
|
||||
let true_jmp = Inst::jmp_if_true(3, 9);
|
||||
assert_eq!(true_jmp.b, Operand::Label(9));
|
||||
|
||||
let nullish = Inst::jmp_if_nullish(3, 9);
|
||||
assert_eq!(nullish.b, Operand::Label(9));
|
||||
|
||||
let try_begin = Inst::try_begin(9);
|
||||
assert_eq!(try_begin.b, Operand::Label(9));
|
||||
assert_eq!(try_begin.rd, Operand::None);
|
||||
|
||||
let try_fin = Inst::try_finally_begin(9);
|
||||
assert_eq!(try_fin.b, Operand::Label(9));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn template_str_packs_segment_count_and_hint() {
|
||||
let inst = Inst::template_str(Operand::Reg(1), 3, 10, &[0x1234, 0x8000_0000 | 5]);
|
||||
assert_eq!(inst.op, OpCode::TEMPLATE_STR);
|
||||
assert_eq!(inst.rd, Operand::Reg(1));
|
||||
assert_eq!(inst.ext.len(), 3);
|
||||
assert_eq!(inst.ext[0], (3 << 16) | 10);
|
||||
assert_eq!(inst.ext[1], 0x1234);
|
||||
assert_eq!(inst.ext[2], 0x8000_0000 | 5);
|
||||
}
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
//! IRFunction → CompiledModule 等价转换。
|
||||
//!
|
||||
//! 字节码格式(4 字节 u32 + 扩展字)与现状逐指令一致(D-13 锚)。
|
||||
//! label 用前缀和:`label_pos` 记 Inst 下标,平铺时维护 inst→instr 映射,
|
||||
//! 跳转 offset 以 Instr(含 ext 字)为单位(D-17)。
|
||||
|
||||
use oxide_bytecode::module::CompiledModule;
|
||||
use oxide_bytecode::opcode::{self, OpCode};
|
||||
|
||||
use super::inst::Inst;
|
||||
use super::operand::Operand;
|
||||
use super::IRFunction;
|
||||
|
||||
/// IRFunction → CompiledModule。错误消息与现状(退役前 compiler.rs)逐字一致。
|
||||
pub fn lower(f: &IRFunction) -> Result<CompiledModule, String> {
|
||||
// 溢出检查 1:寄存器(仅 Operand::Reg 变体;This/NewTarget 是语义操作数,映射 254/255 合法)
|
||||
if f.reg_overflow {
|
||||
return Err("RangeError: function body uses too many registers (max 253)".into());
|
||||
}
|
||||
for inst in &f.insts {
|
||||
for o in [&inst.rd, &inst.a, &inst.b] {
|
||||
if let Operand::Reg(r) = o {
|
||||
if *r > 253 {
|
||||
return Err("RangeError: function body uses too many registers (max 253)".into());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 溢出检查 2:常量池
|
||||
if f.const_overflow || f.constants.len() > u16::MAX as usize {
|
||||
return Err("RangeError: too many constants".into());
|
||||
}
|
||||
|
||||
let mut instrs: Vec<u32> = Vec::new();
|
||||
let mut inst_to_instr: Vec<usize> = Vec::with_capacity(f.insts.len());
|
||||
// (当前指令的起始 Instr index, label id)
|
||||
let mut jumps: Vec<(usize, u32)> = Vec::new();
|
||||
|
||||
for inst in &f.insts {
|
||||
inst_to_instr.push(instrs.len());
|
||||
encode_inst(inst, &mut instrs, &mut jumps)?;
|
||||
}
|
||||
|
||||
// 跳转回填:offset = 目标 Instr - 当前 Instr(前缀和,D-17)
|
||||
for (instr_idx, label) in jumps {
|
||||
let target_inst = f
|
||||
.label_pos
|
||||
.get(label as usize)
|
||||
.and_then(|p| *p)
|
||||
.ok_or_else(|| format!("Label {label} not found in bytecode map"))?;
|
||||
if target_inst >= inst_to_instr.len() {
|
||||
return Err(format!("Label {label} not found in bytecode map"));
|
||||
}
|
||||
let target_instr = inst_to_instr[target_inst];
|
||||
let offset = target_instr as isize - instr_idx as isize;
|
||||
if offset < i16::MIN as isize || offset > i16::MAX as isize {
|
||||
return Err("RangeError: jump offset out of range".into());
|
||||
}
|
||||
let offset = offset as i16;
|
||||
let existing = instrs[instr_idx];
|
||||
let op = opcode::opcode(existing);
|
||||
let rd = opcode::rd(existing);
|
||||
instrs[instr_idx] = opcode::encode(op, rd, (offset as u16 & 0xFF) as u8, ((offset as u16 >> 8) & 0xFF) as u8);
|
||||
}
|
||||
|
||||
let mut sub_modules = Vec::with_capacity(f.nested.len());
|
||||
for nested in &f.nested {
|
||||
sub_modules.push(lower(nested)?);
|
||||
}
|
||||
|
||||
Ok(CompiledModule {
|
||||
bytecode: instrs,
|
||||
constants: f.constants.clone(),
|
||||
n_registers: f.n_registers,
|
||||
n_args: f.param_layout.count as u8,
|
||||
param_base: f.param_layout.base as u8,
|
||||
builtin_reg_map: f.builtin_reg_map.clone(),
|
||||
sub_modules,
|
||||
is_arrow: f.is_arrow,
|
||||
captured_this_const_idx: f.captured_this_const_idx,
|
||||
function_name: f.function_name.clone(),
|
||||
is_class_constructor: f.is_class_constructor,
|
||||
is_derived_constructor: f.is_derived_constructor,
|
||||
needs_home_object: f.needs_home_object,
|
||||
upvalue_captures: f.upvalue_captures.clone(),
|
||||
cells_needed: f.cells_needed,
|
||||
})
|
||||
}
|
||||
|
||||
/// Operand → u8 槽位。Const/Imm 在 a 槽走拆字(operand_pair_to_u8);
|
||||
/// 出现在 b/rd 槽时按低字节直写(const_flag、cell_idx、uv_idx 等一字节立即数)。
|
||||
fn operand_to_u8(o: &Operand) -> u8 {
|
||||
match o {
|
||||
Operand::Reg(r) => *r as u8,
|
||||
Operand::This => 254,
|
||||
Operand::NewTarget => 255,
|
||||
Operand::Imm(v) => (v & 0xFF) as u8,
|
||||
Operand::Const(v) => (v & 0xFF) as u8,
|
||||
Operand::Label(_) | Operand::None => 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// 普通指令的 a/b 槽:a 槽的 Const/Imm 拆 lo/hi(对应 LOAD_CONST/CREATE_CLOSURE 布局)。
|
||||
fn operand_pair_to_u8(a: &Operand, b: &Operand) -> (u8, u8) {
|
||||
match a {
|
||||
Operand::Const(idx) => ((idx & 0xFF) as u8, (idx >> 8) as u8),
|
||||
Operand::Imm(v) => ((v & 0xFF) as u8, (v >> 8) as u8),
|
||||
_ => (operand_to_u8(a), operand_to_u8(b)),
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_inst(inst: &Inst, instrs: &mut Vec<u32>, jumps: &mut Vec<(usize, u32)>) -> Result<(), String> {
|
||||
if is_jump_op(inst.op) {
|
||||
let label = match inst.b {
|
||||
Operand::Label(id) => id,
|
||||
_ => return Err(format!("jump op {:?} requires Label operand in b slot", inst.op)),
|
||||
};
|
||||
let cond = operand_to_u8(&inst.rd);
|
||||
let start = instrs.len();
|
||||
let instr = match inst.op {
|
||||
OpCode::JMP => opcode::encode_jmp(0),
|
||||
OpCode::JMP_IF_FALSE => opcode::encode_jmp_if_false(cond, 0),
|
||||
OpCode::JMP_IF_TRUE => opcode::encode_jmp_if_true(cond, 0),
|
||||
OpCode::JMP_IF_NULLISH => opcode::encode_jmp_if_nullish(cond, 0),
|
||||
OpCode::TRY_BEGIN => opcode::encode_try_begin(0),
|
||||
OpCode::TRY_FINALLY_BEGIN => opcode::encode_try_finally_begin(0),
|
||||
_ => unreachable!("is_jump_op mismatch"),
|
||||
};
|
||||
instrs.push(instr);
|
||||
jumps.push((start, label));
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let rd = operand_to_u8(&inst.rd);
|
||||
let (a, b) = operand_pair_to_u8(&inst.a, &inst.b);
|
||||
instrs.push(opcode::encode(inst.op, rd, a, b));
|
||||
instrs.extend_from_slice(&inst.ext);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_jump_op(op: OpCode) -> bool {
|
||||
matches!(
|
||||
op,
|
||||
OpCode::JMP
|
||||
| OpCode::JMP_IF_FALSE
|
||||
| OpCode::JMP_IF_TRUE
|
||||
| OpCode::JMP_IF_NULLISH
|
||||
| OpCode::TRY_BEGIN
|
||||
| OpCode::TRY_FINALLY_BEGIN
|
||||
)
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
//! IR 中间表示:AST→IR→字节码 流水线的程序表示。
|
||||
//!
|
||||
//! 单一程序表示(D-01),分域组合(D-02):
|
||||
//! - code: 指令流 + label 位置
|
||||
//! - data: 常量池
|
||||
//! - bindings: 参数布局
|
||||
//! - builtins: 内置全局 → 寄存器
|
||||
//! - closures: 闭包捕获 + cell
|
||||
//! - meta: 函数元信息 + 溢出证据
|
||||
//! - nested: 子函数(递归 IR,替代 sub_modules)
|
||||
//!
|
||||
//! 分析视图(CFG/LiveInfo/AllocMap)是 pass 输出,不住进 IR。
|
||||
|
||||
pub mod inst;
|
||||
pub mod lower;
|
||||
pub mod operand;
|
||||
|
||||
use oxide_bytecode::module::{Constant, UpvalueCapture};
|
||||
|
||||
use self::inst::Inst;
|
||||
|
||||
/// 参数段布局:base 起 count 个寄存器连续(VM 调用契约),RegAlloc 输入。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct ParamLayout {
|
||||
pub base: u32,
|
||||
pub count: u32,
|
||||
}
|
||||
|
||||
/// 单函数 IR。分域组合,嵌套封顶两层。
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct IRFunction {
|
||||
// code 域
|
||||
pub insts: Vec<Inst>,
|
||||
/// label id → 指令下标。id 连续递增,Vec 索引即 id;写入前须扩容。
|
||||
pub label_pos: Vec<Option<usize>>,
|
||||
pub label_count: u32,
|
||||
// data 域
|
||||
pub constants: Vec<Constant>,
|
||||
// bindings 域
|
||||
pub param_layout: ParamLayout,
|
||||
// builtins 域
|
||||
pub builtin_reg_map: Vec<(String, u8)>,
|
||||
// closures 域
|
||||
pub upvalue_captures: Vec<UpvalueCapture>,
|
||||
pub cells_needed: u8,
|
||||
// meta 域
|
||||
pub n_registers: u8,
|
||||
pub is_arrow: bool,
|
||||
pub is_class_constructor: bool,
|
||||
pub is_derived_constructor: bool,
|
||||
pub needs_home_object: bool,
|
||||
pub captured_this_const_idx: u16,
|
||||
pub function_name: Option<String>,
|
||||
/// emit 侧溢出证据,lowering 读标志报错(D-20)。
|
||||
pub reg_overflow: bool,
|
||||
pub const_overflow: bool,
|
||||
// nested 域
|
||||
pub nested: Vec<IRFunction>,
|
||||
}
|
||||
|
||||
impl IRFunction {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
insts: Vec::new(),
|
||||
label_pos: Vec::new(),
|
||||
label_count: 0,
|
||||
constants: Vec::new(),
|
||||
param_layout: ParamLayout { base: 0, count: 0 },
|
||||
builtin_reg_map: Vec::new(),
|
||||
upvalue_captures: Vec::new(),
|
||||
cells_needed: 0,
|
||||
n_registers: 0,
|
||||
is_arrow: false,
|
||||
is_class_constructor: false,
|
||||
is_derived_constructor: false,
|
||||
needs_home_object: false,
|
||||
captured_this_const_idx: 0,
|
||||
function_name: None,
|
||||
reg_overflow: false,
|
||||
const_overflow: false,
|
||||
nested: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for IRFunction {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
//! 类型安全操作数。IR 中不出现裸寄存器下标。
|
||||
//!
|
||||
//! - `Reg(u32)`:寄存器。本阶段存物理号(≤253);254/255 为 VM 保留,语义由 `This`/`NewTarget` 表达
|
||||
//! - `Const(u16)`:常量池下标
|
||||
//! - `Label(u32)`:跳转目标(`LabelId`)
|
||||
//! - `Imm(u16)`:立即数
|
||||
//! - `This` / `NewTarget`:语义化特殊寄存器,由 lowering 映射物理 254/255(IR 中禁止裸下标)
|
||||
//! - `None`:操作数槽未使用
|
||||
|
||||
/// 统一跳转目标标识(取代语义化 `Label` 枚举)。
|
||||
pub type LabelId = u32;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Operand {
|
||||
Reg(u32),
|
||||
Const(u16),
|
||||
Label(u32),
|
||||
Imm(u16),
|
||||
This,
|
||||
NewTarget,
|
||||
None,
|
||||
}
|
||||
@@ -7,7 +7,6 @@ pub mod compiler_log;
|
||||
pub mod emit_ctx;
|
||||
pub mod expr;
|
||||
pub mod hash;
|
||||
pub mod ir;
|
||||
pub mod prepass;
|
||||
pub mod shared;
|
||||
pub mod stmt;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::{LabelId, Operand};
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::{LabelId, Operand};
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::{ChainElement, Expression, LogicalOperator, PropertyKey};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::{Expression, PropertyKey};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_parser::PropertyKey;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::compiler::{CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use oxide_ir::inst::Inst;
|
||||
|
||||
impl Compiler {
|
||||
pub(crate) fn emit_break_statement(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::Statement;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::Statement;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::{Statement, VariableDeclarationKind};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{CompileCtx, Compiler, ParamSpec};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::Statement;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::{BindingPattern, Expression, Statement, VariableDeclarationKind};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::Statement;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::{Statement, VariableDeclarationKind};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::compiler::{CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_parser::Statement;
|
||||
|
||||
impl Compiler {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::{BindingPattern, ForStatementInit, Statement, VariableDeclarationKind};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::{ForStatementLeft, Statement, VariableDeclarationKind};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::{ForStatementLeft, Statement};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::compiler::{CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_parser::Statement;
|
||||
|
||||
impl Compiler {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::compiler::{CompileCtx, Compiler};
|
||||
use crate::ir::inst::Inst;
|
||||
use crate::ir::operand::Operand;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_parser::Statement;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user