210 lines
6.5 KiB
Markdown
210 lines
6.5 KiB
Markdown
|
|
# 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()` constructor
|
||
|
|
- `merge/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()` constructor
|
||
|
|
- `exit_binding.rs:87-116` - Exit binding builder
|
||
|
|
- `merge/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 from `builder.variable_map` directly
|
||
|
|
- `condition_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**:
|
||
|
|
```rust
|
||
|
|
// 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**:
|
||
|
|
```rust
|
||
|
|
// 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()`:
|
||
|
|
|
||
|
|
1. **Loop variable** (`i`):
|
||
|
|
- Allocated by JoinIR lowerer: `i_param = alloc_value()` → `ValueId(0)`
|
||
|
|
- Used in condition: `i < end`
|
||
|
|
- Properly registered in boundary ✅
|
||
|
|
|
||
|
|
2. **Condition variables** (`start`, `end`):
|
||
|
|
- Read from HOST: `builder.variable_map.get("start")` → `ValueId(33)`
|
||
|
|
- Used in condition: `start < end`
|
||
|
|
- **NOT registered in boundary** ❌
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Files Involved
|
||
|
|
|
||
|
|
### Boundary Definition
|
||
|
|
- `src/mir/join_ir/lowering/inline_boundary.rs` (340 lines)
|
||
|
|
- `JoinInlineBoundary` struct
|
||
|
|
- `join_inputs`, `host_inputs` fields
|
||
|
|
- **Missing**: Condition inputs field
|
||
|
|
|
||
|
|
### Boundary Builder
|
||
|
|
- `src/mir/builder/control_flow/joinir/patterns/exit_binding.rs` (401 lines)
|
||
|
|
- `LoopExitBinding` struct
|
||
|
|
- `ExitBindingBuilder` - 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 coordinator
|
||
|
|
- `reconnect_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 conversion
|
||
|
|
- `lower_value_expression()` - reads from `builder.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`
|