Files
hakorune/docs/development/current/main/phase47-norm-p3-design.md
nyash-codex c3a26e705e feat(joinir): Phase 47-A-IMPL - P3 Normalized infrastructure
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
2025-12-12 05:23:18 +09:00

12 KiB
Raw Blame History

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]
  • 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 Status

Phase 47-A-PREP ( Complete, commit 42ecd7a7):

  • Fixture added: pattern3_if_sum_minimal in normalized/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.rsstep_schedule.rs
  • P3 StepKind added: IfCond, ThenUpdates, ElseUpdates
  • Pattern2 lowering separation: P3 steps panic in P2 lowering (clean boundary)
  • ShapeGuard: Pattern3IfSumMinimal detection 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:

  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 (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 outputphase212_if_sum_min.hako 相当): sum = 23 回中 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:

  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