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

@ -68,22 +68,49 @@ impl LoopUpdateAnalyzer {
// Extract carrier names for quick lookup
let carrier_names: Vec<&str> = carriers.iter().map(|c| c.name.as_str()).collect();
// Scan all statements in the loop body
for node in body {
if let ASTNode::Assignment { target, value, .. } = node {
// Check if this is a carrier update (e.g., sum = sum + i)
if let Some(target_name) = Self::extract_variable_name(target) {
if carrier_names.contains(&target_name.as_str()) {
// This is a carrier update, analyze the RHS
if let Some(update_expr) = Self::analyze_update_value(&target_name, value) {
updates.insert(target_name, update_expr);
// Recursively scan all statements in the loop body
Self::scan_nodes(body, &carrier_names, &mut updates);
updates
}
/// Recursively scan AST nodes for carrier updates
///
/// Phase 33-19: Extended to scan into if-else branches to handle
/// Pattern B (else-continue) after normalization.
fn scan_nodes(
nodes: &[ASTNode],
carrier_names: &[&str],
updates: &mut HashMap<String, UpdateExpr>,
) {
for node in nodes {
match node {
ASTNode::Assignment { target, value, .. } => {
// Check if this is a carrier update (e.g., sum = sum + i)
if let Some(target_name) = Self::extract_variable_name(target) {
if carrier_names.contains(&target_name.as_str()) {
// This is a carrier update, analyze the RHS
if let Some(update_expr) = Self::analyze_update_value(&target_name, value) {
updates.insert(target_name, update_expr);
}
}
}
}
// Phase 33-19: Recursively scan if-else branches
ASTNode::If {
then_body,
else_body,
..
} => {
Self::scan_nodes(then_body, carrier_names, updates);
if let Some(else_stmts) = else_body {
Self::scan_nodes(else_stmts, carrier_names, updates);
}
}
// Add more recursive cases as needed (loops, etc.)
_ => {}
}
}
updates
}
/// Extract variable name from AST node (for assignment target)