Files
hakorune/docs/development/current/main/phase176-pattern2-limitations.md
nyash-codex 99d329096f feat(joinir): Phase 176 Pattern2 multi-carrier lowering complete
Task 176-1: Pattern2 limitation investigation
- Identified 10 limitation points where only position carrier was handled
- Added TODO markers for Phase 176-2/3 implementation
- Created phase176-pattern2-limitations.md documentation

Task 176-2: CarrierUpdateLowerer helper implementation
- Implemented emit_carrier_update() helper function
- Supports CounterLike and AccumulationLike UpdateExpr patterns
- Added 6 unit tests (all passing)
- Fail-Fast error handling for carrier/variable not found

Task 176-3: Pattern2 lowerer multi-carrier extension
- Extended header PHI generation for all carriers
- Implemented loop update for all carriers using emit_carrier_update()
- Extended ExitLine/ExitMeta construction for all carriers
- Updated function call/jump args to include all carriers
- 9/10 tests passing (1 pre-existing test issue)

Task 176-4: E2E testing and bug fixes
- Fixed Trim pattern loop_var_name overwrite bug (pattern2_with_break.rs)
- Fixed InstructionRewriter latch_incoming mapping bug
- All E2E tests passing (RC=0): pos + result dual-carrier loops work
- test_jsonparser_parse_string_min2.hako verified

Task 176-5: Documentation updates
- Created phase176-completion-report.md
- Updated phase175-multicarrier-design.md with completion status
- Updated joinir-architecture-overview.md roadmap
- Updated CURRENT_TASK.md with Phase 176 completion + Phase 177 TODO
- Updated loop_pattern_space.md F-axis (multi-carrier support complete)

Technical achievements:
- Pattern2 now handles single/multiple carriers uniformly
- CarrierInfo architecture proven to work end-to-end
- Two critical bugs fixed (loop_var overwrite, latch_incoming mapping)
- No regressions in existing tests

Next: Phase 177 - Apply to JsonParser _parse_string full implementation
2025-12-08 15:17:53 +09:00

7.4 KiB

Phase 176: Pattern2 Lowerer Multi-Carrier Limitations Report

Overview

This document identifies all locations in loop_with_break_minimal.rs where the Pattern2 lowerer currently only handles the position carrier (i) and ignores additional carriers from CarrierInfo.carriers.

Limitation Points

1. ValueId Allocation (Line 172-173)

Location: ValueId allocation section Current Behavior: Only allocates ValueIds for the position carrier (i_init, i_param, i_next, i_exit) Missing: No allocation for additional carriers (e.g., sum_init, sum_param, sum_next, sum_exit)

// TODO(Phase 176): Multi-carrier support - currently only allocates position carrier
// Future: iterate over CarrierInfo.carriers and allocate for each carrier

Impact: Cannot represent multi-carrier loops in JoinIR local ValueId space.


2. Main Function Parameters (Line 208-209)

Location: main() function creation Current Behavior: Only takes i_init as parameter Missing: Should take all carrier init values as parameters

// TODO(Phase 176): Multi-carrier support - main() params should include all carriers
// Future: params = vec![i_init, sum_init, count_init, ...] from CarrierInfo
let mut main_func = JoinFunction::new(main_id, "main".to_string(), vec![i_init]);

Impact: Additional carriers cannot be passed into the JoinIR fragment.


3. Loop Step Call Arguments (Line 214-215)

Location: main()loop_step() call Current Behavior: Only passes i_init to loop_step() Missing: Should pass all carrier init values

// TODO(Phase 176): Multi-carrier support - Call args should include all carrier inits
// Future: args = vec![i_init, sum_init, count_init, ...] from CarrierInfo
main_func.body.push(JoinInst::Call {
    func: loop_step_id,
    args: vec![i_init],  // Only position carrier
    k_next: None,
    dst: Some(loop_result),
});

Impact: Additional carriers lost at loop entry.


4. Loop Step Function Parameters (Line 234-235)

Location: loop_step() function creation Current Behavior: Only takes i_param as parameter Missing: Should take all carrier params

// TODO(Phase 176): Multi-carrier support - loop_step params should include all carriers
// Future: params = vec![i_param, sum_param, count_param, ...] from CarrierInfo
let mut loop_step_func = JoinFunction::new(
    loop_step_id,
    "loop_step".to_string(),
    vec![i_param],  // Only position carrier
);

Impact: Cannot access additional carriers inside loop body.


5. Natural Exit Jump Arguments (Line 257-258)

Location: Natural exit condition → k_exit jump Current Behavior: Only passes i_param to k_exit Missing: Should pass all carrier values

