📋 Phase 15セルフホスティング戦略整理 & LLVM改善
## Phase 15戦略整理 - セルフホスティング戦略2025年9月版を作成 - Phase 15.2-15.5の段階的実装計画を明確化 - 15.2: LLVM独立化(nyash-llvm-compiler crate) - 15.3: Nyashコンパイラ実装でセルフホスト達成 - 15.4: VM層のNyash化(革新的アプローチ) - 15.5: ABI移行(LLVM完成後) - ROADMAP.mdの優先順位調整、README.md更新 ## LLVM改善(ChatGPT5協力) - BuilderCursor::with_block改善(状態の適切な保存/復元) - seal_blockでの挿入位置管理を厳密化 - 前任ブロックのみ処理、重複PHI incoming防止 - defined_in_blockトラッキングで値のスコープ管理 ## 洞察 - コンパイル不要のセルフホスティング実現可能 - VM層をNyashで書けば即座実行可能 - Phase 22(Nyash LLVMコンパイラ)への道筋 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -20,6 +20,7 @@ Update — 2025-09-12 (LLVM flow + BB naming)
|
|||||||
|
|
||||||
Hot Update — 2025-09-12 (quick)
|
Hot Update — 2025-09-12 (quick)
|
||||||
- Flow: moved function verify to post‑lower; added terminator fallback only when MIR lacks one
|
- Flow: moved function verify to post‑lower; added terminator fallback only when MIR lacks one
|
||||||
|
- PHI(sealed): seal_block isolates cast insertion by saving/restoring the insertion point in the predecessor block and wires incoming only for the current predecessor (zero-synth as a last resort, logged). Avoids duplicate incoming and builder state leaks.
|
||||||
- Compare: allow ptr↔int comparisons for all ops via ptr→i64 bridge
|
- Compare: allow ptr↔int comparisons for all ops via ptr→i64 bridge
|
||||||
- Strings: substring now accepts i64(handle) receiver (i2p); len/lastIndexOf stable
|
- Strings: substring now accepts i64(handle) receiver (i2p); len/lastIndexOf stable
|
||||||
- Arrays: method_id未注入でも get/set/push/length を NyRT 経由で処理
|
- Arrays: method_id未注入でも get/set/push/length を NyRT 経由で処理
|
||||||
@ -45,20 +46,20 @@ Next Steps(Sealed SSA 段階導入)
|
|||||||
4) グリーン後、Sealed をデフォルトONにする前にスモーク一式で回帰確認。
|
4) グリーン後、Sealed をデフォルトONにする前にスモーク一式で回帰確認。
|
||||||
|
|
||||||
TODO — Sealed SSA 段階導入(実装タスク)
|
TODO — Sealed SSA 段階導入(実装タスク)
|
||||||
- [ ] block_end_values 追加(LLVM Lower 内の per-BB 終端スナップショット)
|
- [x] block_end_values 追加(LLVM Lower 内の per-BB 終端スナップショット)
|
||||||
- 追加先: `src/backend/llvm/compiler/codegen/mod.rs`
|
- 追加先: `src/backend/llvm/compiler/codegen/mod.rs`
|
||||||
- 形式: `HashMap<BasicBlockId, HashMap<ValueId, BasicValueEnum>>`
|
- 形式: `HashMap<BasicBlockId, HashMap<ValueId, BasicValueEnum>>`
|
||||||
- タイミング: 各BBの命令をすべて Lower した「直後」、終端命令を発行する「直前」に `vmap.clone()` を保存
|
- タイミング: 各BBの命令をすべて Lower した「直後」、終端命令を発行する「直前」に `vmap.clone()` を保存
|
||||||
- 目的: `seal_block` で pred 終端時点の値を安定取得する(現在の vmap 直接参照をやめる)
|
- 目的: `seal_block` で pred 終端時点の値を安定取得する(現在の vmap 直接参照をやめる)
|
||||||
- [ ] `seal_block` をスナップショット参照に切替
|
- [x] `seal_block` をスナップショット参照に切替
|
||||||
- 対象: `src/backend/llvm/compiler/codegen/instructions/flow.rs::seal_block`
|
- 対象: `src/backend/llvm/compiler/codegen/instructions/flow.rs::seal_block`
|
||||||
- 取得: `block_end_values[bid].get(in_vid)` を用いて `val` を取得
|
- 取得: `block_end_values[bid].get(in_vid)` を用いて `val` を取得
|
||||||
- フォールバック: もしスナップショットが無ければ(例外ケース)従来の `vmap` を参照し、警告ログを出す
|
- フォールバック: もしスナップショットが無ければ(例外ケース)従来の `vmap` を参照し、警告ログを出す
|
||||||
- ログ: `NYASH_LLVM_TRACE_PHI=1` 時に `[PHI] sealed add pred_bb=.. val=.. ty=.. (snapshot)` と明示
|
- ログ: `NYASH_LLVM_TRACE_PHI=1` 時に `[PHI] sealed add pred_bb=.. val=.. ty=.. (snapshot)` と明示
|
||||||
- [ ] 非 sealed 経路の維持(回帰防止)
|
- [x] 非 sealed 経路の維持(回帰防止)
|
||||||
- `emit_jump/emit_branch` は sealed=OFF の時のみ incoming を追加(現状仕様を維持)
|
- `emit_jump/emit_branch` は sealed=OFF の時のみ incoming を追加(現状仕様を維持)
|
||||||
- sealed=ON の時は incoming 配線は一切行わず、`seal_block` のみで完結
|
- sealed=ON の時は incoming 配線は一切行わず、`seal_block` のみで完結
|
||||||
- [ ] 型整合(coerce)の継続強化
|
- [x] 型整合(coerce)の継続強化
|
||||||
- 対象: `src/backend/llvm/compiler/codegen/instructions/flow.rs::coerce_to_type`
|
- 対象: `src/backend/llvm/compiler/codegen/instructions/flow.rs::coerce_to_type`
|
||||||
- 方針: PHI の型は i8* 優先(String/Box/Array を含む場合)。ptr/int 混在は明示 cast で橋渡し
|
- 方針: PHI の型は i8* 優先(String/Box/Array を含む場合)。ptr/int 混在は明示 cast で橋渡し
|
||||||
- 検討: i1 ブリッジ(bool)の zext/trunc の置き場所は PHI 外側に寄せる(必要時)
|
- 検討: i1 ブリッジ(bool)の zext/trunc の置き場所は PHI 外側に寄せる(必要時)
|
||||||
@ -95,6 +96,30 @@ Note
|
|||||||
- フェーズ1では「終端APIと位置ずれの構造化」の最小適用に留め、フォールバック(最終unreachable)を併用
|
- フェーズ1では「終端APIと位置ずれの構造化」の最小適用に留め、フォールバック(最終unreachable)を併用
|
||||||
- フェーズ2で with_block の適用範囲を広げ、余剰なガード・分岐フォールバックを削除していく(ソースは小さくシンプルに)
|
- フェーズ2で with_block の適用範囲を広げ、余剰なガード・分岐フォールバックを削除していく(ソースは小さくシンプルに)
|
||||||
|
|
||||||
|
Progress — 2025-09-12(Sealed + RAII 最小導入)
|
||||||
|
- Sealed: block_end_values(BB内定義のみをフィルタ)を導入し、incoming は pred 終端時点の snapshot から配線
|
||||||
|
- Cast 挿入: pred 終端直前(position_before)に限定、終端後挿入を回避
|
||||||
|
- BuilderCursor: emit_return/emit_jump/emit_branch を構造化(closed ブロックへの挿入を禁止)
|
||||||
|
- Call/BoxCall: 実引数を callee の型へ coerce(i2p/p2i/zext/trunc 等)
|
||||||
|
- Const String: nyash_string_new を entry ブロックで Hoist し、支配性違反を解消
|
||||||
|
- Fallback(暫定): PHI 欠落や lhs/rhs missing に対し型ゼロを合成して進行(ログ付)
|
||||||
|
|
||||||
|
Open Issues(要対応)
|
||||||
|
- Sealed配線: 一部合流で『各predに1 incoming』が未充足(synth で穴埋め中)
|
||||||
|
- Dominance: ループ/合流で稀に「Instruction does not dominate all uses!」が再出
|
||||||
|
- 位置ずれ: Sealed 内の cast 生成が builder の挿入位置に影響する可能性(要隔離)
|
||||||
|
|
||||||
|
Next TODO(優先度順)
|
||||||
|
1) flow::seal_block の挿入位置を完全隔離
|
||||||
|
- 専用Builder or Cursor.with_block で pred 終端直前に cast を挿入(メインbuilder位置を汚さない)
|
||||||
|
2) preds_by_block を構築し、PHI incoming を実CFGの pred へ正規リマップ
|
||||||
|
- snapshot から in_vid を取得し、pred 数ぶんを網羅(synth は最終手段)
|
||||||
|
- 検証: incoming=pred数 を assert/ログ
|
||||||
|
3) with_block の適用拡大(entry_builder/配列alloca等のホットスポット)
|
||||||
|
- 位置ずれ温床を解消 → 余剰ガード/フォールバックを削除(コード縮小)
|
||||||
|
4) 回帰: Sealed=ON/OFF の一致確認(dep_tree_min_string ほか代表)
|
||||||
|
- NYASH_LLVM_TRACE_PHI=1 で配線ログ確認、ゼロ合成が消えることを目標
|
||||||
|
|
||||||
Plan — PHI/SSA Hardening (Sealed SSA)
|
Plan — PHI/SSA Hardening (Sealed SSA)
|
||||||
- Sealed SSA 入れ替え(安全に段階導入)
|
- Sealed SSA 入れ替え(安全に段階導入)
|
||||||
- Blockごとに `sealed: bool` と `incomplete_phis: Map<Var, Phi>` を保持
|
- Blockごとに `sealed: bool` と `incomplete_phis: Map<Var, Phi>` を保持
|
||||||
|
|||||||
@ -14,6 +14,25 @@ MIR 13命令の美しさを最大限に活かし、外部コンパイラ依存
|
|||||||
4. **エコシステムの自立**: Nyashだけで完結する開発環境
|
4. **エコシステムの自立**: Nyashだけで完結する開発環境
|
||||||
5. **劇的なコード圧縮**: 75%削減で保守性・可読性の革命
|
5. **劇的なコード圧縮**: 75%削減で保守性・可読性の革命
|
||||||
|
|
||||||
|
## 🚀 実装戦略(2025年9月更新)
|
||||||
|
|
||||||
|
### Phase 15.2: LLVM層の独立化(実装中)
|
||||||
|
- nyash-llvm-compiler crateの分離
|
||||||
|
- MIR JSON/バイナリ入力 → ネイティブEXE出力
|
||||||
|
- 独立したツールとして配布可能
|
||||||
|
|
||||||
|
### Phase 15.3: Nyashコンパイラ実装
|
||||||
|
- NyashでNyashパーサー実装
|
||||||
|
- AST→MIR変換
|
||||||
|
- ブートストラップでセルフホスティング達成!
|
||||||
|
|
||||||
|
### Phase 15.4: VM層のNyash化(革新的)
|
||||||
|
- MIR解釈エンジンをNyashで実装
|
||||||
|
- コンパイル不要の即座実行
|
||||||
|
- デバッグ・開発効率の劇的向上
|
||||||
|
|
||||||
|
詳細:[セルフホスティング戦略 2025年9月版](implementation/self-hosting-strategy-2025-09.md)
|
||||||
|
|
||||||
## 📊 主要成果物
|
## 📊 主要成果物
|
||||||
|
|
||||||
### コンパイラコンポーネント
|
### コンパイラコンポーネント
|
||||||
@ -207,10 +226,11 @@ ny_free_buf(buffer)
|
|||||||
|
|
||||||
## 📅 実施時期
|
## 📅 実施時期
|
||||||
|
|
||||||
- **開始条件**: Phase 10-14完了後
|
- **現在進行中**(2025年9月)
|
||||||
- **推定開始**: 2026年前半
|
- **Phase 15.2**: LLVM独立化(実装中)
|
||||||
- **推定期間**: 6-8ヶ月
|
- **Phase 15.3**: Nyashコンパイラ(2025年後半)
|
||||||
- **早期着手**: YAML自動生成は今すぐ開始可能
|
- **Phase 15.4**: VM層Nyash化(2026年前半)
|
||||||
|
- **Phase 15.5**: ABI移行(LLVM完成後)
|
||||||
|
|
||||||
## 💡 期待される成果
|
## 💡 期待される成果
|
||||||
|
|
||||||
|
|||||||
@ -18,23 +18,29 @@ This roadmap is a living checklist to advance Phase 15 with small, safe boxes. U
|
|||||||
|
|
||||||
## Next (small boxes)
|
## Next (small boxes)
|
||||||
|
|
||||||
1) Standard Ny std impl (P0→実体化)
|
1) LLVM Native EXE Generation (Phase 15.2) 🚀
|
||||||
- Implement P0 methods for string/array/map in Nyash (keep NyRT primitives minimal)
|
|
||||||
- Enable via `nyash.toml` `[ny_plugins]` (opt‑in); extend `tools/jit_smoke.sh`
|
|
||||||
2) Ny compiler MVP (Ny→MIR on JIT path)
|
|
||||||
- Ny tokenizer + recursive‑descent parser (current subset) in Ny; drive existing MIR builder
|
|
||||||
- Flag path: `NYASH_USE_NY_COMPILER=1` to switch rust→ny compiler; rust parser as fallback
|
|
||||||
- Add apps/selfhost-compiler/ and minimal smokes
|
|
||||||
3) Bootstrap loop (c0→c1→c1’)
|
|
||||||
- Use existing trace/hash harness to compare parity; add optional CI gate
|
|
||||||
4) Plugins CI split (継続)
|
|
||||||
- Core always‑on (JIT, plugins disabled); Plugins as optional job (strict off by default)
|
|
||||||
5) LLVM Native EXE Generation
|
|
||||||
- LLVM backend object → executable pipeline completion
|
- LLVM backend object → executable pipeline completion
|
||||||
- Separate `nyash-llvm-compiler` crate (reduce main build weight)
|
- Separate `nyash-llvm-compiler` crate (reduce main build weight)
|
||||||
- Input: MIR (JSON/binary) → Output: native executable
|
- Input: MIR (JSON/binary) → Output: native executable
|
||||||
- Link with nyrt runtime (static/dynamic options)
|
- Link with nyrt runtime (static/dynamic options)
|
||||||
- Integration: `nyash --backend llvm --emit exe program.nyash -o program.exe`
|
- Integration: `nyash --backend llvm --emit exe program.nyash -o program.exe`
|
||||||
|
2) Standard Ny std impl (P0→実体化)
|
||||||
|
- Implement P0 methods for string/array/map in Nyash (keep NyRT primitives minimal)
|
||||||
|
- Enable via `nyash.toml` `[ny_plugins]` (opt‑in); extend `tools/jit_smoke.sh`
|
||||||
|
3) Ny compiler MVP (Ny→MIR on JIT path) (Phase 15.3) 🎯
|
||||||
|
- Ny tokenizer + recursive‑descent parser (current subset) in Ny; drive existing MIR builder
|
||||||
|
- Flag path: `NYASH_USE_NY_COMPILER=1` to switch rust→ny compiler; rust parser as fallback
|
||||||
|
- Add apps/selfhost-compiler/ and minimal smokes
|
||||||
|
4) Bootstrap loop (c0→c1→c1')
|
||||||
|
- Use existing trace/hash harness to compare parity; add optional CI gate
|
||||||
|
- **This achieves self-hosting!** Nyash compiles Nyash
|
||||||
|
5) VM Layer in Nyash (Phase 15.4) ⚡
|
||||||
|
- Implement MIR interpreter in Nyash (13 core instructions)
|
||||||
|
- BoxCall/ExternCall bridge to existing infrastructure
|
||||||
|
- Optional LLVM JIT acceleration for hot paths
|
||||||
|
- Enable instant execution without compilation
|
||||||
|
6) Plugins CI split (継続)
|
||||||
|
- Core always‑on (JIT, plugins disabled); Plugins as optional job (strict off by default)
|
||||||
|
|
||||||
## Later (incremental)
|
## Later (incremental)
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,132 @@
|
|||||||
|
# Phase 15 セルフホスティング戦略 2025年9月版
|
||||||
|
|
||||||
|
## 🎯 セルフホスティングの段階的実現戦略
|
||||||
|
|
||||||
|
### 現在地
|
||||||
|
- ✅ v0 Nyパーサー(Ny→JSON IR)完成
|
||||||
|
- ✅ MIR生成基盤あり(Rust実装)
|
||||||
|
- 🚧 LLVM層改善中(ChatGPT5協力)
|
||||||
|
- 📅 NyコンパイラMVP計画中
|
||||||
|
|
||||||
|
### 君の提案の妥当性検証
|
||||||
|
|
||||||
|
## 📊 セルフホスティングの段階
|
||||||
|
|
||||||
|
### Stage 1: LLVM層の独立(最優先)✅
|
||||||
|
```
|
||||||
|
現在: Rustモノリス → MIR → LLVM → オブジェクト
|
||||||
|
提案: Rustモノリス → MIR(JSON) → [LLVM EXE] → ネイティブEXE
|
||||||
|
```
|
||||||
|
|
||||||
|
**実装詳細**:
|
||||||
|
1. `nyash-llvm-compiler` crateを分離
|
||||||
|
2. 入力:MIR(JSON/バイナリ)
|
||||||
|
3. 出力:ネイティブ実行ファイル
|
||||||
|
4. nyrtランタイムとのリンク
|
||||||
|
|
||||||
|
**メリット**:
|
||||||
|
- ビルド時間短縮(Rust側は軽量化)
|
||||||
|
- 独立したツールとして配布可能
|
||||||
|
- パイプライン明確化
|
||||||
|
|
||||||
|
### Stage 2: Nyashコンパイラ実装(現在計画中)✅
|
||||||
|
```
|
||||||
|
現在: Rustパーサー → MIR
|
||||||
|
提案: Nyashコンパイラ → AST/JSON → MIR生成層
|
||||||
|
```
|
||||||
|
|
||||||
|
**実装詳細**:
|
||||||
|
1. Nyashで再帰下降パーサー実装
|
||||||
|
2. AST構造をJSONで出力
|
||||||
|
3. 既存のMIR生成層に接続
|
||||||
|
4. `NYASH_USE_NY_COMPILER=1`で切替
|
||||||
|
|
||||||
|
**これでセルフホスティング達成!**
|
||||||
|
- Nyashで書いたコンパイラがNyashをコンパイル
|
||||||
|
- Rustコンパイラは不要に
|
||||||
|
|
||||||
|
### Stage 3: VM層のNyash実装(革新的)⚡
|
||||||
|
```
|
||||||
|
現在: Rust VM → MIR解釈
|
||||||
|
提案: Nyash VM → MIR解釈 → (必要時LLVM呼び出し)
|
||||||
|
```
|
||||||
|
|
||||||
|
**実装詳細**:
|
||||||
|
```nyash
|
||||||
|
box NyashVMBox {
|
||||||
|
mir_module: MIRModuleBox
|
||||||
|
pc_stack: ArrayBox
|
||||||
|
value_stack: ArrayBox
|
||||||
|
frame_stack: ArrayBox
|
||||||
|
|
||||||
|
execute(mir_json) {
|
||||||
|
me.mir_module = MIRModuleBox.parse(mir_json)
|
||||||
|
me.runFunction("main")
|
||||||
|
}
|
||||||
|
|
||||||
|
runFunction(name) {
|
||||||
|
local func = me.mir_module.getFunction(name)
|
||||||
|
local frame = new FrameBox(func)
|
||||||
|
me.frame_stack.push(frame)
|
||||||
|
|
||||||
|
loop(frame.pc < func.instructions.length()) {
|
||||||
|
local inst = func.instructions[frame.pc]
|
||||||
|
me.executeInstruction(inst, frame)
|
||||||
|
frame.pc = frame.pc + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**メリット**:
|
||||||
|
- **コンパイル不要**で即実行
|
||||||
|
- VMロジックを動的に変更可能
|
||||||
|
- デバッグ・実験が容易
|
||||||
|
|
||||||
|
## 🚀 実現順序の提案
|
||||||
|
|
||||||
|
### Phase 15.2: LLVM独立化
|
||||||
|
1. LLVM層をcrateに分離
|
||||||
|
2. MIR JSONインターフェース確立
|
||||||
|
3. スタンドアロンEXE生成
|
||||||
|
|
||||||
|
### Phase 15.3: Nyashコンパイラ
|
||||||
|
1. パーサー実装(Nyashで)
|
||||||
|
2. AST→MIRブリッジ
|
||||||
|
3. ブートストラップテスト
|
||||||
|
|
||||||
|
### Phase 15.4: VM層Nyash化
|
||||||
|
1. MIR解釈エンジン(基本13命令)
|
||||||
|
2. BoxCall/ExternCallブリッジ
|
||||||
|
3. パフォーマンス最適化(JIT連携)
|
||||||
|
|
||||||
|
## 💡 ABI移行タイミング
|
||||||
|
|
||||||
|
**LLVM独立化完了後が最適**理由:
|
||||||
|
1. インターフェース確定後に最適化
|
||||||
|
2. 独立EXEならABI変更の影響限定的
|
||||||
|
3. パフォーマンス測定してから判断
|
||||||
|
|
||||||
|
## 📋 検証結果
|
||||||
|
|
||||||
|
**君の提案は正しい!**
|
||||||
|
|
||||||
|
1. **LLVM EXE独立** → MIR JSONで疎結合
|
||||||
|
2. **Nyashコンパイラ** → AST/JSONでMIR生成
|
||||||
|
3. **セルフホスト完了** → Rustコンパイラ不要
|
||||||
|
4. **VM層Nyash化** → 究極の柔軟性
|
||||||
|
|
||||||
|
この順序なら:
|
||||||
|
- 段階的に実現可能
|
||||||
|
- 各段階で動作確認
|
||||||
|
- リスク最小化
|
||||||
|
- 最終的に完全セルフホスト
|
||||||
|
|
||||||
|
## 🎯 次のアクション
|
||||||
|
|
||||||
|
1. **LLVM crateの設計開始**
|
||||||
|
2. **MIR JSONスキーマ確定**
|
||||||
|
3. **Nyパーサー拡張計画**
|
||||||
|
4. **VMプロトタイプ設計**
|
||||||
|
|
||||||
|
これが現実的で革新的なロードマップにゃ!
|
||||||
@ -26,6 +26,8 @@ impl<'ctx, 'b> BuilderCursor<'ctx, 'b> {
|
|||||||
let prev_bb = self.cur_llbb;
|
let prev_bb = self.cur_llbb;
|
||||||
// Preserve previous closed state
|
// Preserve previous closed state
|
||||||
let prev_closed = prev_bid.and_then(|id| self.closed_by_bid.get(&id).copied());
|
let prev_closed = prev_bid.and_then(|id| self.closed_by_bid.get(&id).copied());
|
||||||
|
// Preserve target block closed state and restore after
|
||||||
|
let tgt_closed_before = self.closed_by_bid.get(&bid).copied();
|
||||||
|
|
||||||
self.at_end(bid, bb);
|
self.at_end(bid, bb);
|
||||||
let r = body(self);
|
let r = body(self);
|
||||||
@ -39,6 +41,13 @@ impl<'ctx, 'b> BuilderCursor<'ctx, 'b> {
|
|||||||
if let (Some(pid), Some(closed)) = (prev_bid, prev_closed) {
|
if let (Some(pid), Some(closed)) = (prev_bid, prev_closed) {
|
||||||
self.closed_by_bid.insert(pid, closed);
|
self.closed_by_bid.insert(pid, closed);
|
||||||
}
|
}
|
||||||
|
if let Some(closed) = tgt_closed_before {
|
||||||
|
self.closed_by_bid.insert(bid, closed);
|
||||||
|
} else {
|
||||||
|
// If previously unknown, keep it marked as closed if a terminator exists
|
||||||
|
let has_term = unsafe { bb.get_terminator() }.is_some();
|
||||||
|
self.closed_by_bid.insert(bid, has_term);
|
||||||
|
}
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -250,8 +250,9 @@ fn coerce_to_type<'ctx>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Sealed-SSA style: when a block is finalized, add PHI incoming for all successor blocks.
|
/// Sealed-SSA style: when a block is finalized, add PHI incoming for all successor blocks.
|
||||||
pub(in super::super) fn seal_block<'ctx>(
|
pub(in super::super) fn seal_block<'ctx, 'b>(
|
||||||
codegen: &CodegenContext<'ctx>,
|
codegen: &CodegenContext<'ctx>,
|
||||||
|
cursor: &mut BuilderCursor<'ctx, 'b>,
|
||||||
func: &MirFunction,
|
func: &MirFunction,
|
||||||
bid: BasicBlockId,
|
bid: BasicBlockId,
|
||||||
succs: &HashMap<BasicBlockId, Vec<BasicBlockId>>,
|
succs: &HashMap<BasicBlockId, Vec<BasicBlockId>>,
|
||||||
@ -269,11 +270,12 @@ pub(in super::super) fn seal_block<'ctx>(
|
|||||||
for sb in slist {
|
for sb in slist {
|
||||||
if let Some(pl) = phis_by_block.get(sb) {
|
if let Some(pl) = phis_by_block.get(sb) {
|
||||||
for (_dst, phi, inputs) in pl {
|
for (_dst, phi, inputs) in pl {
|
||||||
if let Some((_, in_vid)) = inputs.iter().find(|(pred, _)| pred == &bid) {
|
// Handle only the current predecessor (bid)
|
||||||
|
if let Some((_, in_vid)) = inputs.iter().find(|(p, _)| p == &bid) {
|
||||||
// Prefer the predecessor's block-end snapshot; fall back to current vmap
|
// Prefer the predecessor's block-end snapshot; fall back to current vmap
|
||||||
let snap_opt = block_end_values
|
let snap_opt = block_end_values
|
||||||
.get(&bid)
|
.get(&bid)
|
||||||
.and_then(|m| m.get(in_vid).copied());
|
.and_then(|m| m.get(in_vid).copied());
|
||||||
let mut val = if let Some(sv) = snap_opt {
|
let mut val = if let Some(sv) = snap_opt {
|
||||||
sv
|
sv
|
||||||
} else {
|
} else {
|
||||||
@ -298,72 +300,69 @@ pub(in super::super) fn seal_block<'ctx>(
|
|||||||
BT::FloatType(ft) => ft.const_zero().into(),
|
BT::FloatType(ft) => ft.const_zero().into(),
|
||||||
BT::PointerType(pt) => pt.const_zero().into(),
|
BT::PointerType(pt) => pt.const_zero().into(),
|
||||||
_ => return Err(format!(
|
_ => return Err(format!(
|
||||||
"phi incoming (seal) missing: pred={} succ_bb={} in_vid={} (no snapshot)",
|
"phi incoming (seal) missing: pred={} succ_bb={} in_vid={} (no snapshot)",
|
||||||
bid.as_u32(), sb.as_u32(), in_vid.as_u32()
|
bid.as_u32(), sb.as_u32(), in_vid.as_u32()
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Ensure any required casts are inserted BEFORE the predecessor's terminator
|
// Insert any required casts in the predecessor block, right before its terminator
|
||||||
// Save and restore current insertion point around coercion
|
let saved_block = codegen.builder.get_insert_block();
|
||||||
let saved_block = codegen.builder.get_insert_block();
|
if let Some(pred_llbb) = bb_map.get(&bid) {
|
||||||
if let Some(pred_llbb) = bb_map.get(&bid) {
|
let term = unsafe { pred_llbb.get_terminator() };
|
||||||
let term = unsafe { pred_llbb.get_terminator() };
|
if let Some(t) = term {
|
||||||
if let Some(t) = term {
|
codegen.builder.position_before(&t);
|
||||||
// Insert casts right before the terminator of predecessor
|
} else {
|
||||||
codegen.builder.position_before(&t);
|
codegen.builder.position_at_end(*pred_llbb);
|
||||||
} else {
|
}
|
||||||
codegen.builder.position_at_end(*pred_llbb);
|
}
|
||||||
|
val = coerce_to_type(codegen, phi, val)?;
|
||||||
|
if let Some(bb) = saved_block { codegen.builder.position_at_end(bb); }
|
||||||
|
let pred_bb = *bb_map.get(&bid).ok_or("pred bb missing")?;
|
||||||
|
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||||
|
let tys = phi
|
||||||
|
.as_basic_value()
|
||||||
|
.get_type()
|
||||||
|
.print_to_string()
|
||||||
|
.to_string();
|
||||||
|
eprintln!(
|
||||||
|
"[PHI] sealed add pred_bb={} val={} ty={}{}",
|
||||||
|
bid.as_u32(),
|
||||||
|
in_vid.as_u32(),
|
||||||
|
tys,
|
||||||
|
if snap_opt.is_some() { " (snapshot)" } else { " (vmap)" }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
match val {
|
||||||
|
BasicValueEnum::IntValue(iv) => phi.add_incoming(&[(&iv, pred_bb)]),
|
||||||
|
BasicValueEnum::FloatValue(fv) => phi.add_incoming(&[(&fv, pred_bb)]),
|
||||||
|
BasicValueEnum::PointerValue(pv) => phi.add_incoming(&[(&pv, pred_bb)]),
|
||||||
|
_ => return Err("unsupported phi incoming value (seal)".to_string()),
|
||||||
}
|
}
|
||||||
}
|
|
||||||
val = coerce_to_type(codegen, phi, val)?;
|
|
||||||
if let Some(bb) = saved_block { codegen.builder.position_at_end(bb); }
|
|
||||||
let pred_bb = *bb_map.get(&bid).ok_or("pred bb missing")?;
|
|
||||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
|
||||||
let tys = phi
|
|
||||||
.as_basic_value()
|
|
||||||
.get_type()
|
|
||||||
.print_to_string()
|
|
||||||
.to_string();
|
|
||||||
eprintln!(
|
|
||||||
"[PHI] sealed add pred_bb={} val={} ty={}{}",
|
|
||||||
bid.as_u32(),
|
|
||||||
in_vid.as_u32(),
|
|
||||||
tys,
|
|
||||||
if snap_opt.is_some() { " (snapshot)" } else { " (vmap)" }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
match val {
|
|
||||||
BasicValueEnum::IntValue(iv) => phi.add_incoming(&[(&iv, pred_bb)]),
|
|
||||||
BasicValueEnum::FloatValue(fv) => phi.add_incoming(&[(&fv, pred_bb)]),
|
|
||||||
BasicValueEnum::PointerValue(pv) => phi.add_incoming(&[(&pv, pred_bb)]),
|
|
||||||
_ => return Err("unsupported phi incoming value (seal)".to_string()),
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// inputs に pred が見つからない場合でも、検証器は「各predに1エントリ」を要求する。
|
// Missing mapping for this predecessor: synthesize a typed zero
|
||||||
// ゼロ(型に応じた null/0)を合成して追加する(ログ付)
|
let pred_bb = *bb_map.get(&bid).ok_or("pred bb missing")?;
|
||||||
let pred_bb = *bb_map.get(&bid).ok_or("pred bb missing")?;
|
use inkwell::types::BasicTypeEnum as BT;
|
||||||
use inkwell::types::BasicTypeEnum as BT;
|
let bt = phi.as_basic_value().get_type();
|
||||||
let bt = phi.as_basic_value().get_type();
|
let z: BasicValueEnum = match bt {
|
||||||
let z: BasicValueEnum = match bt {
|
BT::IntType(it) => it.const_zero().into(),
|
||||||
BT::IntType(it) => it.const_zero().into(),
|
BT::FloatType(ft) => ft.const_zero().into(),
|
||||||
BT::FloatType(ft) => ft.const_zero().into(),
|
BT::PointerType(pt) => pt.const_zero().into(),
|
||||||
BT::PointerType(pt) => pt.const_zero().into(),
|
_ => return Err("unsupported phi type for zero synth (seal)".to_string()),
|
||||||
_ => return Err("unsupported phi type for zero synth (seal)".to_string()),
|
};
|
||||||
};
|
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
eprintln!(
|
||||||
eprintln!(
|
"[PHI] sealed add (synth) pred_bb={} zero-ty={}",
|
||||||
"[PHI] sealed add (synth) pred_bb={} zero-ty={}",
|
bid.as_u32(),
|
||||||
bid.as_u32(),
|
bt.print_to_string().to_string()
|
||||||
bt.print_to_string().to_string()
|
);
|
||||||
);
|
}
|
||||||
}
|
match z {
|
||||||
match z {
|
BasicValueEnum::IntValue(iv) => phi.add_incoming(&[(&iv, pred_bb)]),
|
||||||
BasicValueEnum::IntValue(iv) => phi.add_incoming(&[(&iv, pred_bb)]),
|
BasicValueEnum::FloatValue(fv) => phi.add_incoming(&[(&fv, pred_bb)]),
|
||||||
BasicValueEnum::FloatValue(fv) => phi.add_incoming(&[(&fv, pred_bb)]),
|
BasicValueEnum::PointerValue(pv) => phi.add_incoming(&[(&pv, pred_bb)]),
|
||||||
BasicValueEnum::PointerValue(pv) => phi.add_incoming(&[(&pv, pred_bb)]),
|
_ => return Err("unsupported phi incoming (synth)".to_string()),
|
||||||
_ => return Err("unsupported phi incoming (synth)".to_string()),
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -125,7 +125,7 @@ impl LLVMCompiler {
|
|||||||
> = HashMap::new();
|
> = HashMap::new();
|
||||||
// Snapshot of values at the end of each basic block (for sealed-SSA PHI wiring)
|
// Snapshot of values at the end of each basic block (for sealed-SSA PHI wiring)
|
||||||
let mut block_end_values: HashMap<crate::mir::BasicBlockId, HashMap<ValueId, BasicValueEnum>> = HashMap::new();
|
let mut block_end_values: HashMap<crate::mir::BasicBlockId, HashMap<ValueId, BasicValueEnum>> = HashMap::new();
|
||||||
// Build successors map (for optional sealed-SSA PHI wiring)
|
// Build successors and predecessors map (for optional sealed-SSA PHI wiring)
|
||||||
let mut succs: HashMap<crate::mir::BasicBlockId, Vec<crate::mir::BasicBlockId>> = HashMap::new();
|
let mut succs: HashMap<crate::mir::BasicBlockId, Vec<crate::mir::BasicBlockId>> = HashMap::new();
|
||||||
for (bid, block) in &func.blocks {
|
for (bid, block) in &func.blocks {
|
||||||
let v: Vec<crate::mir::BasicBlockId> = block.successors.iter().copied().collect();
|
let v: Vec<crate::mir::BasicBlockId> = block.successors.iter().copied().collect();
|
||||||
@ -388,7 +388,7 @@ impl LLVMCompiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if sealed_mode {
|
if sealed_mode {
|
||||||
instructions::flow::seal_block(&codegen, func, *bid, &succs, &bb_map, &phis_by_block, &block_end_values, &vmap)?;
|
instructions::flow::seal_block(&codegen, &mut cursor, func, *bid, &succs, &bb_map, &phis_by_block, &block_end_values, &vmap)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Finalize function: ensure every basic block is closed with a terminator.
|
// Finalize function: ensure every basic block is closed with a terminator.
|
||||||
|
|||||||
Reference in New Issue
Block a user