Add design documents for Phase 131 P1.5 DirectValue mode: - Root cause analysis of PHI-based exit merge assumptions - Option B (DirectValue) analysis and trade-offs - Implementation guide for exit value reconnection Also add exit_reconnector.rs module stub for future extraction. Related: - Phase 131: loop(true) break-once Normalized support - Normalized shadow path uses continuations, not PHI - Exit values reconnect directly to host variable_map 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
9.3 KiB
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:
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:
// 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:
//! 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:
- Add env_layout to JoinFragmentMeta (if not already there)
- Detect Normalized shadow path
- Use ExitReconnectorBox instead of merge pipeline
Code Change:
// 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:
pub mod exit_reconnector;
pub use exit_reconnector::ExitReconnectorBox;
Step 4: Testing
Unit Tests (in exit_reconnector.rs)
#[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
-
VM Smoke:
bash tools/smokes/v2/profiles/integration/apps/phase131_loop_true_break_once_vm.shExpected:
[PASS] Output verified: 1 (exit code: 1) -
LLVM EXE Smoke:
bash tools/smokes/v2/profiles/integration/apps/phase131_loop_true_break_once_llvm_exe.shExpected:
[PASS] Output verified: 1 -
Regression:
# 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.shExpected: All PASS (既定挙動不変)
Step 5: Verification Checklist
Before committing:
cargo build --release→ 0 errorscargo 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
- Don't touch merge pipeline: Standard loops (Pattern 1-4) still use it
- Don't generate PHI: Normalized IR is PHI-free by design
- Don't forget strict mode: Contract violations should freeze
- Don't skip regression tests: 既定挙動不変 is critical
Key Principles (SSOT)
- PHI禁止維持: Normalized IR never generates PHI nodes
- 既定挙動不変: Standard patterns use existing merge pipeline
- 責務分離: ExitReconnectorBox handles Normalized-specific wiring
- 箱化モジュール化: New logic in dedicated box
- 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