feat(joinir): Phase 33-19 ContinueBranchNormalizer for unified continue handling

## Problem
Pattern 4 (loop with continue) needs to handle both:
- if (cond) { continue } (then-continue)
- if (cond) { body } else { continue } (else-continue)

Previously, else-continue patterns required separate handling, preventing unified processing.

## Solution

### 1. ContinueBranchNormalizer Implementation
New file: `src/mir/join_ir/lowering/continue_branch_normalizer.rs`
- Detects: `if (cond) { body } else { continue }`
- Transforms to: `if (!cond) { continue } else { body }`
- Enables uniform Pattern 4 handling of all continue patterns
- No-op for other if statements

### 2. Pattern 4 Integration
- Normalize loop body before lowering (line 140)
- Use normalized body for carrier analysis (line 169)
- Preserves existing then-continue patterns

### 3. Carrier Filtering Enhancement
Lines 171-178: Only treat updated variables as carriers
- Fixes: Constant variables (M, args) no longer misidentified as carriers
- Enables: Condition-only variables without carrier slot overhead

### 4. LoopUpdateAnalyzer Enhancement
- Recursively scan if-else branches for carrier updates
- Correctly detect updates in normalized code

## Test Results
 Pattern 3 (If PHI): sum=9
 Pattern 4 (Then-continue): 25 (1+3+5+7+9)
 Pattern 4 (Else-continue): New test cases added
 No SSA-undef errors
 Carrier filtering works correctly

## Files Changed
- New: continue_branch_normalizer.rs (comprehensive implementation + tests)
- Modified: pattern4_with_continue.rs (integrated normalizer)
- Modified: loop_update_analyzer.rs (recursive branch scanning)
- Modified: lowering/mod.rs (module export)
- Added: 3 test cases (then/else continue patterns)

## Impact
This enables JsonParserBox / trim and other continue-heavy loops to work with
JoinIR Phase 4 lowering, paving the way for Phase 166/170 integration.

🤖 Generated with Claude Code

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-07 19:00:12 +09:00
parent 53af63c96c
commit 45aeb11cab
7 changed files with 478 additions and 18 deletions

View File

@ -124,6 +124,7 @@ impl MirBuilder {
debug: bool,
) -> Result<Option<ValueId>, String> {
use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, CarrierVar};
use crate::mir::join_ir::lowering::continue_branch_normalizer::ContinueBranchNormalizer;
use crate::mir::join_ir::lowering::loop_update_analyzer::LoopUpdateAnalyzer;
use crate::mir::join_ir::lowering::loop_with_continue_minimal::lower_loop_with_continue_minimal;
use crate::mir::join_ir_vm_bridge::convert_join_module_to_mir_with_meta;
@ -133,6 +134,12 @@ impl MirBuilder {
// Phase 195: Use unified trace
trace::trace().debug("pattern4", "Calling Pattern 4 minimal lowerer");
// Phase 33-19: Normalize else-continue patterns to then-continue
// This transforms: if (cond) { body } else { continue }
// into: if (!cond) { continue } else { body }
let normalized_body = ContinueBranchNormalizer::normalize_loop_body(_body);
let body_to_analyze = &normalized_body;
// Extract loop variables from condition (i and sum)
let loop_var_name = self.extract_loop_variable_from_condition(condition)?;
let loop_var_id = self
@ -146,18 +153,30 @@ impl MirBuilder {
)
})?;
// Phase 196: Build CarrierInfo from variable_map
// Collect all non-loop-var variables as potential carriers
let mut carriers = Vec::new();
// Phase 197: Analyze carrier update expressions FIRST to identify actual carriers
// Phase 33-19: Use normalized_body for analysis (after else-continue transformation)
// Build temporary carrier list for initial analysis
let mut temp_carriers = Vec::new();
for (var_name, &var_id) in &self.variable_map {
if var_name != &loop_var_name {
carriers.push(CarrierVar {
temp_carriers.push(CarrierVar {
name: var_name.clone(),
host_id: var_id,
});
}
}
let carrier_updates = LoopUpdateAnalyzer::analyze_carrier_updates(body_to_analyze, &temp_carriers);
// Phase 33-19: Build CarrierInfo with ONLY updated variables as carriers
// This prevents constant variables (like M, args) from being treated as carriers
let mut carriers = Vec::new();
for temp_carrier in &temp_carriers {
if carrier_updates.contains_key(&temp_carrier.name) {
carriers.push(temp_carrier.clone());
}
}
let carrier_info = CarrierInfo {
loop_var_name: loop_var_name.clone(),
loop_var_id,
@ -173,9 +192,6 @@ impl MirBuilder {
)
);
// Phase 197: Analyze carrier update expressions from loop body
let carrier_updates = LoopUpdateAnalyzer::analyze_carrier_updates(_body, &carrier_info.carriers);
trace::trace().debug(
"pattern4",
&format!("Analyzed {} carrier update expressions", carrier_updates.len())