feat(joinir): FuncScannerBox.trim/1 JoinIR VM Bridge A/B test

Phase 30.x: JoinIR VM Bridge でPHI問題を設計で解決できることを実証

変更内容:
- src/runner/modes/vm.rs: FuncScannerBox.trim/1 検出時にJoinIR経由実行
- src/mir/join_ir_vm_bridge.rs: Non-tail call サポート追加
  - dst=Some(id) の場合、結果を格納して次の命令へ継続
- src/tests/joinir_vm_bridge_trim.rs: A/Bテスト新規作成
  - メインテスト: Route A (VM) → "void" (PHI bug), Route C (JoinIR) → "abc" 
  - エッジケース: 5パターン全てPASS
- src/config/env.rs: joinir_vm_bridge_debug() 追加
- docs: Phase 30 TASKS.md に L-0.4 追加

テスト結果:
  Route A (MIR→VM直接):     "void"  ← PHI バグ
  Route C (MIR→JoinIR→VM):  "abc"   ← 正解 

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-25 12:22:08 +09:00
parent de16ff9b7f
commit ee2b3115ee
5 changed files with 565 additions and 79 deletions

View File

@ -2,6 +2,13 @@ use super::super::NyashRunner;
use nyash_rust::{ast::ASTNode, mir::MirCompiler, parser::NyashParser};
use std::{fs, process};
// Phase 30.x: JoinIR VM Bridge integration (experimental)
// Used only when NYASH_JOINIR_EXPERIMENT=1 AND NYASH_JOINIR_VM_BRIDGE=1
use crate::config::env::{joinir_experiment_enabled, joinir_vm_bridge_enabled};
use crate::mir::join_ir::{lower_funcscanner_trim_to_joinir, lower_skip_ws_to_joinir, JoinFuncId};
use crate::mir::join_ir_ops::JoinValue;
use crate::mir::join_ir_vm_bridge::run_joinir_via_vm;
impl NyashRunner {
/// Execute VM mode with full plugin initialization and AST prelude merge
pub(crate) fn execute_vm_mode(&self, filename: &str) {
@ -495,6 +502,106 @@ impl NyashRunner {
}
}
// Phase 30.x: JoinIR VM Bridge experimental path
// Activated when NYASH_JOINIR_EXPERIMENT=1 AND NYASH_JOINIR_VM_BRIDGE=1
// Currently only supports minimal_ssa_skip_ws.hako (Main.skip function)
let joinir_path_attempted = if joinir_experiment_enabled() && joinir_vm_bridge_enabled() {
// Check if this module contains Main.skip/1 (minimal_ssa_skip_ws target)
// Note: function names include arity suffix like "Main.skip/1"
let has_main_skip = module_vm.functions.contains_key("Main.skip/1");
let has_trim = module_vm.functions.contains_key("FuncScannerBox.trim/1");
if has_main_skip {
eprintln!("[joinir/vm_bridge] Attempting JoinIR path for Main.skip");
match lower_skip_ws_to_joinir(&module_vm) {
Some(join_module) => {
// Get input argument from NYASH_JOINIR_INPUT or use default
let input = std::env::var("NYASH_JOINIR_INPUT")
.unwrap_or_else(|_| " abc".to_string());
eprintln!("[joinir/vm_bridge] Input: {:?}", input);
match run_joinir_via_vm(
&join_module,
JoinFuncId::new(0),
&[JoinValue::Str(input)],
) {
Ok(result) => {
let exit_code = match &result {
JoinValue::Int(v) => *v as i32,
JoinValue::Bool(b) => if *b { 1 } else { 0 },
_ => 0,
};
eprintln!("[joinir/vm_bridge] ✅ JoinIR result: {:?}", result);
if !quiet_pipe {
println!("RC: {}", exit_code);
}
process::exit(exit_code);
}
Err(e) => {
eprintln!("[joinir/vm_bridge] ❌ JoinIR execution failed: {:?}", e);
eprintln!("[joinir/vm_bridge] Falling back to normal VM path");
false // Continue to normal VM execution
}
}
}
None => {
eprintln!("[joinir/vm_bridge] lower_skip_ws_to_joinir returned None");
eprintln!("[joinir/vm_bridge] Falling back to normal VM path");
false
}
}
} else if has_trim {
// Phase 30.x: FuncScannerBox.trim/1 JoinIR path
eprintln!("[joinir/vm_bridge] Attempting JoinIR path for FuncScannerBox.trim");
match lower_funcscanner_trim_to_joinir(&module_vm) {
Some(join_module) => {
// Get input argument from NYASH_JOINIR_INPUT or use default
let input = std::env::var("NYASH_JOINIR_INPUT")
.unwrap_or_else(|_| " abc ".to_string());
eprintln!("[joinir/vm_bridge] Input: {:?}", input);
match run_joinir_via_vm(
&join_module,
JoinFuncId::new(0),
&[JoinValue::Str(input)],
) {
Ok(result) => {
// trim returns a string, print it and exit with 0
eprintln!("[joinir/vm_bridge] ✅ JoinIR trim result: {:?}", result);
if !quiet_pipe {
match &result {
JoinValue::Str(s) => println!("{}", s),
_ => println!("{:?}", result),
}
}
process::exit(0);
}
Err(e) => {
eprintln!("[joinir/vm_bridge] ❌ JoinIR trim failed: {:?}", e);
eprintln!("[joinir/vm_bridge] Falling back to normal VM path");
false
}
}
}
None => {
eprintln!("[joinir/vm_bridge] lower_funcscanner_trim_to_joinir returned None");
eprintln!("[joinir/vm_bridge] Falling back to normal VM path");
false
}
}
} else {
false // No supported JoinIR target function
}
} else {
false
};
// Normal VM execution path (fallback or default)
if joinir_path_attempted {
// This branch is never reached because successful JoinIR path calls process::exit()
unreachable!("JoinIR path should have exited");
}
match vm.execute_module(&module_vm) {
Ok(ret) => {
use crate::box_trait::{BoolBox, IntegerBox};