Files
hakorune/docs/development/current/main/phases/phase-134/normalization-p0.md
nyash-codex f4ab5ca5f4 refactor(control_flow): Phase 134 P0 - Normalization entry point SSOT
Consolidate dual entry points into unified NormalizationPlan system:

Problem:
- Dual entry points: try_normalized_shadow() + suffix_router_box
- ~220 lines of duplicated pattern detection logic
- Maintenance burden: changes required in two places

Solution:
- New normalization module with Box-First architecture
- NormalizationPlanBox: Single source of truth for pattern detection
- NormalizationExecuteBox: Single source of truth for execution

Implementation:
- src/mir/builder/control_flow/normalization/ (new module)
  - README.md: Design contract (SSOT)
  - plan.rs: NormalizationPlan data structure
  - plan_box.rs: Pattern detection (7 unit tests)
  - execute_box.rs: Execution logic
  - mod.rs: Module integration

Refactored files:
- routing.rs::try_normalized_shadow(): 165 → 87 lines (-78 lines)
- suffix_router_box::try_lower_loop_suffix(): 258 → 116 lines (-142 lines)

Pattern support maintained:
- Phase 131: loop(true) only (consumed: 1)
- Phase 132: loop + single post (consumed: 3)
- Phase 133: loop + multiple post (consumed: 2+N)

Box-First principles:
- Plan Box: Detection responsibility only
- Execute Box: Execution responsibility only
- README.md: Contract documentation (SSOT)
- Clear separation enables independent testing

Test results:
- cargo test --lib: 1186 PASS (10 new tests added)
- Phase 133 VM/LLVM EXE: PASS (exit code 6)
- Phase 132 LLVM EXE: PASS (exit code 3)
- Phase 131 LLVM EXE: PASS (exit code 1)

Benefits:
- Code duplication eliminated (~220 lines)
- Single source of truth for normalization decisions
- Improved maintainability and testability
- Future-proof extensibility (easy to add new patterns)

Default behavior unchanged: Dev-only guard maintained

Related: Phase 134 normalization infrastructure improvement

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 22:29:29 +09:00

7.9 KiB

Phase 134 P0: Normalization Entry Point Consolidation

Date: 2025-12-18 Status: Complete Scope: Unified entry point for Normalized shadow detection and execution


Background

Problem (Before):

  • Dual entry points with duplicated responsibility:
    • try_normalized_shadow() in routing.rs (loop-only patterns)
    • suffix_router_box in patterns/policies/ (loop + post statements)
  • Decision logic scattered: "What to lower" was decided in two different places
  • Maintenance burden: Changes to pattern detection required updates in multiple locations

Solution (Phase 134 P0)

Unified decision point using Box-First architecture:

  1. NormalizationPlanBox: SSOT for pattern detection ("what to normalize")
  2. NormalizationExecuteBox: SSOT for execution ("how to execute")
  3. Both entry points use the same PlanBox for consistent decisions

Implementation

New Module Structure

src/mir/builder/control_flow/normalization/ (5 files created):

  • README.md: Design documentation and contract (SSOT)
  • plan.rs: NormalizationPlan data structure
  • plan_box.rs: NormalizationPlanBox (pattern detection)
  • execute_box.rs: NormalizationExecuteBox (execution logic)
  • mod.rs: Module integration

NormalizationPlan Structure

pub struct NormalizationPlan {
    pub consumed: usize,          // Statements to consume from remaining
    pub kind: PlanKind,           // What to lower
    pub requires_return: bool,    // Whether pattern includes return
}

pub enum PlanKind {
    LoopOnly,                     // Phase 131: loop(true) alone
    LoopWithPost {                // Phase 132-133: loop + assigns + return
        post_assign_count: usize,
    },
}

NormalizationPlanBox API

pub fn plan_block_suffix(
    builder: &MirBuilder,
    remaining: &[ASTNode],
    func_name: &str,
    debug: bool,
) -> Result<Option<NormalizationPlan>, String>

Returns:

  • Ok(Some(plan)): Pattern detected, proceed with normalization
  • Ok(None): Not a normalized pattern, use legacy fallback
  • Err(_): Internal error

NormalizationExecuteBox API

pub fn execute(
    builder: &mut MirBuilder,
    plan: &NormalizationPlan,
    remaining: &[ASTNode],
    func_name: &str,
    debug: bool,
) -> Result<ValueId, String>

Side Effects:

  • Modifies builder state (adds blocks, instructions)
  • Updates variable_map with exit values (DirectValue mode)
  • May emit return statement (for suffix patterns)

Code Changes

1. normalized_shadow_suffix_router_box.rs

Before (258 lines):

  • Pattern detection logic (50+ lines)
  • Merge logic (50+ lines)
  • StepTree building and lowering

After (116 lines, -142 lines):

  • Delegates to NormalizationPlanBox for detection
  • Delegates to NormalizationExecuteBox for execution
  • Only handles suffix-specific logic (return emission)

2. routing.rs try_normalized_shadow()

Before (165 lines):

  • StepTree building
  • AvailableInputsCollector
  • Boundary creation
  • Merge logic
  • Exit reconnection

After (87 lines, -78 lines):

  • Delegates to NormalizationPlanBox for detection
  • Delegates to NormalizationExecuteBox for execution
  • Pattern kind validation (loop-only vs loop+post)

Pattern Detection Logic

Phase 131: Loop-Only

  • Pattern: loop(true) { ... break }
  • Consumed: 1 statement
  • Example: loop(true) { x = 1; break }; return x

Phase 132: Loop + Single Post

  • Pattern: loop(true) { ... break }; <assign>; return <expr>
  • Consumed: 3 statements
  • Example: loop(true) { x = 1; break }; x = x + 2; return x

Phase 133: Loop + Multiple Post

  • Pattern: loop(true) { ... break }; <assign>+; return <expr>
  • Consumed: 2 + N statements
  • Example: loop(true) { x = 1; break }; x = x + 2; x = x + 3; return x

Testing

Unit Tests (7 tests)

Location: src/mir/builder/control_flow/normalization/plan_box.rs

test_plan_block_suffix_phase131_loop_only test_plan_block_suffix_phase132_loop_post_single test_plan_block_suffix_phase133_loop_post_multi test_plan_block_suffix_return_boundary test_plan_block_suffix_no_match_empty test_plan_block_suffix_no_match_not_loop test_plan_block_suffix_no_match_no_return

Lib Tests

cargo test --lib --package nyash-rust

Result: 1186 passed; 0 failed; 56 ignored

Regression Smokes

Phase 133 VM: phase133_loop_true_break_once_post_multi_add_vm.sh - PASS Phase 133 LLVM: phase133_loop_true_break_once_post_multi_add_llvm_exe.sh - PASS (exit code 6) Phase 132 LLVM: phase132_loop_true_break_once_post_add_llvm_exe.sh - PASS (exit code 3) Phase 131 LLVM: phase131_loop_true_break_once_llvm_exe.sh - PASS (exit code 1) Phase 131 VM: phase131_loop_true_break_once_vm.sh - PASS


Benefits

Code Quality

  • -220 lines of duplicated code eliminated
  • Single responsibility: Each Box has one clear purpose
  • Testability: Plan detection can be tested independently

Maintainability

  • SSOT: Pattern detection in one place (NormalizationPlanBox)
  • Documentation: Comprehensive README.md with contract
  • Clear separation: Detection vs execution

Future-Proofing

  • Extensible: Easy to add new pattern kinds
  • Flexible: ExecuteBox can be swapped for different implementations
  • Traceable: Debug logging at each decision point

Design Principles Applied

Box-First

  • Plan Box: Single responsibility for detection
  • Execute Box: Single responsibility for execution
  • Clear boundaries enable independent evolution

SSOT (Single Source of Truth)

  • Entry: NormalizationPlanBox is the only place for normalization decisions
  • Contract: Documented in normalization/README.md
  • No duplication: Both entry points use the same logic

Fail-Fast

  • Invalid patterns return Err (not silent fallback)
  • STRICT mode treats contract violations as panics
  • Clear error messages with hints

Legacy Preservation

  • Existing behavior unchanged (dev-only guard)
  • Non-normalized patterns return Ok(None) → legacy fallback
  • No breaking changes to existing smokes

Files Modified

  • src/mir/builder/control_flow/normalization/ (NEW module, 5 files)
  • src/mir/builder/control_flow/mod.rs (added normalization module)
  • src/mir/builder/control_flow/joinir/patterns/policies/normalized_shadow_suffix_router_box.rs (refactored, -142 lines)
  • src/mir/builder/control_flow/joinir/routing.rs (refactored, -78 lines)

Net Change: +~300 lines (new module), -220 lines (refactoring) = +80 lines overall


Acceptance Criteria

  • Entry SSOT: normalization/README.md documents the contract
  • By-name avoidance: Uses boundary contract SSOT
  • cargo test --lib: 1186/1186 PASS
  • Phase 133 smokes: 2/2 PASS (VM + LLVM EXE)
  • Phase 131/132 regression: PASS
  • Default behavior unchanged: Dev-only guard maintained

Next Steps

P1: Additional Patterns (Future)

  • Phase 135+: Support for more complex post-loop patterns
  • Conditional post: if (cond) { assign }; return
  • Nested structures: Multiple loops with post statements

P2: Performance Optimization (Future)

  • Caching: Pattern detection results
  • Lazy evaluation: Only build StepTree when needed
  • Parallel detection: Check multiple patterns simultaneously

P3: Enhanced Debugging (Future)

  • Structured tracing: JSON-formatted trace output
  • Visualization: DOT graph of normalization decisions
  • Metrics: Track pattern match rates

References

  • Module: src/mir/builder/control_flow/normalization/
  • Contract: src/mir/builder/control_flow/normalization/README.md
  • Tests: src/mir/builder/control_flow/normalization/plan_box.rs::tests
  • Phase 131: loop(true) break-once Normalized
  • Phase 132: loop(true) + post-loop minimal
  • Phase 133: loop(true) + multiple post-loop assigns