Files
hakorune/docs/development/current/main/phase47-norm-p3-design.md
nyash-codex 23ebb86e6e docs(joinir): Phase 47 - Normalized P3 (If-Sum) design
Complete design for extending Normalized JoinIR to Pattern3 (if-sum) loops,
reusing 90% of existing P2 infrastructure.

Key insight: P3 already uses P2's Structured JoinIR foundation (Phase 220
ConditionEnv integration), so Normalized extension is straightforward.

Design documents:
- phase47-norm-p3-design.md: Complete P3 Normalized design
  - Infrastructure reuse analysis (ConditionEnv, CarrierInfo, ExitLine)
  - Representative loops identified (phase212_if_sum_min primary target)
  - StepScheduleBox extension (IfCond, ThenUpdates, ElseUpdates)
  - JpInst reuse (If instruction already exists)
  - Implementation strategy (dev-only → canonical)
  - File impact estimate (~365 lines, pure additive)

- phase47-test-files-inventory.md: P3 test file catalog
  - 3 P3 test files analyzed
  - phase212_if_sum_min.hako: Single carrier (PRIMARY)
  - phase217_if_sum_multi_min.hako: Multi-carrier (sum + count)
  - phase218_json_if_sum_min.hako: Variable accumulation (sum + i)

Architecture:
- Unified Normalized for P1/P2/P3 (same pipeline)
- Conditional updates only difference vs P2
- No P2 code changes needed (pure additive)

Benefits:
- 90% infrastructure reuse from P2
- Proven incremental rollout (dev → canonical)
- Clear path to P4 (continue pattern)

Implementation roadmap:
- Phase 47-A: Minimal sum_count (dev-only)
- Phase 47-B: array_filter (body-local + method calls)
- Phase 47-C: Canonical promotion

Updates:
- joinir-architecture-overview.md: Added Phase 47 section
- CURRENT_TASK.md: Updated Phase 47 entry (Design Complete)

Status: Design phase complete, ready for implementation
2025-12-12 04:50:26 +09:00

11 KiB

Phase 47: Normalized P3 (If-Sum) Design

Status: Design Complete, Implementation Planned 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]
  • If branching in Normalized JpInst
    • Already exists: If { cond, then_target, else_target, env }
    • Just need to emit for P3 body structure

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 Strategy

Phase 47-A: Minimal sum_count (dev-only)

Goal: Prove P3 can use Normalized infrastructure with minimal additions.

Steps:

  1. ShapeGuard: Add Pattern3IfSumMinimal shape
  2. StepScheduleBox: Add ConditionalUpdate step kind
  3. Normalized lowering:
    • Generate If JpInst for body if-statement
    • Emit carrier updates in then/else branches
  4. Test: Verify Structured→Normalized→MIR(direct) matches Structured→MIR

Expected additions:

  • shape_guard.rs: +1 shape variant
  • pattern2_step_schedule.rs: Rename to step_schedule.rs, add P3 support
  • normalized_bridge/direct.rs: Handle If JpInst with carrier updates
  • tests/normalized_joinir_min.rs: +1 P3 test

Dev fixture: phase212_if_sum_min.hakojsonparser_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:

  • If instruction for body if-statement
  • then_target → block with carrier updates
  • else_target → block without updates (or with different updates)

No new JpInst needed!

Testing Strategy

Phase 47-A: Minimal

Test: test_normalized_pattern3_if_sum_minimal

#[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())
    "#;

    // Compare Structured→MIR vs Normalized→MIR(direct)
    assert_vm_output_matches(source);
}

Expected output:

sum = 6  (1 + 3 + 5)
count = 3

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:

  1. test_normalized_pattern3_if_sum_minimal passes (dev-only)
  2. Structured→Normalized→MIR(direct) output matches Structured→MIR
  3. All 937+ tests still pass (no regressions)
  4. ShapeGuard can detect Pattern3IfSumMinimal
  5. Documentation updated (architecture overview, CURRENT_TASK)

Phase 47-B complete when:

  1. array_filter test passes (dev-only)
  2. Body-local + method calls work in P3 Normalized

Phase 47-C complete when:

  1. P3 minimal promoted to canonical (always Normalized)
  2. 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):

  1. shape_guard.rs: +20 lines (Pattern3IfSumMinimal shape)
  2. step_schedule.rs: +40 lines (P3 step kinds, rename from pattern2_*)
  3. normalized_bridge/direct.rs: +60 lines (If instruction handling)
  4. tests/normalized_joinir_min.rs: +30 lines (P3 test)
  5. phase47-norm-p3-design.md: +200 lines (this doc)
  6. joinir-architecture-overview.md: +10 lines (Phase 47 section)
  7. CURRENT_TASK.md: +5 lines (Phase 47 entry)

Total: ~365 lines (+), pure additive (no P2 code changes)

Benefits

  1. Infrastructure reuse: 90% of P2 Normalized code works for P3
  2. Unified pipeline: All patterns (P1/P2/P3) use same Normalized
  3. Incremental rollout: Dev-only → canonical, same as P2
  4. Clear path to P4: Pattern4 (continue) follows same approach

Next Steps After Phase 47

  1. NORM-P4: Pattern4 (continue) Normalized support
  2. Canonical promotion: Move P3/P4 from dev-only to canonical
  3. Selfhost loops: Complex patterns from selfhost compiler
  4. Performance optimization: Profile Normalized vs Structured

References