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>
This commit is contained in:
@ -0,0 +1,261 @@
|
||||
# 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
|
||||
|
||||
```rust
|
||||
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
|
||||
|
||||
```rust
|
||||
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
|
||||
|
||||
```rust
|
||||
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
|
||||
```bash
|
||||
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
|
||||
Reference in New Issue
Block a user