Files
hakorune/docs/development/current/main/phase47-norm-p3-design.md
nyash-codex 4ecb6435d3 docs(joinir): Phase 48 - Normalized P4 (Continue) design
Complete design documentation for Pattern4 (continue) Normalized support,
extending unified Normalized infrastructure to all 4 loop patterns.

Design documents:
- phase48-norm-p4-design.md: Complete P4 Normalized design (380 lines)
  - Key insight: continue = immediate TailCallFn(loop_step, ...) (no new instruction!)
  - Infrastructure reuse: 95%+ from P2/P3 (only ContinueCheck step kind new)
  - Target loops prioritized:
    - ◎ _parse_array (skip whitespace) - PRIMARY (Phase 48-A)
    - ○ _parse_object (skip whitespace) - Extended
    - △ _unescape_string, _parse_string - Later
  - Control flow: ContinueCheck before Updates (skip processing early)
  - Same EnvLayout/ConditionEnv/CarrierInfo/ExitLine as P2/P3
  - Implementation strategy: dev-only → canonical (proven approach)

Architecture:
- Unified Normalized for P1/P2/P3/P4 (all patterns same pipeline)
- P4 uses loop_step(env, k_exit) skeleton (same as P2/P3)
- continue semantics: TailCallFn = skip to next iteration (CPS natural fit)

Benefits:
- 95% infrastructure reuse from P2/P3
- No new JpInst needed (continue = existing TailCallFn)
- Incremental rollout (dev → canonical)
- Clear semantic: continue = immediate recursion

Implementation roadmap:
- Phase 48-A: Minimal continue (dev-only)
- Phase 48-B: Extended patterns (multi-carrier)
- Phase 48-C: Canonical promotion

Updates:
- joinir-architecture-overview.md: Added Phase 48 section
- CURRENT_TASK.md: Phase 48 entry (Design Complete)
- phase47-norm-p3-design.md: Minor formatting

Status: Design phase complete (doc-only, no implementation yet)
Next: Phase 48-A implementation (when requested)
2025-12-12 06:06:39 +09:00

13 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責務境界を明確化
  • ShapeGuard: Pattern3IfSumMinimal detection added構造ベース検出
  • Normalized bridge: normalize_pattern3_if_sum_minimal を通じて P3 最小ケースを Normalized→MIR(direct) パイプラインに統合
  • Runner / VM tests: P3 minimal について Structured 経路と Normalized→MIR(direct) 経路が一致することを dev-only スイートで確認
  • 938/938 tests PASS退行なし

Next PhasePhase 47-B 以降):

  • array_filter など body-local + method call を含む P3 ループへの適用
  • multi-carrier if-sumsum + countや JsonParser 由来 P3 への拡張
  • Canonical 昇格P3 minimal を dev-only から常時 Normalized 経路へ)

Implementation Strategy

Phase 47-A: Minimal sum_count (dev-only, 実装済みの範囲)

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

実装済みステップ:

  1. ShapeGuard: Pattern3IfSumMinimal shape 追加
    • Compare + Select + tail call を持つ 3 関数構成の P3 if-sum 最小パターンを検出。
  2. StepScheduleBox: P3 用 StepKind 追加
    • IfCond / ThenUpdates / ElseUpdatesstep_schedule.rs に導入し、P2/P3 共通の箱として再利用。
  3. Normalized loweringminimal:
    • normalize_pattern3_if_sum_minimal により、P3 最小ケースを P2 と同じ器の NormalizedModule に変換し、direct ブリッジで扱える形に整備。
  4. Tests:
    • Runner 経路: normalized_pattern3_if_sum_minimal_runner_dev_switch_matches_structured
    • Normalization 経路: test_phase47a_pattern3_if_sum_minimal_normalizationshape + normalize 成功)
    • (詳細な VM Bridge 比較は Phase 47-B 以降で拡張していく)

Dev fixture: apps/tests/phase212_if_sum_min.hako(最小 if-sum パターン)

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