Phase 197-B: Multi-Carrier Exit Mechanism - Fixed reconnect_boundary() to use remapper for per-carrier exit values - ExitMeta now uses carrier_param_ids (Jump arguments) instead of carrier_exit_ids (k_exit parameters) - Root cause: k_exit parameters aren't defined when JoinIR functions merge into host MIR Phase 197-C: AST-Based Update Expression - LoopUpdateAnalyzer extracts update patterns from loop body AST - Pattern4 lowerer uses UpdateExpr for semantically correct RHS - sum = sum + i → uses i_next (current iteration value) - count = count + 1 → uses const_1 Files modified: - src/mir/builder/control_flow/joinir/merge/mod.rs - src/mir/join_ir/lowering/loop_with_continue_minimal.rs - src/mir/join_ir/lowering/loop_update_analyzer.rs (new) Test results: - loop_continue_multi_carrier.hako: 25, 5 ✅ - loop_continue_pattern4.hako: 25 ✅ Pattern 1–4 now unified with: - Structure-based detection (LoopFeatures + classify) - Carrier/Exit metadata (CarrierInfo + ExitMeta) - Boundary connection (JoinInlineBoundary + LoopExitBinding) - AST-based update expressions (LoopUpdateAnalyzer) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
4.6 KiB
4.6 KiB
Phase 197: Pattern 4 Update Semantics
Status: In Progress(197-B で ExitMeta/Boundary 経路の修正は完了) Date: 2025-12-06 Goal: Fix loop_continue_multi_carrier.hako to output correct values (25, 5)
Problem Statement
Current Behavior
- Output:
5, 5 - Expected:
25, 5
Root Cause
The update expressions for carriers are hardcoded in loop_with_continue_minimal.rs:310-317:
let rhs = if carrier_name == "count" {
const_1 // count = count + 1
} else {
i_next // sum = sum + i_next (and other accumulators)
};
This works for the simple pattern where:
countcarriers always use+1- Other carriers always use
+i_next
However, this breaks semantic correctness because:
sum = sum + iin the source code should use the currentivalue- The current implementation uses
i_next(the next iteration's value)
Test Case Analysis
From loop_continue_multi_carrier.hako:
local i = 0
local sum = 0
local count = 0
loop(i < 10) {
i = i + 1 // i goes 1,2,3,4,5,6,7,8,9,10
if (i % 2 == 0) {
continue // Skip even: 2,4,6,8,10
}
sum = sum + i // sum = sum + i (current i)
count = count + 1 // count = count + 1
}
// Expected: sum=25 (1+3+5+7+9), count=5
The update expressions should be:
i:i = i + 1(constant increment)sum:sum = sum + i(accumulate current i)count:count = count + 1(constant increment)
Current Implementation Problem
In loop_with_continue_minimal.rs, the Select statement for carriers:
// carrier_next = carrier_param + rhs
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
dst: carrier_next,
op: BinOpKind::Add,
lhs: carrier_param,
rhs, // This is WRONG for sum!
}));
For sum:
rhs = i_next(next iteration value)- Should be:
rhs = i_current(current iteration value)
197-B: ExitMeta / Boundary 経路の修正(完了)
さらに、Phase 197-B では「どの ValueId を出口として見るか」という経路も修正した:
-
問題:
- もともと k_exit 関数のパラメータ(
carrier_exit_ids= 例えば ValueId(20), ValueId(21))を ExitMeta に入れていたが、 - これらは JoinIR 関数インライン後の MIR では「定義されない」ValueId になってしまうケースがあった。
- 一方、
loop_step内の Jump 引数(carrier_param_ids)は MIR マージ時に正しい ValueId に remap される。
- もともと k_exit 関数のパラメータ(
-
修正内容:
loop_with_continue_minimal.rs側で、ExitMeta には k_exit のパラメータではなく、Jump の引数(carrier_param_ids)を使うよう変更。reconnect_boundary(mod.rs)にremapperパラメータを追加し、Boundary に書かれた JoinIR 側 ValueId を remapper 経由で MIR の ValueId に変換してから variable_map に接続。
この結果:
- carrier の出口 ValueId は「必ず MIR 上で定義がある値」になり、
- ExitBindingBuilder 〜 JoinInlineBoundary 〜 merge_joinir_mir_blocks までの配管が、multi-carrier でも破綻しない状態になった。
Solution Design
Phase 197-2: LoopUpdateAnalyzer
Create src/mir/join_ir/lowering/loop_update_analyzer.rs:
pub enum UpdateExpr {
Const(i64), // count = count + 1
BinOp { lhs: String, op: BinOpKind, rhs: String }, // sum = sum + i
}
pub struct LoopUpdateAnalyzer;
impl LoopUpdateAnalyzer {
pub fn analyze_carrier_updates(
body: &[ASTNode],
carriers: &[CarrierVar]
) -> HashMap<String, UpdateExpr> {
// Extract update expressions from assignment statements
// in the loop body
}
}
Phase 197-3: Update Lowerer
Modify loop_with_continue_minimal.rs:
- Call
LoopUpdateAnalyzer::analyze_carrier_updates()at the start - Remove hardcoded
if carrier_name == "count"logic - Use extracted
UpdateExprmetadata to generate correct RHS:- For
Const(n): Useconst_n - For
BinOp { rhs: "i", ... }: Usei_current(noti_next)
- For
Expected Fix
After implementation:
sum = sum + iwill use currentivalue- Output will be
25, 5as expected
Implementation Steps
- Task 197-1: Create this documentation
- Task 197-2: Implement
LoopUpdateAnalyzer - Task 197-3: Update
loop_with_continue_minimal.rsto use metadata - Task 197-4: Test and verify output
References
- Test case:
apps/tests/loop_continue_multi_carrier.hako - Lowerer:
src/mir/join_ir/lowering/loop_with_continue_minimal.rs - Pattern detection:
src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs