feat(llvm): Phase 131-4 P1 完了 - PHI ordering 修正(multi-pass architecture)

Phase 131-4 P1: PHI After Terminator Bug 修正

問題:
- LLVM IR で PHI が terminator の後に出現(LLVM invariant 違反)
- Case B (loop_min_while.hako) が TAG-EMIT で失敗

修正:
- Multi-pass block lowering architecture 実装:
  - Pass A: non-terminator instructions のみ emit
  - Pass B: PHI finalization(block head に確実に配置)
  - Pass C: deferred terminators を最後に emit

変更ファイル:
- src/llvm_py/builders/block_lower.py (~40行):
  - lower_blocks() で terminator を defer
  - lower_terminators() 新設(Pass C)
  - _deferred_terminators dict で管理
- src/llvm_py/builders/function_lower.py (3行):
  - Pass 順序更新: A→B→C
- src/llvm_py/instructions/ret.py (5行):
  - _disable_phi_synthesis flag で Pass C 中の PHI 生成を抑制

テスト結果:
- Case B EMIT:  (修正成功)
- Case B LINK:  (新 TAG-LINK: undefined nyash_console_log)
- Case A/B2:  (退行なし)

箱化モジュール化:
-  Multi-pass で責務分離
-  Flag mechanism で構造的制御
-  ハードコード禁止原則遵守

Next: Phase 131-5 (TAG-LINK 修正)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-14 06:12:31 +09:00
parent 5709026812
commit 73613dcef0
4 changed files with 138 additions and 30 deletions

View File

@ -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-EMIT** - Loop generates invalid LLVM IR (observed: PHI placement/order issue; also mentions empty block) |
| B | `apps/tests/loop_min_while.hako` | | | - | **TAG-LINK** - EMIT fixed (Phase 131-4), LINK fails (undefined nyash_console_log) |
| 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 |
@ -72,9 +72,51 @@ This strongly suggests an **emission ordering / insertion-position** problem in
- PHI insertion rules + debug: `src/llvm_py/phi_wiring/wiring.py` (`NYASH_PHI_ORDERING_DEBUG=1`)
- “Empty block” safety pass (separate concern): `src/llvm_py/builders/function_lower.py:_enforce_terminators`
**✅ FIXED (Phase 131-4)**: Multi-pass block lowering architecture
**Solution implemented**:
- **Pass A**: Lower non-terminator instructions (body ops only)
- **Pass B**: Finalize PHIs (wire incoming edges) - happens in `function_lower.py`
- **Pass C**: Lower deferred terminators (after PHIs are placed)
**Key changes**:
1. `src/llvm_py/builders/block_lower.py`:
- Split `lower_blocks()` to defer terminators
- Added `lower_terminators()` function for Pass C
- Deferred terminators stored in `builder._deferred_terminators`
2. `src/llvm_py/builders/function_lower.py`:
- Updated pass ordering: Pass A → Pass B → Pass C
- Added call to `_lower_terminators()` after `_finalize_phis()`
3. `src/llvm_py/instructions/ret.py`:
- Added `_disable_phi_synthesis` flag check
- Prevents PHI creation during Pass C (terminators should only use existing values)
**Result**:
- Case B EMIT now succeeds ✅
- Generated LLVM IR is valid (PHIs before terminators)
- No regression in Cases A and B2
---
### 2. TAG-EMIT: JoinIR Pattern Mismatch (Case C)
### 2. TAG-LINK: Missing runtime symbols (Case B)
**File**: `apps/tests/loop_min_while.hako`
**Link Error**:
```
/usr/bin/ld: /home/tomoaki/git/hakorune-selfhost/target/aot_objects/loop_min_while.o: in function `condition_fn':
<string>:(.text+0x99): undefined reference to `nyash_console_log'
```
**Root Cause**: ExternCall lowering emits calls to runtime functions (e.g., `nyash_console_log`) but these symbols are not provided by NyKernel (`libnyash_kernel.a`).
**Next Steps**: Map ExternCall names to actual NyKernel symbols or add missing runtime functions.
---
### 3. TAG-EMIT: JoinIR Pattern Mismatch (Case C)
**File**: `apps/tests/llvm_stage3_loop_only.hako`
@ -127,24 +169,33 @@ Hint: This loop pattern is not supported. All loops must use JoinIR lowering.
## Next Steps
### Priority 1: Fix TAG-EMIT (PHI After Terminator Bug) ⚠️ CRITICAL
### Priority 1: COMPLETED - Fix TAG-EMIT (PHI After Terminator Bug)
**Target**: Case B (`loop_min_while.hako`)
**Goal**: Ensure PHIs are always emitted/inserted before any terminator in the same basic block.
**Status**: ✅ FIXED in Phase 131-4 (see Root Cause #1 above)
**Candidate approach** (docs-only; implementation to be decided):
- Split lowering into multi-pass so that PHI placeholders exist before terminators are emitted, or delay terminator emission until after PHI finalization:
- (A) Predeclare PHIs at block creation time (placeholders), then emit body ops, then wire incomings, then emit terminators.
- (B) Keep current finalize order, but guarantee `ensure_phi()` always inserts at head even when a terminator exists (verify llvmlite positioning behavior).
**Primary files to look at for the fix**:
- `src/llvm_py/builders/function_lower.py` (pass ordering)
- `src/llvm_py/builders/block_lower.py` (terminator emission split point)
- `src/llvm_py/phi_wiring/wiring.py` (PHI insertion positioning)
**Result**: Case B EMIT now succeeds. LINK still fails (TAG-LINK), but that's a separate issue (Priority 2).
---
### Priority 2: Fix TAG-EMIT (JoinIR Pattern Coverage)
### Priority 2: Fix TAG-LINK (Missing Runtime Symbols)
**Target**: Case B (`loop_min_while.hako`)
**Approach**:
1. Identify all ExternCall lowering paths in Python harness
2. Map to actual NyKernel symbols (e.g., `nyash_console_log``ny_console_log` or similar)
3. Update ExternCall lowering to use correct symbol names
4. OR: Add wrapper functions in NyKernel to provide missing symbols
**Files**:
- `src/llvm_py/instructions/externcall.py` - ExternCall lowering
- `crates/nyash_kernel/src/lib.rs` - NyKernel runtime symbols
**Expected**: Case B should LINK ✅ RUN ✅ after fix
---
### Priority 3: Fix TAG-EMIT (JoinIR Pattern Coverage)
**Target**: Case C (`llvm_stage3_loop_only.hako`)
**Approach**: