Proper HOST↔JoinIR ValueId separation for condition variables: - Add ConditionEnv struct (name → JoinIR-local ValueId mapping) - Add ConditionBinding struct (HOST/JoinIR ValueId pairs) - Modify condition_to_joinir to use ConditionEnv instead of builder.variable_map - Update Pattern2 lowerer to build ConditionEnv and ConditionBindings - Extend JoinInlineBoundary with condition_bindings field - Update BoundaryInjector to inject Copy instructions for condition variables This fixes the undefined ValueId errors where HOST ValueIds were being used directly in JoinIR instructions. Programs now execute (RC: 0), though loop variable exit values still need Phase 172 work. Key invariants established: 1. JoinIR uses ONLY JoinIR-local ValueIds 2. HOST↔JoinIR bridging is ONLY through JoinInlineBoundary 3. condition_to_joinir NEVER accesses builder.variable_map 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
6.5 KiB
6.5 KiB
Phase 171-1: Boundary Coverage Analysis
Date: 2025-12-07 Status: Analysis Complete
Current Boundary Coverage Table
| Component | Currently in Boundary? | How Mapped? | File Location |
|---|---|---|---|
Loop variable (i) |
✅ Yes | join_inputs[0] → host_inputs[0] |
inline_boundary.rs:115-124 |
Carriers (sum, count) |
✅ Yes | exit_bindings (Phase 190+) |
inline_boundary.rs:150-167 |
| Exit values | ✅ Yes | exit_bindings.join_exit_value |
exit_binding.rs:87-116 |
Condition inputs (start, end, len) |
❌ NO | MISSING | N/A |
Detailed Analysis
1. Loop Variable (i) - ✅ Properly Mapped
Mapping Flow:
HOST: variable_map["i"] = ValueId(5)
↓ join_inputs = [ValueId(0)] (JoinIR local ID)
↓ host_inputs = [ValueId(5)] (HOST ID)
JoinIR: main() parameter = ValueId(0)
↓ merge_joinir_mir_blocks() injects:
MIR: ValueId(100) = Copy ValueId(5) // Boundary Copy instruction
Evidence:
inline_boundary.rs:175-189-new_inputs_only()constructormerge/mod.rs:104-106- Boundary reconnection call- Works correctly in all existing JoinIR tests
2. Carriers (sum, count) - ✅ Properly Mapped (Phase 190+)
Mapping Flow:
HOST: variable_map["sum"] = ValueId(10)
↓ exit_bindings = [
LoopExitBinding {
carrier_name: "sum",
join_exit_value: ValueId(18), // k_exit param in JoinIR
host_slot: ValueId(10) // HOST variable
}
]
JoinIR: k_exit(sum_exit) - parameter = ValueId(18)
↓ merge_joinir_mir_blocks() remaps:
↓ remapper.set_value(ValueId(18), ValueId(200))
MIR: variable_map["sum"] = ValueId(200) // Reconnected
Evidence:
inline_boundary.rs:259-311-new_with_exit_bindings()constructorexit_binding.rs:87-116- Exit binding buildermerge/mod.rs:188-267-reconnect_boundary()implementation- Works correctly in Pattern 3/4 tests
3. Condition Inputs (start, end, len) - ❌ NOT MAPPED
Current Broken Flow:
HOST: variable_map["start"] = ValueId(33)
↓ condition_to_joinir() reads from variable_map DIRECTLY
↓ lower_value_expression() returns ValueId(33)
JoinIR: Uses ValueId(33) in Compare instruction
↓ NO BOUNDARY REGISTRATION
↓ NO REMAPPING
MIR: ValueId(33) undefined → RUNTIME ERROR
Evidence (from Phase 170):
[ssa-undef-debug] fn=TrimTest.trim/1 bb=BasicBlockId(12)
inst_idx=0 used=ValueId(33)
Root Cause:
condition_to_joinir.rs:183-189- Reads frombuilder.variable_mapdirectlycondition_to_joinir.rs:236-239- Returns HOST ValueId unchanged- NO registration in
JoinInlineBoundary - NO remapping in
merge_joinir_mir_blocks()
Why This is a Problem
Example: loop(start < end)
What SHOULD happen:
// HOST preparation
let start_host = ValueId(33);
let end_host = ValueId(34);
// JoinIR lowerer
let start_joinir = ValueId(0); // Local param
let end_joinir = ValueId(1); // Local param
// Boundary
JoinInlineBoundary {
join_inputs: [ValueId(0), ValueId(1)], // start, end in JoinIR
host_inputs: [ValueId(33), ValueId(34)], // start, end in HOST
// ...
}
// Merge
// Injects: ValueId(100) = Copy ValueId(33) // start
// Injects: ValueId(101) = Copy ValueId(34) // end
// Remaps all JoinIR ValueId(0) → ValueId(100), ValueId(1) → ValueId(101)
What CURRENTLY happens:
// JoinIR lowerer
let start = builder.variable_map.get("start"); // Returns ValueId(33) - HOST ID!
let end = builder.variable_map.get("end"); // Returns ValueId(34) - HOST ID!
// JoinIR uses HOST ValueIds directly
Compare { lhs: ValueId(33), rhs: ValueId(34) } // WRONG - uses HOST IDs
// No boundary registration → No remapping → UNDEFINED VALUE ERROR
Comparison: Loop Variable vs Condition Inputs
| Aspect | Loop Variable (i) |
Condition Inputs (start, end) |
|---|---|---|
| Who allocates ValueId? | JoinIR lowerer (alloc_value()) |
HOST (builder.variable_map) |
| Boundary registration? | ✅ Yes (join_inputs[0]) |
❌ NO |
| Remapping? | ✅ Yes (via boundary Copy) | ❌ NO |
| Result? | ✅ Works | ❌ Undefined ValueId error |
Root Cause Summary
The core problem is two-faced ValueId resolution in condition_to_joinir():
-
Loop variable (
i):- Allocated by JoinIR lowerer:
i_param = alloc_value()→ValueId(0) - Used in condition:
i < end - Properly registered in boundary ✅
- Allocated by JoinIR lowerer:
-
Condition variables (
start,end):- Read from HOST:
builder.variable_map.get("start")→ValueId(33) - Used in condition:
start < end - NOT registered in boundary ❌
- Read from HOST:
Files Involved
Boundary Definition
src/mir/join_ir/lowering/inline_boundary.rs(340 lines)JoinInlineBoundarystructjoin_inputs,host_inputsfields- Missing: Condition inputs field
Boundary Builder
src/mir/builder/control_flow/joinir/patterns/exit_binding.rs(401 lines)LoopExitBindingstructExitBindingBuilder- builds exit bindings- Missing: Condition input builder
Merge Implementation
src/mir/builder/control_flow/joinir/merge/mod.rs(268 lines)merge_joinir_mir_blocks()- main merge coordinatorreconnect_boundary()- updates variable_map with exit values- Missing: Condition input Copy injection
Condition Lowering
src/mir/join_ir/lowering/condition_to_joinir.rs(443 lines)lower_condition_to_joinir()- AST → JoinIR conversionlower_value_expression()- reads frombuilder.variable_map- Problem: Returns HOST ValueIds directly
Loop Lowerers
src/mir/join_ir/lowering/loop_with_break_minimal.rs(295 lines)lower_loop_with_break_minimal()- Pattern 2 lowerer- Calls
lower_condition_to_joinir()at line 138-144 - Missing: Extract condition variables, register in boundary
Next Steps (Phase 171-2)
We need to design a "box" for condition inputs. Three options:
Option A: Extend JoinInlineBoundary with condition_inputs field
Option B: Create new LoopInputBinding structure
Option C: Extend LoopExitBinding to include condition inputs
Proceed to Phase 171-2 for design decision.
References
- Phase 170 Analysis:
phase170-valueid-boundary-analysis.md - Phase 170 Completion:
phase170-completion-report.md - JoinIR Design:
docs/development/current/main/phase33-10-if-joinir-design.md