Files
hakorune/src/backend/mir_interpreter/exec.rs

302 lines
13 KiB
Rust
Raw Normal View History

use super::*;
use crate::mir::basic_block::BasicBlock;
use std::mem;
impl MirInterpreter {
fn trace_enabled() -> bool {
std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1")
|| std::env::var("NYASH_VM_TRACE_EXEC").ok().as_deref() == Some("1")
}
pub(super) fn exec_function_inner(
&mut self,
func: &MirFunction,
arg_vals: Option<&[VMValue]>,
) -> Result<VMValue, VMError> {
// Phase 1: delegate cross-class reroute / narrow fallbacks to method_router
if let Some(r) = super::method_router::pre_exec_reroute(self, func, arg_vals) { return r; }
let saved_regs = mem::take(&mut self.regs);
let saved_fn = self.cur_fn.clone();
self.cur_fn = Some(func.signature.name.clone());
Phase 21.2 Complete: VM Adapter正規実装 + devブリッジ完全撤去 ## 🎉 Phase 21.2完全達成 ### ✅ 実装完了 - VM static box 永続化(singleton infrastructure) - devブリッジ完全撤去(adapter_dev.rs削除、by-name dispatch削除) - .hako正規実装(MirCallV1Handler, AbiAdapterRegistry等) - text-merge経路完全動作 - 全phase2120 adapter reps PASS(7テスト) ### 🐛 バグ修正 1. strip_local_decl修正 - トップレベルのみlocal削除、メソッド内は保持 - src/runner/modes/common_util/hako.rs:29 2. static box フィールド永続化 - MirInterpreter singleton storage実装 - me parameter binding修正(1:1マッピング) - getField/setField string→singleton解決 - src/backend/mir_interpreter/{mod,exec,handlers/boxes_object_fields}.rs 3. Map.len alias rc=0修正 - [map/missing]パターン検出でnull扱い(4箇所) - lang/src/vm/boxes/mir_call_v1_handler.hako:91-93,131-133,151-153,199-201 ### 📁 主要変更ファイル #### Rust(VM Runtime) - src/backend/mir_interpreter/mod.rs - static box singleton storage - src/backend/mir_interpreter/exec.rs - parameter binding fix - src/backend/mir_interpreter/handlers/boxes_object_fields.rs - singleton resolution - src/backend/mir_interpreter/handlers/calls.rs - dev bridge removal - src/backend/mir_interpreter/utils/mod.rs - adapter_dev module removal - src/backend/mir_interpreter/utils/adapter_dev.rs - DELETED (7555 bytes) - src/runner/modes/vm.rs - static box declaration collection - src/runner/modes/common_util/hako.rs - strip_local_decl fix - src/instance_v2.rs - Clone implementation #### Hako (.hako実装) - lang/src/vm/boxes/mir_call_v1_handler.hako - [map/missing] detection - lang/src/vm/boxes/abi_adapter_registry.hako - NEW (adapter registry) - lang/src/vm/helpers/method_alias_policy.hako - method alias support #### テスト - tools/smokes/v2/profiles/quick/core/phase2120/s3_vm_adapter_*.sh - 7 new tests ### 🎯 テスト結果 ``` ✅ s3_vm_adapter_array_len_canary_vm.sh ✅ s3_vm_adapter_array_len_per_recv_canary_vm.sh ✅ s3_vm_adapter_array_length_alias_canary_vm.sh ✅ s3_vm_adapter_array_size_alias_canary_vm.sh ✅ s3_vm_adapter_map_len_alias_state_canary_vm.sh ✅ s3_vm_adapter_map_length_alias_state_canary_vm.sh ✅ s3_vm_adapter_map_size_struct_canary_vm.sh ``` 環境フラグ: HAKO_ABI_ADAPTER=1 HAKO_ABI_ADAPTER_DEV=0 ### 🏆 設計品質 - ✅ ハードコード禁止(AGENTS.md 5.1)完全準拠 - ✅ 構造的・一般化設計(特定Box名のif分岐なし) - ✅ 後方互換性保持(既存コード破壊ゼロ) - ✅ text-merge経路(.hako依存関係正しくマージ) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 19:32:44 +09:00
// Check if this is a static box method call
let static_box_name = self.is_static_box_method(&func.signature.name);
match arg_vals {
Some(args) => {
Phase 21.2 Complete: VM Adapter正規実装 + devブリッジ完全撤去 ## 🎉 Phase 21.2完全達成 ### ✅ 実装完了 - VM static box 永続化(singleton infrastructure) - devブリッジ完全撤去(adapter_dev.rs削除、by-name dispatch削除) - .hako正規実装(MirCallV1Handler, AbiAdapterRegistry等) - text-merge経路完全動作 - 全phase2120 adapter reps PASS(7テスト) ### 🐛 バグ修正 1. strip_local_decl修正 - トップレベルのみlocal削除、メソッド内は保持 - src/runner/modes/common_util/hako.rs:29 2. static box フィールド永続化 - MirInterpreter singleton storage実装 - me parameter binding修正(1:1マッピング) - getField/setField string→singleton解決 - src/backend/mir_interpreter/{mod,exec,handlers/boxes_object_fields}.rs 3. Map.len alias rc=0修正 - [map/missing]パターン検出でnull扱い(4箇所) - lang/src/vm/boxes/mir_call_v1_handler.hako:91-93,131-133,151-153,199-201 ### 📁 主要変更ファイル #### Rust(VM Runtime) - src/backend/mir_interpreter/mod.rs - static box singleton storage - src/backend/mir_interpreter/exec.rs - parameter binding fix - src/backend/mir_interpreter/handlers/boxes_object_fields.rs - singleton resolution - src/backend/mir_interpreter/handlers/calls.rs - dev bridge removal - src/backend/mir_interpreter/utils/mod.rs - adapter_dev module removal - src/backend/mir_interpreter/utils/adapter_dev.rs - DELETED (7555 bytes) - src/runner/modes/vm.rs - static box declaration collection - src/runner/modes/common_util/hako.rs - strip_local_decl fix - src/instance_v2.rs - Clone implementation #### Hako (.hako実装) - lang/src/vm/boxes/mir_call_v1_handler.hako - [map/missing] detection - lang/src/vm/boxes/abi_adapter_registry.hako - NEW (adapter registry) - lang/src/vm/helpers/method_alias_policy.hako - method alias support #### テスト - tools/smokes/v2/profiles/quick/core/phase2120/s3_vm_adapter_*.sh - 7 new tests ### 🎯 テスト結果 ``` ✅ s3_vm_adapter_array_len_canary_vm.sh ✅ s3_vm_adapter_array_len_per_recv_canary_vm.sh ✅ s3_vm_adapter_array_length_alias_canary_vm.sh ✅ s3_vm_adapter_array_size_alias_canary_vm.sh ✅ s3_vm_adapter_map_len_alias_state_canary_vm.sh ✅ s3_vm_adapter_map_length_alias_state_canary_vm.sh ✅ s3_vm_adapter_map_size_struct_canary_vm.sh ``` 環境フラグ: HAKO_ABI_ADAPTER=1 HAKO_ABI_ADAPTER_DEV=0 ### 🏆 設計品質 - ✅ ハードコード禁止(AGENTS.md 5.1)完全準拠 - ✅ 構造的・一般化設計(特定Box名のif分岐なし) - ✅ 後方互換性保持(既存コード破壊ゼロ) - ✅ text-merge経路(.hako依存関係正しくマージ) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 19:32:44 +09:00
// Regular parameter binding: params and args are 1:1
for (i, pid) in func.params.iter().enumerate() {
let v = args.get(i).cloned().unwrap_or(VMValue::Void);
self.regs.insert(*pid, v);
}
}
None => {
// Seed all parameters with Void so SSA consumers observe defined values.
for pid in &func.params {
self.regs.insert(*pid, VMValue::Void);
}
}
}
let mut cur = func.entry_block;
let mut last_pred: Option<BasicBlockId> = None;
// Dev/runtime safety valve: cap the number of interpreter steps per function
// to prevent infinite loops from hanging the process.
let max_steps: u64 = std::env::var("HAKO_VM_MAX_STEPS")
.ok()
.and_then(|v| v.parse::<u64>().ok())
.or_else(|| std::env::var("NYASH_VM_MAX_STEPS").ok().and_then(|v| v.parse::<u64>().ok()))
.unwrap_or(1_000_000);
let mut steps: u64 = 0;
loop {
steps += 1;
if steps > max_steps {
return Err(VMError::InvalidInstruction(format!(
"vm step budget exceeded (max_steps={})",
max_steps
)));
}
let block = func
.blocks
.get(&cur)
.ok_or_else(|| VMError::InvalidBasicBlock(format!("bb {:?} not found", cur)))?;
if Self::trace_enabled() {
eprintln!(
"[vm-trace] enter bb={:?} pred={:?} fn={}",
cur,
last_pred,
self.cur_fn.as_deref().unwrap_or("")
);
}
self.apply_phi_nodes(block, last_pred)?;
if let Err(e) = self.execute_block_instructions(block) {
if Self::trace_enabled() {
eprintln!(
"[vm-trace] error in bb={:?}: {:?}\n last_inst={:?}",
cur, e, self.last_inst
);
}
return Err(e);
}
match self.handle_terminator(block)? {
BlockOutcome::Return(result) => {
self.cur_fn = saved_fn;
self.regs = saved_regs;
return Ok(result);
}
BlockOutcome::Next {
target,
predecessor,
} => {
last_pred = Some(predecessor);
cur = target;
}
}
}
}
fn apply_phi_nodes(
&mut self,
block: &BasicBlock,
last_pred: Option<BasicBlockId>,
) -> Result<(), VMError> {
let trace_phi = std::env::var("NYASH_VM_TRACE_PHI").ok().as_deref() == Some("1");
if trace_phi {
eprintln!(
"[vm-trace-phi] enter bb={:?} last_pred={:?} preds={:?}",
block.id,
last_pred,
block.predecessors
);
}
for inst in block.phi_instructions() {
if let MirInstruction::Phi { dst, inputs } = inst {
let dst_id = *dst;
if trace_phi {
let in_preds: Vec<_> = inputs.iter().map(|(bb, _)| *bb).collect();
eprintln!(
"[vm-trace-phi] phi dst={:?} inputs.pred={:?}",
dst_id, in_preds
);
}
if let Some(pred) = last_pred {
if let Some((_, val)) = inputs.iter().find(|(bb, _)| *bb == pred) {
let v = match self.reg_load(*val) {
Ok(v) => v,
Err(e) => {
// Dev safety valve: tolerate undefined phi inputs by substituting Void
if std::env::var("NYASH_VM_PHI_TOLERATE_UNDEFINED").ok().as_deref() == Some("1") {
if Self::trace_enabled() {
eprintln!("[vm-trace] phi tolerate undefined input {:?} -> Void (err={:?})", val, e);
}
VMValue::Void
} else {
return Err(e);
}
}
};
self.regs.insert(dst_id, v);
if Self::trace_enabled() {
eprintln!(
"[vm-trace] phi dst={:?} take pred={:?} val={:?}",
dst_id, pred, val
);
}
} else {
// No matching predecessor in PHI inputs. Fallback policy:
// - Prefer first input when available to avoid use-before-def crashes.
// - Emit a trace note when tracing is enabled.
// Strict mode: fail-fast when enabled.
// Strict PHI (default ON). Can be disabled with HAKO_VM_PHI_STRICT=0 (or NYASH_VM_PHI_STRICT=0).
let strict = {
let on = |s: &str| {
matches!(s, "1"|"true"|"on"|"yes"|"y"|"t"|"enabled"|"enable")
};
let off = |s: &str| {
matches!(s, "0"|"false"|"off"|"no"|"n"|"f"|"disabled"|"disable")
};
if let Ok(v) = std::env::var("HAKO_VM_PHI_STRICT") {
let v = v.to_ascii_lowercase();
if off(&v) { false } else if on(&v) { true } else { true }
} else if let Ok(v) = std::env::var("NYASH_VM_PHI_STRICT") {
let v = v.to_ascii_lowercase();
if off(&v) { false } else if on(&v) { true } else { true }
} else {
true
}
};
if strict {
if trace_phi {
eprintln!(
"[vm-trace-phi][strict] mismatch dst={:?} last_pred={:?} inputs={:?} preds_of_bb={:?}",
dst_id,
pred,
inputs,
block.predecessors
);
}
return Err(VMError::InvalidInstruction(format!(
"phi pred mismatch at {:?}: no input for predecessor {:?}",
dst_id, pred
)));
}
if let Some((_, val0)) = inputs.first() {
let v = match self.reg_load(*val0) {
Ok(v) => v,
Err(e) => {
if std::env::var("NYASH_VM_PHI_TOLERATE_UNDEFINED").ok().as_deref() == Some("1") {
if Self::trace_enabled() {
eprintln!("[vm-trace] phi missing pred match {:?}; fallback first input {:?} -> Void (err={:?})", pred, val0, e);
}
VMValue::Void
} else {
return Err(e);
}
}
};
self.regs.insert(dst_id, v);
if Self::trace_enabled() {
eprintln!(
"[vm-trace] phi dst={:?} pred-miss fallback first val={:?}",
dst_id, val0
);
}
} else {
// Empty inputs — assign Void (with optional trace) to avoid undefined dst
let v = VMValue::Void;
self.regs.insert(dst_id, v);
if Self::trace_enabled() {
eprintln!("[vm-trace] phi dst={:?} no inputs; assign Void", dst_id);
}
}
}
} else if let Some((_, val)) = inputs.first() {
let v = match self.reg_load(*val) {
Ok(v) => v,
Err(e) => {
if std::env::var("NYASH_VM_PHI_TOLERATE_UNDEFINED").ok().as_deref() == Some("1") {
if Self::trace_enabled() {
eprintln!("[vm-trace] phi tolerate undefined default input {:?} -> Void (err={:?})", val, e);
}
VMValue::Void
} else {
return Err(e);
}
}
};
self.regs.insert(dst_id, v);
if Self::trace_enabled() {
eprintln!(
"[vm-trace] phi dst={:?} take default val={:?}",
dst_id, val
);
}
}
}
}
Ok(())
}
fn execute_block_instructions(&mut self, block: &BasicBlock) -> Result<(), VMError> {
for inst in block.non_phi_instructions() {
self.last_block = Some(block.id);
self.last_inst = Some(inst.clone());
if Self::trace_enabled() {
eprintln!("[vm-trace] inst bb={:?} {:?}", block.id, inst);
}
self.execute_instruction(inst)?;
}
Ok(())
}
fn handle_terminator(&mut self, block: &BasicBlock) -> Result<BlockOutcome, VMError> {
match &block.terminator {
Some(MirInstruction::Return { value }) => {
let result = if let Some(v) = value {
self.reg_load(*v)?
} else {
VMValue::Void
};
Ok(BlockOutcome::Return(result))
}
Some(MirInstruction::Jump { target }) => Ok(BlockOutcome::Next {
target: *target,
predecessor: block.id,
}),
Some(MirInstruction::Branch {
condition,
then_bb,
else_bb,
}) => {
let cond = self.reg_load(*condition)?;
let branch = to_bool_vm(&cond).map_err(VMError::TypeError)?;
let target = if branch { *then_bb } else { *else_bb };
Ok(BlockOutcome::Next {
target,
predecessor: block.id,
})
}
None => Err(VMError::InvalidBasicBlock(format!(
"unterminated block {:?}",
block.id
))),
Some(other) => Err(VMError::InvalidInstruction(format!(
"invalid terminator in MIR interp: {:?}",
other
))),
}
}
}
enum BlockOutcome {
Return(VMValue),
Next {
target: BasicBlockId,
predecessor: BasicBlockId,
},
}