feat(joinir): Phase 27-shortterm S-4 完了 - JoinIR → Rust VM ブリッジ参考実装

## 実装内容

### S-4.1: VM インターフェース調査
- VMValue 定義調査(Integer/Float/Bool/String/Future/Void/BoxRef)
- execute_module() API 調査

### S-4.2: JoinValue ↔ VMValue 変換関数実装
- src/mir/join_ir_ops.rs に変換関数追加(+121行)
- to_vm_value()/from_vm_value() 実装
- Float/Future/BoxRef は非対応エラー

### S-4.3: join_ir_vm_bridge.rs 基本構造実装
- src/mir/join_ir_vm_bridge.rs 新規作成(362行)
- run_joinir_via_vm() API 実装
- convert_join_function_to_mir() 実装
- 7つのコンパイルエラー修正完了

### S-4.4-A: Call/Jump 命令実装(skip_ws パターンのみ)
- Call: 末尾呼び出しのみサポート
- Jump: Multi-block CFG 生成(Branch + Return)
- 非対応パターンは unimplemented!() で落とす

### S-4.4-B: A/B テスト作成
- src/tests/joinir_vm_bridge_skip_ws.rs 作成(104行)
- Route A: 直接 VM 実行
- Route C: JoinIR → VM bridge 経由
- skip_ws("   abc") → 3 の A/B 比較実装

## 戦略転換

ChatGPT 先生 + Task 先生の分析により、**Approach 2 (JoinIR Runner 本線化 + ガードレール)** への移行が決定。

本ブリッジ実装は参考実装として保持し、これ以上は太らせない方針。次フェーズで JoinIR Runner を method_router 経由で Rust VM と統合する。
This commit is contained in:
nyash-codex
2025-11-24 07:24:42 +09:00
parent 7a9b23c9d1
commit bff223eff9
3 changed files with 229 additions and 25 deletions

View File

