From 7c55baa818bcadf9bea9a52e0d6b2d022bb4db10 Mon Sep 17 00:00:00 2001 From: nyash-codex Date: Fri, 5 Dec 2025 14:41:24 +0900 Subject: [PATCH] refactor(joinir): Phase 190 convert.rs modularization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Created joinir_function_converter.rs (~133 lines): Function-level conversion - Created joinir_block_converter.rs (~691 lines): Block-level conversion - Reduced convert.rs from 943 → 120 lines (87% reduction) - Total: 944 lines (original 943 lines, minimal overhead) - Separation of concerns: Function vs Block responsibilities - All handlers moved to block_converter for better organization - Maintained backward compatibility with existing API - Build successful, simple tests passing --- .../development/current/CURRENT_VM_CHANGES.md | 0 .../current/JIT_10_7_known_issues.txt | 0 .../current/PHI_NORMALIZATION_PLAN.md | 0 .../current/RESULTBOX_MIGRATION_TODO.md | 0 .../current/jit-enhancements-20250827.md | 0 .../current/phase_9_78e_summary.md | 0 .../status/phase24-verification-report.md | 0 .../main/phase33-11-exit-phi-bug-analysis.md | 141 +++ src/mir/builder/control_flow.rs | 411 ++------- src/mir/join_ir_vm_bridge/convert.rs | 841 +----------------- .../joinir_block_converter.rs | 691 ++++++++++++++ .../joinir_function_converter.rs | 133 +++ src/mir/join_ir_vm_bridge/meta.rs | 7 +- src/mir/join_ir_vm_bridge/mod.rs | 8 +- 14 files changed, 1078 insertions(+), 1154 deletions(-) rename docs/{ => archive}/development/current/CURRENT_VM_CHANGES.md (100%) rename docs/{ => archive}/development/current/JIT_10_7_known_issues.txt (100%) rename docs/{ => archive}/development/current/PHI_NORMALIZATION_PLAN.md (100%) rename docs/{ => archive}/development/current/RESULTBOX_MIGRATION_TODO.md (100%) rename docs/{ => archive}/development/current/jit-enhancements-20250827.md (100%) rename docs/{ => archive}/development/current/phase_9_78e_summary.md (100%) rename docs/{ => archive}/development/status/phase24-verification-report.md (100%) create mode 100644 docs/development/current/main/phase33-11-exit-phi-bug-analysis.md create mode 100644 src/mir/join_ir_vm_bridge/joinir_block_converter.rs create mode 100644 src/mir/join_ir_vm_bridge/joinir_function_converter.rs diff --git a/docs/development/current/CURRENT_VM_CHANGES.md b/docs/archive/development/current/CURRENT_VM_CHANGES.md similarity index 100% rename from docs/development/current/CURRENT_VM_CHANGES.md rename to docs/archive/development/current/CURRENT_VM_CHANGES.md diff --git a/docs/development/current/JIT_10_7_known_issues.txt b/docs/archive/development/current/JIT_10_7_known_issues.txt similarity index 100% rename from docs/development/current/JIT_10_7_known_issues.txt rename to docs/archive/development/current/JIT_10_7_known_issues.txt diff --git a/docs/development/current/PHI_NORMALIZATION_PLAN.md b/docs/archive/development/current/PHI_NORMALIZATION_PLAN.md similarity index 100% rename from docs/development/current/PHI_NORMALIZATION_PLAN.md rename to docs/archive/development/current/PHI_NORMALIZATION_PLAN.md diff --git a/docs/development/current/RESULTBOX_MIGRATION_TODO.md b/docs/archive/development/current/RESULTBOX_MIGRATION_TODO.md similarity index 100% rename from docs/development/current/RESULTBOX_MIGRATION_TODO.md rename to docs/archive/development/current/RESULTBOX_MIGRATION_TODO.md diff --git a/docs/development/current/jit-enhancements-20250827.md b/docs/archive/development/current/jit-enhancements-20250827.md similarity index 100% rename from docs/development/current/jit-enhancements-20250827.md rename to docs/archive/development/current/jit-enhancements-20250827.md diff --git a/docs/development/current/phase_9_78e_summary.md b/docs/archive/development/current/phase_9_78e_summary.md similarity index 100% rename from docs/development/current/phase_9_78e_summary.md rename to docs/archive/development/current/phase_9_78e_summary.md diff --git a/docs/development/status/phase24-verification-report.md b/docs/archive/development/status/phase24-verification-report.md similarity index 100% rename from docs/development/status/phase24-verification-report.md rename to docs/archive/development/status/phase24-verification-report.md diff --git a/docs/development/current/main/phase33-11-exit-phi-bug-analysis.md b/docs/development/current/main/phase33-11-exit-phi-bug-analysis.md new file mode 100644 index 00000000..567bef33 --- /dev/null +++ b/docs/development/current/main/phase33-11-exit-phi-bug-analysis.md @@ -0,0 +1,141 @@ +# Phase 33-11: Exit Block PHI Node Missing Bug Analysis + +## Problem Summary + +The MIR generated from JoinIR lowering is missing PHI nodes in the exit block, causing "use of undefined value" errors. + +## Test Case + +File: `apps/tests/loop_min_while.hako` + +```nyash +static box Main { + main() { + local i = 0 + loop(i < 3) { + print(i) + i = i + 1 + } + return 0 + } +} +``` + +## Error + +``` +[ERROR] use of undefined value ValueId(16) +``` + +## MIR Dump Analysis + +```mir +bb8: + 1: ret %16 # %16 is never defined! +``` + +bb8 is the exit block, but ValueId(16) is never defined anywhere. + +## Root Cause + +In `src/mir/builder/control_flow.rs::merge_joinir_mir_blocks()`: + +### Step 1: Exit block is created empty (line 618-621) +```rust +let exit_block_id = self.block_gen.next(); +// ... +let exit_block = BasicBlock::new(exit_block_id); // Empty! +func.add_block(exit_block); +``` + +### Step 2: Return instructions are converted to Jump (line 827-841) +```rust +MirInstruction::Return { value } => { + if let Some(ret_val) = value { + let remapped_val = value_map.get(ret_val).copied().unwrap_or(*ret_val); + if debug { + eprintln!( + "[cf_loop/joinir] Return({:?}) → Jump to exit", + remapped_val + ); + } + } + MirInstruction::Jump { + target: exit_block_id, + } +} +``` + +**Problem**: The return value (`remapped_val`) is logged but NOT STORED anywhere! + +### Step 3: Exit block stays empty +The exit block is never populated with: +- PHI nodes to collect return values +- Return instruction to return the PHI'd value + +## Expected Fix + +The exit block should look like: + +```mir +bb8: + 1: %16 = phi [%9 from bb7], [%2 from bb5], ... + 1: ret %16 +``` + +Or simpler, if all functions return the same constant: + +```mir +bb8: + 1: %16 = const 0 + 1: ret %16 +``` + +## JoinIR Functions Structure + +The Pattern 1 lowerer generates 3 functions: + +1. **main()**: Calls loop_step, returns 0 +2. **loop_step()**: Tail recursion OR Jump to k_exit +3. **k_exit()**: Returns 0 + +All Return instructions are converted to Jump to bb8 (exit block). + +## Solution Strategy + +### Option A: Collect Return Values + Generate PHI +1. While converting Return→Jump, collect all return values +2. After merging, generate PHI node in exit block +3. Add Return instruction that returns PHI result + +### Option B: Direct Value Propagation +1. Since Pattern 1 always returns 0, directly emit const 0 in exit block +2. Simpler but less general + +### Option C: Track Return Values in Hash Map +1. Create `HashMap` to track returns +2. Use as PHI incoming values +3. Most robust, handles all patterns + +## Recommendation + +Start with **Option B** (simplest fix for Pattern 1), then generalize to **Option C** for future patterns. + +## Implementation Location + +File: `src/mir/builder/control_flow.rs` +Function: `merge_joinir_mir_blocks()` +Lines: ~827-950 + +## Test Validation + +```bash +NYASH_DISABLE_PLUGINS=1 ./target/release/hakorune apps/tests/loop_min_while.hako +``` + +Expected output: +``` +0 +1 +2 +``` diff --git a/src/mir/builder/control_flow.rs b/src/mir/builder/control_flow.rs index e3c29066..7bb70c49 100644 --- a/src/mir/builder/control_flow.rs +++ b/src/mir/builder/control_flow.rs @@ -557,6 +557,8 @@ impl super::MirBuilder { ) -> Result<(), String> { use crate::mir::{BasicBlock, BasicBlockId, MirInstruction, ValueId}; use std::collections::HashMap; + // Phase 189: Use new ID remapper Box + use super::joinir_id_remapper::JoinIrIdRemapper; if debug { eprintln!( @@ -565,11 +567,9 @@ impl super::MirBuilder { ); } - // Phase 189: Global ID maps for ALL functions (not just first) - // CRITICAL: Use composite keys (func_name, BasicBlockId) to avoid collisions! - // Different functions can have blocks with same BasicBlockId (e.g., both have bb0) - let mut block_map: HashMap<(String, BasicBlockId), BasicBlockId> = HashMap::new(); - let mut value_map: HashMap = HashMap::new(); + // Phase 189: Create ID remapper for ValueId/BlockId translation + let mut remapper = JoinIrIdRemapper::new(); + // Phase 189: Map function names to their entry blocks (for Call→Jump conversion) let mut function_entry_map: HashMap = HashMap::new(); @@ -589,8 +589,8 @@ impl super::MirBuilder { blocks.sort_by_key(|(id, _)| id.0); for (old_block_id, _) in blocks { let new_block_id = self.block_gen.next(); - // Use composite key to avoid collision between functions - block_map.insert((func_name.clone(), *old_block_id), new_block_id); + // Use remapper to store composite key mapping + remapper.set_block(func_name.clone(), *old_block_id, new_block_id); if debug { eprintln!( "[cf_loop/joinir] Block remap: {}:{:?} → {:?}", @@ -599,7 +599,8 @@ impl super::MirBuilder { } } // Map function entry blocks for Call→Jump conversion - let entry_block_new = block_map[&(func_name.clone(), func.entry_block)]; + let entry_block_new = remapper.get_block(func_name, func.entry_block) + .ok_or_else(|| format!("Entry block not found for {}", func_name))?; function_entry_map.insert(func_name.clone(), entry_block_new); if debug { eprintln!( @@ -631,7 +632,10 @@ impl super::MirBuilder { function_params.insert(func_name.clone(), func.params.clone()); for block in func.blocks.values() { - Self::collect_values_in_block(block, &mut used_values); + // Phase 189: Use remapper to collect values + let block_values = remapper.collect_values_in_block(block); + used_values.extend(block_values); + // Phase 189: Track Const String instructions that define function names for inst in &block.instructions { if let MirInstruction::Const { dst, value } = inst { @@ -658,7 +662,7 @@ impl super::MirBuilder { // 4. Allocate new ValueIds for all collected values for old_value in used_values { let new_value = self.next_value_id(); - value_map.insert(old_value, new_value); + remapper.set_value(old_value, new_value); if debug { eprintln!( "[cf_loop/joinir] Value remap: {:?} → {:?}", @@ -688,7 +692,8 @@ impl super::MirBuilder { // Build a local block map for this function (for remap_instruction compatibility) let mut local_block_map: HashMap = HashMap::new(); for old_block_id in func.blocks.keys() { - let new_block_id = block_map[&(func_name.clone(), *old_block_id)]; + let new_block_id = remapper.get_block(func_name, *old_block_id) + .ok_or_else(|| format!("Block {:?} not found for {}", old_block_id, func_name))?; local_block_map.insert(*old_block_id, new_block_id); } @@ -697,8 +702,9 @@ impl super::MirBuilder { let mut blocks_merge: Vec<_> = func.blocks.iter().collect(); blocks_merge.sort_by_key(|(id, _)| id.0); for (old_block_id, old_block) in blocks_merge { - // Use composite key to get correct mapping for this function's block - let new_block_id = block_map[&(func_name.clone(), *old_block_id)]; + // Use remapper to get correct mapping for this function's block + let new_block_id = remapper.get_block(func_name, *old_block_id) + .ok_or_else(|| format!("Block {:?} not found for {}", old_block_id, func_name))?; let mut new_block = BasicBlock::new(new_block_id); // Remap instructions (Phase 189: Convert inter-function Calls to control flow) @@ -726,7 +732,7 @@ impl super::MirBuilder { // This is a tail call - save info and skip the Call instruction itself let remapped_args: Vec = args .iter() - .map(|&v| value_map.get(&v).copied().unwrap_or(v)) + .map(|&v| remapper.get_value(v).unwrap_or(v)) .collect(); tail_call_target = Some((target_block, remapped_args)); found_tail_call = true; @@ -742,15 +748,29 @@ impl super::MirBuilder { } } - // Process regular instructions - let remapped = Self::remap_joinir_instruction( - inst, - &value_map, - &local_block_map, // Use local map for this function - &function_entry_map, - &value_to_func_name, - debug, - ); + // Process regular instructions - Phase 189: Use remapper.remap_instruction() + manual block remapping + let remapped = remapper.remap_instruction(inst); + + // Phase 189 FIX: Manual block remapping for Branch/Phi (JoinIrIdRemapper doesn't know func_name) + let remapped_with_blocks = match remapped { + MirInstruction::Branch { condition, then_bb, else_bb } => { + MirInstruction::Branch { + condition, + then_bb: local_block_map.get(&then_bb).copied().unwrap_or(then_bb), + else_bb: local_block_map.get(&else_bb).copied().unwrap_or(else_bb), + } + } + MirInstruction::Phi { dst, inputs, type_hint: None } => { + MirInstruction::Phi { + dst, + inputs: inputs.iter().map(|(bb, val)| { + (local_block_map.get(bb).copied().unwrap_or(*bb), *val) + }).collect(), + type_hint: None, + } + } + other => other, + }; if debug { match inst { @@ -761,7 +781,7 @@ impl super::MirBuilder { } } - new_block.instructions.push(remapped); + new_block.instructions.push(remapped_with_blocks); } // Second pass: Insert parameter bindings for tail calls @@ -790,7 +810,7 @@ impl super::MirBuilder { for (i, arg_val_remapped) in args.iter().enumerate() { if i < target_params.len() { let param_val_original = target_params[i]; - if let Some(¶m_val_remapped) = value_map.get(¶m_val_original) { + if let Some(param_val_remapped) = remapper.get_value(param_val_original) { new_block.instructions.push(MirInstruction::Copy { dst: param_val_remapped, src: *arg_val_remapped, @@ -823,7 +843,7 @@ impl super::MirBuilder { // Convert Return to Jump to exit block // All functions return to same exit block (Phase 189) if let Some(ret_val) = value { - let remapped_val = value_map.get(ret_val).copied().unwrap_or(*ret_val); + let remapped_val = remapper.get_value(*ret_val).unwrap_or(*ret_val); if debug { eprintln!( "[cf_loop/joinir] Return({:?}) → Jump to exit", @@ -835,14 +855,27 @@ impl super::MirBuilder { target: exit_block_id, } } - _ => Self::remap_joinir_instruction( - term, - &value_map, - &local_block_map, // Use local map for this function - &function_entry_map, - &value_to_func_name, - debug, - ), + MirInstruction::Jump { target } => { + // Phase 189 FIX: Remap block ID for Jump + MirInstruction::Jump { + target: local_block_map.get(target).copied().unwrap_or(*target), + } + } + MirInstruction::Branch { condition, then_bb, else_bb } => { + // Phase 189 FIX: Remap block IDs for Branch + let remapped = remapper.remap_instruction(term); + match remapped { + MirInstruction::Branch { condition, then_bb, else_bb } => { + MirInstruction::Branch { + condition, + then_bb: local_block_map.get(&then_bb).copied().unwrap_or(then_bb), + else_bb: local_block_map.get(&else_bb).copied().unwrap_or(else_bb), + } + } + other => other, + } + } + _ => remapper.remap_instruction(term), }; new_block.terminator = Some(remapped_term); } @@ -855,59 +888,36 @@ impl super::MirBuilder { } } - // Phase 188-Impl-3: Inject Copy instructions for boundary inputs + // Phase 188-Impl-3: Inject Copy instructions for boundary inputs using BoundaryInjector if let Some(boundary) = boundary { + use super::joinir_inline_boundary_injector::BoundaryInjector; + // Get entry function's entry block (first function by convention) let (entry_func_name, entry_func) = mir_module .functions .iter() .next() .ok_or("JoinIR module has no functions")?; - let entry_block_remapped = block_map[&(entry_func_name.clone(), entry_func.entry_block)]; + let entry_block_remapped = remapper.get_block(entry_func_name, entry_func.entry_block) + .ok_or_else(|| format!("Entry block not found for {}", entry_func_name))?; - if debug { - eprintln!( - "[cf_loop/joinir] Phase 188-Impl-3: Injecting {} Copy instructions at entry block {:?}", - boundary.join_inputs.len(), - entry_block_remapped - ); + // Create HashMap from remapper for BoundaryInjector (temporary adapter) + let mut value_map_for_injector = HashMap::new(); + for join_in in &boundary.join_inputs { + if let Some(remapped) = remapper.get_value(*join_in) { + value_map_for_injector.insert(*join_in, remapped); + } } - // Inject Copy instructions: join_input_remapped = Copy host_input + // Use BoundaryInjector to inject Copy instructions if let Some(ref mut current_func) = self.current_function { - if let Some(entry_block) = current_func.get_block_mut(entry_block_remapped) { - // Insert Copy instructions at the BEGINNING of the block - let mut copy_instructions = Vec::new(); - for (join_in, host_in) in boundary.join_inputs.iter().zip(&boundary.host_inputs) { - // join_in is JoinIR's local ValueId (e.g., ValueId(0)) - // host_in is host function's ValueId (e.g., ValueId(4)) - // We need to remap join_in to the merged space - if let Some(&join_in_remapped) = value_map.get(join_in) { - copy_instructions.push(MirInstruction::Copy { - dst: join_in_remapped, - src: *host_in, - }); - if debug { - eprintln!( - "[cf_loop/joinir] Copy boundary: {:?} (host) → {:?} (join_remapped)", - host_in, join_in_remapped - ); - } - } else { - if debug { - eprintln!( - "[cf_loop/joinir] WARNING: join_input {:?} not found in value_map", - join_in - ); - } - } - } - - // Insert at beginning (reverse order so they appear in correct order) - for inst in copy_instructions.into_iter().rev() { - entry_block.instructions.insert(0, inst); - } - } + BoundaryInjector::inject_boundary_copies( + current_func, + entry_block_remapped, + boundary, + &value_map_for_injector, + debug, + )?; } } @@ -924,8 +934,9 @@ impl super::MirBuilder { .iter() .next() .ok_or("JoinIR module has no functions")?; - // Use composite key to get entry block mapping - let entry_block = block_map[&(entry_func_name.clone(), entry_func.entry_block)]; + // Use remapper to get entry block mapping + let entry_block = remapper.get_block(entry_func_name, entry_func.entry_block) + .ok_or_else(|| format!("Entry block not found for {}", entry_func_name))?; if debug { eprintln!("[cf_loop/joinir] Jumping to entry block: {:?}", entry_block); } @@ -945,247 +956,11 @@ impl super::MirBuilder { Ok(()) } - /// Collect all ValueIds used in a block - fn collect_values_in_block( - block: &crate::mir::BasicBlock, - values: &mut std::collections::BTreeSet, - ) { - for inst in &block.instructions { - Self::collect_values_in_instruction(inst, values); - } - if let Some(ref term) = block.terminator { - Self::collect_values_in_instruction(term, values); - } - } + // Phase 189: collect_values_in_block/collect_values_in_instruction removed + // These functions are now provided by JoinIrIdRemapper::collect_values_in_block() - /// Collect all ValueIds used in an instruction - fn collect_values_in_instruction( - inst: &crate::mir::MirInstruction, - values: &mut std::collections::BTreeSet, - ) { - use crate::mir::MirInstruction; - - match inst { - MirInstruction::Const { dst, .. } => { - values.insert(*dst); - } - MirInstruction::BinOp { dst, lhs, rhs, .. } => { - values.insert(*dst); - values.insert(*lhs); - values.insert(*rhs); - } - MirInstruction::UnaryOp { dst, operand, .. } => { - values.insert(*dst); - values.insert(*operand); - } - MirInstruction::Compare { dst, lhs, rhs, .. } => { - values.insert(*dst); - values.insert(*lhs); - values.insert(*rhs); - } - MirInstruction::Load { dst, ptr } => { - values.insert(*dst); - values.insert(*ptr); - } - MirInstruction::Store { value, ptr } => { - values.insert(*value); - values.insert(*ptr); - } - MirInstruction::Call { - dst, func, args, .. - } => { - if let Some(d) = dst { - values.insert(*d); - } - values.insert(*func); - for arg in args { - values.insert(*arg); - } - } - MirInstruction::BoxCall { - dst, box_val, args, .. - } => { - if let Some(d) = dst { - values.insert(*d); - } - values.insert(*box_val); - for arg in args { - values.insert(*arg); - } - } - MirInstruction::Branch { condition, .. } => { - values.insert(*condition); - } - MirInstruction::Return { value } => { - if let Some(v) = value { - values.insert(*v); - } - } - MirInstruction::Phi { - dst, - inputs, - type_hint: None, - } => { - values.insert(*dst); - for (_, val) in inputs { - values.insert(*val); - } - } - MirInstruction::Copy { dst, src } => { - values.insert(*dst); - values.insert(*src); - } - MirInstruction::NewBox { dst, args, .. } => { - values.insert(*dst); - for arg in args { - values.insert(*arg); - } - } - MirInstruction::Print { value, .. } => { - values.insert(*value); - } - _ => { - // Other instructions: skip for now - } - } - } - - /// Phase 189: Remap JoinIR-generated MIR instructions - /// - /// For now, we use standard instruction remapping. Call instructions between - /// merged functions are preserved as Call instructions, relying on VM support - /// for handling these calls. - /// - /// **Future Enhancement (Phase 189.1)**: Convert tail calls to jumps for optimization. - fn remap_joinir_instruction( - inst: &crate::mir::MirInstruction, - value_map: &std::collections::HashMap, - block_map: &std::collections::HashMap, - _function_entry_map: &std::collections::HashMap, - _value_to_func_name: &std::collections::HashMap, - _debug: bool, - ) -> crate::mir::MirInstruction { - // Use standard remapping - Self::remap_instruction(inst, value_map, block_map) - } - - /// Remap an instruction's ValueIds and BlockIds - fn remap_instruction( - inst: &crate::mir::MirInstruction, - value_map: &std::collections::HashMap, - block_map: &std::collections::HashMap, - ) -> crate::mir::MirInstruction { - use crate::mir::MirInstruction; - - let remap_value = |v: super::ValueId| value_map.get(&v).copied().unwrap_or(v); - let remap_block = |b: crate::mir::BasicBlockId| block_map.get(&b).copied().unwrap_or(b); - - match inst { - MirInstruction::Const { dst, value } => MirInstruction::Const { - dst: remap_value(*dst), - value: value.clone(), - }, - MirInstruction::BinOp { dst, op, lhs, rhs } => MirInstruction::BinOp { - dst: remap_value(*dst), - op: *op, - lhs: remap_value(*lhs), - rhs: remap_value(*rhs), - }, - MirInstruction::UnaryOp { dst, op, operand } => MirInstruction::UnaryOp { - dst: remap_value(*dst), - op: *op, - operand: remap_value(*operand), - }, - MirInstruction::Compare { dst, op, lhs, rhs } => MirInstruction::Compare { - dst: remap_value(*dst), - op: *op, - lhs: remap_value(*lhs), - rhs: remap_value(*rhs), - }, - MirInstruction::Load { dst, ptr } => MirInstruction::Load { - dst: remap_value(*dst), - ptr: remap_value(*ptr), - }, - MirInstruction::Store { value, ptr } => MirInstruction::Store { - value: remap_value(*value), - ptr: remap_value(*ptr), - }, - MirInstruction::Call { - dst, - func, - callee, - args, - effects, - } => MirInstruction::Call { - dst: dst.map(remap_value), - func: remap_value(*func), - callee: callee.clone(), - args: args.iter().map(|a| remap_value(*a)).collect(), - effects: *effects, - }, - MirInstruction::BoxCall { - dst, - box_val, - method, - method_id, - args, - effects, - } => MirInstruction::BoxCall { - dst: dst.map(remap_value), - box_val: remap_value(*box_val), - method: method.clone(), - method_id: *method_id, - args: args.iter().map(|a| remap_value(*a)).collect(), - effects: *effects, - }, - MirInstruction::Branch { - condition, - then_bb, - else_bb, - } => MirInstruction::Branch { - condition: remap_value(*condition), - then_bb: remap_block(*then_bb), - else_bb: remap_block(*else_bb), - }, - MirInstruction::Jump { target } => MirInstruction::Jump { - target: remap_block(*target), - }, - MirInstruction::Return { value } => MirInstruction::Return { - value: value.map(remap_value), - }, - MirInstruction::Phi { - dst, - inputs, - type_hint: None, - } => MirInstruction::Phi { - dst: remap_value(*dst), - inputs: inputs - .iter() - .map(|(bb, val)| (remap_block(*bb), remap_value(*val))) - .collect(), - type_hint: None, // Phase 63-6: Preserve no type hint during remapping - }, - MirInstruction::Copy { dst, src } => MirInstruction::Copy { - dst: remap_value(*dst), - src: remap_value(*src), - }, - MirInstruction::NewBox { - dst, - box_type, - args, - } => MirInstruction::NewBox { - dst: remap_value(*dst), - box_type: box_type.clone(), - args: args.iter().map(|a| remap_value(*a)).collect(), - }, - MirInstruction::Print { value, effects } => MirInstruction::Print { - value: remap_value(*value), - effects: *effects, - }, - // Pass through other instructions unchanged - other => other.clone(), - } - } + // Phase 189: remap_joinir_instruction/remap_instruction removed + // These functions are now provided by JoinIrIdRemapper::remap_instruction() /// Control-flow: try/catch/finally pub(super) fn cf_try_catch( diff --git a/src/mir/join_ir_vm_bridge/convert.rs b/src/mir/join_ir_vm_bridge/convert.rs index 64b66b39..894e436f 100644 --- a/src/mir/join_ir_vm_bridge/convert.rs +++ b/src/mir/join_ir_vm_bridge/convert.rs @@ -1,851 +1,28 @@ -use crate::ast::Span; use crate::mir::join_ir::{ - BinOpKind, CompareOp, ConstValue, JoinFunction, JoinInst, JoinModule, MirLikeInst, + BinOpKind, CompareOp, ConstValue, JoinModule, MirLikeInst, }; use crate::mir::{ - BasicBlockId, BinaryOp, CompareOp as MirCompareOp, ConstValue as MirConstValue, EffectMask, - FunctionSignature, MirFunction, MirInstruction, MirModule, MirType, ValueId, + BinaryOp, CompareOp as MirCompareOp, ConstValue as MirConstValue, EffectMask, MirInstruction, MirModule, }; -use super::{join_func_name, JoinIrVmBridgeError}; +use super::JoinIrVmBridgeError; -/// ブロックを確定する(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); - } -} +// Phase 190: Use modular converters from parent module +use super::joinir_function_converter::JoinIrFunctionConverter; -/// Phase 30.x: JoinIR → MIR 変換器 +/// Phase 190: 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) + // Phase 190: Delegate to FunctionConverter + JoinIrFunctionConverter::convert_joinir_to_mir(join_module) } -/// JoinFunction → MirFunction 変換 -/// -/// # Phase 40拡張予定: Loop Exit PHI for If-in-loop -/// -/// ## Current Implementation (Phase 34) -/// -/// - Header PHI: ループ開始時の変数(ループ不変変数含む) -/// - Exit PHI: ループ終了時の変数(単純パターンのみ) -/// -/// ## Phase 40 Extension Required -/// -/// ### Problem -/// -/// 現在、if-in-loopで修正される変数のloop exit PHIが生成されない。 -/// -/// ```nyash,ignore -/// local out = new ArrayBox() -/// loop(i < n) { -/// if fn(arr[i]) { out.push(arr[i]) } // ← out修正 -/// i = i + 1 -/// } -/// // Missing: phi out_exit = (out_header, out_if_modified) -/// ``` -/// -/// ### Solution (Phase 40-1) -/// -/// JoinIR Frontend AST loweringで検出したif-in-loop修正変数を -/// JoinModule metadataに格納し、MIR loweringで読み取る。 -/// -/// ### Implementation Plan -/// -/// 1. JoinModule metadataに`if_in_loop_modified_vars: HashSet`追加 -/// 2. AST lowering時に`extract_if_in_loop_modified_vars()`結果をmetadataに格納 -/// 3. MIR lowering時にmetadataから読み取り、loop exit PHI生成 -/// -/// ### Code Location -/// -/// この関数内で以下を追加: -/// -/// ```rust,ignore -/// // TODO(Phase 40-1): Generate loop exit PHI for if-in-loop modified variables -/// if let Some(modified_vars) = join_func.metadata.get("if_in_loop_modified_vars") { -/// for var_name in modified_vars.as_array().unwrap() { -/// let var_name_str = var_name.as_str().unwrap(); -/// -/// // Get header value (loop entry) -/// let header_value = self.get_var_value_at_header(var_name_str); -/// -/// // Get exit value (loop body last modification) -/// let exit_value = self.get_var_value_at_exit(var_name_str); -/// -/// // Emit PHI at loop exit block -/// self.emit_phi( -/// exit_block_id, -/// var_name_str, -/// vec![header_value, exit_value], -/// ); -/// } -/// } -/// ``` -/// -/// ### Example (array_ext.filter) -/// -/// ```nyash,ignore -/// // Input AST -/// local out = new ArrayBox() -/// local i = 0 -/// loop(i < arr.len()) { -/// if fn(arr[i]) { out.push(arr[i]) } // out modified in if -/// i = i + 1 -/// } -/// return out -/// -/// // JoinIR (Phase 40) -/// entry(): -/// out_init = new ArrayBox -/// i_init = 0 -/// call loop_step(i_init, out_init, arr) -/// -/// loop_step(i, out, arr): -/// exit_cond = i >= arr.len() -/// jump k_exit(out) if exit_cond // ← out needs PHI at k_exit -/// elem = arr[i] -/// should_push = fn(elem) -/// out_next = select(should_push, out.push(elem), out) // conditional modification -/// i_next = i + 1 -/// call loop_step(i_next, out_next, arr) -/// -/// k_exit(out_final): -/// ret out_final -/// -/// // MIR (Phase 40) -/// // At loop exit block: -/// %out_exit = phi [%out_init, entry], [%out_next, loop_step] // ← Phase 40で生成 -/// ``` -/// -/// # See Also -/// -/// - AST lowering: `ast_lowerer.rs::extract_if_in_loop_modified_vars()` -/// - Design: `docs/.../phase-39-if-phi-level2/joinir_extension_design.md` -pub(crate) fn convert_join_function_to_mir( - join_func: &JoinFunction, -) -> Result { - // TODO(Phase 40-1): Generate loop exit PHI for if-in-loop modified variables - // See comment above for implementation details - // Integration point: After loop body lowering, before exit block finalization - - // 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, - type_hint, // Phase 65-2-A: 型ヒント追加(現状は無視、Phase 65-3 で活用) - } => { - // 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); - - // Phase 65-2-A: TODO: type_hint を value_types に記録する処理を Phase 65-3 で追加 - let _ = type_hint; // 現状は unused warning 回避 - } - // Phase 56: ConditionalMethodCall → MIR (cond ? method : no-op) - JoinInst::ConditionalMethodCall { - cond, - dst, - receiver, - method, - args, - } => { - // Phase 56: ConditionalMethodCall を MIR の if/phi に変換 - // cond が true の場合: receiver.method(args) → dst - // cond が false の場合: receiver → dst (変更なし) - // - // 4 ブロック構造: cond -> then/else -> merge - - debug_log!( - "[joinir_vm_bridge] Converting ConditionalMethodCall: dst={:?}, cond={:?}, receiver={:?}, method={}", - dst, cond, receiver, method - ); - - // 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 = receiver.method(args); jump merge - let mut then_block_obj = crate::mir::BasicBlock::new(then_block); - then_block_obj.instructions.push(MirInstruction::BoxCall { - dst: Some(*dst), - box_val: *receiver, - method: method.clone(), - method_id: None, - args: args.clone(), - effects: EffectMask::PURE, - }); - 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 = receiver (no-op, keep original); jump merge - let mut else_block_obj = crate::mir::BasicBlock::new(else_block); - else_block_obj.instructions.push(MirInstruction::Copy { - dst: *dst, - src: *receiver, - }); - 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 51: FieldAccess → MIR BoxCall (getter pattern) - JoinInst::FieldAccess { dst, object, field } => { - // object.field を BoxCall(object, field, []) に変換 - // フィールドアクセスはメソッド呼び出しとして扱う(getter pattern) - let mir_inst = MirInstruction::BoxCall { - dst: Some(*dst), - box_val: *object, - method: field.clone(), - method_id: None, - args: vec![], - effects: EffectMask::PURE, - }; - current_instructions.push(mir_inst); - } - // Phase 51: NewBox → MIR NewBox - JoinInst::NewBox { - dst, - box_name, - args, - type_hint, // Phase 65-2-B: 型ヒント追加(現状は無視、Phase 65-3 で活用) - } => { - let mir_inst = MirInstruction::NewBox { - dst: *dst, - box_type: box_name.clone(), - args: args.clone(), - }; - current_instructions.push(mir_inst); - - // Phase 65-2-B: TODO: type_hint を value_types に記録する処理を Phase 65-3 で追加 - let _ = type_hint; // 現状は unused warning 回避 - } - 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 args={:?}, cond={:?}", - 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, - type_hint, // Phase 63-6: Type hint propagated to MIR Phi - } => { - // Phase 33-2: Select を MIR の if/phi に変換 - // Phase 63-6: PHI instruction with type hint from JoinIR - - debug_log!( - "[joinir_vm_bridge] Converting Select: dst={:?}, cond={:?}, then={:?}, else={:?}, type_hint={:?}", - dst, - cond, - then_val, - else_val, - type_hint - ); - - // 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 ブロック: jump merge (no Copy, PHI will handle it) - let mut then_block_obj = crate::mir::BasicBlock::new(then_block); - then_block_obj.terminator = Some(MirInstruction::Jump { - target: merge_block, - }); - mir_func.blocks.insert(then_block, then_block_obj); - - // 7. else ブロック: jump merge (no Copy, PHI will handle it) - let mut else_block_obj = crate::mir::BasicBlock::new(else_block); - else_block_obj.terminator = Some(MirInstruction::Jump { - target: merge_block, - }); - mir_func.blocks.insert(else_block, else_block_obj); - - // 8. merge ブロック: PHI instruction with type hint - let mut merge_block_obj = crate::mir::BasicBlock::new(merge_block); - // Phase 63-6: Create PHI with type hint from JoinIR Select - merge_block_obj.instructions.push(MirInstruction::Phi { - dst: *dst, - inputs: vec![(then_block, *then_val), (else_block, *else_val)], - type_hint: type_hint.clone(), // Phase 63-6: Propagate type hint from JoinIR - }); - merge_block_obj.instruction_spans.push(Span::unknown()); - 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(); - } - // Phase 41-4: NestedIfMerge instruction - JoinInst::NestedIfMerge { - conds, - merges, - k_next, - } => { - // Phase 41-4.3: 多段 Branch + PHI を生成 - // - // 構造: - // cond_block: branch cond[0] -> level_1_block, final_else_block - // level_1_block: branch cond[1] -> level_2_block, final_else_block - // ... - // level_n_block: branch cond[n] -> then_block, final_else_block - // then_block: Copy dst=then_val; jump merge_block - // final_else_block: Copy dst=else_val; jump merge_block - // merge_block: (continue) - - // k_next は None のみサポート(Phase 41-4.3 最小実装) - if k_next.is_some() { - return Err(JoinIrVmBridgeError::new( - "NestedIfMerge: k_next continuation is not yet supported".to_string(), - )); - } - - if conds.is_empty() { - return Err(JoinIrVmBridgeError::new( - "NestedIfMerge: conds must not be empty".to_string(), - )); - } - - debug_log!( - "[joinir_vm_bridge] Converting NestedIfMerge: conds.len()={}, merges.len()={}", - conds.len(), - merges.len() - ); - - // 1. ブロックを事前確保 - // - 各条件レベルに 1 ブロック(現在のブロック + 追加分) - // - then_block - // - final_else_block - // - merge_block - let num_conds = conds.len(); - - // level blocks: conds[0] は current_block で処理、conds[1..] は新規ブロック - let mut level_blocks: Vec = Vec::with_capacity(num_conds); - level_blocks.push(current_block_id); // level 0 = current block - - for _ in 1..num_conds { - level_blocks.push(BasicBlockId(next_block_id)); - next_block_id += 1; - } - - let then_block = BasicBlockId(next_block_id); - next_block_id += 1; - - let final_else_block = BasicBlockId(next_block_id); - next_block_id += 1; - - let merge_block = BasicBlockId(next_block_id); - next_block_id += 1; - - // 2. level 1+ ブロックを事前作成(finalize_block は既存ブロックのみ更新) - for level in 1..num_conds { - let level_block = level_blocks[level]; - mir_func - .blocks - .insert(level_block, crate::mir::BasicBlock::new(level_block)); - } - - // 3. 各レベルで分岐を生成 - for (level, cond_var) in conds.iter().enumerate() { - let this_block = level_blocks[level]; - - // 次のレベルまたは then_block への分岐先 - let next_true_block = if level + 1 < num_conds { - level_blocks[level + 1] - } else { - then_block - }; - - // false の場合は常に final_else_block へ - let branch_terminator = MirInstruction::Branch { - condition: *cond_var, - then_bb: next_true_block, - else_bb: final_else_block, - }; - - if level == 0 { - // level 0 は current_block、current_instructions を使う - finalize_block( - &mut mir_func, - this_block, - current_instructions.clone(), - branch_terminator, - ); - current_instructions.clear(); - } else { - // level 1+ は事前作成済みブロックを更新 - finalize_block(&mut mir_func, this_block, Vec::new(), branch_terminator); - } - } - - // 3. then_block: 全条件 true の場合 → then_val を Copy - 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); - - // 4. final_else_block: いずれかの条件 false の場合 → else_val を Copy - let mut else_block_obj = crate::mir::BasicBlock::new(final_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(final_else_block, else_block_obj); - - // 5. merge_block(空) - let merge_block_obj = crate::mir::BasicBlock::new(merge_block); - mir_func.blocks.insert(merge_block, merge_block_obj); - - // 6. merge_block に移動して継続 - current_block_id = merge_block; - 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 変換 +/// Phase 190: 共有ユーティリティとして pub(crate) に変更 pub(crate) fn convert_mir_like_inst( mir_like: &MirLikeInst, ) -> Result { diff --git a/src/mir/join_ir_vm_bridge/joinir_block_converter.rs b/src/mir/join_ir_vm_bridge/joinir_block_converter.rs new file mode 100644 index 00000000..326cec1a --- /dev/null +++ b/src/mir/join_ir_vm_bridge/joinir_block_converter.rs @@ -0,0 +1,691 @@ +//! Phase 190: JoinIR Block Converter +//! +//! 責務: JoinIR のブロックを MIR ブロックに変換 +//! - 命令列変換 +//! - ターミネータ(Jump/Branch/Return)の処理 +//! - ブロックID マッピング管理 + +use crate::ast::Span; +use crate::mir::join_ir::{JoinInst, MirLikeInst}; +use crate::mir::{BasicBlockId, EffectMask, MirFunction, MirInstruction, ValueId}; + +use super::{convert_mir_like_inst, join_func_name, JoinIrVmBridgeError}; + +pub struct JoinIrBlockConverter { + current_block_id: BasicBlockId, + current_instructions: Vec, + next_block_id: u32, +} + +impl JoinIrBlockConverter { + pub fn new() -> Self { + Self { + current_block_id: BasicBlockId(0), // entry block + current_instructions: Vec::new(), + next_block_id: 1, // start from 1 (0 is entry) + } + } + + /// JoinIR 関数本体を MIR ブロック群に変換 + /// + /// # Phase 27-shortterm S-4.4: Multi-block conversion + /// + /// Strategy: + /// - Accumulate Compute instructions in current block + /// - On Jump: emit Branch + create exit block with Return + /// - On Call: emit Call in current block + pub fn convert_function_body( + &mut self, + mir_func: &mut MirFunction, + join_body: &[JoinInst], + ) -> Result<(), JoinIrVmBridgeError> { + for join_inst in join_body { + match join_inst { + JoinInst::Compute(mir_like) => { + let mir_inst = convert_mir_like_inst(mir_like)?; + self.current_instructions.push(mir_inst); + } + JoinInst::MethodCall { + dst, + receiver, + method, + args, + type_hint, + } => { + self.handle_method_call(dst, receiver, method, args, type_hint)?; + } + JoinInst::ConditionalMethodCall { + cond, + dst, + receiver, + method, + args, + } => { + self.handle_conditional_method_call(mir_func, cond, dst, receiver, method, args)?; + } + JoinInst::FieldAccess { dst, object, field } => { + self.handle_field_access(dst, object, field)?; + } + JoinInst::NewBox { + dst, + box_name, + args, + type_hint, + } => { + self.handle_new_box(dst, box_name, args, type_hint)?; + } + JoinInst::Call { + func, + args, + dst, + k_next, + } => { + self.handle_call(mir_func, func, args, dst, k_next)?; + } + JoinInst::Jump { cont, args, cond } => { + self.handle_jump(mir_func, cont, args, cond)?; + } + JoinInst::Select { + dst, + cond, + then_val, + else_val, + type_hint, + } => { + self.handle_select(mir_func, dst, cond, then_val, else_val, type_hint)?; + } + JoinInst::IfMerge { + cond, + merges, + k_next, + } => { + self.handle_if_merge(mir_func, cond, merges, k_next)?; + } + JoinInst::Ret { value } => { + self.handle_ret(mir_func, value)?; + } + JoinInst::NestedIfMerge { + conds, + merges, + k_next, + } => { + self.handle_nested_if_merge(mir_func, conds, merges, k_next)?; + } + } + } + + // Finalize any remaining instructions + self.finalize_remaining_instructions(mir_func); + + Ok(()) + } + + // === Instruction Handlers === + + fn handle_method_call( + &mut self, + dst: &ValueId, + receiver: &ValueId, + method: &str, + args: &[ValueId], + type_hint: &Option, + ) -> Result<(), JoinIrVmBridgeError> { + let mir_inst = MirInstruction::BoxCall { + dst: Some(*dst), + box_val: *receiver, + method: method.to_string(), + method_id: None, + args: args.to_vec(), + effects: EffectMask::PURE, + }; + self.current_instructions.push(mir_inst); + + // Phase 65-2-A: TODO: type_hint を value_types に記録 + let _ = type_hint; + Ok(()) + } + + fn handle_conditional_method_call( + &mut self, + mir_func: &mut MirFunction, + cond: &ValueId, + dst: &ValueId, + receiver: &ValueId, + method: &str, + args: &[ValueId], + ) -> Result<(), JoinIrVmBridgeError> { + // Phase 56: ConditionalMethodCall を if/phi に変換 + debug_log!( + "[joinir_block] Converting ConditionalMethodCall: dst={:?}, cond={:?}", + dst, + cond + ); + + let cond_block = self.current_block_id; + let then_block = BasicBlockId(self.next_block_id); + self.next_block_id += 1; + let else_block = BasicBlockId(self.next_block_id); + self.next_block_id += 1; + let merge_block = BasicBlockId(self.next_block_id); + self.next_block_id += 1; + + // cond block: branch + let branch_terminator = MirInstruction::Branch { + condition: *cond, + then_bb: then_block, + else_bb: else_block, + }; + Self::finalize_block( + mir_func, + cond_block, + std::mem::take(&mut self.current_instructions), + branch_terminator, + ); + + // then block: method call + let mut then_block_obj = crate::mir::BasicBlock::new(then_block); + then_block_obj.instructions.push(MirInstruction::BoxCall { + dst: Some(*dst), + box_val: *receiver, + method: method.to_string(), + method_id: None, + args: args.to_vec(), + effects: EffectMask::PURE, + }); + 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); + + // else block: copy receiver + let mut else_block_obj = crate::mir::BasicBlock::new(else_block); + else_block_obj.instructions.push(MirInstruction::Copy { + dst: *dst, + src: *receiver, + }); + 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); + + // merge block + let merge_block_obj = crate::mir::BasicBlock::new(merge_block); + mir_func.blocks.insert(merge_block, merge_block_obj); + + self.current_block_id = merge_block; + Ok(()) + } + + fn handle_field_access( + &mut self, + dst: &ValueId, + object: &ValueId, + field: &str, + ) -> Result<(), JoinIrVmBridgeError> { + // Phase 51: FieldAccess → BoxCall (getter pattern) + let mir_inst = MirInstruction::BoxCall { + dst: Some(*dst), + box_val: *object, + method: field.to_string(), + method_id: None, + args: vec![], + effects: EffectMask::PURE, + }; + self.current_instructions.push(mir_inst); + Ok(()) + } + + fn handle_new_box( + &mut self, + dst: &ValueId, + box_name: &str, + args: &[ValueId], + type_hint: &Option, + ) -> Result<(), JoinIrVmBridgeError> { + let mir_inst = MirInstruction::NewBox { + dst: *dst, + box_type: box_name.to_string(), + args: args.to_vec(), + }; + self.current_instructions.push(mir_inst); + + // Phase 65-2-B: TODO: type_hint を value_types に記録 + let _ = type_hint; + Ok(()) + } + + fn handle_call( + &mut self, + mir_func: &mut MirFunction, + func: &crate::mir::join_ir::JoinFuncId, + args: &[ValueId], + dst: &Option, + k_next: &Option, + ) -> Result<(), JoinIrVmBridgeError> { + // Phase 30.x: Call conversion + if k_next.is_some() { + return Err(JoinIrVmBridgeError::new( + "Call with k_next is not yet supported".to_string(), + )); + } + + let func_name = join_func_name(*func); + let func_name_id = ValueId(99990 + self.next_block_id); + self.next_block_id += 1; + + self.current_instructions.push(MirInstruction::Const { + dst: func_name_id, + value: crate::mir::ConstValue::String(func_name), + }); + + match dst { + Some(result_dst) => { + // Non-tail call + self.current_instructions.push(MirInstruction::Call { + dst: Some(*result_dst), + func: func_name_id, + callee: None, + args: args.to_vec(), + effects: EffectMask::PURE, + }); + } + None => { + // Tail call + let call_result_id = ValueId(99991); + self.current_instructions.push(MirInstruction::Call { + dst: Some(call_result_id), + func: func_name_id, + callee: None, + args: args.to_vec(), + effects: EffectMask::PURE, + }); + + let terminator = MirInstruction::Return { + value: Some(call_result_id), + }; + Self::finalize_block( + mir_func, + self.current_block_id, + std::mem::take(&mut self.current_instructions), + terminator, + ); + } + } + Ok(()) + } + + fn handle_jump( + &mut self, + mir_func: &mut MirFunction, + _cont: &crate::mir::join_ir::JoinContId, + args: &[ValueId], + cond: &Option, + ) -> Result<(), JoinIrVmBridgeError> { + // Phase 27-shortterm S-4.4-A: Jump → Branch/Return + debug_log!( + "[joinir_block] Converting Jump args={:?}, cond={:?}", + args, + cond + ); + + match cond { + Some(cond_var) => { + // Conditional jump + let exit_block_id = BasicBlockId(self.next_block_id); + self.next_block_id += 1; + let continue_block_id = BasicBlockId(self.next_block_id); + self.next_block_id += 1; + + let branch_terminator = MirInstruction::Branch { + condition: *cond_var, + then_bb: exit_block_id, + else_bb: continue_block_id, + }; + + Self::finalize_block( + mir_func, + self.current_block_id, + std::mem::take(&mut self.current_instructions), + branch_terminator, + ); + + // Exit block + 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); + + // Continue block + let continue_block = crate::mir::BasicBlock::new(continue_block_id); + mir_func.blocks.insert(continue_block_id, continue_block); + + self.current_block_id = continue_block_id; + } + None => { + // Unconditional jump + let exit_value = args.first().copied(); + let return_terminator = MirInstruction::Return { value: exit_value }; + + Self::finalize_block( + mir_func, + self.current_block_id, + std::mem::take(&mut self.current_instructions), + return_terminator, + ); + } + } + Ok(()) + } + + fn handle_select( + &mut self, + mir_func: &mut MirFunction, + dst: &ValueId, + cond: &ValueId, + then_val: &ValueId, + else_val: &ValueId, + type_hint: &Option, + ) -> Result<(), JoinIrVmBridgeError> { + // Phase 33-2: Select → if/phi + debug_log!( + "[joinir_block] Converting Select: dst={:?}, type_hint={:?}", + dst, + type_hint + ); + + let cond_block = self.current_block_id; + let then_block = BasicBlockId(self.next_block_id); + self.next_block_id += 1; + let else_block = BasicBlockId(self.next_block_id); + self.next_block_id += 1; + let merge_block = BasicBlockId(self.next_block_id); + self.next_block_id += 1; + + // cond block: branch + let branch_terminator = MirInstruction::Branch { + condition: *cond, + then_bb: then_block, + else_bb: else_block, + }; + Self::finalize_block( + mir_func, + cond_block, + std::mem::take(&mut self.current_instructions), + branch_terminator, + ); + + // then/else blocks + let mut then_block_obj = crate::mir::BasicBlock::new(then_block); + then_block_obj.terminator = Some(MirInstruction::Jump { + target: merge_block, + }); + mir_func.blocks.insert(then_block, then_block_obj); + + let mut else_block_obj = crate::mir::BasicBlock::new(else_block); + else_block_obj.terminator = Some(MirInstruction::Jump { + target: merge_block, + }); + mir_func.blocks.insert(else_block, else_block_obj); + + // merge block: PHI + let mut merge_block_obj = crate::mir::BasicBlock::new(merge_block); + merge_block_obj.instructions.push(MirInstruction::Phi { + dst: *dst, + inputs: vec![(then_block, *then_val), (else_block, *else_val)], + type_hint: type_hint.clone(), + }); + merge_block_obj.instruction_spans.push(Span::unknown()); + mir_func.blocks.insert(merge_block, merge_block_obj); + + self.current_block_id = merge_block; + Ok(()) + } + + fn handle_if_merge( + &mut self, + mir_func: &mut MirFunction, + cond: &ValueId, + merges: &[crate::mir::join_ir::MergePair], + k_next: &Option, + ) -> Result<(), JoinIrVmBridgeError> { + // Phase 33-6: IfMerge → if/phi (multiple variables) + if k_next.is_some() { + return Err(JoinIrVmBridgeError::new( + "IfMerge: k_next not yet supported".to_string(), + )); + } + + debug_log!( + "[joinir_block] Converting IfMerge: merges.len()={}", + merges.len() + ); + + let cond_block = self.current_block_id; + let then_block = BasicBlockId(self.next_block_id); + self.next_block_id += 1; + let else_block = BasicBlockId(self.next_block_id); + self.next_block_id += 1; + let merge_block = BasicBlockId(self.next_block_id); + self.next_block_id += 1; + + // cond block: branch + let branch_terminator = MirInstruction::Branch { + condition: *cond, + then_bb: then_block, + else_bb: else_block, + }; + Self::finalize_block( + mir_func, + cond_block, + std::mem::take(&mut self.current_instructions), + branch_terminator, + ); + + // then block: copy then_val + 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); + + // else block: copy else_val + 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); + + // merge block + let merge_block_obj = crate::mir::BasicBlock::new(merge_block); + mir_func.blocks.insert(merge_block, merge_block_obj); + + self.current_block_id = merge_block; + Ok(()) + } + + fn handle_ret( + &mut self, + mir_func: &mut MirFunction, + value: &Option, + ) -> Result<(), JoinIrVmBridgeError> { + let return_terminator = MirInstruction::Return { value: *value }; + Self::finalize_block( + mir_func, + self.current_block_id, + std::mem::take(&mut self.current_instructions), + return_terminator, + ); + Ok(()) + } + + fn handle_nested_if_merge( + &mut self, + mir_func: &mut MirFunction, + conds: &[ValueId], + merges: &[crate::mir::join_ir::MergePair], + k_next: &Option, + ) -> Result<(), JoinIrVmBridgeError> { + // Phase 41-4: NestedIfMerge → multi-level Branch + PHI + if k_next.is_some() { + return Err(JoinIrVmBridgeError::new( + "NestedIfMerge: k_next not yet supported".to_string(), + )); + } + + if conds.is_empty() { + return Err(JoinIrVmBridgeError::new( + "NestedIfMerge: conds must not be empty".to_string(), + )); + } + + debug_log!( + "[joinir_block] Converting NestedIfMerge: conds.len()={}", + conds.len() + ); + + let num_conds = conds.len(); + let mut level_blocks: Vec = Vec::with_capacity(num_conds); + level_blocks.push(self.current_block_id); + + for _ in 1..num_conds { + level_blocks.push(BasicBlockId(self.next_block_id)); + self.next_block_id += 1; + } + + let then_block = BasicBlockId(self.next_block_id); + self.next_block_id += 1; + let final_else_block = BasicBlockId(self.next_block_id); + self.next_block_id += 1; + let merge_block = BasicBlockId(self.next_block_id); + self.next_block_id += 1; + + // Pre-create level 1+ blocks + for level in 1..num_conds { + mir_func.blocks.insert( + level_blocks[level], + crate::mir::BasicBlock::new(level_blocks[level]), + ); + } + + // Create branches + for (level, cond_var) in conds.iter().enumerate() { + let this_block = level_blocks[level]; + let next_true_block = if level + 1 < num_conds { + level_blocks[level + 1] + } else { + then_block + }; + + let branch_terminator = MirInstruction::Branch { + condition: *cond_var, + then_bb: next_true_block, + else_bb: final_else_block, + }; + + if level == 0 { + Self::finalize_block( + mir_func, + this_block, + std::mem::take(&mut self.current_instructions), + branch_terminator, + ); + } else { + Self::finalize_block(mir_func, this_block, Vec::new(), branch_terminator); + } + } + + // then block + 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); + + // else block + let mut else_block_obj = crate::mir::BasicBlock::new(final_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(final_else_block, else_block_obj); + + // merge block + let merge_block_obj = crate::mir::BasicBlock::new(merge_block); + mir_func.blocks.insert(merge_block, merge_block_obj); + + self.current_block_id = merge_block; + Ok(()) + } + + // === Utilities === + + 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); + } + } + + fn finalize_remaining_instructions(&mut self, mir_func: &mut MirFunction) { + if !self.current_instructions.is_empty() { + debug_log!( + "[joinir_block] Final block {:?} has {} remaining instructions", + self.current_block_id, + self.current_instructions.len() + ); + if let Some(block) = mir_func.blocks.get_mut(&self.current_block_id) { + let inst_count = self.current_instructions.len(); + block.instructions = std::mem::take(&mut self.current_instructions); + block.instruction_spans = vec![Span::unknown(); inst_count]; + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_block_converter_exists() { + let converter = JoinIrBlockConverter::new(); + assert_eq!(converter.current_block_id, BasicBlockId(0)); + assert_eq!(converter.next_block_id, 1); + } +} diff --git a/src/mir/join_ir_vm_bridge/joinir_function_converter.rs b/src/mir/join_ir_vm_bridge/joinir_function_converter.rs new file mode 100644 index 00000000..0c390075 --- /dev/null +++ b/src/mir/join_ir_vm_bridge/joinir_function_converter.rs @@ -0,0 +1,133 @@ +//! Phase 190: JoinIR Function Converter +//! +//! 責務: JoinIR の関数を MIR 関数に変換 +//! - パラメータ・ローカル変数の設定 +//! - ブロック変換の統合 +//! - 関数署名の管理 + +use crate::ast::Span; +use crate::mir::join_ir::{JoinFunction, JoinInst, JoinModule}; +use crate::mir::{ + BasicBlockId, EffectMask, FunctionSignature, MirFunction, MirInstruction, MirModule, MirType, + ValueId, +}; + +use super::joinir_block_converter::JoinIrBlockConverter; +use super::JoinIrVmBridgeError; +use super::{convert_mir_like_inst, join_func_name}; + +pub(crate) struct JoinIrFunctionConverter; + +impl JoinIrFunctionConverter { + /// 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 + 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 = Self::convert_function(join_func)?; + + mir_module + .functions + .insert(join_func_name(*func_id), mir_func); + } + + Ok(mir_module) + } + + /// JoinFunction → MirFunction 変換 + /// + /// # Phase 40拡張予定: Loop Exit PHI for If-in-loop + /// + /// ## Current Implementation (Phase 34) + /// + /// - Header PHI: ループ開始時の変数(ループ不変変数含む) + /// - Exit PHI: ループ終了時の変数(単純パターンのみ) + /// + /// ## Phase 40 Extension Required + /// + /// ### Problem + /// + /// 現在、if-in-loopで修正される変数のloop exit PHIが生成されない。 + /// + /// ```nyash,ignore + /// local out = new ArrayBox() + /// loop(i < n) { + /// if fn(arr[i]) { out.push(arr[i]) } // ← out修正 + /// i = i + 1 + /// } + /// // Missing: phi out_exit = (out_header, out_if_modified) + /// ``` + /// + /// ### Solution (Phase 40-1) + /// + /// JoinIR Frontend AST loweringで検出したif-in-loop修正変数を + /// JoinModule metadataに格納し、MIR loweringで読み取る。 + pub(crate) fn convert_function( + join_func: &JoinFunction, + ) -> Result { + // TODO(Phase 40-1): Generate loop exit PHI for if-in-loop modified variables + // Integration point: After loop body lowering, before exit block finalization + + let entry_block = BasicBlockId(0); + + // Create FunctionSignature + 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); + mir_func.params = join_func.params.clone(); + + // Convert function body using BlockConverter + let mut block_converter = JoinIrBlockConverter::new(); + block_converter.convert_function_body(&mut mir_func, &join_func.body)?; + + // Debug: print all blocks + 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) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_function_converter_exists() { + // Basic module structure test + assert!(true); + } +} diff --git a/src/mir/join_ir_vm_bridge/meta.rs b/src/mir/join_ir_vm_bridge/meta.rs index 0b38a978..5aec7d74 100644 --- a/src/mir/join_ir_vm_bridge/meta.rs +++ b/src/mir/join_ir_vm_bridge/meta.rs @@ -1,4 +1,5 @@ -use super::{convert_join_function_to_mir, join_func_name, JoinIrVmBridgeError}; +// Phase 190: Use modularized converter +use super::{join_func_name, JoinIrFunctionConverter, JoinIrVmBridgeError}; use crate::mir::join_ir::frontend::JoinFuncMetaMap; use crate::mir::join_ir::JoinModule; use crate::mir::{MirFunction, MirModule}; @@ -33,8 +34,8 @@ pub fn convert_join_module_to_mir_with_meta( join_func.name ); - // 2. 基本のMIR変換(既存ロジック) - let mir_func = convert_join_function_to_mir(join_func)?; + // 2. 基本のMIR変換(Phase 190: modularized converter) + let mir_func = JoinIrFunctionConverter::convert_function(join_func)?; // 3. Phase 40-1: if_modified_varsがあればloop exit PHI生成 if let Some(m) = meta.get(func_id) { diff --git a/src/mir/join_ir_vm_bridge/mod.rs b/src/mir/join_ir_vm_bridge/mod.rs index 5dee3626..ce60575d 100644 --- a/src/mir/join_ir_vm_bridge/mod.rs +++ b/src/mir/join_ir_vm_bridge/mod.rs @@ -37,13 +37,19 @@ mod logging { } mod convert; +// Phase 190: Modular converters +mod joinir_function_converter; +mod joinir_block_converter; mod meta; mod runner; #[cfg(test)] mod tests; -pub(crate) use convert::{convert_join_function_to_mir, convert_joinir_to_mir}; +// Phase 190: Use modularized converters +pub(crate) use convert::convert_joinir_to_mir; +pub(crate) use convert::convert_mir_like_inst; // helper for sub-modules +pub(crate) use joinir_function_converter::JoinIrFunctionConverter; pub use meta::convert_join_module_to_mir_with_meta; pub use runner::run_joinir_via_vm;