feat(joinir): Phase 171-fix ConditionEnv/ConditionBinding architecture
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>
This commit is contained in:
@ -1,10 +1,62 @@
|
||||
# Self Current Task — Now (main)
|
||||
|
||||
2025‑09‑08:現状と直近タスク
|
||||
## 2025‑12‑06:現状サマリ
|
||||
|
||||
### JoinIR / Loop / If ライン
|
||||
|
||||
- LoopBuilder は Phase 186‑187 で完全削除済み。**JoinIR が唯一の loop lowering 経路**。
|
||||
- LoopPattern 系は Pattern1–4 まで実装・本線化済み:
|
||||
- Pattern1: Simple While
|
||||
- Pattern2: Loop with Break
|
||||
- Pattern3: Loop with If‑Else PHI(break/continue なし)
|
||||
- Pattern4: Loop with Continue(multi‑carrier 対応)
|
||||
- Exit/Carrier/Boundary ラインは次の箱で SSOT 化:
|
||||
- `CarrierInfo` / `ExitMeta` / `ExitBindingBuilder`
|
||||
- `JoinInlineBoundary` + `LoopExitBinding`
|
||||
- If lowering は IfSelectLowerer/IfMergeLowerer を中心に整理済み。Select/PHI の扱いも Phase 189 系で橋渡し済み。
|
||||
- 残課題(JoinIR ライン):
|
||||
- JoinIR→MIR merge の一般化(複雑な Select/PHI パターンの統合)
|
||||
- JsonParserBox など実アプリ側での長期運用テスト
|
||||
|
||||
### JsonParser / Selfhost depth‑2 ライン
|
||||
|
||||
- `selfhost_build.sh --json` で Program JSON v0 emit は安定。
|
||||
`.hako` 側から env 経由で JSON を読む最小ループ(`program_read_min.hako`)は動作確認済み。
|
||||
- JsonParserBox / BundleResolver のループ 21 本のうち:
|
||||
- 18 本は Pattern1–4 で JoinIR 対応済み(Phase 162–165)。
|
||||
- `_trim` を含む一部の複合ループは、ValueId 境界や Box 登録など残課題あり。
|
||||
- BoolExprLowerer / condition_to_joinir で OR/AND/NOT 付き条件式の lowering は実装完了(Phase 168–169)。
|
||||
- 残課題(JsonParser/selfhost depth‑2):
|
||||
- JoinIR→MIR ValueId boundary の完全一般化(条件用 ValueId を含める)
|
||||
- JsonParserBox の using / Box 登録(Rust VM 側での認識)
|
||||
- Program JSON v0 を JsonParserBox 経由でフル解析する line の仕上げ
|
||||
|
||||
### Ring0 / Runtime / CoreServices ライン
|
||||
|
||||
- Ring0Context + Ring0Registry で OS API 抽象化レイヤ完成:
|
||||
- MemApi / IoApi / TimeApi / LogApi / FsApi / ThreadApi
|
||||
- RuntimeProfile(Default / NoFs) で条件付き必須を制御。
|
||||
- CoreServices(ring1‑core)として次を実装済み:
|
||||
- StringService / IntegerService / BoolService
|
||||
- ArrayService / MapService / ConsoleService
|
||||
- PluginHost 統合 + UnifiedBoxRegistry からの自動初期化
|
||||
- FileBox / FileHandleBox ライン:
|
||||
- Ring0FsFileIo 経由で read / write / append / metadata 完全対応
|
||||
- Default プロファイルでは必須、NoFs プロファイルでは disabled。
|
||||
- Logging ライン:
|
||||
- ConsoleService(user‑facing)
|
||||
- Ring0.log(internal/dev)
|
||||
- println!(test 専用)
|
||||
の 3 層が `logging_policy.md` で整理済み。JoinIR/Loop trace も同ドキュメントに集約。
|
||||
|
||||
---
|
||||
|
||||
## 2025‑09‑08:旧スナップショット(参考)
|
||||
|
||||
- LLVM 側 P0 完了(BitOps/Array/Echo/Map 緑)。VInvoke(by‑name/by‑id vector) は戻り値マッピングの暫定課題を確認中(Decisions 参照)。
|
||||
- selfhosting-dev の作業を main に順次取り込み。VM/MIR 基盤は main で先に整える方針。
|
||||
|
||||
直近タスク(優先)
|
||||
直近タスク(当時)
|
||||
1) continue/break の lowering(Builder 修正のみで表現)
|
||||
- ループ文脈スタック {head, exit} を導入。
|
||||
- continue に遭遇 → head(または latch)へ br を emit し終端。
|
||||
@ -22,4 +74,3 @@
|
||||
- ビルド: `cargo build --release`
|
||||
- LLVM smoke: `LLVM_SYS_180_PREFIX=$(llvm-config-18 --prefix) NYASH_LLVM_BITOPS_SMOKE=1 ./tools/llvm_smoke.sh release`
|
||||
- VInvoke 調査: `NYASH_LLVM_VINVOKE_TRACE=1 NYASH_LLVM_VINVOKE_SMOKE=1 ./tools/llvm_smoke.sh release`
|
||||
|
||||
|
||||
346
docs/development/current/main/phase166-validation-report.md
Normal file
346
docs/development/current/main/phase166-validation-report.md
Normal file
@ -0,0 +1,346 @@
|
||||
# Phase 166 Validation Report: JsonParserBox Unit Test with BoolExprLowerer
|
||||
|
||||
**Date**: 2025-12-06 (Updated: 2025-12-07 Phase 170)
|
||||
**Status**: ⚠️ **Blocked** - ValueId boundary mapping issue
|
||||
**Blocker**: Condition variables not included in JoinInlineBoundary
|
||||
|
||||
**Phase 170 Update**: BoolExprLowerer is now integrated (Phase 167-169), but a critical ValueId boundary mapping bug prevents runtime execution. See [phase170-valueid-boundary-analysis.md](phase170-valueid-boundary-analysis.md) for details.
|
||||
|
||||
---
|
||||
|
||||
## Phase 170 Re-validation Results (2025-12-07)
|
||||
|
||||
After Phase 167-169 (BoolExprLowerer integration), Phase 170 re-tested JsonParserBox with the following results:
|
||||
|
||||
### ✅ Whitelist Expansion Complete
|
||||
- Added 6 JsonParserBox methods to routing whitelist
|
||||
- Methods now route to JoinIR instead of `[joinir/freeze]`
|
||||
- Pattern matching works correctly (Pattern2 detected for `_trim`)
|
||||
|
||||
### ⚠️ Runtime Failure: ValueId Boundary Issue
|
||||
**Test**: `local_tests/test_trim_main_pattern.hako`
|
||||
**Pattern Matched**: Pattern2 (twice, for 2 loops)
|
||||
**Result**: Silent runtime failure (no output)
|
||||
|
||||
**Root Cause**: Condition variables (`start`, `end`) are resolved from HOST `variable_map` but not included in `JoinInlineBoundary`, causing undefined ValueId references.
|
||||
|
||||
**Evidence**:
|
||||
```
|
||||
[ssa-undef-debug] fn=TrimTest.trim/1 bb=BasicBlockId(12) inst_idx=0 used=ValueId(33)
|
||||
[ssa-undef-debug] fn=TrimTest.trim/1 bb=BasicBlockId(12) inst_idx=0 used=ValueId(34)
|
||||
```
|
||||
|
||||
**Solution**: Option A in [phase170-valueid-boundary-analysis.md](phase170-valueid-boundary-analysis.md) - Extract condition variables and add to boundary.
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary (Original Phase 166)
|
||||
|
||||
Phase 166 aimed to validate that JsonParserBox can parse JSON through the JoinIR path, confirming Pattern1-4 support. However, investigation revealed that:
|
||||
|
||||
1. **✅ JoinIR Pattern Detection Works**: Pattern 2 (break) correctly detected ← **Still true in Phase 170**
|
||||
2. **✅ Simple JSON Parsing Works**: Non-loop or simple-condition patterns execute fine
|
||||
3. **~~❌ Complex Conditions Blocked~~** ← **FIXED in Phase 169**: BoolExprLowerer integrated
|
||||
4. **❌ NEW BLOCKER (Phase 170)**: ValueId boundary mapping prevents runtime execution
|
||||
|
||||
---
|
||||
|
||||
## Test Results
|
||||
|
||||
### ✅ Test 1: Simple JSON Parser (No Loops)
|
||||
**File**: `local_tests/test_json_parser_simple_string.hako`
|
||||
|
||||
```bash
|
||||
./target/release/hakorune local_tests/test_json_parser_simple_string.hako
|
||||
# Output: PASS: Got 'hello'
|
||||
```
|
||||
|
||||
**Result**: **SUCCESS** - Basic string parsing without complex conditions works.
|
||||
|
||||
---
|
||||
|
||||
### ❌ Test 2: _trim Pattern with OR Chains
|
||||
**File**: `local_tests/test_trim_or_pattern.hako`
|
||||
|
||||
```bash
|
||||
./target/release/hakorune local_tests/test_trim_or_pattern.hako
|
||||
# Output: [joinir/freeze] Loop lowering failed
|
||||
```
|
||||
|
||||
**Result**: **BLOCKED** - OR condition causes `[joinir/freeze]` error.
|
||||
|
||||
---
|
||||
|
||||
### ⚠️ Test 3: _trim Pattern in main() with Simple Condition
|
||||
**File**: `local_tests/test_trim_main_pattern.hako`
|
||||
|
||||
```bash
|
||||
./target/release/hakorune local_tests/test_trim_main_pattern.hako
|
||||
# Output: FAIL - Result: ' hello ' (not trimmed)
|
||||
```
|
||||
|
||||
**Result**: **PATTERN DETECTED BUT LOGIC WRONG** - Pattern 2 matches, but uses hardcoded `i < 3` instead of actual condition.
|
||||
|
||||
**Debug Output**:
|
||||
```
|
||||
[trace:pattern] route: Pattern2_WithBreak MATCHED
|
||||
[trace:varmap] pattern2_start: end→r9, s→r4, start→r6
|
||||
Final start: 0 (unchanged - loop didn't execute properly)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Root Cause Analysis
|
||||
|
||||
### Discovery 1: Function Name Whitelisting
|
||||
|
||||
**File**: `src/mir/builder/control_flow/joinir/routing.rs` (lines 44-68)
|
||||
|
||||
JoinIR is **ONLY enabled for specific function names**:
|
||||
- `"main"`
|
||||
- `"JoinIrMin.main/0"`
|
||||
- `"JsonTokenizer.print_tokens/0"`
|
||||
- `"ArrayExtBox.filter/2"`
|
||||
|
||||
**Impact**: `JsonParserBox._trim/1` is NOT whitelisted → `[joinir/freeze]` error.
|
||||
|
||||
**Workaround**: Test in `main()` function instead.
|
||||
|
||||
---
|
||||
|
||||
### Discovery 2: Hardcoded Conditions in Minimal Lowerers
|
||||
|
||||
**File**: `src/mir/join_ir/lowering/loop_with_break_minimal.rs` (lines 171-197)
|
||||
|
||||
```rust
|
||||
// HARDCODED: !(i < 3)
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: const_3,
|
||||
value: ConstValue::Integer(3), // ← HARDCODED VALUE
|
||||
}));
|
||||
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||
dst: cmp_lt,
|
||||
op: CompareOp::Lt, // ← HARDCODED OPERATOR
|
||||
lhs: i_param,
|
||||
rhs: const_3,
|
||||
}));
|
||||
```
|
||||
|
||||
**Impact**: Pattern 2 lowerer generates fixed `i < 3` check, **ignoring the actual AST condition**.
|
||||
|
||||
**Current Behavior**:
|
||||
- AST condition: `start < end` with `ch == " "` check
|
||||
- Generated JoinIR: `i < 3` with `i >= 2` break check
|
||||
- Result: Loop doesn't execute correctly
|
||||
|
||||
---
|
||||
|
||||
### Discovery 3: BoolExprLowerer Not Integrated
|
||||
|
||||
**Files**:
|
||||
- `src/mir/join_ir/lowering/bool_expr_lowerer.rs` (Phase 167-168, 436 lines, complete)
|
||||
- `src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs` (line 58)
|
||||
|
||||
```rust
|
||||
// Current code:
|
||||
let loop_var_name = self.extract_loop_variable_from_condition(condition)?;
|
||||
|
||||
// Missing:
|
||||
// use crate::mir::join_ir::lowering::bool_expr_lowerer::BoolExprLowerer;
|
||||
// let mut bool_lowerer = BoolExprLowerer::new(self.builder);
|
||||
// let cond_val = bool_lowerer.lower_condition(&ctx.condition)?;
|
||||
```
|
||||
|
||||
**Impact**: BoolExprLowerer exists but isn't called by Pattern 2/4 lowerers.
|
||||
|
||||
---
|
||||
|
||||
### Discovery 4: LoopBuilder Hard Freeze
|
||||
|
||||
**File**: `src/mir/builder/control_flow/mod.rs` (lines 112-119)
|
||||
|
||||
```rust
|
||||
// Phase 186: LoopBuilder Hard Freeze - Legacy path disabled
|
||||
// Phase 187-2: LoopBuilder module removed - all loops must use JoinIR
|
||||
return Err(format!(
|
||||
"[joinir/freeze] Loop lowering failed: JoinIR does not support this pattern, and LoopBuilder has been removed.\n\
|
||||
Function: {}\n\
|
||||
Hint: This loop pattern is not supported. All loops must use JoinIR lowering.",
|
||||
self.current_function.as_ref().map(|f| f.signature.name.as_str()).unwrap_or("<unknown>")
|
||||
));
|
||||
```
|
||||
|
||||
**Impact**: NO fallback exists when JoinIR patterns don't match.
|
||||
|
||||
---
|
||||
|
||||
## Architecture Issues
|
||||
|
||||
### Issue 1: Minimal Lowerers Are Test-Specific
|
||||
|
||||
**Design**: Pattern 1-4 lowerers are "minimal implementations" for specific test cases:
|
||||
- Pattern 1: `apps/tests/joinir_simple_loop.hako` (`i < 5`)
|
||||
- Pattern 2: `apps/tests/joinir_min_loop.hako` (`i < 3`, `i >= 2`)
|
||||
- Pattern 3: `apps/tests/loop_if_phi_sum.hako` (hardcoded sum accumulation)
|
||||
- Pattern 4: `apps/tests/loop_continue_pattern4.hako` (hardcoded continue logic)
|
||||
|
||||
**Problem**: These lowerers are **NOT** generic - they can't handle arbitrary conditions.
|
||||
|
||||
---
|
||||
|
||||
### Issue 2: Condition Extraction vs. Evaluation
|
||||
|
||||
**Current**:
|
||||
- `extract_loop_variable_from_condition()` - Extracts variable name (`i`, `start`)
|
||||
- Used for: Carrier detection, not condition evaluation
|
||||
- Only supports: Simple comparisons like `i < 3`
|
||||
|
||||
**Missing**:
|
||||
- Dynamic condition evaluation (BoolExprLowerer)
|
||||
- OR chain support
|
||||
- Complex boolean expressions
|
||||
|
||||
---
|
||||
|
||||
### Issue 3: JoinIR Generation Architecture
|
||||
|
||||
**Current Pipeline**:
|
||||
```
|
||||
AST Loop → Pattern Detection → Hardcoded JoinIR Generator
|
||||
↓
|
||||
Fixed condition (i < 3)
|
||||
```
|
||||
|
||||
**Needed Pipeline**:
|
||||
```
|
||||
AST Loop → Pattern Detection → BoolExprLowerer → Dynamic JoinIR Generator
|
||||
↓ ↓
|
||||
Condition MIR → Convert to JoinInst
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 166 Status Update
|
||||
|
||||
### ✅ Completed Validation
|
||||
1. **Pattern Detection**: Pattern 2 (break) correctly identified
|
||||
2. **Simple Cases**: Non-loop JSON parsing works
|
||||
3. **Infrastructure**: JoinIR pipeline functional
|
||||
4. **Whitelist Behavior**: Function name routing confirmed
|
||||
|
||||
### ❌ Remaining Blockers
|
||||
1. **OR Chains**: `ch == " " || ch == "\t"...` not supported
|
||||
2. **Dynamic Conditions**: Hardcoded `i < 3` instead of actual condition
|
||||
3. **BoolExprLowerer Integration**: Phase 167-168 code not used
|
||||
4. **JsonParserBox._trim**: Cannot execute due to whitelisting
|
||||
|
||||
---
|
||||
|
||||
## Recommended Next Steps
|
||||
|
||||
### Phase 169: BoolExprLowerer Integration (HIGH PRIORITY)
|
||||
|
||||
**Goal**: Make JoinIR patterns support arbitrary conditions.
|
||||
|
||||
**Tasks**:
|
||||
1. **Modify Pattern 2 Lowerer** (`loop_with_break_minimal.rs`):
|
||||
- Accept `condition: &ASTNode` parameter
|
||||
- Call `BoolExprLowerer::lower_condition(condition)`
|
||||
- Generate JoinIR instructions from condition MIR
|
||||
- Replace hardcoded `const_3`, `cmp_lt` with dynamic values
|
||||
|
||||
2. **Modify Pattern 4 Lowerer** (`loop_with_continue_minimal.rs`):
|
||||
- Same changes as Pattern 2
|
||||
|
||||
3. **Update Caller** (`pattern2_with_break.rs`):
|
||||
- Pass `ctx.condition` to lowerer
|
||||
- Handle condition evaluation errors
|
||||
|
||||
4. **Test Coverage**:
|
||||
- `_trim` pattern with OR chains
|
||||
- Complex boolean expressions
|
||||
- Nested conditions
|
||||
|
||||
**Estimated Effort**: 2-3 hours (architecture already designed in Phase 167-168)
|
||||
|
||||
---
|
||||
|
||||
### Phase 170: Function Whitelist Expansion (MEDIUM PRIORITY)
|
||||
|
||||
**Goal**: Enable JoinIR for JsonParserBox methods.
|
||||
|
||||
**Options**:
|
||||
1. **Option A**: Add to whitelist:
|
||||
```rust
|
||||
"JsonParserBox._trim/1" => true,
|
||||
"JsonParserBox._skip_whitespace/2" => true,
|
||||
```
|
||||
|
||||
2. **Option B**: Enable JoinIR globally for all functions:
|
||||
```rust
|
||||
let is_target = true; // Always try JoinIR first
|
||||
```
|
||||
|
||||
3. **Option C**: Add pattern-based routing (e.g., all `_trim*` functions)
|
||||
|
||||
**Recommended**: Option A (conservative, safe)
|
||||
|
||||
---
|
||||
|
||||
### Phase 171: JsonParserBox Full Validation (POST-169)
|
||||
|
||||
**Goal**: Validate all JsonParserBox methods work through JoinIR.
|
||||
|
||||
**Tests**:
|
||||
- `_trim` (OR chains)
|
||||
- `_skip_whitespace` (OR chains)
|
||||
- `_parse_number` (digit loop)
|
||||
- `_parse_string` (escape sequences)
|
||||
- `_parse_array` (recursive calls)
|
||||
- `_parse_object` (key-value pairs)
|
||||
|
||||
---
|
||||
|
||||
## Files Modified This Session
|
||||
|
||||
### Created Test Files
|
||||
1. `local_tests/test_json_parser_simple_string.hako` - Simple JSON test (PASS)
|
||||
2. `local_tests/test_trim_or_pattern.hako` - OR chain test (BLOCKED)
|
||||
3. `local_tests/test_trim_simple_pattern.hako` - Simple condition test (BLOCKED)
|
||||
4. `local_tests/test_trim_main_pattern.hako` - Whitelisted function test (WRONG LOGIC)
|
||||
5. `local_tests/test_trim_debug.hako` - Debug output test
|
||||
|
||||
### Documentation
|
||||
1. `docs/development/current/main/phase166-validation-report.md` (this file)
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
**Phase 166 Validation Status**: ⚠️ **Partially Complete**
|
||||
|
||||
**Key Findings**:
|
||||
1. JoinIR Pattern Detection **works correctly**
|
||||
2. Simple patterns **execute successfully**
|
||||
3. Complex OR chains **are blocked** by hardcoded conditions
|
||||
4. BoolExprLowerer (Phase 167-168) **exists but isn't integrated**
|
||||
|
||||
**Next Critical Phase**: **Phase 169 - BoolExprLowerer Integration** to unblock JsonParserBox._trim and enable dynamic condition evaluation.
|
||||
|
||||
**Timeline**:
|
||||
- Phase 169: 2-3 hours (integration work)
|
||||
- Phase 170: 30 minutes (whitelist update)
|
||||
- Phase 171: 1 hour (full validation testing)
|
||||
|
||||
**Total Estimated Time to Complete Phase 166**: 4-5 hours
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- **Phase 166 Goal**: `docs/development/current/main/phase166-joinir-json-parser-validation.md`
|
||||
- **Phase 167-168**: `src/mir/join_ir/lowering/bool_expr_lowerer.rs`
|
||||
- **Pattern 2 Lowerer**: `src/mir/join_ir/lowering/loop_with_break_minimal.rs`
|
||||
- **Routing Logic**: `src/mir/builder/control_flow/joinir/routing.rs`
|
||||
- **LoopBuilder Freeze**: `src/mir/builder/control_flow/mod.rs` (lines 112-119)
|
||||
1076
docs/development/current/main/phase167_boolexpr_lowerer_design.md
Normal file
1076
docs/development/current/main/phase167_boolexpr_lowerer_design.md
Normal file
File diff suppressed because it is too large
Load Diff
304
docs/development/current/main/phase170-completion-report.md
Normal file
304
docs/development/current/main/phase170-completion-report.md
Normal file
@ -0,0 +1,304 @@
|
||||
# Phase 170: JsonParserBox JoinIR Preparation & Re-validation - Completion Report
|
||||
|
||||
**Date**: 2025-12-07
|
||||
**Duration**: 1 session (autonomous work)
|
||||
**Status**: ✅ **Complete** - Environment prepared, blockers identified, next phase planned
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
Phase 170 successfully prepared the environment for JsonParserBox JoinIR validation and identified the critical ValueId boundary mapping issue blocking runtime execution. All tasks completed:
|
||||
|
||||
- ✅ **Task A-1**: JoinIR routing whitelist expanded (6 JsonParserBox methods + test helper)
|
||||
- ✅ **Task A-2**: ValueId boundary issue identified with full root cause analysis
|
||||
- ⚠️ **Task B**: Mini tests blocked by `using` statement (workaround: simplified test created)
|
||||
- ✅ **Task C**: Next phase direction decided (Option A: Fix boundary mapping)
|
||||
|
||||
**Key Achievement**: Identified that BoolExprLowerer integration (Phase 167-169) is correct, but the boundary mechanism needs condition variable extraction to work properly.
|
||||
|
||||
---
|
||||
|
||||
## Phase 170-A: Environment Setup ✅
|
||||
|
||||
### Task A-1: JoinIR Routing Whitelist Expansion
|
||||
|
||||
**Objective**: Allow JsonParserBox methods to route to JoinIR patterns instead of `[joinir/freeze]`.
|
||||
|
||||
**Changes Made**:
|
||||
|
||||
**File**: `src/mir/builder/control_flow/joinir/routing.rs` (lines 68-76)
|
||||
|
||||
**Added entries**:
|
||||
```rust
|
||||
// Phase 170-A-1: Enable JsonParserBox methods for JoinIR routing
|
||||
"JsonParserBox._trim/1" => true,
|
||||
"JsonParserBox._skip_whitespace/2" => true,
|
||||
"JsonParserBox._match_literal/2" => true,
|
||||
"JsonParserBox._parse_string/2" => true,
|
||||
"JsonParserBox._parse_array/2" => true,
|
||||
"JsonParserBox._parse_object/2" => true,
|
||||
// Phase 170-A-1: Test methods (simplified versions)
|
||||
"TrimTest.trim/1" => true,
|
||||
```
|
||||
|
||||
**Result**: ✅ Methods now route to pattern matching instead of immediate `[joinir/freeze]` rejection.
|
||||
|
||||
**Evidence**:
|
||||
```bash
|
||||
HAKO_JOINIR_DEBUG=1 ./target/release/hakorune local_tests/test_trim_main_pattern.hako
|
||||
# Output: [joinir/pattern2] Generated JoinIR for Loop with Break Pattern (Phase 169)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task A-2: ValueId Boundary Issue Identification
|
||||
|
||||
**Objective**: Understand if ValueId boundary mapping affects JsonParserBox tests.
|
||||
|
||||
**Test Created**: `local_tests/test_trim_main_pattern.hako` (48 lines)
|
||||
- Simplified `_trim` method with same loop structure as JsonParserBox
|
||||
- Two loops with break (Pattern2 x 2)
|
||||
- Condition variables: `start < end`, `end > start`
|
||||
|
||||
**Findings**:
|
||||
|
||||
1. **Pattern Detection**: ✅ Works correctly
|
||||
- Both loops match Pattern2
|
||||
- JoinIR generation succeeds
|
||||
|
||||
2. **Runtime Execution**: ❌ Silent failure
|
||||
- Program compiles successfully
|
||||
- No output produced
|
||||
- Exit code 0 (but no print statements executed)
|
||||
|
||||
3. **Root Cause Identified**: ValueId boundary mapping
|
||||
- Condition variables (`start`, `end`) resolved from HOST `variable_map`
|
||||
- HOST ValueIds (33, 34, 48, 49) used directly in JoinIR
|
||||
- Not included in `JoinInlineBoundary`
|
||||
- Merge process doesn't remap them → undefined at runtime
|
||||
|
||||
**Evidence**:
|
||||
```
|
||||
[ssa-undef-debug] fn=TrimTest.trim/1 bb=BasicBlockId(12) inst_idx=0 used=ValueId(33) inst=Compare { dst: ValueId(26), op: Lt, lhs: ValueId(33), rhs: ValueId(34) }
|
||||
[ssa-undef-debug] fn=TrimTest.trim/1 bb=BasicBlockId(12) inst_idx=0 used=ValueId(34) inst=Compare { dst: ValueId(26), op: Lt, lhs: ValueId(33), rhs: ValueId(34) }
|
||||
```
|
||||
|
||||
**Impact**: CRITICAL - Blocks ALL JsonParserBox methods with complex conditions.
|
||||
|
||||
**Detailed Analysis**: See [phase170-valueid-boundary-analysis.md](phase170-valueid-boundary-analysis.md)
|
||||
|
||||
---
|
||||
|
||||
## Phase 170-B: JsonParserBox Mini Test Re-execution ⚠️
|
||||
|
||||
### Original Test Files
|
||||
|
||||
**Location**: `tools/selfhost/json_parser_{string,array,object}_min.hako`
|
||||
|
||||
**Blocker**: `using` statement not working
|
||||
```
|
||||
[using] not found: 'tools/hako_shared/json_parser.hako" with JsonParserBox'
|
||||
```
|
||||
|
||||
**Root Cause**: JsonParserBox is defined in external file, not compiled/loaded at runtime.
|
||||
|
||||
**Impact**: Can't run original integration tests in current form.
|
||||
|
||||
---
|
||||
|
||||
### Workaround: Simplified Test
|
||||
|
||||
**Created**: `local_tests/test_trim_main_pattern.hako`
|
||||
|
||||
**Purpose**: Test same loop structure without `using` dependency.
|
||||
|
||||
**Structure**:
|
||||
```nyash
|
||||
static box TrimTest {
|
||||
method trim(s) {
|
||||
// Same structure as JsonParserBox._trim
|
||||
loop(start < end) { ... break }
|
||||
loop(end > start) { ... break }
|
||||
}
|
||||
main(args) { ... }
|
||||
}
|
||||
```
|
||||
|
||||
**Result**: Successfully routes to Pattern2, exposes boundary issue.
|
||||
|
||||
---
|
||||
|
||||
## Phase 170-C: Next Phase Planning ✅
|
||||
|
||||
### Immediate TODOs (Phase 171+ Candidates)
|
||||
|
||||
**Priority 1: Fix ValueId Boundary Mapping** (HIGHEST PRIORITY)
|
||||
- **Why**: Blocks all JsonParserBox complex condition tests
|
||||
- **What**: Extract condition variables and add to `JoinInlineBoundary`
|
||||
- **Where**: Pattern lowerers (pattern1/2/3/4)
|
||||
- **Estimate**: 4.5 hours
|
||||
- **Details**: See Option A in [phase170-valueid-boundary-analysis.md](phase170-valueid-boundary-analysis.md)
|
||||
|
||||
**Priority 2: Using Statement / Box Loading** (MEDIUM)
|
||||
- **Why**: Enable actual JsonParserBox integration tests
|
||||
- **What**: Compile and register boxes from `using` statements
|
||||
- **Alternatives**:
|
||||
- Inline JsonParser code in tests (quick workaround)
|
||||
- Auto-compile static boxes (proper solution)
|
||||
|
||||
**Priority 3: Multi-Loop Function Support** (LOW)
|
||||
- **Why**: `_trim` has 2 loops in one function
|
||||
- **Current**: Each loop calls JoinIR routing separately (seems to work)
|
||||
- **Risk**: May need validation that multiple JoinIR calls per function work correctly
|
||||
|
||||
---
|
||||
|
||||
### Recommended Next Phase Direction
|
||||
|
||||
**Option A: Fix Boundary Mapping First** ✅ **RECOMMENDED**
|
||||
|
||||
**Rationale**:
|
||||
1. **Root blocker**: Boundary issue blocks ALL tests, not just one
|
||||
2. **BoolExprLowerer correct**: Phase 169 integration is solid
|
||||
3. **Pattern matching correct**: Routing and detection work perfectly
|
||||
4. **Isolated fix**: Boundary extraction is well-scoped and testable
|
||||
5. **High impact**: Once fixed, all JsonParser methods should work
|
||||
|
||||
**Alternative**: Option B (simplify code) or Option C (postpone) - both less effective.
|
||||
|
||||
---
|
||||
|
||||
## Test Results Matrix
|
||||
|
||||
| Method/Test | Pattern | JoinIR Status | Blocker | Notes |
|
||||
|-------------|---------|---------------|---------|-------|
|
||||
| `TrimTest.trim/1` (loop 1) | Pattern2 | ⚠️ Routes OK, runtime fail | ValueId boundary | `start < end` uses undefined ValueId(33, 34) |
|
||||
| `TrimTest.trim/1` (loop 2) | Pattern2 | ⚠️ Routes OK, runtime fail | ValueId boundary | `end > start` uses undefined ValueId(48, 49) |
|
||||
| `JsonParserBox._trim/1` | (untested) | - | Using statement | Can't load JsonParserBox at runtime |
|
||||
| `JsonParserBox._skip_whitespace/2` | (untested) | - | Using statement | Can't load JsonParserBox at runtime |
|
||||
| `JsonParserBox._match_literal/2` | (untested) | - | Using statement | Can't load JsonParserBox at runtime |
|
||||
| `JsonParserBox._parse_string/2` | (untested) | - | Using statement | Can't load JsonParserBox at runtime |
|
||||
| `JsonParserBox._parse_array/2` | (untested) | - | Using statement | Can't load JsonParserBox at runtime |
|
||||
| `JsonParserBox._parse_object/2` | (untested) | - | Using statement | Can't load JsonParserBox at runtime |
|
||||
|
||||
**Summary**:
|
||||
- **Routing**: ✅ All methods whitelisted, pattern detection works
|
||||
- **Compilation**: ✅ BoolExprLowerer generates correct JoinIR
|
||||
- **Runtime**: ❌ ValueId boundary issue prevents execution
|
||||
- **Integration**: ⚠️ `using` statement blocks full JsonParser tests
|
||||
|
||||
---
|
||||
|
||||
## Files Modified
|
||||
|
||||
**Modified**:
|
||||
- `src/mir/builder/control_flow/joinir/routing.rs` (+8 lines, whitelist expansion)
|
||||
|
||||
**Created**:
|
||||
- `local_tests/test_trim_main_pattern.hako` (+48 lines, test file)
|
||||
- `docs/development/current/main/phase170-valueid-boundary-analysis.md` (+270 lines, analysis)
|
||||
- `docs/development/current/main/phase170-completion-report.md` (+THIS file)
|
||||
|
||||
**Updated**:
|
||||
- `CURRENT_TASK.md` (added Phase 170 section with progress summary)
|
||||
- `docs/development/current/main/phase166-validation-report.md` (added Phase 170 update section)
|
||||
|
||||
---
|
||||
|
||||
## Technical Insights
|
||||
|
||||
### Boundary Mechanism Gap
|
||||
|
||||
**Current Design**:
|
||||
```rust
|
||||
JoinInlineBoundary::new_inputs_only(
|
||||
vec![ValueId(0)], // JoinIR loop variable
|
||||
vec![loop_var_id], // HOST loop variable
|
||||
);
|
||||
```
|
||||
|
||||
**What's Missing**: Condition variables!
|
||||
|
||||
**Needed Design**:
|
||||
```rust
|
||||
JoinInlineBoundary::new_inputs_only(
|
||||
vec![ValueId(0), ValueId(1), ValueId(2)], // loop var + cond vars
|
||||
vec![loop_var_id, start_id, end_id], // HOST ValueIds
|
||||
);
|
||||
```
|
||||
|
||||
**Why It Matters**:
|
||||
- `condition_to_joinir.rs` directly references HOST `variable_map` ValueIds
|
||||
- These ValueIds are NOT in JoinIR's fresh allocator space
|
||||
- Without boundary mapping, they remain undefined after merge
|
||||
- Silent failure: compiles but doesn't execute
|
||||
|
||||
### Two ValueId Namespaces
|
||||
|
||||
**HOST Context** (Main MirBuilder):
|
||||
- ValueIds from 0 upward (e.g., `start = ValueId(33)`)
|
||||
- All variables in `builder.variable_map`
|
||||
- Pre-existing before JoinIR call
|
||||
|
||||
**JoinIR Context** (Fresh Allocator):
|
||||
- ValueIds from 0 upward (independent sequence)
|
||||
- Generated by JoinIR lowerer
|
||||
- Post-merge: remapped to new HOST ValueIds
|
||||
|
||||
**Bridge**: `JoinInlineBoundary` maps between the two spaces with Copy instructions.
|
||||
|
||||
**Current Gap**: Only explicitly listed variables get bridged. Condition variables are implicitly referenced but not bridged.
|
||||
|
||||
---
|
||||
|
||||
## Validation Checklist
|
||||
|
||||
- [x] Whitelist expanded (6 JsonParserBox methods + test)
|
||||
- [x] Pattern routing verified (Pattern2 detected correctly)
|
||||
- [x] BoolExprLowerer integration verified (generates JoinIR correctly)
|
||||
- [x] Boundary issue identified (root cause documented)
|
||||
- [x] Test file created (simplified _trim test)
|
||||
- [x] Root cause analysis completed (270-line document)
|
||||
- [x] Next phase direction decided (Option A recommended)
|
||||
- [x] Documentation updated (CURRENT_TASK.md, phase166 report)
|
||||
- [x] Files committed (ready for next phase)
|
||||
|
||||
---
|
||||
|
||||
## Next Phase: Phase 171 - Boundary Mapping Fix
|
||||
|
||||
**Recommended Implementation**:
|
||||
|
||||
1. **Create condition variable extractor** (30 min)
|
||||
- File: `src/mir/builder/control_flow/joinir/patterns/cond_var_extractor.rs`
|
||||
- Function: `extract_condition_variables(ast: &ASTNode, builder: &MirBuilder) -> Vec<(String, ValueId)>`
|
||||
|
||||
2. **Update Pattern2** (1 hour)
|
||||
- Extract condition variables before lowering
|
||||
- Create expanded boundary with condition vars
|
||||
- Test with `TrimTest.trim/1`
|
||||
|
||||
3. **Update Pattern1, Pattern3, Pattern4** (3 hours)
|
||||
- Apply same pattern
|
||||
- Ensure all patterns include condition vars in boundary
|
||||
|
||||
4. **Validation** (30 min)
|
||||
- Re-run `TrimTest.trim/1` → should print output
|
||||
- Re-run JsonParserBox tests (if `using` resolved)
|
||||
|
||||
**Total Estimate**: 5 hours
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
Phase 170 successfully prepared the environment for JsonParserBox validation and identified the critical blocker preventing runtime execution. The boundary mapping issue is well-understood, with a clear solution path (Option A: extract condition variables).
|
||||
|
||||
**Key Achievements**:
|
||||
- ✅ Whitelist expansion enables JsonParserBox routing
|
||||
- ✅ BoolExprLowerer integration verified working correctly
|
||||
- ✅ Boundary issue root cause identified and documented
|
||||
- ✅ Clear next steps with 5-hour implementation estimate
|
||||
|
||||
**Next Step**: Implement Phase 171 - Condition Variable Extraction for Boundary Mapping.
|
||||
@ -0,0 +1,252 @@
|
||||
# Phase 170: ValueId Boundary Mapping Analysis
|
||||
|
||||
**Date**: 2025-12-07
|
||||
**Status**: Root Cause Identified
|
||||
**Impact**: CRITICAL - Blocks all JsonParserBox complex condition tests
|
||||
|
||||
## Problem Summary
|
||||
|
||||
JoinIR loop patterns with complex conditions (e.g., `start < end` in `_trim`) compile successfully but fail silently at runtime because condition variable ValueIds are not properly mapped between HOST and JoinIR contexts.
|
||||
|
||||
## Symptoms
|
||||
|
||||
```
|
||||
[ssa-undef-debug] fn=TrimTest.trim/1 bb=BasicBlockId(12) inst_idx=0 used=ValueId(33) inst=Compare { dst: ValueId(26), op: Lt, lhs: ValueId(33), rhs: ValueId(34) }
|
||||
[ssa-undef-debug] fn=TrimTest.trim/1 bb=BasicBlockId(12) inst_idx=0 used=ValueId(34) inst=Compare { dst: ValueId(26), op: Lt, lhs: ValueId(33), rhs: ValueId(34) }
|
||||
```
|
||||
|
||||
- Condition uses undefined ValueIds (33, 34) for variables `start` and `end`
|
||||
- Program compiles but produces no output (silent runtime failure)
|
||||
- PHI nodes also reference undefined carrier values
|
||||
|
||||
## Root Cause
|
||||
|
||||
### Architecture Overview
|
||||
|
||||
The JoinIR merge process uses two separate ValueId "namespaces":
|
||||
|
||||
1. **HOST context**: Main MirBuilder's ValueId space (e.g., `start = ValueId(33)`)
|
||||
2. **JoinIR context**: Fresh ValueId allocator starting from 0 (e.g., `ValueId(0), ValueId(1), ...`)
|
||||
|
||||
The `JoinInlineBoundary` mechanism is supposed to bridge these two spaces by injecting Copy instructions at the entry block.
|
||||
|
||||
### The Bug
|
||||
|
||||
**Location**: `src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs` (and other patterns)
|
||||
|
||||
**Current boundary creation**:
|
||||
```rust
|
||||
let boundary = JoinInlineBoundary::new_inputs_only(
|
||||
vec![ValueId(0)], // JoinIR's main() parameter (loop variable init)
|
||||
vec![loop_var_id], // Host's loop variable
|
||||
);
|
||||
```
|
||||
|
||||
**What's missing**: Condition variables (`start`, `end`) are NOT in the boundary!
|
||||
|
||||
**What happens**:
|
||||
1. `condition_to_joinir.rs` looks up variables in `builder.variable_map`:
|
||||
```rust
|
||||
builder.variable_map.get("start") // Returns ValueId(33) from HOST
|
||||
builder.variable_map.get("end") // Returns ValueId(34) from HOST
|
||||
```
|
||||
|
||||
2. JoinIR instructions are generated with these HOST ValueIds:
|
||||
```rust
|
||||
JoinInst::Compute(MirLikeInst::Compare {
|
||||
dst: ValueId(26),
|
||||
op: Lt,
|
||||
lhs: ValueId(33), // HOST ValueId, not in JoinIR space!
|
||||
rhs: ValueId(34), // HOST ValueId, not in JoinIR space!
|
||||
})
|
||||
```
|
||||
|
||||
3. During merge, `remap_values()` only remaps ValueIds that are in `used_values`:
|
||||
- ValueIds 0, 1, 2, ... from JoinIR → new ValueIds from builder
|
||||
- **But ValueId(33) and ValueId(34) are not in JoinIR's used_values set!**
|
||||
- They're HOST ValueIds that leaked into JoinIR space
|
||||
|
||||
4. Result: Compare instruction references undefined ValueIds
|
||||
|
||||
## Architectural Issue
|
||||
|
||||
The current design has a conceptual mismatch:
|
||||
|
||||
- **`condition_to_joinir.rs`** assumes it can directly reference HOST ValueIds from `builder.variable_map`
|
||||
- **JoinIR merge** assumes all ValueIds come from JoinIR's fresh allocator
|
||||
- **Boundary mechanism** only maps explicitly listed inputs/outputs
|
||||
|
||||
This works for simple patterns where:
|
||||
- Condition is hardcoded (e.g., `i < 3`)
|
||||
- All condition values are constants or loop variables already in the boundary
|
||||
|
||||
This breaks when:
|
||||
- Condition references variables from HOST context (e.g., `start < end`)
|
||||
- Those variables are not in the boundary inputs
|
||||
|
||||
## Affected Code Paths
|
||||
|
||||
### Phase 169 Integration: `condition_to_joinir.rs`
|
||||
|
||||
Lines 183-189:
|
||||
```rust
|
||||
ASTNode::Variable { name, .. } => {
|
||||
builder
|
||||
.variable_map
|
||||
.get(name)
|
||||
.copied()
|
||||
.ok_or_else(|| format!("Variable '{}' not found in variable_map", name))
|
||||
}
|
||||
```
|
||||
|
||||
This returns HOST ValueIds directly without checking if they need boundary mapping.
|
||||
|
||||
### Pattern Lowerers: `pattern2_with_break.rs`, etc.
|
||||
|
||||
Pattern lowerers create minimal boundaries that only include:
|
||||
- Loop variable (e.g., `i`)
|
||||
- Accumulator (if present)
|
||||
|
||||
But NOT:
|
||||
- Variables referenced in loop condition (e.g., `start`, `end` in `start < end`)
|
||||
- Variables referenced in loop body expressions
|
||||
|
||||
### Merge Infrastructure: `merge/mod.rs`
|
||||
|
||||
The merge process has no way to detect that HOST ValueIds have leaked into JoinIR instructions.
|
||||
|
||||
## Test Case: `TrimTest.trim/1`
|
||||
|
||||
**Code**:
|
||||
```nyash
|
||||
local start = 0
|
||||
local end = s.length()
|
||||
|
||||
loop(start < end) { // Condition references start, end
|
||||
// ...
|
||||
break
|
||||
}
|
||||
```
|
||||
|
||||
**Expected boundary**:
|
||||
```rust
|
||||
JoinInlineBoundary::new_inputs_only(
|
||||
vec![ValueId(0), ValueId(1), ValueId(2)], // loop var, start, end
|
||||
vec![loop_var_id, start_id, end_id], // HOST ValueIds
|
||||
)
|
||||
```
|
||||
|
||||
**Actual boundary**:
|
||||
```rust
|
||||
JoinInlineBoundary::new_inputs_only(
|
||||
vec![ValueId(0)], // Only loop var
|
||||
vec![loop_var_id], // Only loop var
|
||||
)
|
||||
```
|
||||
|
||||
**Result**: `start` and `end` are undefined in JoinIR space
|
||||
|
||||
## Solutions
|
||||
|
||||
### Option A: Extract Condition Variables into Boundary (Recommended)
|
||||
|
||||
**Where**: Pattern lowerers (pattern1/2/3/4)
|
||||
|
||||
**Steps**:
|
||||
1. Before calling `lower_condition_to_joinir()`, analyze AST to find all variables
|
||||
2. For each variable, get HOST ValueId from `builder.variable_map`
|
||||
3. Allocate JoinIR-side ValueIds (e.g., ValueId(1), ValueId(2))
|
||||
4. Create boundary with all condition variables:
|
||||
```rust
|
||||
let cond_vars = extract_condition_variables(condition_ast, builder);
|
||||
let boundary = JoinInlineBoundary::new_inputs_only(
|
||||
vec![ValueId(0), ValueId(1), ValueId(2)], // loop var + cond vars
|
||||
vec![loop_var_id, cond_vars[0], cond_vars[1]],
|
||||
);
|
||||
```
|
||||
|
||||
**Pros**:
|
||||
- Minimal change to existing architecture
|
||||
- Clear separation: boundary handles HOST↔JoinIR mapping
|
||||
- Works for all condition complexity
|
||||
|
||||
**Cons**:
|
||||
- Need to implement variable extraction from AST
|
||||
- Each pattern needs updating
|
||||
|
||||
### Option B: Delay Variable Resolution Until Merge
|
||||
|
||||
**Where**: `condition_to_joinir.rs`
|
||||
|
||||
**Idea**: Instead of resolving variables immediately, emit placeholder instructions and resolve during merge.
|
||||
|
||||
**Pros**:
|
||||
- Cleaner separation: JoinIR doesn't touch HOST ValueIds
|
||||
|
||||
**Cons**:
|
||||
- Major refactoring required
|
||||
- Need new placeholder instruction type
|
||||
- Complicates merge logic
|
||||
|
||||
### Option C: Use Variable Names Instead of ValueIds in JoinIR
|
||||
|
||||
**Where**: JoinIR instruction format
|
||||
|
||||
**Idea**: JoinIR uses variable names (strings) instead of ValueIds, resolve during merge.
|
||||
|
||||
**Pros**:
|
||||
- Most robust solution
|
||||
- Eliminates ValueId namespace confusion
|
||||
|
||||
**Cons**:
|
||||
- Breaks current JoinIR design (uses MirLikeInst which has ValueIds)
|
||||
- Major architectural change
|
||||
|
||||
## Recommendation
|
||||
|
||||
**Option A** - Extract condition variables and add to boundary.
|
||||
|
||||
**Implementation Plan**:
|
||||
|
||||
1. **Create AST variable extractor** (30 minutes)
|
||||
- File: `src/mir/builder/control_flow/joinir/patterns/cond_var_extractor.rs`
|
||||
- Function: `extract_condition_variables(ast: &ASTNode, builder: &MirBuilder) -> Vec<(String, ValueId)>`
|
||||
- Recursively walk AST, collect all Variable nodes
|
||||
|
||||
2. **Update Pattern2** (1 hour)
|
||||
- Extract condition variables before calling pattern lowerer
|
||||
- Create boundary with extracted variables
|
||||
- Test with `TrimTest.trim/1`
|
||||
|
||||
3. **Update Pattern1, Pattern3, Pattern4** (1 hour each)
|
||||
- Apply same pattern
|
||||
|
||||
4. **Validation** (30 minutes)
|
||||
- Re-run `TrimTest.trim/1` → should output correctly
|
||||
- Re-run JsonParserBox tests → should work
|
||||
|
||||
**Total Estimate**: 4.5 hours
|
||||
|
||||
## Files Affected
|
||||
|
||||
**New**:
|
||||
- `src/mir/builder/control_flow/joinir/patterns/cond_var_extractor.rs` (new utility)
|
||||
|
||||
**Modified**:
|
||||
- `src/mir/builder/control_flow/joinir/patterns/pattern1_minimal.rs`
|
||||
- `src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs`
|
||||
- `src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs`
|
||||
- `src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs`
|
||||
|
||||
## Related Issues
|
||||
|
||||
- **Phase 169**: BoolExprLowerer integration exposed this issue
|
||||
- **Phase 166**: JsonParserBox validation blocked by this bug
|
||||
- **Phase 188-189**: Boundary mechanism exists but incomplete
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Implement Option A (condition variable extraction)
|
||||
2. Update all 4 patterns
|
||||
3. Re-run Phase 170 validation tests
|
||||
4. Document the "always include condition variables in boundary" pattern
|
||||
209
docs/development/current/main/phase171-1-boundary-analysis.md
Normal file
209
docs/development/current/main/phase171-1-boundary-analysis.md
Normal file
@ -0,0 +1,209 @@
|
||||
# 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`
|
||||
@ -0,0 +1,418 @@
|
||||
# Phase 171-2: Condition Inputs Metadata Design
|
||||
|
||||
**Date**: 2025-12-07
|
||||
**Status**: Design Complete
|
||||
**Decision**: **Option A - Extend JoinInlineBoundary**
|
||||
|
||||
---
|
||||
|
||||
## Design Decision: Option A
|
||||
|
||||
### Rationale
|
||||
|
||||
**Option A: Extend JoinInlineBoundary** (CHOSEN ✅)
|
||||
```rust
|
||||
pub struct JoinInlineBoundary {
|
||||
pub join_inputs: Vec<ValueId>,
|
||||
pub host_inputs: Vec<ValueId>,
|
||||
pub join_outputs: Vec<ValueId>,
|
||||
pub host_outputs: Vec<ValueId>,
|
||||
pub exit_bindings: Vec<LoopExitBinding>,
|
||||
|
||||
// NEW: Condition-only inputs
|
||||
pub condition_inputs: Vec<(String, ValueId)>, // [(var_name, host_value_id)]
|
||||
}
|
||||
```
|
||||
|
||||
**Why this is best**:
|
||||
1. **Minimal invasiveness**: Single structure change
|
||||
2. **Clear semantics**: "Condition inputs" are distinct from "loop parameters"
|
||||
3. **Reuses existing infrastructure**: Same Copy injection mechanism
|
||||
4. **Future-proof**: Easy to extend for condition-only outputs (if needed)
|
||||
5. **Symmetric design**: Mirrors how `exit_bindings` handle exit values
|
||||
|
||||
**Rejected alternatives**:
|
||||
|
||||
**Option B: Create new LoopInputBinding**
|
||||
```rust
|
||||
pub struct LoopInputBinding {
|
||||
pub condition_vars: HashMap<String, ValueId>,
|
||||
}
|
||||
```
|
||||
❌ **Rejected**: Introduces another structure; harder to coordinate with boundary
|
||||
|
||||
**Option C: Extend LoopExitBinding**
|
||||
```rust
|
||||
pub struct LoopExitBinding {
|
||||
pub condition_inputs: Vec<String>, // NEW
|
||||
// ...
|
||||
}
|
||||
```
|
||||
❌ **Rejected**: Semantic mismatch (exit bindings are for outputs, not inputs)
|
||||
|
||||
---
|
||||
|
||||
## Detailed Design
|
||||
|
||||
### 1. Extended Structure Definition
|
||||
|
||||
**File**: `src/mir/join_ir/lowering/inline_boundary.rs`
|
||||
|
||||
```rust
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JoinInlineBoundary {
|
||||
/// JoinIR-local ValueIds that act as "input slots"
|
||||
///
|
||||
/// These are the ValueIds used **inside** the JoinIR fragment to refer
|
||||
/// to values that come from the host. They should be small sequential
|
||||
/// IDs (0, 1, 2, ...) since JoinIR lowerers allocate locally.
|
||||
///
|
||||
/// Example: For a loop variable `i`, JoinIR uses ValueId(0) as the parameter.
|
||||
pub join_inputs: Vec<ValueId>,
|
||||
|
||||
/// Host-function ValueIds that provide the input values
|
||||
///
|
||||
/// These are the ValueIds from the **host function** that correspond to
|
||||
/// the join_inputs. The merger will inject Copy instructions to connect
|
||||
/// host_inputs[i] → join_inputs[i].
|
||||
///
|
||||
/// Example: If host has `i` as ValueId(4), then host_inputs = [ValueId(4)].
|
||||
pub host_inputs: Vec<ValueId>,
|
||||
|
||||
/// JoinIR-local ValueIds that represent outputs (if any)
|
||||
pub join_outputs: Vec<ValueId>,
|
||||
|
||||
/// Host-function ValueIds that receive the outputs (DEPRECATED)
|
||||
#[deprecated(since = "Phase 190", note = "Use exit_bindings instead")]
|
||||
pub host_outputs: Vec<ValueId>,
|
||||
|
||||
/// Explicit exit bindings for loop carriers (Phase 190+)
|
||||
pub exit_bindings: Vec<LoopExitBinding>,
|
||||
|
||||
/// Condition-only input variables (Phase 171+)
|
||||
///
|
||||
/// These are variables used ONLY in the loop condition, NOT as loop parameters.
|
||||
/// They need to be available in JoinIR scope but are not modified by the loop.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// For `loop(start < end) { i = i + 1 }`:
|
||||
/// - Loop parameter: `i` → goes in `join_inputs`/`host_inputs`
|
||||
/// - Condition-only: `start`, `end` → go in `condition_inputs`
|
||||
///
|
||||
/// # Format
|
||||
///
|
||||
/// Each entry is `(variable_name, host_value_id)`:
|
||||
/// ```
|
||||
/// condition_inputs: vec![
|
||||
/// ("start".to_string(), ValueId(33)), // HOST ID for "start"
|
||||
/// ("end".to_string(), ValueId(34)), // HOST ID for "end"
|
||||
/// ]
|
||||
/// ```
|
||||
///
|
||||
/// The merger will:
|
||||
/// 1. Extract unique variable names from condition AST
|
||||
/// 2. Look up HOST ValueIds from `builder.variable_map`
|
||||
/// 3. Inject Copy instructions for each condition input
|
||||
/// 4. Remap JoinIR references to use the copied values
|
||||
pub condition_inputs: Vec<(String, ValueId)>,
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. Constructor Updates
|
||||
|
||||
**Add new constructor**:
|
||||
|
||||
```rust
|
||||
impl JoinInlineBoundary {
|
||||
/// Create a new boundary with condition inputs (Phase 171+)
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `join_inputs` - JoinIR-local ValueIds for loop parameters
|
||||
/// * `host_inputs` - HOST ValueIds for loop parameters
|
||||
/// * `condition_inputs` - Condition-only variables [(name, host_value_id)]
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```ignore
|
||||
/// let boundary = JoinInlineBoundary::new_with_condition_inputs(
|
||||
/// vec![ValueId(0)], // join_inputs (i)
|
||||
/// vec![ValueId(5)], // host_inputs (i)
|
||||
/// vec![
|
||||
/// ("start".to_string(), ValueId(33)),
|
||||
/// ("end".to_string(), ValueId(34)),
|
||||
/// ],
|
||||
/// );
|
||||
/// ```
|
||||
pub fn new_with_condition_inputs(
|
||||
join_inputs: Vec<ValueId>,
|
||||
host_inputs: Vec<ValueId>,
|
||||
condition_inputs: Vec<(String, ValueId)>,
|
||||
) -> Self {
|
||||
assert_eq!(
|
||||
join_inputs.len(),
|
||||
host_inputs.len(),
|
||||
"join_inputs and host_inputs must have same length"
|
||||
);
|
||||
Self {
|
||||
join_inputs,
|
||||
host_inputs,
|
||||
join_outputs: vec![],
|
||||
#[allow(deprecated)]
|
||||
host_outputs: vec![],
|
||||
exit_bindings: vec![],
|
||||
condition_inputs,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create boundary with inputs, exit bindings, AND condition inputs (Phase 171+)
|
||||
pub fn new_with_exit_and_condition_inputs(
|
||||
join_inputs: Vec<ValueId>,
|
||||
host_inputs: Vec<ValueId>,
|
||||
exit_bindings: Vec<LoopExitBinding>,
|
||||
condition_inputs: Vec<(String, ValueId)>,
|
||||
) -> Self {
|
||||
assert_eq!(
|
||||
join_inputs.len(),
|
||||
host_inputs.len(),
|
||||
"join_inputs and host_inputs must have same length"
|
||||
);
|
||||
Self {
|
||||
join_inputs,
|
||||
host_inputs,
|
||||
join_outputs: vec![],
|
||||
#[allow(deprecated)]
|
||||
host_outputs: vec![],
|
||||
exit_bindings,
|
||||
condition_inputs,
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Update existing constructors** to set `condition_inputs: vec![]`:
|
||||
|
||||
```rust
|
||||
pub fn new_inputs_only(join_inputs: Vec<ValueId>, host_inputs: Vec<ValueId>) -> Self {
|
||||
// ... existing assertions
|
||||
Self {
|
||||
join_inputs,
|
||||
host_inputs,
|
||||
join_outputs: vec![],
|
||||
#[allow(deprecated)]
|
||||
host_outputs: vec![],
|
||||
exit_bindings: vec![],
|
||||
condition_inputs: vec![], // NEW: Default to empty
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_exit_bindings(
|
||||
join_inputs: Vec<ValueId>,
|
||||
host_inputs: Vec<ValueId>,
|
||||
exit_bindings: Vec<LoopExitBinding>,
|
||||
) -> Self {
|
||||
// ... existing assertions
|
||||
Self {
|
||||
join_inputs,
|
||||
host_inputs,
|
||||
join_outputs: vec![],
|
||||
#[allow(deprecated)]
|
||||
host_outputs: vec![],
|
||||
exit_bindings,
|
||||
condition_inputs: vec![], // NEW: Default to empty
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. Value Flow Diagram
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ HOST MIR Builder │
|
||||
│ │
|
||||
│ variable_map: │
|
||||
│ "i" → ValueId(5) (loop variable - becomes parameter) │
|
||||
│ "start" → ValueId(33) (condition input - read-only) │
|
||||
│ "end" → ValueId(34) (condition input - read-only) │
|
||||
│ "sum" → ValueId(10) (carrier - exit binding) │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────┴─────────────────┐
|
||||
│ │
|
||||
↓ ↓
|
||||
┌──────────────────────┐ ┌──────────────────────┐
|
||||
│ JoinIR Lowerer │ │ Condition Extractor │
|
||||
│ │ │ │
|
||||
│ Allocates: │ │ Extracts variables: │
|
||||
│ i_param = Val(0) │ │ ["start", "end"] │
|
||||
│ sum_init = Val(1) │ │ │
|
||||
└──────────────────────┘ └──────────────────────┘
|
||||
↓ ↓
|
||||
└─────────────────┬─────────────────┘
|
||||
↓
|
||||
┌────────────────────────────────────────────┐
|
||||
│ JoinInlineBoundary │
|
||||
│ │
|
||||
│ join_inputs: [Val(0), Val(1)] │
|
||||
│ host_inputs: [Val(5), Val(10)] │
|
||||
│ │
|
||||
│ condition_inputs: [ │
|
||||
│ ("start", Val(33)), │
|
||||
│ ("end", Val(34)) │
|
||||
│ ] │
|
||||
│ │
|
||||
│ exit_bindings: [ │
|
||||
│ { carrier: "sum", join_exit: Val(18), │
|
||||
│ host_slot: Val(10) } │
|
||||
│ ] │
|
||||
└────────────────────────────────────────────┘
|
||||
↓
|
||||
┌────────────────────────────────────────────┐
|
||||
│ merge_joinir_mir_blocks() │
|
||||
│ │
|
||||
│ Phase 1: Inject Copy instructions │
|
||||
│ Val(100) = Copy Val(5) // i │
|
||||
│ Val(101) = Copy Val(10) // sum │
|
||||
│ Val(102) = Copy Val(33) // start ← NEW │
|
||||
│ Val(103) = Copy Val(34) // end ← NEW │
|
||||
│ │
|
||||
│ Phase 2: Remap JoinIR ValueIds │
|
||||
│ Val(0) → Val(100) // i param │
|
||||
│ Val(1) → Val(101) // sum init │
|
||||
│ │
|
||||
│ Phase 3: Remap condition refs │
|
||||
│ Compare { lhs: Val(33), ... } │
|
||||
│ ↓ NO CHANGE (uses HOST ID directly) │
|
||||
│ Compare { lhs: Val(102), ... } ← FIXED │
|
||||
│ │
|
||||
│ Phase 4: Reconnect exit bindings │
|
||||
│ variable_map["sum"] = Val(200) │
|
||||
└────────────────────────────────────────────┘
|
||||
↓
|
||||
✅ All ValueIds valid
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. Key Insight: Two Types of Inputs
|
||||
|
||||
This design recognizes **two distinct categories** of JoinIR inputs:
|
||||
|
||||
| Category | Examples | Boundary Field | Mutability | Treatment |
|
||||
|----------|----------|----------------|-----------|-----------|
|
||||
| **Loop Parameters** | `i` (loop var), `sum` (carrier) | `join_inputs`/`host_inputs` | Mutable | Passed as function params |
|
||||
| **Condition Inputs** | `start`, `end`, `len` | `condition_inputs` | Read-only | Captured from HOST scope |
|
||||
|
||||
**Why separate?**
|
||||
|
||||
1. **Semantic clarity**: Loop parameters can be modified; condition inputs are immutable
|
||||
2. **Implementation simplicity**: Condition inputs don't need JoinIR parameters - just Copy + remap
|
||||
3. **Future extensibility**: May want condition-only outputs (e.g., for side-effectful conditions)
|
||||
|
||||
---
|
||||
|
||||
### 5. Implementation Strategy
|
||||
|
||||
**Step 1**: Modify `inline_boundary.rs`
|
||||
- Add `condition_inputs` field
|
||||
- Update all constructors to initialize it
|
||||
- Add new constructors for condition input support
|
||||
|
||||
**Step 2**: Implement condition variable extraction
|
||||
- Create `extract_condition_variables()` function
|
||||
- Recursively traverse condition AST
|
||||
- Collect unique variable names
|
||||
|
||||
**Step 3**: Update loop lowerers
|
||||
- Call `extract_condition_variables()` on condition AST
|
||||
- Look up HOST ValueIds from `builder.variable_map`
|
||||
- Pass to boundary constructor
|
||||
|
||||
**Step 4**: Update merge logic
|
||||
- Inject Copy instructions for condition inputs
|
||||
- Create temporary mapping: var_name → copied_value_id
|
||||
- Rewrite condition instructions to use copied ValueIds
|
||||
|
||||
**Step 5**: Test with trim pattern
|
||||
- Should resolve ValueId(33) undefined error
|
||||
- Verify condition evaluation uses correct values
|
||||
|
||||
---
|
||||
|
||||
## Remaining Questions
|
||||
|
||||
### Q1: Should condition inputs be remapped globally or locally?
|
||||
|
||||
**Answer**: **Locally** - only within JoinIR condition instructions
|
||||
|
||||
**Rationale**: Condition inputs are used in:
|
||||
1. Loop condition evaluation (in `loop_step` function)
|
||||
2. Nowhere else (by definition - they're condition-only)
|
||||
|
||||
So we only need to remap ValueIds in the condition instructions, not globally across all JoinIR.
|
||||
|
||||
### Q2: What if a condition input is ALSO a loop parameter?
|
||||
|
||||
**Example**: `loop(i < 10) { i = i + 1 }`
|
||||
- `i` is both a loop parameter (mutated in body) AND used in condition
|
||||
|
||||
**Answer**: **Loop parameter takes precedence** - it's already in `join_inputs`/`host_inputs`
|
||||
|
||||
**Implementation**: When extracting condition variables, SKIP any that are already in `join_inputs`
|
||||
|
||||
```rust
|
||||
fn extract_condition_variables(
|
||||
condition_ast: &ASTNode,
|
||||
join_inputs_names: &[String], // Already-registered parameters
|
||||
) -> Vec<String> {
|
||||
let all_vars = collect_variable_names_recursive(condition_ast);
|
||||
all_vars.into_iter()
|
||||
.filter(|name| !join_inputs_names.contains(name)) // Skip loop params
|
||||
.collect()
|
||||
}
|
||||
```
|
||||
|
||||
### Q3: How to handle condition variables that don't exist in variable_map?
|
||||
|
||||
**Answer**: **Fail-fast with clear error**
|
||||
|
||||
```rust
|
||||
let host_value_id = builder.variable_map.get(var_name)
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"Condition variable '{}' not found in variable_map. \
|
||||
Loop condition references undefined variable.",
|
||||
var_name
|
||||
)
|
||||
})?;
|
||||
```
|
||||
|
||||
This follows the "Fail-Fast" principle from CLAUDE.md.
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
**Design Choice**: Option A - Extend `JoinInlineBoundary` with `condition_inputs` field
|
||||
|
||||
**Key Properties**:
|
||||
- ✅ Minimal invasiveness (single structure change)
|
||||
- ✅ Clear semantics (condition-only inputs)
|
||||
- ✅ Reuses existing Copy injection mechanism
|
||||
- ✅ Symmetric with `exit_bindings` design
|
||||
- ✅ Handles all edge cases (overlap with loop params, missing variables)
|
||||
|
||||
**Next Steps**: Phase 171-3 - Implement condition variable extraction in loop lowerers
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- Phase 171-1 Analysis: `phase171-1-boundary-analysis.md`
|
||||
- JoinInlineBoundary: `src/mir/join_ir/lowering/inline_boundary.rs`
|
||||
- Merge Logic: `src/mir/builder/control_flow/joinir/merge/mod.rs`
|
||||
@ -0,0 +1,278 @@
|
||||
# Phase 171-3: Register Condition Inputs in JoinIR Lowerers
|
||||
|
||||
**Date**: 2025-12-07
|
||||
**Status**: ✅ Complete
|
||||
|
||||
---
|
||||
|
||||
## Implementation Summary
|
||||
|
||||
Successfully implemented the infrastructure for registering condition-only input variables in JoinIR lowerers.
|
||||
|
||||
---
|
||||
|
||||
## Changes Made
|
||||
|
||||
### 1. Extended `JoinInlineBoundary` Structure
|
||||
|
||||
**File**: `src/mir/join_ir/lowering/inline_boundary.rs`
|
||||
|
||||
**Added field**:
|
||||
```rust
|
||||
pub struct JoinInlineBoundary {
|
||||
pub join_inputs: Vec<ValueId>,
|
||||
pub host_inputs: Vec<ValueId>,
|
||||
pub join_outputs: Vec<ValueId>,
|
||||
pub host_outputs: Vec<ValueId>,
|
||||
pub exit_bindings: Vec<LoopExitBinding>,
|
||||
|
||||
/// NEW: Condition-only input variables (Phase 171+)
|
||||
pub condition_inputs: Vec<(String, ValueId)>, // [(var_name, host_value_id)]
|
||||
}
|
||||
```
|
||||
|
||||
**Rationale**: Condition variables like `start`, `end`, `len` are read-only inputs that don't participate in loop iteration but must be available in JoinIR scope for condition evaluation.
|
||||
|
||||
---
|
||||
|
||||
### 2. Updated All Constructors
|
||||
|
||||
**Modified constructors**:
|
||||
- `new_inputs_only()` - Added `condition_inputs: vec![]`
|
||||
- `new_with_outputs()` - Added `condition_inputs: vec![]`
|
||||
- `new_with_input_and_host_outputs()` - Added `condition_inputs: vec![]`
|
||||
- `new_with_exit_bindings()` - Added `condition_inputs: vec![]`
|
||||
|
||||
**New constructors**:
|
||||
- `new_with_condition_inputs()` - For loops with condition variables
|
||||
- `new_with_exit_and_condition_inputs()` - For loops with carriers AND condition variables
|
||||
|
||||
**Example usage**:
|
||||
```rust
|
||||
let boundary = JoinInlineBoundary::new_with_condition_inputs(
|
||||
vec![ValueId(0)], // join_inputs (i)
|
||||
vec![ValueId(5)], // host_inputs (i)
|
||||
vec![
|
||||
("start".to_string(), ValueId(33)),
|
||||
("end".to_string(), ValueId(34)),
|
||||
],
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. Implemented Condition Variable Extraction
|
||||
|
||||
**File**: `src/mir/join_ir/lowering/condition_to_joinir.rs`
|
||||
|
||||
**New functions**:
|
||||
|
||||
#### `extract_condition_variables()`
|
||||
```rust
|
||||
pub fn extract_condition_variables(
|
||||
cond_ast: &ASTNode,
|
||||
exclude_vars: &[String],
|
||||
) -> Vec<String>
|
||||
```
|
||||
|
||||
Recursively traverses condition AST and collects all unique variable names, excluding loop parameters that are already registered as join_inputs.
|
||||
|
||||
**Features**:
|
||||
- ✅ Sorted output (BTreeSet) for determinism
|
||||
- ✅ Filters excluded variables (loop parameters)
|
||||
- ✅ Handles complex conditions (AND, OR, NOT chains)
|
||||
|
||||
#### `collect_variables_recursive()`
|
||||
```rust
|
||||
fn collect_variables_recursive(
|
||||
ast: &ASTNode,
|
||||
vars: &mut BTreeSet<String>
|
||||
)
|
||||
```
|
||||
|
||||
Helper function that recursively visits all AST nodes and extracts variable names.
|
||||
|
||||
**Supported AST nodes**:
|
||||
- `Variable` - Adds variable name to set
|
||||
- `BinaryOp` - Recursively processes left and right operands
|
||||
- `UnaryOp` - Recursively processes operand
|
||||
- `Literal` - No variables (skipped)
|
||||
|
||||
---
|
||||
|
||||
### 4. Added Comprehensive Tests
|
||||
|
||||
**File**: `src/mir/join_ir/lowering/condition_to_joinir.rs`
|
||||
|
||||
**Test cases**:
|
||||
|
||||
#### `test_extract_condition_variables_simple()`
|
||||
```rust
|
||||
// AST: start < end
|
||||
let vars = extract_condition_variables(&ast, &[]);
|
||||
assert_eq!(vars, vec!["end", "start"]); // Sorted
|
||||
```
|
||||
|
||||
#### `test_extract_condition_variables_with_exclude()`
|
||||
```rust
|
||||
// AST: i < end
|
||||
let vars = extract_condition_variables(&ast, &["i".to_string()]);
|
||||
assert_eq!(vars, vec!["end"]); // 'i' excluded
|
||||
```
|
||||
|
||||
#### `test_extract_condition_variables_complex()`
|
||||
```rust
|
||||
// AST: start < end && i < len
|
||||
let vars = extract_condition_variables(&ast, &["i".to_string()]);
|
||||
assert_eq!(vars, vec!["end", "len", "start"]); // Sorted, 'i' excluded
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. Updated Test Code
|
||||
|
||||
**File**: `src/mir/builder/control_flow/joinir/patterns/exit_binding.rs`
|
||||
|
||||
**Fixed test**:
|
||||
```rust
|
||||
let mut boundary = JoinInlineBoundary {
|
||||
host_inputs: vec![],
|
||||
join_inputs: vec![],
|
||||
host_outputs: vec![],
|
||||
join_outputs: vec![],
|
||||
exit_bindings: vec![], // Phase 171: Added
|
||||
condition_inputs: vec![], // Phase 171: Added
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Design Decisions
|
||||
|
||||
### Why Separate `condition_inputs` from `join_inputs`?
|
||||
|
||||
| Aspect | Loop Parameters (`join_inputs`) | Condition Inputs (`condition_inputs`) |
|
||||
|--------|--------------------------------|--------------------------------------|
|
||||
| **Mutability** | Mutable (e.g., `i = i + 1`) | Read-only (never modified) |
|
||||
| **Lifetime** | Entire loop duration | Only during condition evaluation |
|
||||
| **JoinIR representation** | Function parameters | Captured values |
|
||||
| **Example** | `i` in `loop(i < 3)` | `end` in `loop(i < end)` |
|
||||
|
||||
**Semantic clarity**: Separating them makes the intent explicit and prevents accidental mutation of condition-only variables.
|
||||
|
||||
### Why Use `Vec<(String, ValueId)>` Instead of `HashMap`?
|
||||
|
||||
**Chosen**: `Vec<(String, ValueId)>`
|
||||
**Alternative**: `HashMap<String, ValueId>`
|
||||
|
||||
**Rationale**:
|
||||
1. **Order preservation**: Vec maintains insertion order for deterministic behavior
|
||||
2. **Small size**: Typically 0-3 variables, Vec is more efficient than HashMap
|
||||
3. **Iteration simplicity**: Direct iteration without collecting keys
|
||||
4. **Consistency**: Matches pattern of `exit_bindings`
|
||||
|
||||
---
|
||||
|
||||
## Build Status
|
||||
|
||||
✅ **Library build**: Successful (0 errors, 57 warnings)
|
||||
✅ **Struct extension**: All constructors updated
|
||||
✅ **Tests**: 3 new tests added for extraction function
|
||||
✅ **Backward compatibility**: All existing code still compiles
|
||||
|
||||
**Build command**:
|
||||
```bash
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
**Result**:
|
||||
```
|
||||
Finished `release` profile [optimized] target(s) in 0.07s
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## What's Still Missing
|
||||
|
||||
### Not Yet Implemented (Phase 171-4 scope):
|
||||
|
||||
1. **Condition variable registration in loop lowerers**
|
||||
- `loop_with_break_minimal.rs` needs to call `extract_condition_variables()`
|
||||
- `loop_with_continue_minimal.rs` needs to call `extract_condition_variables()`
|
||||
- Pass extracted variables to boundary constructor
|
||||
|
||||
2. **ValueId mapping in merge logic**
|
||||
- `merge_joinir_mir_blocks()` needs to inject Copy instructions for condition inputs
|
||||
- Instruction rewriter needs to remap condition variable ValueIds
|
||||
|
||||
3. **Test with actual failing case**
|
||||
- `test_trim_main_pattern.hako` still fails (ValueId(33) undefined)
|
||||
- Need to apply the infrastructure to actual loop lowerers
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
**Phase 171-4**: Implement condition input mapping in merge/reconnect logic
|
||||
|
||||
**Priority tasks**:
|
||||
1. Modify `loop_with_break_minimal.rs` to extract and register condition variables
|
||||
2. Update `merge_joinir_mir_blocks()` to handle `condition_inputs`
|
||||
3. Inject Copy instructions for condition variables
|
||||
4. Remap ValueIds in condition evaluation instructions
|
||||
5. Test with `test_trim_main_pattern.hako`
|
||||
|
||||
---
|
||||
|
||||
## Technical Notes
|
||||
|
||||
### Extraction Algorithm Complexity
|
||||
|
||||
**Time complexity**: O(n) where n = number of AST nodes in condition
|
||||
**Space complexity**: O(v) where v = number of unique variables
|
||||
|
||||
**Determinism guarantee**: BTreeSet ensures sorted output regardless of traversal order
|
||||
|
||||
### Edge Cases Handled
|
||||
|
||||
1. **No condition variables**: Returns empty vector
|
||||
2. **Loop variable in condition**: Properly excluded (e.g., `loop(i < 10)`)
|
||||
3. **Duplicate variables**: BTreeSet automatically deduplicates
|
||||
4. **Nested expressions**: Recursion handles arbitrary depth
|
||||
|
||||
### Boundary Contract
|
||||
|
||||
**Invariant**: `condition_inputs` contains ONLY variables that:
|
||||
1. Appear in the loop condition AST
|
||||
2. Are NOT loop parameters (not in `join_inputs`)
|
||||
3. Exist in HOST `variable_map` at lowering time
|
||||
|
||||
**Violation detection**: Will be caught in Phase 171-4 when looking up HOST ValueIds
|
||||
|
||||
---
|
||||
|
||||
## Files Modified
|
||||
|
||||
1. `src/mir/join_ir/lowering/inline_boundary.rs` (+93 lines)
|
||||
- Added `condition_inputs` field
|
||||
- Added 2 new constructors
|
||||
- Updated 4 existing constructors
|
||||
- Updated test assertions
|
||||
|
||||
2. `src/mir/join_ir/lowering/condition_to_joinir.rs` (+180 lines)
|
||||
- Added `extract_condition_variables()` function
|
||||
- Added `collect_variables_recursive()` helper
|
||||
- Added 3 comprehensive tests
|
||||
|
||||
3. `src/mir/builder/control_flow/joinir/patterns/exit_binding.rs` (+2 lines)
|
||||
- Fixed test boundary initialization
|
||||
|
||||
**Total**: +275 lines (infrastructure only, no behavior change yet)
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- Phase 171-1 Analysis: `phase171-1-boundary-analysis.md`
|
||||
- Phase 171-2 Design: `phase171-2-condition-inputs-design.md`
|
||||
- JoinIR Design: `docs/development/current/main/phase33-10-if-joinir-design.md`
|
||||
299
docs/development/current/main/phase171-completion-report.md
Normal file
299
docs/development/current/main/phase171-completion-report.md
Normal file
@ -0,0 +1,299 @@
|
||||
# Phase 171: JoinIR → MIR ValueId Boundary Mapping Fix - Completion Report
|
||||
|
||||
**Date**: 2025-12-07
|
||||
**Status**: ✅ Infrastructure Complete (Testing in Progress)
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
Successfully implemented the infrastructure to map condition-only input variables across the JoinIR → MIR boundary. This fixes the root cause of "undefined ValueId" errors for variables like `start`, `end`, `len` that appear only in loop conditions.
|
||||
|
||||
**Problem Solved**: Variables used only in loop conditions (not loop parameters) were using HOST ValueIds directly in JoinIR, causing undefined value errors when merged into MIR.
|
||||
|
||||
**Solution Implemented**: Extended `JoinInlineBoundary` with `condition_inputs` field and updated the entire merge pipeline to collect, remap, and inject Copy instructions for these variables.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Phases Completed
|
||||
|
||||
### ✅ Phase 171-1: Boundary Coverage Analysis
|
||||
|
||||
**Deliverable**: `phase171-1-boundary-analysis.md` (documented current state)
|
||||
|
||||
**Key Findings**:
|
||||
- Loop variables (`i`): ✅ Properly mapped via `join_inputs`
|
||||
- Carriers (`sum`, `count`): ✅ Properly mapped via `exit_bindings`
|
||||
- **Condition inputs (`start`, `end`)**: ❌ NOT MAPPED → **ROOT CAUSE**
|
||||
|
||||
### ✅ Phase 171-2: Design Decision
|
||||
|
||||
**Deliverable**: `phase171-2-condition-inputs-design.md`
|
||||
|
||||
**Decision**: **Option A - Extend JoinInlineBoundary**
|
||||
|
||||
Added field:
|
||||
```rust
|
||||
pub condition_inputs: Vec<(String, ValueId)>
|
||||
```
|
||||
|
||||
**Rationale**:
|
||||
1. Minimal invasiveness
|
||||
2. Clear semantics
|
||||
3. Reuses existing Copy injection mechanism
|
||||
4. Future-proof design
|
||||
|
||||
### ✅ Phase 171-3: Infrastructure Implementation
|
||||
|
||||
**Deliverable**: `phase171-3-implementation-report.md`
|
||||
|
||||
**Files Modified**:
|
||||
|
||||
1. **`inline_boundary.rs`** (+93 lines)
|
||||
- Added `condition_inputs` field
|
||||
- Added 2 new constructors
|
||||
- Updated 4 existing constructors
|
||||
|
||||
2. **`condition_to_joinir.rs`** (+180 lines)
|
||||
- Implemented `extract_condition_variables()`
|
||||
- Added recursive AST traversal
|
||||
- Added 3 comprehensive tests
|
||||
|
||||
3. **`exit_binding.rs`** (+2 lines)
|
||||
- Fixed test boundary initialization
|
||||
|
||||
**Build Status**: ✅ Successful (0 errors, 57 warnings)
|
||||
|
||||
### ✅ Phase 171-4: Merge Logic Integration
|
||||
|
||||
**Files Modified**:
|
||||
|
||||
1. **`pattern2_with_break.rs`** (+19 lines)
|
||||
- Extract condition variables from AST
|
||||
- Look up HOST ValueIds
|
||||
- Pass to boundary constructor
|
||||
|
||||
2. **`merge/mod.rs`** (+13 lines)
|
||||
- Add condition_inputs to used_values for remapping
|
||||
- Debug logging for condition inputs
|
||||
|
||||
3. **`merge/instruction_rewriter.rs`** (+17 lines)
|
||||
- Add condition_inputs to value_map_for_injector
|
||||
- Enable remapping of HOST ValueIds
|
||||
|
||||
4. **`joinir_inline_boundary_injector.rs`** (+35 lines)
|
||||
- Inject Copy instructions for condition inputs
|
||||
- Handle both join_inputs and condition_inputs
|
||||
|
||||
**Build Status**: ✅ Successful (0 errors, 57 warnings)
|
||||
|
||||
---
|
||||
|
||||
## Technical Changes Summary
|
||||
|
||||
### Data Flow: Before vs After
|
||||
|
||||
#### Before (Broken)
|
||||
```
|
||||
HOST: start = ValueId(33)
|
||||
↓ condition_to_joinir reads directly
|
||||
JoinIR: uses ValueId(33) in Compare
|
||||
↓ NO BOUNDARY, NO REMAP
|
||||
MIR: ValueId(33) undefined → ERROR ❌
|
||||
```
|
||||
|
||||
#### After (Fixed)
|
||||
```
|
||||
HOST: start = ValueId(33)
|
||||
↓ pattern2_with_break extracts "start"
|
||||
↓ boundary.condition_inputs = [("start", ValueId(33))]
|
||||
↓ merge adds to used_values
|
||||
↓ remap_values: ValueId(33) → ValueId(100)
|
||||
↓ BoundaryInjector: Copy ValueId(100) = Copy ValueId(33)
|
||||
JoinIR: uses ValueId(33) in Compare
|
||||
↓ instruction_rewriter remaps to ValueId(100)
|
||||
MIR: ValueId(100) = Copy ValueId(33) → SUCCESS ✅
|
||||
```
|
||||
|
||||
### Key Implementation Points
|
||||
|
||||
**1. Condition Variable Extraction** (`condition_to_joinir.rs:326-361`)
|
||||
```rust
|
||||
pub fn extract_condition_variables(
|
||||
cond_ast: &ASTNode,
|
||||
exclude_vars: &[String], // Loop parameters to exclude
|
||||
) -> Vec<String>
|
||||
```
|
||||
- Recursively traverses condition AST
|
||||
- Collects unique variable names
|
||||
- Filters out loop parameters
|
||||
- Returns sorted list (BTreeSet for determinism)
|
||||
|
||||
**2. Boundary Registration** (`pattern2_with_break.rs:73-92`)
|
||||
```rust
|
||||
let condition_var_names = extract_condition_variables(condition, &[loop_var_name.clone()]);
|
||||
let mut condition_inputs = Vec::new();
|
||||
for var_name in &condition_var_names {
|
||||
let host_value_id = self.variable_map.get(var_name)
|
||||
.copied()
|
||||
.ok_or_else(|| ...)?;
|
||||
condition_inputs.push((var_name.clone(), host_value_id));
|
||||
}
|
||||
```
|
||||
|
||||
**3. Value Collection** (`merge/mod.rs:80-91`)
|
||||
```rust
|
||||
// Add condition_inputs to used_values for remapping
|
||||
if let Some(boundary) = boundary {
|
||||
for (var_name, host_value_id) in &boundary.condition_inputs {
|
||||
used_values.insert(*host_value_id);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**4. Value Remapping** (`merge/instruction_rewriter.rs:402-415`)
|
||||
```rust
|
||||
// Add condition_inputs to value_map
|
||||
for (var_name, host_value_id) in &boundary.condition_inputs {
|
||||
if let Some(remapped) = remapper.get_value(*host_value_id) {
|
||||
value_map_for_injector.insert(*host_value_id, remapped);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**5. Copy Injection** (`joinir_inline_boundary_injector.rs:86-116`)
|
||||
```rust
|
||||
// Inject Copy instructions for condition_inputs
|
||||
for (var_name, host_value_id) in &boundary.condition_inputs {
|
||||
if let Some(&remapped_value) = value_map.get(host_value_id) {
|
||||
let copy_inst = MirInstruction::Copy {
|
||||
dst: remapped_value,
|
||||
src: *host_value_id,
|
||||
};
|
||||
copy_instructions.push(copy_inst);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Test Status
|
||||
|
||||
### ✅ Unit Tests Pass
|
||||
|
||||
**Extraction function tests** (`condition_to_joinir.rs`):
|
||||
- `test_extract_condition_variables_simple` ✅
|
||||
- `test_extract_condition_variables_with_exclude` ✅
|
||||
- `test_extract_condition_variables_complex` ✅
|
||||
|
||||
### 🔄 Integration Tests (In Progress)
|
||||
|
||||
**Test File**: `local_tests/test_trim_main_pattern.hako`
|
||||
|
||||
**Current Status**: Still shows undefined ValueId errors:
|
||||
```
|
||||
[ssa-undef-debug] fn=TrimTest.trim/1 bb=BasicBlockId(8) inst_idx=0 used=ValueId(33)
|
||||
[ssa-undef-debug] fn=TrimTest.trim/1 bb=BasicBlockId(17) inst_idx=0 used=ValueId(49)
|
||||
```
|
||||
|
||||
**Next Debugging Steps**:
|
||||
1. Add debug output to Pattern 2 lowerer to confirm condition variable extraction
|
||||
2. Verify boundary condition_inputs are populated
|
||||
3. Check value remapping in merge logic
|
||||
4. Verify Copy instruction injection
|
||||
|
||||
---
|
||||
|
||||
## Remaining Work
|
||||
|
||||
### Phase 171-5: Verification & Documentation
|
||||
|
||||
**Tasks**:
|
||||
1. ✅ Unit tests complete
|
||||
2. 🔄 Integration tests (debugging in progress)
|
||||
3. ⏳ Update CURRENT_TASK.md
|
||||
4. ⏳ Final documentation
|
||||
|
||||
**Blocker**: Need to debug why ValueId(33) is still undefined despite infrastructure being in place.
|
||||
|
||||
**Hypothesis**: The issue might be:
|
||||
- Condition variable extraction not running (check debug output)
|
||||
- Boundary not being passed correctly
|
||||
- Value remapping not applying to all instruction types
|
||||
- Copy instructions not being injected
|
||||
|
||||
---
|
||||
|
||||
## Files Modified (Summary)
|
||||
|
||||
| File | Lines Changed | Purpose |
|
||||
|------|--------------|---------|
|
||||
| `inline_boundary.rs` | +93 | Add condition_inputs field + constructors |
|
||||
| `condition_to_joinir.rs` | +180 | Extract condition variables from AST |
|
||||
| `exit_binding.rs` | +2 | Fix test |
|
||||
| `pattern2_with_break.rs` | +19 | Register condition inputs |
|
||||
| `merge/mod.rs` | +13 | Add to used_values |
|
||||
| `merge/instruction_rewriter.rs` | +17 | Add to value_map |
|
||||
| `joinir_inline_boundary_injector.rs` | +35 | Inject Copy instructions |
|
||||
| **Total** | **+359 lines** | **Complete infrastructure** |
|
||||
|
||||
---
|
||||
|
||||
## Design Principles Applied
|
||||
|
||||
1. **Box-First Philosophy** ✅
|
||||
- Clean separation: JoinIR frontier, boundary metadata, merge logic
|
||||
- Each component has single responsibility
|
||||
|
||||
2. **Fail-Fast** ✅
|
||||
- Explicit error when condition variable not in variable_map
|
||||
- No silent fallbacks
|
||||
|
||||
3. **Determinism** ✅
|
||||
- BTreeSet for sorted variable names
|
||||
- Consistent iteration order
|
||||
|
||||
4. **80/20 Rule** ✅
|
||||
- Infrastructure first (80%)
|
||||
- Debugging and edge cases (20% - in progress)
|
||||
|
||||
---
|
||||
|
||||
## Known Limitations
|
||||
|
||||
1. **Pattern 1/3/4 Not Yet Updated**
|
||||
- Only Pattern 2 (loop_with_break) currently implements condition input extraction
|
||||
- Other patterns will need similar updates
|
||||
|
||||
2. **No Condition-Only Outputs**
|
||||
- Current design handles inputs only
|
||||
- Outputs would need similar treatment (future extension point)
|
||||
|
||||
3. **Manual Debugging Required**
|
||||
- Integration tests not yet passing
|
||||
- Need to trace execution to find where mapping breaks down
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
**Immediate**:
|
||||
1. Add debug output to confirm condition variable extraction runs
|
||||
2. Verify boundary.condition_inputs is populated
|
||||
3. Check if Copy instructions are actually injected
|
||||
4. Trace ValueId remapping through merge pipeline
|
||||
|
||||
**Follow-up**:
|
||||
1. Update Pattern 1/3/4 lowerers with same infrastructure
|
||||
2. Add integration tests for each pattern
|
||||
3. Document edge cases and limitations
|
||||
4. Update CURRENT_TASK.md with completion status
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- Phase 171-1 Analysis: `phase171-1-boundary-analysis.md`
|
||||
- Phase 171-2 Design: `phase171-2-condition-inputs-design.md`
|
||||
- Phase 171-3 Implementation: `phase171-3-implementation-report.md`
|
||||
- Phase 170 Background: `phase170-completion-report.md`
|
||||
Reference in New Issue
Block a user