refactor(joinir): Phase 190 convert.rs modularization
- 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
This commit is contained in:
@ -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<BasicBlockId, ValueId>` 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
|
||||||
|
```
|
||||||
@ -557,6 +557,8 @@ impl super::MirBuilder {
|
|||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
use crate::mir::{BasicBlock, BasicBlockId, MirInstruction, ValueId};
|
use crate::mir::{BasicBlock, BasicBlockId, MirInstruction, ValueId};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
// Phase 189: Use new ID remapper Box
|
||||||
|
use super::joinir_id_remapper::JoinIrIdRemapper;
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
@ -565,11 +567,9 @@ impl super::MirBuilder {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase 189: Global ID maps for ALL functions (not just first)
|
// Phase 189: Create ID remapper for ValueId/BlockId translation
|
||||||
// CRITICAL: Use composite keys (func_name, BasicBlockId) to avoid collisions!
|
let mut remapper = JoinIrIdRemapper::new();
|
||||||
// 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<ValueId, ValueId> = HashMap::new();
|
|
||||||
// Phase 189: Map function names to their entry blocks (for Call→Jump conversion)
|
// Phase 189: Map function names to their entry blocks (for Call→Jump conversion)
|
||||||
let mut function_entry_map: HashMap<String, BasicBlockId> = HashMap::new();
|
let mut function_entry_map: HashMap<String, BasicBlockId> = HashMap::new();
|
||||||
|
|
||||||
@ -589,8 +589,8 @@ impl super::MirBuilder {
|
|||||||
blocks.sort_by_key(|(id, _)| id.0);
|
blocks.sort_by_key(|(id, _)| id.0);
|
||||||
for (old_block_id, _) in blocks {
|
for (old_block_id, _) in blocks {
|
||||||
let new_block_id = self.block_gen.next();
|
let new_block_id = self.block_gen.next();
|
||||||
// Use composite key to avoid collision between functions
|
// Use remapper to store composite key mapping
|
||||||
block_map.insert((func_name.clone(), *old_block_id), new_block_id);
|
remapper.set_block(func_name.clone(), *old_block_id, new_block_id);
|
||||||
if debug {
|
if debug {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[cf_loop/joinir] Block remap: {}:{:?} → {:?}",
|
"[cf_loop/joinir] Block remap: {}:{:?} → {:?}",
|
||||||
@ -599,7 +599,8 @@ impl super::MirBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Map function entry blocks for Call→Jump conversion
|
// 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);
|
function_entry_map.insert(func_name.clone(), entry_block_new);
|
||||||
if debug {
|
if debug {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
@ -631,7 +632,10 @@ impl super::MirBuilder {
|
|||||||
function_params.insert(func_name.clone(), func.params.clone());
|
function_params.insert(func_name.clone(), func.params.clone());
|
||||||
|
|
||||||
for block in func.blocks.values() {
|
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
|
// Phase 189: Track Const String instructions that define function names
|
||||||
for inst in &block.instructions {
|
for inst in &block.instructions {
|
||||||
if let MirInstruction::Const { dst, value } = inst {
|
if let MirInstruction::Const { dst, value } = inst {
|
||||||
@ -658,7 +662,7 @@ impl super::MirBuilder {
|
|||||||
// 4. Allocate new ValueIds for all collected values
|
// 4. Allocate new ValueIds for all collected values
|
||||||
for old_value in used_values {
|
for old_value in used_values {
|
||||||
let new_value = self.next_value_id();
|
let new_value = self.next_value_id();
|
||||||
value_map.insert(old_value, new_value);
|
remapper.set_value(old_value, new_value);
|
||||||
if debug {
|
if debug {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[cf_loop/joinir] Value remap: {:?} → {:?}",
|
"[cf_loop/joinir] Value remap: {:?} → {:?}",
|
||||||
@ -688,7 +692,8 @@ impl super::MirBuilder {
|
|||||||
// Build a local block map for this function (for remap_instruction compatibility)
|
// Build a local block map for this function (for remap_instruction compatibility)
|
||||||
let mut local_block_map: HashMap<BasicBlockId, BasicBlockId> = HashMap::new();
|
let mut local_block_map: HashMap<BasicBlockId, BasicBlockId> = HashMap::new();
|
||||||
for old_block_id in func.blocks.keys() {
|
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);
|
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();
|
let mut blocks_merge: Vec<_> = func.blocks.iter().collect();
|
||||||
blocks_merge.sort_by_key(|(id, _)| id.0);
|
blocks_merge.sort_by_key(|(id, _)| id.0);
|
||||||
for (old_block_id, old_block) in blocks_merge {
|
for (old_block_id, old_block) in blocks_merge {
|
||||||
// Use composite key to get correct mapping for this function's block
|
// Use remapper to get correct mapping for this function's block
|
||||||
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))?;
|
||||||
let mut new_block = BasicBlock::new(new_block_id);
|
let mut new_block = BasicBlock::new(new_block_id);
|
||||||
|
|
||||||
// Remap instructions (Phase 189: Convert inter-function Calls to control flow)
|
// 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
|
// This is a tail call - save info and skip the Call instruction itself
|
||||||
let remapped_args: Vec<ValueId> = args
|
let remapped_args: Vec<ValueId> = args
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&v| value_map.get(&v).copied().unwrap_or(v))
|
.map(|&v| remapper.get_value(v).unwrap_or(v))
|
||||||
.collect();
|
.collect();
|
||||||
tail_call_target = Some((target_block, remapped_args));
|
tail_call_target = Some((target_block, remapped_args));
|
||||||
found_tail_call = true;
|
found_tail_call = true;
|
||||||
@ -742,15 +748,29 @@ impl super::MirBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process regular instructions
|
// Process regular instructions - Phase 189: Use remapper.remap_instruction() + manual block remapping
|
||||||
let remapped = Self::remap_joinir_instruction(
|
let remapped = remapper.remap_instruction(inst);
|
||||||
inst,
|
|
||||||
&value_map,
|
// Phase 189 FIX: Manual block remapping for Branch/Phi (JoinIrIdRemapper doesn't know func_name)
|
||||||
&local_block_map, // Use local map for this function
|
let remapped_with_blocks = match remapped {
|
||||||
&function_entry_map,
|
MirInstruction::Branch { condition, then_bb, else_bb } => {
|
||||||
&value_to_func_name,
|
MirInstruction::Branch {
|
||||||
debug,
|
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 {
|
if debug {
|
||||||
match inst {
|
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
|
// Second pass: Insert parameter bindings for tail calls
|
||||||
@ -790,7 +810,7 @@ impl super::MirBuilder {
|
|||||||
for (i, arg_val_remapped) in args.iter().enumerate() {
|
for (i, arg_val_remapped) in args.iter().enumerate() {
|
||||||
if i < target_params.len() {
|
if i < target_params.len() {
|
||||||
let param_val_original = target_params[i];
|
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 {
|
new_block.instructions.push(MirInstruction::Copy {
|
||||||
dst: param_val_remapped,
|
dst: param_val_remapped,
|
||||||
src: *arg_val_remapped,
|
src: *arg_val_remapped,
|
||||||
@ -823,7 +843,7 @@ impl super::MirBuilder {
|
|||||||
// Convert Return to Jump to exit block
|
// Convert Return to Jump to exit block
|
||||||
// All functions return to same exit block (Phase 189)
|
// All functions return to same exit block (Phase 189)
|
||||||
if let Some(ret_val) = value {
|
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 {
|
if debug {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[cf_loop/joinir] Return({:?}) → Jump to exit",
|
"[cf_loop/joinir] Return({:?}) → Jump to exit",
|
||||||
@ -835,14 +855,27 @@ impl super::MirBuilder {
|
|||||||
target: exit_block_id,
|
target: exit_block_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => Self::remap_joinir_instruction(
|
MirInstruction::Jump { target } => {
|
||||||
term,
|
// Phase 189 FIX: Remap block ID for Jump
|
||||||
&value_map,
|
MirInstruction::Jump {
|
||||||
&local_block_map, // Use local map for this function
|
target: local_block_map.get(target).copied().unwrap_or(*target),
|
||||||
&function_entry_map,
|
}
|
||||||
&value_to_func_name,
|
}
|
||||||
debug,
|
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);
|
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 {
|
if let Some(boundary) = boundary {
|
||||||
|
use super::joinir_inline_boundary_injector::BoundaryInjector;
|
||||||
|
|
||||||
// Get entry function's entry block (first function by convention)
|
// Get entry function's entry block (first function by convention)
|
||||||
let (entry_func_name, entry_func) = mir_module
|
let (entry_func_name, entry_func) = mir_module
|
||||||
.functions
|
.functions
|
||||||
.iter()
|
.iter()
|
||||||
.next()
|
.next()
|
||||||
.ok_or("JoinIR module has no functions")?;
|
.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 {
|
// Create HashMap from remapper for BoundaryInjector (temporary adapter)
|
||||||
eprintln!(
|
let mut value_map_for_injector = HashMap::new();
|
||||||
"[cf_loop/joinir] Phase 188-Impl-3: Injecting {} Copy instructions at entry block {:?}",
|
for join_in in &boundary.join_inputs {
|
||||||
boundary.join_inputs.len(),
|
if let Some(remapped) = remapper.get_value(*join_in) {
|
||||||
entry_block_remapped
|
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(ref mut current_func) = self.current_function {
|
||||||
if let Some(entry_block) = current_func.get_block_mut(entry_block_remapped) {
|
BoundaryInjector::inject_boundary_copies(
|
||||||
// Insert Copy instructions at the BEGINNING of the block
|
current_func,
|
||||||
let mut copy_instructions = Vec::new();
|
entry_block_remapped,
|
||||||
for (join_in, host_in) in boundary.join_inputs.iter().zip(&boundary.host_inputs) {
|
boundary,
|
||||||
// join_in is JoinIR's local ValueId (e.g., ValueId(0))
|
&value_map_for_injector,
|
||||||
// host_in is host function's ValueId (e.g., ValueId(4))
|
debug,
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -924,8 +934,9 @@ impl super::MirBuilder {
|
|||||||
.iter()
|
.iter()
|
||||||
.next()
|
.next()
|
||||||
.ok_or("JoinIR module has no functions")?;
|
.ok_or("JoinIR module has no functions")?;
|
||||||
// Use composite key to get entry block mapping
|
// Use remapper to get entry block mapping
|
||||||
let entry_block = block_map[&(entry_func_name.clone(), entry_func.entry_block)];
|
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 {
|
if debug {
|
||||||
eprintln!("[cf_loop/joinir] Jumping to entry block: {:?}", entry_block);
|
eprintln!("[cf_loop/joinir] Jumping to entry block: {:?}", entry_block);
|
||||||
}
|
}
|
||||||
@ -945,247 +956,11 @@ impl super::MirBuilder {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Collect all ValueIds used in a block
|
// Phase 189: collect_values_in_block/collect_values_in_instruction removed
|
||||||
fn collect_values_in_block(
|
// These functions are now provided by JoinIrIdRemapper::collect_values_in_block()
|
||||||
block: &crate::mir::BasicBlock,
|
|
||||||
values: &mut std::collections::BTreeSet<super::ValueId>,
|
|
||||||
) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Collect all ValueIds used in an instruction
|
// Phase 189: remap_joinir_instruction/remap_instruction removed
|
||||||
fn collect_values_in_instruction(
|
// These functions are now provided by JoinIrIdRemapper::remap_instruction()
|
||||||
inst: &crate::mir::MirInstruction,
|
|
||||||
values: &mut std::collections::BTreeSet<super::ValueId>,
|
|
||||||
) {
|
|
||||||
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<super::ValueId, super::ValueId>,
|
|
||||||
block_map: &std::collections::HashMap<crate::mir::BasicBlockId, crate::mir::BasicBlockId>,
|
|
||||||
_function_entry_map: &std::collections::HashMap<String, crate::mir::BasicBlockId>,
|
|
||||||
_value_to_func_name: &std::collections::HashMap<super::ValueId, String>,
|
|
||||||
_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<super::ValueId, super::ValueId>,
|
|
||||||
block_map: &std::collections::HashMap<crate::mir::BasicBlockId, crate::mir::BasicBlockId>,
|
|
||||||
) -> 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(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Control-flow: try/catch/finally
|
/// Control-flow: try/catch/finally
|
||||||
pub(super) fn cf_try_catch(
|
pub(super) fn cf_try_catch(
|
||||||
|
|||||||
@ -1,851 +1,28 @@
|
|||||||
use crate::ast::Span;
|
|
||||||
use crate::mir::join_ir::{
|
use crate::mir::join_ir::{
|
||||||
BinOpKind, CompareOp, ConstValue, JoinFunction, JoinInst, JoinModule, MirLikeInst,
|
BinOpKind, CompareOp, ConstValue, JoinModule, MirLikeInst,
|
||||||
};
|
};
|
||||||
use crate::mir::{
|
use crate::mir::{
|
||||||
BasicBlockId, BinaryOp, CompareOp as MirCompareOp, ConstValue as MirConstValue, EffectMask,
|
BinaryOp, CompareOp as MirCompareOp, ConstValue as MirConstValue, EffectMask, MirInstruction, MirModule,
|
||||||
FunctionSignature, MirFunction, MirInstruction, MirModule, MirType, ValueId,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{join_func_name, JoinIrVmBridgeError};
|
use super::JoinIrVmBridgeError;
|
||||||
|
|
||||||
/// ブロックを確定する(instructions + spans + terminator を設定)
|
// Phase 190: Use modular converters from parent module
|
||||||
fn finalize_block(
|
use super::joinir_function_converter::JoinIrFunctionConverter;
|
||||||
mir_func: &mut MirFunction,
|
|
||||||
block_id: BasicBlockId,
|
|
||||||
instructions: Vec<MirInstruction>,
|
|
||||||
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 30.x: JoinIR → MIR 変換器
|
/// Phase 190: JoinIR → MIR 変換器(統合エントリーポイント)
|
||||||
///
|
///
|
||||||
/// Phase 32 L-2.2 Step-3: テストから呼び出し可能に `pub(crate)` 化
|
/// Phase 32 L-2.2 Step-3: テストから呼び出し可能に `pub(crate)` 化
|
||||||
pub(crate) fn convert_joinir_to_mir(
|
pub(crate) fn convert_joinir_to_mir(
|
||||||
join_module: &JoinModule,
|
join_module: &JoinModule,
|
||||||
) -> Result<MirModule, JoinIrVmBridgeError> {
|
) -> Result<MirModule, JoinIrVmBridgeError> {
|
||||||
let mut mir_module = MirModule::new("joinir_bridge".to_string());
|
// Phase 190: Delegate to FunctionConverter
|
||||||
|
JoinIrFunctionConverter::convert_joinir_to_mir(join_module)
|
||||||
// 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 変換
|
|
||||||
///
|
|
||||||
/// # 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<String>`追加
|
|
||||||
/// 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<MirFunction, JoinIrVmBridgeError> {
|
|
||||||
// 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::<Vec<_>>();
|
|
||||||
|
|
||||||
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<BasicBlockId> = 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 変換
|
/// MirLikeInst → MirInstruction 変換
|
||||||
|
/// Phase 190: 共有ユーティリティとして pub(crate) に変更
|
||||||
pub(crate) fn convert_mir_like_inst(
|
pub(crate) fn convert_mir_like_inst(
|
||||||
mir_like: &MirLikeInst,
|
mir_like: &MirLikeInst,
|
||||||
) -> Result<MirInstruction, JoinIrVmBridgeError> {
|
) -> Result<MirInstruction, JoinIrVmBridgeError> {
|
||||||
|
|||||||
691
src/mir/join_ir_vm_bridge/joinir_block_converter.rs
Normal file
691
src/mir/join_ir_vm_bridge/joinir_block_converter.rs
Normal file
@ -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<MirInstruction>,
|
||||||
|
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<crate::mir::MirType>,
|
||||||
|
) -> 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<crate::mir::MirType>,
|
||||||
|
) -> 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<ValueId>,
|
||||||
|
k_next: &Option<crate::mir::join_ir::JoinContId>,
|
||||||
|
) -> 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<ValueId>,
|
||||||
|
) -> 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<crate::mir::MirType>,
|
||||||
|
) -> 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<crate::mir::join_ir::JoinContId>,
|
||||||
|
) -> 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<ValueId>,
|
||||||
|
) -> 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<crate::mir::join_ir::JoinContId>,
|
||||||
|
) -> 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<BasicBlockId> = 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<MirInstruction>,
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
133
src/mir/join_ir_vm_bridge/joinir_function_converter.rs
Normal file
133
src/mir/join_ir_vm_bridge/joinir_function_converter.rs
Normal file
@ -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<MirModule, JoinIrVmBridgeError> {
|
||||||
|
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<MirFunction, JoinIrVmBridgeError> {
|
||||||
|
// 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::<Vec<_>>();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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::frontend::JoinFuncMetaMap;
|
||||||
use crate::mir::join_ir::JoinModule;
|
use crate::mir::join_ir::JoinModule;
|
||||||
use crate::mir::{MirFunction, MirModule};
|
use crate::mir::{MirFunction, MirModule};
|
||||||
@ -33,8 +34,8 @@ pub fn convert_join_module_to_mir_with_meta(
|
|||||||
join_func.name
|
join_func.name
|
||||||
);
|
);
|
||||||
|
|
||||||
// 2. 基本のMIR変換(既存ロジック)
|
// 2. 基本のMIR変換(Phase 190: modularized converter)
|
||||||
let mir_func = convert_join_function_to_mir(join_func)?;
|
let mir_func = JoinIrFunctionConverter::convert_function(join_func)?;
|
||||||
|
|
||||||
// 3. Phase 40-1: if_modified_varsがあればloop exit PHI生成
|
// 3. Phase 40-1: if_modified_varsがあればloop exit PHI生成
|
||||||
if let Some(m) = meta.get(func_id) {
|
if let Some(m) = meta.get(func_id) {
|
||||||
|
|||||||
@ -37,13 +37,19 @@ mod logging {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod convert;
|
mod convert;
|
||||||
|
// Phase 190: Modular converters
|
||||||
|
mod joinir_function_converter;
|
||||||
|
mod joinir_block_converter;
|
||||||
mod meta;
|
mod meta;
|
||||||
mod runner;
|
mod runner;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
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 meta::convert_join_module_to_mir_with_meta;
|
||||||
pub use runner::run_joinir_via_vm;
|
pub use runner::run_joinir_via_vm;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user