@ -151,8 +151,9 @@ fn convert_joinir_to_mir(
fn convert_join_function_to_mir(
join_func: &crate::mir::join_ir::JoinFunction,
) -> Result<MirFunction, JoinIrVmBridgeError> {
// Phase 27-shortterm S-4.3 最小実装:
// 1つの basic block に全命令を配置
// Phase 27-shortterm S-4.4: skip_ws パターン対応版
// - Call (tail call): MIR Call に変換
// - Jump (conditional exit): Branch + Return に変換
let entry_block = BasicBlockId(0);
// Create minimal FunctionSignature for JoinIR function
@ -170,44 +171,141 @@ fn convert_join_function_to_mir(
let mut mir_func = MirFunction::new(signature, entry_block);
let mut instructions = Vec::new();
// Phase 27-shortterm S-4.4: Multi-block conversion for Jump instructions
// Strategy:
// - Accumulate Compute instructions in current block
// - On Jump: emit Branch + create exit block with Return
// - On Call: emit Call in current block
let mut current_block_id = entry_block;
let mut current_instructions = Vec::new();
let mut next_block_id = 1u32; // for creating new blocks
// JoinInst → MirInstruction 変換
for join_inst in &join_func.body {
match join_inst {
JoinInst::Compute(mir_like) => {
let mir_inst = convert_mir_like_inst(mir_like)?;
instructions.push(mir_inst);
current_instructions.push(mir_inst);
}
JoinInst::Call { func, args, dst, .. } => {
// TODO: S-4.4 で関数呼び出し実装
eprintln!(
"[joinir_vm_bridge] WARNING: Call instruction not yet implemented (func={:?}, args={:?}, dst={:?})",
func, args, dst
);
JoinInst::Call { func, args, dst, k_next } => {
// Phase 27-shortterm S-4.4-A: skip_ws pattern only (tail calls)
// Validate: dst=None, k_next=None (tail call)
if dst.is_some() || k_next.is_some() {
return Err(JoinIrVmBridgeError::new(format!(
"Non-tail Call not supported (dst={:?}, k_next={:?}). Only skip_ws tail call pattern is supported.",
dst, k_next
)));
}
// Convert JoinFuncId to function name
// For skip_ws: func=0 → "main", func=1 → "join_func_1"
let func_name = if func.0 == 0 {
"main".to_string()
} else {
format!("join_func_{}", func.0)
};
eprintln!("[joinir_vm_bridge] Converting Call to function '{}' with {} args", func_name, args.len());
// Create a temporary ValueId to hold the function name (string constant)
// This is a workaround for MIR's string-based Call resolution
// In Phase 27-shortterm, we use a high ValueId to avoid conflicts
let func_name_id = ValueId(9999);
// Add Const instruction for function name
current_instructions.push(MirInstruction::Const {
dst: func_name_id,
value: MirConstValue::String(func_name.clone()),
});
// Create Call instruction (legacy string-based)
// Phase 27-shortterm: No Callee yet, use func field with string ValueId
current_instructions.push(MirInstruction::Call {
dst: None, // tail call, no return value captured
func: func_name_id,
callee: None, // Phase 27-shortterm: no Callee resolution
args: args.clone(),
effects: EffectMask::PURE,
});
}
JoinInst::Jump { cont, args, cond } => {
// TODO: S-4.4 で継続実装
eprintln!(
"[joinir_vm_bridge] WARNING: Jump instruction not yet implemented (cont={:?}, args={:?}, cond={:?})",
cont, args, cond
);
// Phase 27-shortterm S-4.4-A: Jump with condition → Branch + Return
// Jump represents an exit continuation (k_exit) in skip_ws pattern
eprintln!("[joinir_vm_bridge] Converting Jump to cont={:?}, args={:?}, cond={:?}", cont, args, cond);
match cond {
Some(cond_var) => {
// Conditional jump: Branch to exit block
let exit_block_id = BasicBlockId(next_block_id);
next_block_id += 1;
let continue_block_id = BasicBlockId(next_block_id);
next_block_id += 1;
// Emit Branch in current block
current_instructions.push(MirInstruction::Branch {
condition: *cond_var,
then_bb: exit_block_id,
else_bb: continue_block_id,
});
// Finalize current block
if let Some(block) = mir_func.blocks.get_mut(&current_block_id) {
block.instructions = current_instructions;
}
// Create exit block with Return
let exit_value = args.first().copied();
let mut exit_block = crate::mir::BasicBlock::new(exit_block_id);
exit_block.instructions.push(MirInstruction::Return {
value: exit_value,
});
mir_func.blocks.insert(exit_block_id, exit_block);
// Create continue block (will be populated by subsequent instructions)
let continue_block = crate::mir::BasicBlock::new(continue_block_id);
mir_func.blocks.insert(continue_block_id, continue_block);
// Continue in the next block
current_block_id = continue_block_id;
current_instructions = Vec::new();
}
None => {
// Unconditional jump: direct Return
let exit_value = args.first().copied();
current_instructions.push(MirInstruction::Return {
value: exit_value,
});
// Finalize current block
if let Some(block) = mir_func.blocks.get_mut(&current_block_id) {
block.instructions = current_instructions;
}
// No continuation after unconditional return
current_instructions = Vec::new();
}
}
}
JoinInst::Ret { value } => {
if let Some(val_id) = value {
instructions.push(MirInstruction::Return {
value: Some(*val_id),
});
} else {
instructions.push(MirInstruction::Return { value: None });
current_instructions.push(MirInstruction::Return {
value: *value,
});
// Finalize current block
if let Some(block) = mir_func.blocks.get_mut(&current_block_id) {
block.instructions = current_instructions;
}
current_instructions = Vec::new();
}
}
}
// Entry block に命令を登録
if let Some(block) = mir_func.blocks.get_mut(&entry_block) {
block.instructions = instructions;
// Finalize any remaining instructions in the last block
if !current_instructions.is_empty() {
if let Some(block) = mir_func.blocks.get_mut(&current_block_id) {
block.instructions = current_instructions;
}
}
Ok(mir_func)