|
|
|
|
@ -8,7 +8,7 @@
|
|
|
|
|
| Case | File | Emit | Link | Run | Notes |
|
|
|
|
|
|------|------|------|------|-----|-------|
|
|
|
|
|
| A | `apps/tests/phase87_llvm_exe_min.hako` | ✅ | ✅ | ✅ | **PASS** - Simple return 42, no BoxCall, exit code verified |
|
|
|
|
|
| B | `apps/tests/loop_min_while.hako` | ✅ | ✅ | ❌ | **TAG-RUN** - EMIT/LINK fixed (Phase 131-5), **infinite loop in runtime** (PHI incoming value bug - Phase 131-6) |
|
|
|
|
|
| B | `apps/tests/loop_min_while.hako` | ✅ | ✅ | ✅ | **PASS** - Loop/PHI path runs end-to-end (Phase 131-10): prints `0,1,2` and exits |
|
|
|
|
|
| B2 | `/tmp/case_b_simple.hako` | ✅ | ✅ | ✅ | **PASS** - Simple print(42) without loop works |
|
|
|
|
|
| C | `apps/tests/llvm_stage3_loop_only.hako` | ❌ | - | - | **TAG-EMIT** - Complex loop (break/continue) fails JoinIR pattern matching |
|
|
|
|
|
|
|
|
|
|
@ -137,7 +137,7 @@ declare i64 @nyash.console.log(i8*)
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 3. TAG-RUN: Loop Infinite Iteration (Case B) - ❌ CONFIRMED PHI BUG (Phase 131-6)
|
|
|
|
|
### 3. TAG-RUN: Loop Runtime Incorrect (Case B) - ✅ FIXED (Phase 131-10)
|
|
|
|
|
|
|
|
|
|
**File**: `apps/tests/loop_min_while.hako`
|
|
|
|
|
|
|
|
|
|
@ -150,51 +150,28 @@ $ ./target/release/hakorune apps/tests/loop_min_while.hako
|
|
|
|
|
RC: 0
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Actual Behavior** (LLVM):
|
|
|
|
|
```bash
|
|
|
|
|
$ /tmp/loop_min_while
|
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
|
... (infinite loop, prints 0 forever)
|
|
|
|
|
```
|
|
|
|
|
**Outcome** (Phase 131-10):
|
|
|
|
|
- LLVM AOT now matches VM behavior (prints `0,1,2`, terminates)
|
|
|
|
|
- No regression in Case A / B2
|
|
|
|
|
|
|
|
|
|
**Root Cause** (Phase 131-6 Diagnosis - 2025-12-14):
|
|
|
|
|
**What happened (timeline)**:
|
|
|
|
|
- **Phase 131-6**: Confirmed MIR/VM correctness; investigated as “PHI incoming wiring bug”.
|
|
|
|
|
- Historical diagnosis: `docs/development/current/main/phase131-6-ssa-dominance-diagnosis.md`
|
|
|
|
|
- Historical plan: `docs/development/current/main/phase131-6-next-steps.md`
|
|
|
|
|
- **Phase 131-7**: Multi-pass lowering follow-up: ensure values defined in Pass A are visible to Pass C (global `vmap` sync).
|
|
|
|
|
- **Phase 131-8**: ExternCall argument resolution fixed: use PHI-safe resolution (`resolve_i64_strict`) so PHI values are not treated as null pointers.
|
|
|
|
|
- **Phase 131-9**: MIR global PHI type inference: fix loop-carrier PHI mistakenly inferred as `String` (prevent `"0" + "1"` concatenation behavior).
|
|
|
|
|
- Implemented in `src/mir/builder/lifecycle.rs` (Phase 131-9 section).
|
|
|
|
|
- **Phase 131-10**: Console ABI routing: avoid calling `nyash.console.log(i8*)` with non-string values.
|
|
|
|
|
- Route string literals to `nyash.console.log(i8*)`
|
|
|
|
|
- Route handles/integers to `nyash.console.log_handle(i64)`
|
|
|
|
|
- Unboxed integer fallback handled in `crates/nyash_kernel/src/plugin/console.rs`
|
|
|
|
|
|
|
|
|
|
The MIR is **correct**. The MIR dump shows proper SSA form:
|
|
|
|
|
|
|
|
|
|
```mir
|
|
|
|
|
bb4:
|
|
|
|
|
%3 = phi [%2, bb0], [%12, bb7] // ← Should receive %12 from loop body
|
|
|
|
|
|
|
|
|
|
bb7:
|
|
|
|
|
extern_call env.console.log(%3) // ← Prints current value
|
|
|
|
|
%11 = const 1
|
|
|
|
|
%12 = %3 Add %11 // ← Computes i+1
|
|
|
|
|
br label bb4 // ← Loops back with %12
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Verification**:
|
|
|
|
|
- ✅ VM execution: Correctly outputs `0, 1, 2`
|
|
|
|
|
- ❌ LLVM execution: Infinite loop outputting `0`
|
|
|
|
|
- ✅ MIR SSA dominance: No use-before-def violations
|
|
|
|
|
- ❌ LLVM PHI wiring: Incoming value from bb7 not properly connected
|
|
|
|
|
|
|
|
|
|
**Bug Location**: `/home/tomoaki/git/hakorune-selfhost/src/llvm_py/llvm_builder.py`
|
|
|
|
|
- Function: `finalize_phis()` (lines 601-735+)
|
|
|
|
|
- Suspected issue: Self-carry logic (lines 679-681) or value resolution (line 683) causes %12 to be ignored
|
|
|
|
|
|
|
|
|
|
**Detailed Analysis**: See [`docs/development/current/main/phase131-6-ssa-dominance-diagnosis.md`](/home/tomoaki/git/hakorune-selfhost/docs/development/current/main/phase131-6-ssa-dominance-diagnosis.md)
|
|
|
|
|
|
|
|
|
|
**Next Steps**:
|
|
|
|
|
1. Inspect generated LLVM IR for store instructions after PHI
|
|
|
|
|
2. Check if PHI value is being used in subsequent stores
|
|
|
|
|
3. Verify loop increment instruction sequence
|
|
|
|
|
|
|
|
|
|
**Files to investigate**:
|
|
|
|
|
- `src/llvm_py/instructions/store.py` - Store instruction lowering
|
|
|
|
|
- `src/llvm_py/phi_wiring/wiring.py` - PHI value propagation
|
|
|
|
|
- `target/aot_objects/loop_min_while.ll` - Generated LLVM IR (if saved)
|
|
|
|
|
**Key lesson**:
|
|
|
|
|
- “PHI is correct but loop prints 0 forever” can be a combination of:
|
|
|
|
|
- multi-pass value propagation bugs,
|
|
|
|
|
- ExternCall resolution/ABI mismatch,
|
|
|
|
|
- and MIR type inference drift.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
@ -278,46 +255,20 @@ Hint: This loop pattern is not supported. All loops must use JoinIR lowering.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 🔥 Priority 3: Fix TAG-RUN (Loop Infinite Iteration) - IN PROGRESS (Phase 131-6)
|
|
|
|
|
### ✅ Priority 3: COMPLETED - Fix TAG-RUN (Loop Runtime Correctness)
|
|
|
|
|
**Target**: Case B (`loop_min_while.hako`)
|
|
|
|
|
|
|
|
|
|
**Issue**: Loop counter not updating, causes infinite loop printing `0`
|
|
|
|
|
**Status**: ✅ FIXED (Phase 131-10)
|
|
|
|
|
|
|
|
|
|
**Phase 131-6 Investigation Results**:
|
|
|
|
|
**Fixes applied (high level)**:
|
|
|
|
|
1. Multi-pass lowering value propagation: Pass A outputs are synced for Pass C usage
|
|
|
|
|
2. ExternCall value resolution: unify on PHI-safe resolver (`resolve_i64_strict`)
|
|
|
|
|
3. MIR global PHI type inference: correct loop-carrier PHI types from incomings
|
|
|
|
|
4. Console ABI routing: `console.log(i8*)` vs `console.log_handle(i64)` dispatch
|
|
|
|
|
|
|
|
|
|
#### Bug #1: MIR Copy-to-PHI (FIXED)
|
|
|
|
|
- **Location**: `src/mir/builder/control_flow/joinir/merge/instruction_rewriter.rs` lines 419-440
|
|
|
|
|
- **Problem**: Parameter binding was generating `Copy { dst: PHI_dst, src: value }` in loop latch
|
|
|
|
|
- **Fix**: Added check to skip Copy when `dst` is a header PHI destination
|
|
|
|
|
- **Status**: ✅ Copy instruction removed from block 7
|
|
|
|
|
|
|
|
|
|
#### Bug #2: Type Inference - String vs Integer (PARTIAL FIX)
|
|
|
|
|
- **Location**: `src/llvm_py/instructions/binop.py` lines 128-153
|
|
|
|
|
- **Problem**: MIR marks `i + 1` as `dst_type: StringBox` (forward-looking hint from `print(i)` usage)
|
|
|
|
|
- **Impact**: Python harness was doing string concatenation instead of integer addition
|
|
|
|
|
- **Fix Attempted**: Removed `force_string` logic that trusted `dst_type` hint
|
|
|
|
|
- **Status**: ⚠️ Partially fixed but infinite loop persists
|
|
|
|
|
|
|
|
|
|
#### Bug #3: Instruction Ordering Violation (DISCOVERED)
|
|
|
|
|
- **Location**: MIR builder (instruction scheduling)
|
|
|
|
|
- **Problem**: Copy instructions emitted AFTER values are used (violates SSA dominance)
|
|
|
|
|
- **Example**: `%6 = %4 + %5` appears before `%5 = copy %3`
|
|
|
|
|
- **Impact**: LLVM requires strict SSA form, Rust VM tolerates it
|
|
|
|
|
- **Status**: ❌ Not yet addressed
|
|
|
|
|
|
|
|
|
|
#### Additional Findings
|
|
|
|
|
- Simple test `/tmp/test_simple_add.hako` (`i=0; i=i+1; print(i)`) also fails (prints 0 not 1)
|
|
|
|
|
- Issue exists even without loops, suggesting fundamental binop/type problem
|
|
|
|
|
- String tagging propagation may be marking integer PHI values as strings
|
|
|
|
|
|
|
|
|
|
**Next Steps**:
|
|
|
|
|
1. Trace `resolver.is_stringish()` to see if integers are being marked as strings
|
|
|
|
|
2. Fix MIR instruction scheduling to respect SSA dominance
|
|
|
|
|
3. Consider runtime type checking instead of compile-time inference
|
|
|
|
|
|
|
|
|
|
**Files Modified**:
|
|
|
|
|
- `src/mir/builder/control_flow/joinir/merge/instruction_rewriter.rs` (Phase 131-6 fix)
|
|
|
|
|
- `src/llvm_py/instructions/binop.py` (Phase 131-6 partial fix)
|
|
|
|
|
**Result**:
|
|
|
|
|
- Case B now passes EMIT ✅ LINK ✅ RUN ✅
|
|
|
|
|
- Output matches VM behavior (`0,1,2`), and the program terminates
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
@ -458,7 +409,7 @@ NYASH_LLVM_OBJ_OUT="$OBJ" NYASH_LLVM_USE_HARNESS=1 \
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Timeline Estimate
|
|
|
|
|
## Timeline Estimate (historical)
|
|
|
|
|
|
|
|
|
|
- **P1 (Loop PHI → LLVM IR fix)**: 1-2 hours (harness BB emission logic)
|
|
|
|
|
- **P2 (JoinIR pattern coverage)**: 3-4 hours (pattern design + implementation)
|
|
|
|
|
@ -468,19 +419,18 @@ NYASH_LLVM_OBJ_OUT="$OBJ" NYASH_LLVM_USE_HARNESS=1 \
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Executive Summary
|
|
|
|
|
## Executive Summary (updated)
|
|
|
|
|
|
|
|
|
|
### Phase 131-5 Results (TAG-LINK Fix Complete!)
|
|
|
|
|
### Phase 131-10 Results (Case B End-to-End PASS)
|
|
|
|
|
|
|
|
|
|
**✅ Case A (Minimal)**: PASS - Simple return works perfectly
|
|
|
|
|
- EMIT ✅ LINK ✅ RUN ✅
|
|
|
|
|
- Validates: Build pipeline, NyKernel runtime, basic MIR→LLVM lowering
|
|
|
|
|
|
|
|
|
|
**⚠️ Case B (Loop+PHI)**: EMIT ✅ LINK ✅ RUN ❌
|
|
|
|
|
**✅ Case B (Loop+PHI)**: PASS - Loop/PHI works in LLVM AOT
|
|
|
|
|
- **Phase 131-4**: Fixed TAG-EMIT (PHI after terminator) ✅
|
|
|
|
|
- **Phase 131-5**: Fixed TAG-LINK (symbol name mismatch) ✅
|
|
|
|
|
- **NEW ISSUE**: TAG-RUN (infinite loop - counter not updating) ❌
|
|
|
|
|
- **Progress**: 2/3 milestones achieved, runtime bug discovered
|
|
|
|
|
- **Phase 131-10**: Fixed TAG-RUN (value propagation + console ABI routing) ✅
|
|
|
|
|
|
|
|
|
|
**✅ Case B2 (BoxCall)**: PASS - print() without loops works
|
|
|
|
|
- EMIT ✅ LINK ✅ RUN ✅
|
|
|
|
|
@ -492,7 +442,7 @@ NYASH_LLVM_OBJ_OUT="$OBJ" NYASH_LLVM_USE_HARNESS=1 \
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### Phase 131-5 Achievements
|
|
|
|
|
### Phase 131-5 Achievements (still valid)
|
|
|
|
|
|
|
|
|
|
**✅ Fixed TAG-LINK (Symbol Name Mismatch)**:
|
|
|
|
|
1. **Investigation**: Used `objdump` to discover NyKernel exports symbols with dots
|
|
|
|
|
@ -512,8 +462,8 @@ NYASH_LLVM_OBJ_OUT="$OBJ" NYASH_LLVM_USE_HARNESS=1 \
|
|
|
|
|
|
|
|
|
|
1. ✅ **Fix PHI ordering** (P1 - Phase 131-4) - DONE
|
|
|
|
|
2. ✅ **Fix symbol mapping** (P2 - Phase 131-5) - DONE
|
|
|
|
|
3. 🔥 **Fix loop runtime bug** (P3 - NEW) - IN PROGRESS
|
|
|
|
|
4. ⏳ **Add JoinIR Pattern 5** (P4) - PENDING
|
|
|
|
|
3. ✅ **Fix loop runtime correctness** (P3 - Phase 131-10) - DONE
|
|
|
|
|
4. ⏳ **Add JoinIR infinite-loop early-exit pattern** (P4) - PENDING
|
|
|
|
|
5. ⏳ **Comprehensive test** (P5) - PENDING
|
|
|
|
|
|
|
|
|
|
**Total Effort So Far**: ~3 hours (Investigation + 2 fixes)
|
|
|
|
|
@ -534,7 +484,7 @@ NYASH_LLVM_OBJ_OUT="$OBJ" NYASH_LLVM_USE_HARNESS=1 \
|
|
|
|
|
- **Recommendation**: Box-ify with interface contract (MIR JSON v1 schema)
|
|
|
|
|
|
|
|
|
|
#### 🔧 Technical Debt Found
|
|
|
|
|
1. **PHI emission ordering** - Architectural issue, not a quick fix
|
|
|
|
|
1. **Harness duplication** - Python harness vs Rust crate divergence risk
|
|
|
|
|
2. **Unreachable block handling** - MIR JSON marks all blocks `reachable: false` (may be stale metadata)
|
|
|
|
|
3. **Error logging** - Python harness errors lost after build_llvm.sh exits
|
|
|
|
|
|
|
|
|
|
@ -549,10 +499,11 @@ tmp/case_a
|
|
|
|
|
echo $? # Expected: 42
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Case B (Loop PHI - FAIL at EMIT)
|
|
|
|
|
### Case B (Loop PHI - PASS)
|
|
|
|
|
```bash
|
|
|
|
|
tools/build_llvm.sh apps/tests/loop_min_while.hako -o tmp/case_b
|
|
|
|
|
# Error: empty bb4 in LLVM IR
|
|
|
|
|
tmp/case_b
|
|
|
|
|
# Output: 0,1,2 (+ Result line), then exit
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Case B2 (Simple BoxCall - PASS)
|
|
|
|
|
@ -567,7 +518,7 @@ static box Main {
|
|
|
|
|
EOF
|
|
|
|
|
tools/build_llvm.sh /tmp/case_b_simple.hako -o tmp/case_b2
|
|
|
|
|
tmp/case_b2
|
|
|
|
|
# Output: (empty, but executes without crash)
|
|
|
|
|
# Output: prints `42` (+ Result line), then exit
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Case C (Complex Loop - FAIL at MIR)
|
|
|
|
|
@ -594,12 +545,12 @@ jq '.cfg.functions[] | select(.name=="main") | .blocks[] | select(.id==4)' /tmp/
|
|
|
|
|
|
|
|
|
|
## Success Criteria
|
|
|
|
|
|
|
|
|
|
**Phase 131-5 Complete** when:
|
|
|
|
|
**Phase 131-10 Complete** when:
|
|
|
|
|
1. ✅ Case A continues to pass (regression prevention) - **VERIFIED**
|
|
|
|
|
2. ⚠️ Case B (loop_min_while.hako) compiles to valid LLVM IR and links - **PARTIAL** (EMIT ✅ LINK ✅ RUN ❌)
|
|
|
|
|
2. ✅ Case B (loop_min_while.hako) passes end-to-end - **VERIFIED** (EMIT ✅ LINK ✅ RUN ✅)
|
|
|
|
|
3. ✅ Case B2 continues to pass (BoxCall regression prevention) - **VERIFIED**
|
|
|
|
|
4. ❌ Case C (llvm_stage3_loop_only.hako) lowers to JoinIR and runs - **NOT YET**
|
|
|
|
|
5. ⚠️ All 4 cases produce correct output - **PARTIAL** (2/4 passing)
|
|
|
|
|
5. ⚠️ All 4 cases produce correct output - **PARTIAL** (3/4 passing)
|
|
|
|
|
6. ⚠️ No plugin errors (or plugin errors are benign/documented) - **ACCEPTABLE** (plugin errors don't affect AOT execution)
|
|
|
|
|
|
|
|
|
|
**Definition of Done**:
|
|
|
|
|
|