# Phase 131 P1.5: Implementation Guide (Quick Reference) Status: Implementation Guide Scope: Step-by-step implementation instructions for Option B Related: - Root Cause: `p1.5-root-cause-summary.md` - Design: `p1.5-option-b-analysis.md` ## Implementation Sequence ### Step 1: Create ExitReconnectorBox (New File) **File**: `src/mir/control_tree/normalized_shadow/exit_reconnector.rs` **Responsibility**: Generate host variable bindings from k_exit env parameters (Normalized IR only, PHI-free) **API**: ```rust pub struct ExitReconnectorBox; impl ExitReconnectorBox { /// Reconnect k_exit env parameters to host variable_map /// /// For Normalized IR only: k_exit env params are SSOT for final values pub fn reconnect( builder: &mut MirBuilder, exit_meta: &ExitMeta, env_layout: &EnvLayout, debug: bool, ) -> Result<(), String> { // For each exit_value (var_name, k_exit_param_vid) // - k_exit_param_vid is already the final value (SSOT) // - Update builder.variable_ctx.variable_map[var_name] = k_exit_param_vid // // Strict mode: verify all env_layout.writes are in exit_meta // // Returns: Ok(()) on success, Err(msg) on contract violation } } ``` **Algorithm**: ```rust // 1. Validate (strict mode only) if strict_enabled() { for var in &env_layout.writes { if !exit_meta.exit_values.iter().any(|(name, _)| name == var) { return Err(freeze_with_hint( "phase131/exit_reconnect/missing", &format!("Variable '{}' in writes but not in k_exit env", var), "Ensure env_layout.writes matches k_exit parameter list (SSOT)" )); } } } // 2. Update variable_map directly (no PHI) for (var_name, k_exit_param_vid) in &exit_meta.exit_values { // k_exit_param_vid is the final value (SSOT) builder.variable_ctx.variable_map.insert( var_name.clone(), *k_exit_param_vid ); if debug { eprintln!( "[phase131/exit_reconnect] {} → {:?}", var_name, k_exit_param_vid ); } } ``` **File Template**: ```rust //! Phase 131 P1.5: ExitReconnectorBox - Normalized Exit Reconnection //! //! ## Responsibility //! //! - Wire k_exit env parameters to host variable_map (Normalized IR only) //! - PHI-free: k_exit env params are SSOT for final values //! - Fail-Fast: Strict mode catches contract violations //! //! ## Contract //! //! - Only used for Normalized shadow path //! - k_exit env parameters represent final variable values (SSOT) //! - No PHI generation (direct variable_map update) //! //! ## Design Philosophy //! //! Normalized IR uses continuation-based control flow: //! - main(env) → loop_step(env) → loop_body(env) → k_exit(env) //! - k_exit receives final env state as parameters //! - No loop back edges, no PHI merging needed //! - k_exit env params ARE the truth (SSOT) use crate::mir::builder::MirBuilder; use crate::mir::control_tree::normalized_shadow::env_layout::EnvLayout; use crate::mir::join_ir::lowering::carrier_info::ExitMeta; use crate::mir::join_ir::lowering::error_tags; pub struct ExitReconnectorBox; impl ExitReconnectorBox { // ... implementation here ... } #[cfg(test)] mod tests { // ... tests here ... } ``` ### Step 2: Update routing.rs (Bypass Merge Pipeline) **File**: `src/mir/builder/control_flow/joinir/routing.rs` **Location**: Lines 441-532 (current Normalized shadow handling) **Changes**: 1. **Add env_layout to JoinFragmentMeta** (if not already there) 2. **Detect Normalized shadow path** 3. **Use ExitReconnectorBox instead of merge pipeline** **Code Change**: ```rust // Around line 441 match StepTreeNormalizedShadowLowererBox::try_lower_if_only(&tree, &available_inputs) { Ok(Some((join_module, join_meta))) => { // ... existing trace ... // Phase 131 P1.5: Normalized-specific exit reconnection // Bypass PHI-based merge pipeline, use direct env→host wiring use crate::mir::control_tree::normalized_shadow::exit_reconnector::ExitReconnectorBox; use crate::mir::control_tree::normalized_shadow::env_layout::EnvLayout; // Build env_layout from contract (or get from join_meta if available) let env_layout = EnvLayout::from_contract(&tree.contract, &available_inputs); // Direct reconnection (no PHI) ExitReconnectorBox::reconnect( self, &join_meta.exit_meta, &env_layout, debug, )?; trace::trace().routing( "router/normalized", func_name, "Normalized exit reconnection complete (PHI-free)", ); // Return void constant (loop executed successfully) // Note: If k_exit returns a value, use that instead use crate::mir::{ConstValue, MirInstruction}; let void_id = self.next_value_id(); self.emit_instruction(MirInstruction::Const { dst: void_id, value: ConstValue::Void, })?; Ok(Some(void_id)) } Ok(None) => { // ... existing fallback ... } Err(e) => { // ... existing error handling ... } } ``` **Note**: Remove the existing `ExitMetaCollector::collect()` and `JoinIRConversionPipeline::execute()` calls - those are for standard loops with PHI. ### Step 3: Update mod.rs (Export ExitReconnectorBox) **File**: `src/mir/control_tree/normalized_shadow/mod.rs` **Add**: ```rust pub mod exit_reconnector; pub use exit_reconnector::ExitReconnectorBox; ``` ### Step 4: Testing #### Unit Tests (in exit_reconnector.rs) ```rust #[cfg(test)] mod tests { use super::*; #[test] fn test_empty_writes() { // When env_layout.writes is empty // Should return Ok(()) without updating variable_map } #[test] fn test_single_write() { // When env_layout.writes = ["x"] // And exit_meta.exit_values = [("x", ValueId(6))] // Should update variable_map["x"] = ValueId(6) } #[test] #[should_panic] fn test_missing_variable_strict() { // When strict mode enabled // And env_layout.writes = ["x", "y"] // But exit_meta.exit_values = [("x", ValueId(6))] // Should panic with freeze_with_hint } } ``` #### Integration Tests 1. **VM Smoke**: ```bash bash tools/smokes/v2/profiles/integration/apps/phase131_loop_true_break_once_vm.sh ``` Expected: `[PASS] Output verified: 1 (exit code: 1)` 2. **LLVM EXE Smoke**: ```bash bash tools/smokes/v2/profiles/integration/apps/phase131_loop_true_break_once_llvm_exe.sh ``` Expected: `[PASS] Output verified: 1` 3. **Regression**: ```bash # If-only patterns (Phase 130) bash tools/smokes/v2/profiles/integration/apps/phase130_if_only_post_if_add_vm.sh # Existing JoinIR patterns (Phase 97) bash tools/smokes/v2/profiles/integration/apps/phase97_next_non_ws_llvm_exe.sh ``` Expected: All PASS (既定挙動不変) ### Step 5: Verification Checklist Before committing: - [ ] `cargo build --release` → 0 errors - [ ] `cargo test --lib` → all tests pass - [ ] Phase 131 VM smoke → PASS - [ ] Phase 131 LLVM EXE smoke → PASS - [ ] Phase 130 regression → PASS - [ ] Phase 97 regression → PASS ## Commit Sequence ### Commit 1: Create ExitReconnectorBox ``` feat(normalized): Phase 131 P1.5 ExitReconnectorBox for host variable propagation - Add exit_reconnector.rs with pure function design - Wire k_exit env params to host variable_map (PHI-free) - Strict mode verifier for contract violations - Unit tests for empty/single/multiple writes ``` ### Commit 2: Update Routing ``` fix(joinir/dev): bypass PHI generation for Normalized path - Detect Normalized shadow in routing.rs - Use ExitReconnectorBox instead of merge pipeline - Direct env→host wiring (no PHI generation) - Existing patterns (Pattern 1-4) unaffected ``` ### Commit 3: Enable Tests ``` test(joinir): Phase 131 P1.5 enforce return parity (VM + LLVM EXE) - Enable phase131 VM/LLVM EXE smokes - Verify regression tests (phase130, phase97) - Update test expectations ``` ### Commit 4: Documentation ``` docs: Phase 131 P1.5 DONE (Normalized exit reconnection via Option B) - Update 10-Now.md with completion status - Update phase-131/README.md with P1.5 summary - Add root cause analysis docs ``` ## Common Pitfalls to Avoid 1. **Don't touch merge pipeline**: Standard loops (Pattern 1-4) still use it 2. **Don't generate PHI**: Normalized IR is PHI-free by design 3. **Don't forget strict mode**: Contract violations should freeze 4. **Don't skip regression tests**: 既定挙動不変 is critical ## Key Principles (SSOT) 1. **PHI禁止維持**: Normalized IR never generates PHI nodes 2. **既定挙動不変**: Standard patterns use existing merge pipeline 3. **責務分離**: ExitReconnectorBox handles Normalized-specific wiring 4. **箱化モジュール化**: New logic in dedicated box 5. **Fail-Fast**: Strict mode catches violations immediately ## Success Criteria - [ ] loop(true) { x=1; break }; return x → returns 1 (not 0) - [ ] VM and LLVM EXE both produce same result - [ ] No regression in existing patterns - [ ] Clean trace output (no PHI errors) - [ ] Strict mode catches contract violations ## Reference - Root Cause: `p1.5-root-cause-summary.md` - Design Rationale: `p1.5-option-b-analysis.md` - Phase 131 Overview: `README.md`