// TODO(Phase 176): Multi-carrier support - Jump args should include all carrier values
// Future: args = vec![i_param, sum_param, count_param, ...] from CarrierInfo
loop_step_func.body.push(JoinInst::Jump {
    cont: k_exit_id.as_cont(),
    args: vec![i_param],  // Only position carrier
    cond: Some(exit_cond),
});

Impact: Additional carrier values lost at natural exit.


6. Break Exit Jump Arguments (Line 272-273)

Location: Break condition → k_exit jump Current Behavior: Only passes i_param to k_exit Missing: Should pass all carrier values

// TODO(Phase 176): Multi-carrier support - Jump args should include all carrier values
// Future: args = vec![i_param, sum_param, count_param, ...] from CarrierInfo
loop_step_func.body.push(JoinInst::Jump {
    cont: k_exit_id.as_cont(),
    args: vec![i_param],  // Only position carrier
    cond: Some(break_cond_value),
});

Impact: Additional carrier values lost at break exit.


7. Loop Body Updates (Line 284-285)

Location: Loop body computation Current Behavior: Only computes i_next = i + 1 Missing: Should compute updates for all carriers

// TODO(Phase 176): Multi-carrier support - need to compute updates for all carriers
// Future: for each carrier in CarrierInfo.carriers, emit carrier_next = carrier_update
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
    dst: i_next,
    op: BinOpKind::Add,
    lhs: i_param,
    rhs: const_1,
}));

Impact: Additional carriers cannot be updated in loop body.

Note: This is the HARDEST part - we need actual AST body analysis to determine carrier updates!


8. Tail Call Arguments (Line 304-305)

Location: Tail recursion call to loop_step() Current Behavior: Only passes i_next Missing: Should pass all updated carrier values

// TODO(Phase 176): Multi-carrier support - tail call args should include all updated carriers
// Future: args = vec![i_next, sum_next, count_next, ...] from CarrierInfo
loop_step_func.body.push(JoinInst::Call {
    func: loop_step_id,
    args: vec![i_next],  // Only position carrier
    k_next: None,
    dst: None,
});

Impact: Additional carrier updates lost in iteration.


9. K_Exit Function Parameters (Line 319-320)

Location: k_exit() function creation (Exit PHI) Current Behavior: Only takes i_exit as parameter Missing: Should take all carrier exit values as parameters

// TODO(Phase 176): Multi-carrier support - k_exit params should include all carrier exits
// Future: params = vec![i_exit, sum_exit, count_exit, ...] from CarrierInfo
let mut k_exit_func = JoinFunction::new(
    k_exit_id,
    "k_exit".to_string(),
    vec![i_exit],  // Only position carrier
);

Impact: Additional carrier exit values cannot be received by Exit PHI.


10. ExitMeta Construction (Line 344-345)

Location: Final ExitMeta return value Current Behavior: Only includes position carrier in exit bindings Missing: Should include all carrier bindings

// TODO(Phase 176): Multi-carrier support - ExitMeta should include all carrier bindings
// Future: ExitMeta::multiple(vec![(loop_var_name, i_exit), ("sum", sum_exit), ...])
let exit_meta = ExitMeta::single(loop_var_name.to_string(), i_exit);

Impact: Additional carriers not visible to MIR merge layer - no carrier PHIs generated!


Summary Statistics

  • Total Limitation Points: 10
  • Easy Fixes (iteration over CarrierInfo): 9 points
  • Hard Fix (requires AST body analysis): 1 point (Loop Body Updates)

Architecture Note

The CarrierInfo structure is already multi-carrier ready:

pub struct CarrierInfo {
    pub loop_var_name: String,    // Position carrier
    pub loop_var_id: ValueId,     // Host ValueId
    pub carriers: Vec<CarrierVar>, // Additional carriers (THIS IS IGNORED!)
    pub trim_helper: Option<TrimLoopHelper>,
}

The problem is that lower_loop_with_break_minimal() completely ignores CarrierInfo.carriers and only uses loop_var_name (passed as a separate string parameter).

Next Steps (Phase 176-2/3)

  1. Phase 176-2: Fix iteration-based points (points 1-6, 8-10)

    • Add carrier iteration logic
    • Extend function params, call args, jump args
    • Build multi-carrier ExitMeta
  2. Phase 176-3: Fix loop body updates (point 7)

    • Requires AST body analysis
    • Need to track which carriers are updated by which statements
    • Most complex part of multi-carrier support
  3. Integration Test: Pattern 3 (trim) with Pattern 2 shape

    • Test case: loop(pos < len) { if ch == ' ' { break } pos = pos + 1 }
    • Verify sum/count carriers survive through break exits