diff --git a/crates/oxide_emit/src/emit.rs b/crates/oxide_emit/src/emit.rs index c3328c65..fa443296 100644 --- a/crates/oxide_emit/src/emit.rs +++ b/crates/oxide_emit/src/emit.rs @@ -918,7 +918,9 @@ impl Emitter { let ir = ctx.assemble_ir( oxide_ir::ParamLayout { - base: ctx.scopes.builtin_reg_map.len() as u32, + // 顶层模块无父函数:base 恒 0。曾用 builtin_reg_map.len(),harness 前缀 + // 大时把模块自身低号 vreg 误判为父槽 → 恒等色收缩可分配色集、spill 增多。 + base: 0, count: 0, }, None, diff --git a/crates/oxide_regalloc/src/rewrite.rs b/crates/oxide_regalloc/src/rewrite.rs index 6671a36c..1b96ecee 100644 --- a/crates/oxide_regalloc/src/rewrite.rs +++ b/crates/oxide_regalloc/src/rewrite.rs @@ -235,6 +235,25 @@ fn rewrite_inst( } } let _ = f; + // 防御:imm16 编码位的操作数必须是 Imm/None,不得是 Reg—— + // 否则会被上面的 Reg 重写改号,VM 侧 imm16 读到错值(历史 B020 根因)。 + match inst.op { + OpCode::LOAD_UPVALUE | OpCode::CREATE_CLOSURE => { + debug_assert!( + matches!(a, Operand::Imm(_) | Operand::None), + "{}: a 槽是 imm16 编码位,不得为 Reg", + inst.op + ); + } + OpCode::MAKE_CELL | OpCode::CELL_GET | OpCode::CELL_SET | OpCode::STORE_UPVALUE => { + debug_assert!( + matches!(b, Operand::Imm(_) | Operand::None), + "{}: b 槽是 imm16 编码位,不得为 Reg", + inst.op + ); + } + _ => {} + } (Inst { op: inst.op, rd, a, b, ext }, arg_movs) } diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index 78ada42d..0db54b92 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -836,6 +836,13 @@ impl Vm { let global = self.session.global_object(); if let Some(pos) = self.kernel_core.shape_forge().lookup_position(global.shape_id(), si) { self.regs[*reg as usize] = global.get_prop_at(pos); + } else { + vm_debug!( + "push_frame: builtin '{}' reg={} NOT on global object (stays {})", + name, + reg, + self.regs[*reg as usize] + ); } } diff --git a/crates/oxide_vm/src/vm_dispatch_ctrl.rs b/crates/oxide_vm/src/vm_dispatch_ctrl.rs index 69c1c438..70f032a7 100644 --- a/crates/oxide_vm/src/vm_dispatch_ctrl.rs +++ b/crates/oxide_vm/src/vm_dispatch_ctrl.rs @@ -15,8 +15,7 @@ impl Vm { let obj_ptr = callee.as_js_object_ptr(); if !obj_ptr.is_null() { let obj = unsafe { &*obj_ptr }; - if obj.is_function() { - if obj.is_class_constructor() { + if obj.is_function() { if obj.is_class_constructor() { return self .raise_type_error("class constructor cannot be invoked without 'new'") .map(|_| true); @@ -48,6 +47,19 @@ impl Vm { } } + let fn_flag: u8 = if callee.is_object() { + unsafe { (*callee.as_js_object_ptr()).is_function() as u8 } + } else { + 2 + }; + crate::vm_debug!( + "CALL target not callable: rd={} a={} b={} callee={:?} fn_flag={}", + rd, + a, + b, + callee, + fn_flag + ); self.raise_type_error("CALL target is not callable").map(|_| true) }