Implement Pattern3 (if-sum) Normalized infrastructure, extending existing P2
StepSchedule and ShapeGuard systems.
Key changes:
1. StepSchedule generalization (P2 → P2/P3):
- Renamed: pattern2_step_schedule.rs → step_schedule.rs
- Extended StepKind enum with P3 variants:
- IfCond (if condition in body)
- ThenUpdates (carrier updates in then branch)
- ElseUpdates (carrier updates in else branch)
- Added pattern3_if_sum_schedule() function
- Added unit test: test_pattern3_if_sum_schedule()
- Updated module references (mod.rs, loop_with_break_minimal.rs)
2. ShapeGuard extension:
- Added Pattern3IfSumMinimal variant to NormalizedDevShape
- Added is_pattern3_if_sum_minimal() detector (placeholder)
- Updated shape detector table
- Extended capability_for_shape() mapping
3. Bridge integration:
- bridge.rs: Added P3 shape handling in normalize_for_shape()
- normalized.rs: Added P3 roundtrip match (uses P2 temporarily)
4. P2/P3 separation:
- loop_with_break_minimal.rs: Added panic for P3 steps in P2 lowering
- Clear boundary enforcement (P2 lowerer rejects P3 steps)
5. Documentation:
- CURRENT_TASK.md: Phase 47-A-IMPL status
- phase47-norm-p3-design.md: Implementation status section
Benefits:
- Reuses 90% of P2 infrastructure (ConditionEnv, CarrierInfo, ExitLine)
- Clean P2/P3 separation via StepKind
- Pure additive changes (no P2 behavioral changes)
- Ready for Phase 47-A-LOWERING (full P3 Normalized implementation)
Tests: 938/938 PASS (+1 from step_schedule unit test)
- All existing P1/P2 tests pass (no regressions)
- P3 test uses Structured path temporarily (proper lowering in next phase)
Next phase: Implement full P3 Normalized→MIR(direct) lowering
12 KiB
Phase 47: Normalized P3 (If-Sum) Design
Status: Design Complete, Minimal Dev Test Implemented Date: 2025-12-12
Goal
Extend Normalized JoinIR to support Pattern3 (if-sum) loops using the same infrastructure that successfully handles P1/P2.
Key insight: P3 already shares P2's Structured JoinIR foundation (Phase 220), so Normalized extension reuses existing components.
Background: P2 Normalized Success
Phase 43/245B/46 established canonical Normalized for all P2 patterns:
- ✅ Pattern2Mini (simple break)
- ✅ JsonParser skip_whitespace (Trim pattern)
- ✅ JsonParser _atoi (DigitPos + NumberAccumulation)
- ✅ JsonParser _parse_number (multi-carrier)
Infrastructure complete:
- Structured→Normalized→MIR(direct) pipeline
- EnvLayout, JpInst/JpOp, StepScheduleBox
- ConditionEnv, CarrierInfo, ExitLine
- Mode system (Phase 45), Capability system (Phase 44)
Why P3 Uses Same Normalized
1. Shared Structured JoinIR Foundation (Phase 220)
From joinir-architecture-overview.md (lines 73-84):
Phase 220: P3 if-sum の ConditionEnv 統合完了
- P3 if-sum には ConditionPatternBox + ConditionEnv が必須
- Phase 220-D で loop 条件の変数サポート完了
- Phase 220 で if-sum の expr-result exit contract が P2 と揃った
P3 already uses P2 infrastructure:
- ✅ ConditionEnv (condition analysis)
- ✅ CarrierInfo (state tracking)
- ✅ ExitLine/Boundary (exit handling)
- ✅ LoopHeaderPHI (SSA construction)
2. Pattern Similarity
| Aspect | P2 (Break) | P3 (If-Sum) | Shared? |
|---|---|---|---|
| Loop control | loop(cond) + break |
loop(cond) + conditional update |
✅ Yes |
| Carriers | sum, count, result | sum, count (if-conditional) | ✅ Yes |
| Exit condition | Break early | Continue all iterations | Different |
| ConditionEnv | ✅ Used | ✅ Used (Phase 220) | ✅ Yes |
| ExitLine | ✅ Used | ✅ Used | ✅ Yes |
Key difference: P3 has conditional carrier updates inside loop body, vs P2's unconditional updates before break.
3. Normalized Extension Points
P3 needs minimal additions to existing Normalized:
Already working:
- ConditionEnv (loop + if conditions)
- CarrierInfo (state tracking)
- EnvLayout (carrier fields)
- LoopHeaderPHI (entry/latch values)
Need to add:
- ConditionalUpdate pattern in StepScheduleBox
- P2:
[HeaderCond, BodyInit, BreakCheck, Updates, Tail] - P3:
[HeaderCond, IfCond, ThenUpdates, ElseUpdates, Tail]
- P2:
- If branching in Normalized JpInst
- Already exists:
If { cond, then_target, else_target, env } - Just need to emit for P3 body structure
- Already exists:
Architecture: Unified Normalized
┌──────────────────────────────────────────┐
│ Structured JoinIR (Pattern1-4 共通) │
│ - ConditionEnv (P2/P3/P4 統一 Phase 220) │
│ - CarrierInfo │
│ - ExitLine/Boundary │
└──────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────┐
│ Normalized JoinIR (Pattern1-4 共通) │ ← P3 もここに載せる!
│ - EnvLayout (P2 完成 → P3 拡張) │
│ - JpInst/JpOp (If 分岐追加) │
│ - StepScheduleBox (ConditionalUpdate) │
└──────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────┐
│ MIR (Pattern1-4 共通) │
└──────────────────────────────────────────┘
Representative P3 Loops
Phase 47-A: Minimal (sum_count)
Example: phase212_if_sum_min.hako
local sum = 0
local count = 0
local i = 0
local n = 5
loop(i < n) {
if (i % 2 == 1) {
sum = sum + i
count = count + 1
}
i = i + 1
}
Characteristics:
- Simple condition:
i % 2 == 1 - Two carriers:
sum,count(conditionally updated) - One loop param:
i(always updated) - No break, runs all iterations
Normalized shape:
- EnvLayout:
{ i: int, sum: int, count: int } - StepSchedule:
[HeaderCond(i < n), IfCond(i % 2 == 1), ThenUpdates(sum, count), Updates(i), Tail]
Phase 47-B: JsonParser array_filter
Example: JsonParser array_filter (if present in codebase)
local out = new ArrayBox()
local i = 0
loop(i < arr.length()) {
local v = arr.get(i)
if (predicate(v)) {
out.push(v)
}
i = i + 1
}
Characteristics:
- Method calls:
arr.length(),arr.get(i),out.push(v) - Body-local:
v(used in if condition) - Conditional side effect:
out.push(v)
Complexity: Higher than sum_count (method calls, body-local)
Phase 47-C: Selfhost loops (future)
Complex P3 patterns from selfhost compiler (deferred to later phase).
Implementation Status
Phase 47-A-PREP (✅ Complete, commit 42ecd7a7):
- Fixture added:
pattern3_if_sum_minimalinnormalized/fixtures.rs - Test stub added:
test_normalized_pattern3_if_sum_minimal_runner_dev_switch_matches_structured - Basic infrastructure for P3 development mode testing
Phase 47-A-IMPL (✅ Complete, 2025-12-12):
- ✅ StepSchedule renamed and extended:
pattern2_step_schedule.rs→step_schedule.rs - ✅ P3 StepKind added:
IfCond,ThenUpdates,ElseUpdates - ✅ Pattern2 lowering separation: P3 steps panic in P2 lowering (clean boundary)
- ✅ ShapeGuard:
Pattern3IfSumMinimaldetection added (placeholder stub) - ✅ Normalized bridge: P3 shape handling in
normalize_for_shape()and roundtrip - ✅ 938/938 tests PASS (no regressions)
- ⏳ TODO: Full P3 Normalized lowering (Phase 47-A-LOWERING)
Next Phase (Phase 47-A-LOWERING):
- Implement
lower_pattern3_if_sum_minimal()fully - Generate Normalized JpInst for P3 structure (If branching, conditional updates)
- Test VM output comparison (Normalized vs Structured)
Implementation Strategy
Phase 47-A: Minimal sum_count (dev-only)
Goal: Prove P3 can use Normalized infrastructure with minimal additions.
Steps:
- ShapeGuard: Add
Pattern3IfSumMinimalshape - StepScheduleBox: Add
ConditionalUpdatestep kind - Normalized lowering:
- Generate
IfJpInst for body if-statement - Emit carrier updates in then/else branches
- Generate
- Test: Verify Structured→Normalized→MIR(direct) matches Structured→MIR
Expected additions:
shape_guard.rs: +1 shape variantpattern2_step_schedule.rs: Rename tostep_schedule.rs, add P3 supportnormalized_bridge/direct.rs: HandleIfJpInst with carrier updatestests/normalized_joinir_min.rs: +1 P3 test
Dev fixture: phase212_if_sum_min.hako → jsonparser_if_sum_minimal
Phase 47-B: array_filter (dev-only)
Goal: Extend to body-local + method calls.
Additions:
- Body-local handling in EnvLayout (already exists for P2 DigitPos)
- Method call in if condition (ExprLowerer already supports)
Phase 47-C: Canonical promotion
Goal: Move P3 minimal from dev-only to canonical (like P2).
Criteria:
- All invariants verified (Phase 47-A/B tests passing)
- No regressions in 937+ tests
- Performance acceptable (Normalized vs Structured comparison)
Normalized Components for P3
EnvLayout Extension
P2 example:
struct Pattern2Env {
i: int, // loop param
sum: int, // carrier
}
P3 extension (same structure):
struct Pattern3Env {
i: int, // loop param
sum: int, // carrier (conditionally updated)
count: int, // carrier (conditionally updated)
}
No new fields needed - P3 carriers work same as P2.
StepScheduleBox Extension
P2 steps:
enum StepKind {
HeaderCond, // loop(cond)
BodyInit, // local ch = ...
BreakCheck, // if (cond) break
Updates, // sum = sum + 1
Tail, // i = i + 1
}
P3 addition:
enum StepKind {
// ... existing P2 steps
IfCond, // if (cond) in body
ThenUpdates, // carrier updates in then branch
ElseUpdates, // carrier updates in else branch (if any)
}
P3 schedule:
// sum_count pattern
[HeaderCond, IfCond, ThenUpdates, Updates, Tail]
// vs P2 pattern
[HeaderCond, BodyInit, BreakCheck, Updates, Tail]
JpInst Reuse
Already exists in Normalized (from P2):
pub enum JpInst {
Let { dst, op, args },
If { cond, then_target, else_target, env }, // ← P3 uses this!
TailCallFn { target, args },
TailCallKont { target, args },
}
P3 usage:
Ifinstruction for body if-statementthen_target→ block with carrier updateselse_target→ block without updates (or with different updates)
No new JpInst needed!
Testing Strategy
Phase 47-A: Minimal
Test (runner-based, implemented): normalized_pattern3_if_sum_minimal_runner_dev_switch_matches_structured
#[cfg(feature = "normalized_dev")]
#[test]
fn test_normalized_pattern3_if_sum_minimal() {
let source = r#"
local sum = 0
local count = 0
local i = 0
local n = 5
loop(i < n) {
if (i % 2 == 1) {
sum = sum + i
count = count + 1
}
i = i + 1
}
print("sum = " + sum.to_string())
print("count = " + count.to_string())
"#;
// 現在は Runner ベースで Structured→Normalized→Structured roundtrip のみ実装済み。
// VM Bridge での Normalized→MIR(direct) 比較は後続ステップ。
}
Expected output(phase212_if_sum_min.hako 相当):
sum = 2(3 回中 2 回加算)
Phase 47-B: array_filter
Test: test_normalized_pattern3_array_filter
Similar structure, verify method calls + body-local work in Normalized.
Success Criteria
Phase 47-A complete when:
- ✅
test_normalized_pattern3_if_sum_minimalpasses (dev-only) - ✅ Structured→Normalized→MIR(direct) output matches Structured→MIR
- ✅ All 937+ tests still pass (no regressions)
- ✅ ShapeGuard can detect Pattern3IfSumMinimal
- ✅ Documentation updated (architecture overview, CURRENT_TASK)
Phase 47-B complete when:
- ✅ array_filter test passes (dev-only)
- ✅ Body-local + method calls work in P3 Normalized
Phase 47-C complete when:
- ✅ P3 minimal promoted to canonical (always Normalized)
- ✅ Performance validated
Scope Management
In Scope (Phase 47-A):
- ✅ Minimal P3 (sum_count pattern)
- ✅ Dev-only Normalized support
- ✅ Reuse P2 infrastructure (ConditionEnv, CarrierInfo, ExitLine)
Out of Scope (deferred):
- ❌ Complex P3 patterns (nested if, multiple conditions)
- ❌ Canonical promotion (Phase 47-C)
- ❌ Pattern4 (continue) support (separate NORM-P4 phase)
- ❌ Selfhost loops (later phase)
File Impact Estimate
Expected modifications (Phase 47-A):
shape_guard.rs: +20 lines (Pattern3IfSumMinimal shape)step_schedule.rs: +40 lines (P3 step kinds, rename from pattern2_*)normalized_bridge/direct.rs: +60 lines (If instruction handling)tests/normalized_joinir_min.rs: +30 lines (P3 test)phase47-norm-p3-design.md: +200 lines (this doc)joinir-architecture-overview.md: +10 lines (Phase 47 section)CURRENT_TASK.md: +5 lines (Phase 47 entry)
Total: ~365 lines (+), pure additive (no P2 code changes)
Benefits
- Infrastructure reuse: 90% of P2 Normalized code works for P3
- Unified pipeline: All patterns (P1/P2/P3) use same Normalized
- Incremental rollout: Dev-only → canonical, same as P2
- Clear path to P4: Pattern4 (continue) follows same approach
Next Steps After Phase 47
- NORM-P4: Pattern4 (continue) Normalized support
- Canonical promotion: Move P3/P4 from dev-only to canonical
- Selfhost loops: Complex patterns from selfhost compiler
- Performance optimization: Profile Normalized vs Structured
References
- P2 Completion: PHASE_43_245B_NORMALIZED_COMPLETION.md
- Phase 46 P2-Mid: phase46-norm-canon-p2-mid.md
- Architecture: joinir-architecture-overview.md
- Phase 220 P3 Foundation: joinir-architecture-overview.md lines 73-84