//! Phase 27-shortterm S-4: JoinIR → Rust VM Bridge //! //! 目的: JoinIR(正規化された IR)を Rust VM で実行するブリッジ層 //! //! ## Architecture //! ```text //! JoinIR (normalized) → MirModule → Rust VM → Result //! ↑ ↑ ↑ //! PHI bugs VM input Execution //! eliminated format (GC, plugins) //! ``` //! //! ## Design Principles //! - JoinIR の正規化構造を保持したまま VM に渡す //! - マッピングだけで済ませる(JoinIR でやった正規化は消えない) //! - VM の機能(GC、プラグイン、エラーハンドリング)を活用 //! //! ## Minimal Instruction Set (S-4.3) //! - **Compute**: Const, BinOp, Compare //! - **BoxCall**: StringBox メソッド呼び出し //! - **Call/Jump/Ret**: 制御フロー //! //! Phase 27-shortterm scope: skip_ws で green 化できれば成功 use crate::backend::{MirInterpreter, VMError, VMValue}; use crate::config::env::joinir_vm_bridge_debug; use crate::mir::join_ir::{ BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinInst, JoinModule, MirLikeInst, }; use crate::mir::join_ir_ops::JoinValue; use crate::ast::Span; use crate::mir::{ BasicBlockId, BinaryOp, CompareOp as MirCompareOp, ConstValue as MirConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction, MirModule, MirType, ValueId, }; /// 条件付きデバッグ出力マクロ(NYASH_JOINIR_VM_BRIDGE_DEBUG=1 で有効) macro_rules! debug_log { ($($arg:tt)*) => { if joinir_vm_bridge_debug() { eprintln!($($arg)*); } }; } /// Phase 27-shortterm S-4 エラー型 #[derive(Debug, Clone)] pub struct JoinIrVmBridgeError { pub message: String, } impl JoinIrVmBridgeError { pub fn new(msg: impl Into) -> Self { Self { message: msg.into(), } } } impl From for JoinIrVmBridgeError { fn from(err: VMError) -> Self { JoinIrVmBridgeError::new(format!("VM error: {:?}", err)) } } /// ブロックを確定する(instructions + spans + terminator を設定) fn finalize_block( mir_func: &mut MirFunction, block_id: BasicBlockId, instructions: Vec, terminator: MirInstruction, ) { if let Some(block) = mir_func.blocks.get_mut(&block_id) { let inst_count = instructions.len(); block.instructions = instructions; block.instruction_spans = vec![Span::unknown(); inst_count]; block.terminator = Some(terminator); } } /// JoinFuncId から MIR 用の関数名を生成 fn join_func_name(id: JoinFuncId) -> String { format!("join_func_{}", id.0) } /// Phase 27-shortterm S-4.3: JoinIR → VM 実行のエントリーポイント /// /// ## Arguments /// - `join_module`: JoinIR モジュール(正規化済み) /// - `entry_func`: エントリーポイント関数ID /// - `args`: 初期引数(JoinValue 形式) /// /// ## Returns /// - `Ok(JoinValue)`: 実行結果 /// - `Err(JoinIrVmBridgeError)`: 変換エラーまたは実行エラー /// /// ## Example /// ```ignore /// let join_module = lower_skip_ws_to_joinir(&mir_module)?; /// let result = run_joinir_via_vm( /// &join_module, /// JoinFuncId::new(0), /// &[JoinValue::Str(" hello".to_string()), JoinValue::Int(7)] /// )?; /// assert_eq!(result, JoinValue::Int(2)); /// ``` pub fn run_joinir_via_vm( join_module: &JoinModule, entry_func: JoinFuncId, args: &[JoinValue], ) -> Result { debug_log!("[joinir_vm_bridge] Phase 27-shortterm S-4.3"); debug_log!("[joinir_vm_bridge] Converting JoinIR to MIR for VM execution"); // Step 1: JoinIR → MIR 変換 let mir_module = convert_joinir_to_mir(join_module)?; debug_log!( "[joinir_vm_bridge] Converted {} JoinIR functions to MIR", join_module.functions.len() ); // Step 2: VM 実行 let mut vm = MirInterpreter::new(); debug_log!( "[joinir_vm_bridge] Executing via VM with {} arguments", args.len() ); // Convert JoinValue → VMValue (BoxRef 含む) let vm_args: Vec = args.iter().cloned().map(|v| v.into_vm_value()).collect(); let entry_name = join_func_name(entry_func); let result = vm.execute_function_with_args(&mir_module, &entry_name, &vm_args)?; // Step 3: VMValue → JoinValue 変換 let join_result = JoinValue::from_vm_value(&result) .map_err(|e| JoinIrVmBridgeError::new(format!("Result conversion error: {}", e.message)))?; debug_log!("[joinir_vm_bridge] Execution succeeded: {:?}", join_result); Ok(join_result) } /// Phase 30.x: JoinIR → MIR 変換器 /// /// Phase 32 L-2.2 Step-3: テストから呼び出し可能に `pub(crate)` 化 pub(crate) fn convert_joinir_to_mir(join_module: &JoinModule) -> Result { let mut mir_module = MirModule::new("joinir_bridge".to_string()); // Convert all JoinIR functions to MIR (entry function becomes "skip" or similar) for (func_id, join_func) in &join_module.functions { debug_log!( "[joinir_vm_bridge] Converting JoinFunction {} ({})", func_id.0, join_func.name ); let mir_func = convert_join_function_to_mir(join_func)?; // Use actual function name (not "main") since we'll create a wrapper mir_module.functions.insert(join_func_name(*func_id), mir_func); } Ok(mir_module) } /// JoinFunction → MirFunction 変換 fn convert_join_function_to_mir( join_func: &crate::mir::join_ir::JoinFunction, ) -> Result { // 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 // Phase 27-shortterm: すべて MirType::Unknown として扱う(型情報は JoinIR に無いため) let param_types = join_func .params .iter() .map(|_| MirType::Unknown) .collect::>(); let signature = FunctionSignature { name: join_func.name.clone(), params: param_types, return_type: MirType::Unknown, effects: EffectMask::PURE, }; let mut mir_func = MirFunction::new(signature, entry_block); // Phase 30.x: Set parameter ValueIds from JoinIR function // JoinIR's VarId is an alias for ValueId, so direct copy works mir_func.params = join_func.params.clone(); // 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 for join_inst in &join_func.body { match join_inst { JoinInst::Compute(mir_like) => { let mir_inst = convert_mir_like_inst(mir_like)?; current_instructions.push(mir_inst); } JoinInst::MethodCall { dst, receiver, method, args } => { // Phase 34-6: MethodCall → MIR BoxCall 変換 // receiver.method(args...) を BoxCall(receiver, method, args) に変換 let mir_inst = MirInstruction::BoxCall { dst: Some(*dst), box_val: *receiver, method: method.clone(), method_id: None, args: args.clone(), effects: EffectMask::PURE, }; current_instructions.push(mir_inst); } JoinInst::Call { func, args, dst, k_next, } => { // Phase 30.x: Support both tail calls and non-tail calls // - dst=None, k_next=None: Tail call → call + return // - dst=Some(id), k_next=None: Non-tail call → call + store + continue // - k_next=Some: Not yet supported if k_next.is_some() { let call_target_name = join_func_name(*func); return Err(JoinIrVmBridgeError::new(format!( "Call with k_next is not yet supported\n\ \n\ Current function: {}\n\ Call target: {} (JoinFuncId: {:?})\n\ Arguments: {} args\n\ k_next: {:?}\n\ \n\ 💡 Fix: Change `k_next: Some(...)` to `k_next: None`\n\ 💡 Note: Phase 31 and Phase 34 both use k_next=None (bridge limitation)\n\ \n\ Example:\n\ Call {{\n\ func: loop_step_id,\n\ args: vec![i_next, acc_next, n],\n\ k_next: None, // ⚠️ Must be None\n\ dst: Some(result),\n\ }}", join_func.name, call_target_name, func, args.len(), k_next, ))); } // Convert JoinFuncId to function name let func_name = join_func_name(*func); // Create temporary ValueId for function name let func_name_id = ValueId(99990 + next_block_id); next_block_id += 1; // Add Const instruction for function name current_instructions.push(MirInstruction::Const { dst: func_name_id, value: MirConstValue::String(func_name), }); match dst { Some(result_dst) => { // Non-tail call: store result in dst and continue current_instructions.push(MirInstruction::Call { dst: Some(*result_dst), func: func_name_id, callee: None, args: args.clone(), effects: EffectMask::PURE, }); // Continue to next instruction (no block termination) } None => { // Tail call: call + return result let call_result_id = ValueId(99991); current_instructions.push(MirInstruction::Call { dst: Some(call_result_id), func: func_name_id, callee: None, args: args.clone(), effects: EffectMask::PURE, }); // Return the result of the tail call let terminator = MirInstruction::Return { value: Some(call_result_id) }; finalize_block(&mut mir_func, current_block_id, current_instructions, terminator); current_instructions = Vec::new(); } } } JoinInst::Jump { 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 debug_log!( "[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; // Phase 30.x: Branch terminator (separate from instructions) let branch_terminator = MirInstruction::Branch { condition: *cond_var, then_bb: exit_block_id, else_bb: continue_block_id, }; // Finalize current block with Branch terminator finalize_block(&mut mir_func, current_block_id, current_instructions, branch_terminator); // Create exit block with Return terminator let exit_value = args.first().copied(); let mut exit_block = crate::mir::BasicBlock::new(exit_block_id); exit_block.terminator = Some(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 terminator let exit_value = args.first().copied(); let return_terminator = MirInstruction::Return { value: exit_value }; // Finalize current block with Return terminator finalize_block(&mut mir_func, current_block_id, current_instructions, return_terminator); // No continuation after unconditional return current_instructions = Vec::new(); } } } // Phase 33: Select instruction conversion to MIR JoinInst::Select { dst, cond, then_val, else_val } => { // Phase 33-2: Select を MIR の if/phi に変換 // 最小実装: cond/then/else/merge の 4 ブロック構造 debug_log!( "[joinir_vm_bridge] Converting Select: dst={:?}, cond={:?}, then={:?}, else={:?}", dst, cond, then_val, else_val ); // 1. cond ブロック(現在のブロック) let cond_block = current_block_id; // 2. then ブロック作成 let then_block = BasicBlockId(next_block_id); next_block_id += 1; // 3. else ブロック作成 let else_block = BasicBlockId(next_block_id); next_block_id += 1; // 4. merge ブロック作成 let merge_block = BasicBlockId(next_block_id); next_block_id += 1; // 5. cond ブロックで分岐 let branch_terminator = MirInstruction::Branch { condition: *cond, then_bb: then_block, else_bb: else_block, }; finalize_block(&mut mir_func, cond_block, current_instructions, branch_terminator); // 6. then ブロック: dst = then_val; jump merge let mut then_block_obj = crate::mir::BasicBlock::new(then_block); then_block_obj.instructions.push(MirInstruction::Copy { dst: *dst, src: *then_val, }); then_block_obj.instruction_spans.push(Span::unknown()); then_block_obj.terminator = Some(MirInstruction::Jump { target: merge_block }); mir_func.blocks.insert(then_block, then_block_obj); // 7. else ブロック: dst = else_val; jump merge let mut else_block_obj = crate::mir::BasicBlock::new(else_block); else_block_obj.instructions.push(MirInstruction::Copy { dst: *dst, src: *else_val, }); else_block_obj.instruction_spans.push(Span::unknown()); else_block_obj.terminator = Some(MirInstruction::Jump { target: merge_block }); mir_func.blocks.insert(else_block, else_block_obj); // 8. merge ブロック作成(空) let merge_block_obj = crate::mir::BasicBlock::new(merge_block); mir_func.blocks.insert(merge_block, merge_block_obj); // 9. merge ブロックに移動 current_block_id = merge_block; current_instructions = Vec::new(); } // Phase 33-6: IfMerge instruction conversion to MIR JoinInst::IfMerge { cond, merges, k_next } => { // Phase 33-6: IfMerge を MIR の if/phi に変換 // Select と同じ 4 ブロック構造だが、複数の Copy を生成 // Phase 33-6 最小実装: k_next は None のみサポート if k_next.is_some() { return Err(JoinIrVmBridgeError::new( "IfMerge: k_next continuation is not yet supported (Phase 33-6 minimal)".to_string(), )); } debug_log!( "[joinir_vm_bridge] Converting IfMerge: cond={:?}, merges.len()={}", cond, merges.len() ); // 1. cond ブロック(現在のブロック) let cond_block = current_block_id; // 2. then ブロック作成 let then_block = BasicBlockId(next_block_id); next_block_id += 1; // 3. else ブロック作成 let else_block = BasicBlockId(next_block_id); next_block_id += 1; // 4. merge ブロック作成 let merge_block = BasicBlockId(next_block_id); next_block_id += 1; // 5. cond ブロックで分岐 let branch_terminator = MirInstruction::Branch { condition: *cond, then_bb: then_block, else_bb: else_block, }; finalize_block(&mut mir_func, cond_block, current_instructions, branch_terminator); // 6. then ブロック: 各 merge について dst = then_val; jump merge let mut then_block_obj = crate::mir::BasicBlock::new(then_block); for merge in merges { then_block_obj.instructions.push(MirInstruction::Copy { dst: merge.dst, src: merge.then_val, }); then_block_obj.instruction_spans.push(Span::unknown()); } then_block_obj.terminator = Some(MirInstruction::Jump { target: merge_block }); mir_func.blocks.insert(then_block, then_block_obj); // 7. else ブロック: 各 merge について dst = else_val; jump merge let mut else_block_obj = crate::mir::BasicBlock::new(else_block); for merge in merges { else_block_obj.instructions.push(MirInstruction::Copy { dst: merge.dst, src: merge.else_val, }); else_block_obj.instruction_spans.push(Span::unknown()); } else_block_obj.terminator = Some(MirInstruction::Jump { target: merge_block }); mir_func.blocks.insert(else_block, else_block_obj); // 8. merge ブロック作成(空) let merge_block_obj = crate::mir::BasicBlock::new(merge_block); mir_func.blocks.insert(merge_block, merge_block_obj); // 9. merge ブロックに移動 current_block_id = merge_block; current_instructions = Vec::new(); } JoinInst::Ret { value } => { // Phase 30.x: Return terminator (separate from instructions) let return_terminator = MirInstruction::Return { value: *value }; // Finalize current block with Return terminator finalize_block(&mut mir_func, current_block_id, current_instructions, return_terminator); current_instructions = Vec::new(); } } } // Finalize any remaining instructions in the last block if !current_instructions.is_empty() { debug_log!( "[joinir_vm_bridge] Final block {:?} has {} remaining instructions", current_block_id, current_instructions.len() ); if let Some(block) = mir_func.blocks.get_mut(¤t_block_id) { // Phase 30.x: VM requires instruction_spans to match instructions length let inst_count = current_instructions.len(); block.instructions = current_instructions; block.instruction_spans = vec![Span::unknown(); inst_count]; } } // Debug: print all blocks and their instruction counts + terminators debug_log!( "[joinir_vm_bridge] Function '{}' has {} blocks:", mir_func.signature.name, mir_func.blocks.len() ); for (block_id, block) in &mir_func.blocks { debug_log!( " Block {:?}: {} instructions, terminator={:?}", block_id, block.instructions.len(), block.terminator ); } Ok(mir_func) } /// MirLikeInst → MirInstruction 変換 fn convert_mir_like_inst(mir_like: &MirLikeInst) -> Result { match mir_like { MirLikeInst::Const { dst, value } => { let mir_const = match value { ConstValue::Integer(i) => MirConstValue::Integer(*i), ConstValue::Bool(b) => MirConstValue::Bool(*b), ConstValue::String(s) => MirConstValue::String(s.clone()), ConstValue::Null => MirConstValue::Null, }; Ok(MirInstruction::Const { dst: *dst, value: mir_const, }) } MirLikeInst::BinOp { dst, op, lhs, rhs } => { let mir_op = match op { BinOpKind::Add => BinaryOp::Add, BinOpKind::Sub => BinaryOp::Sub, BinOpKind::Mul => BinaryOp::Mul, BinOpKind::Div => BinaryOp::Div, BinOpKind::Or => BinaryOp::Or, BinOpKind::And => BinaryOp::And, }; Ok(MirInstruction::BinOp { dst: *dst, op: mir_op, lhs: *lhs, rhs: *rhs, }) } MirLikeInst::Compare { dst, op, lhs, rhs } => { let mir_cmp = match op { CompareOp::Lt => MirCompareOp::Lt, CompareOp::Le => MirCompareOp::Le, CompareOp::Gt => MirCompareOp::Gt, CompareOp::Ge => MirCompareOp::Ge, CompareOp::Eq => MirCompareOp::Eq, CompareOp::Ne => MirCompareOp::Ne, }; Ok(MirInstruction::Compare { dst: *dst, op: mir_cmp, lhs: *lhs, rhs: *rhs, }) } MirLikeInst::BoxCall { dst, box_name, method, args, } => { // Phase 27-shortterm S-4.3: BoxCall → MIR BoxCall // box_name は JoinIR で保持しているが、MIR BoxCall には receiver ValueId のみ // 暫定: args[0] を receiver として扱う if args.is_empty() { return Err(JoinIrVmBridgeError::new(format!( "BoxCall requires at least one argument (receiver), got: box_name={}, method={}", box_name, method ))); } let receiver = args[0]; let method_args = args[1..].to_vec(); Ok(MirInstruction::BoxCall { dst: *dst, box_val: receiver, method: method.clone(), method_id: None, // Phase 27-shortterm: no method ID resolution args: method_args, effects: EffectMask::PURE, // Phase 27-shortterm: assume pure }) } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_convert_const_inst() { let join_const = MirLikeInst::Const { dst: ValueId(10), value: ConstValue::Integer(42), }; let mir_inst = convert_mir_like_inst(&join_const).unwrap(); match mir_inst { MirInstruction::Const { dst, value } => { assert_eq!(dst, ValueId(10)); assert!(matches!(value, MirConstValue::Integer(42))); } _ => panic!("Expected Const instruction"), } } #[test] fn test_convert_binop_inst() { let join_binop = MirLikeInst::BinOp { dst: ValueId(20), op: BinOpKind::Add, lhs: ValueId(10), rhs: ValueId(11), }; let mir_inst = convert_mir_like_inst(&join_binop).unwrap(); match mir_inst { MirInstruction::BinOp { dst, op, lhs, rhs } => { assert_eq!(dst, ValueId(20)); assert_eq!(op, BinaryOp::Add); assert_eq!(lhs, ValueId(10)); assert_eq!(rhs, ValueId(11)); } _ => panic!("Expected BinOp instruction"), } } #[test] fn test_convert_compare_inst() { let join_cmp = MirLikeInst::Compare { dst: ValueId(30), op: CompareOp::Ge, lhs: ValueId(10), rhs: ValueId(11), }; let mir_inst = convert_mir_like_inst(&join_cmp).unwrap(); match mir_inst { MirInstruction::Compare { dst, op, lhs, rhs } => { assert_eq!(dst, ValueId(30)); assert_eq!(op, MirCompareOp::Ge); assert_eq!(lhs, ValueId(10)); assert_eq!(rhs, ValueId(11)); } _ => panic!("Expected Compare instruction"), } } }