Files
hakorune/docs/development/current/main/phase47-norm-p3-design.md
nyash-codex 42ecd7a7e7 feat(joinir): Phase 47-A prep - P3 fixture and test stub
Preparation for Phase 47-A (P3 Normalized minimal implementation).
Added fixture and test stub for pattern3_if_sum_minimal.

Changes:
- fixtures.rs: Added pattern3_if_sum_minimal fixture
  - Source: phase212_if_sum_min.hako
  - Pattern: Single carrier (sum), simple condition (i > 0)
  - Dev-only fixture for P3 Normalized development

- normalized_joinir_min.rs: Added test stub
  - test_normalized_pattern3_if_sum_minimal_runner
  - Currently returns early (implementation pending)
  - Feature-gated: #[cfg(feature = "normalized_dev")]

- Documentation updates:
  - CURRENT_TASK.md: Phase 47-A status
  - PHASE_43_245B_NORMALIZED_COMPLETION.md: P3 forward reference
  - joinir-architecture-overview.md: Minor formatting
  - phase47-norm-p3-design.md: Implementation notes

Next steps (Phase 47-A implementation):
1. Rename pattern2_step_schedule.rs → step_schedule.rs
2. Add P3 StepKind (IfCond, ThenUpdates, ElseUpdates)
3. Implement Normalized→MIR(direct) for P3
4. Complete test implementation

Tests: 937/937 PASS (no behavioral changes yet)
2025-12-12 05:07:01 +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 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