feat(ir): 实现 IRFunction 到字节码的 lowering 转换

This commit is contained in:
爱特每羊队
2026-08-05 17:24:24 +08:00
parent 2a3a9a1412
commit 097153ec08
3 changed files with 401 additions and 0 deletions
+154
View File
@@ -0,0 +1,154 @@
//! 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> {
if f.reg_overflow || f.const_overflow {
// 溢出标志在下方统一检查;先做结构验证
}
// 溢出检查 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/Label 不在通用槽位路径(拆字/offset 单独处理)。
fn operand_to_u8(o: &Operand) -> u8 {
match o {
Operand::Reg(r) => *r as u8,
Operand::This => 254,
Operand::NewTarget => 255,
Operand::None => 0,
Operand::Const(_) | Operand::Imm(_) | Operand::Label(_) => 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
View File
@@ -12,6 +12,7 @@
//! 分析视图(CFG/LiveInfo/AllocMap)是 pass 输出,不住进 IR。
pub mod inst;
pub mod lower;
pub mod operand;
use oxide_bytecode::module::{Constant, UpvalueCapture};