📋 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:
Selfhosting Dev
2025-09-12 14:59:03 +09:00
parent f307c4f7b1
commit a530b454f6
7 changed files with 278 additions and 87 deletions

View File

@ -20,6 +20,7 @@ Update — 2025-09-12 (LLVM flow + BB naming)
Hot Update — 2025-09-12 (quick)
- Flow: moved function verify to postlower; 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
- Strings: substring now accepts i64(handle) receiver (i2p); len/lastIndexOf stable
- Arrays: method_id未注入でも get/set/push/length を NyRT 経由で処理
@ -45,20 +46,20 @@ Next StepsSealed SSA 段階導入)
4) グリーン後、Sealed をデフォルトONにする前にスモーク一式で回帰確認。
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`
- 形式: `HashMap<BasicBlockId, HashMap<ValueId, BasicValueEnum>>`
- タイミング: 各BBの命令をすべて Lower した「直後」、終端命令を発行する「直前」に `vmap.clone()` を保存
- 目的: `seal_block` で pred 終端時点の値を安定取得する(現在の vmap 直接参照をやめる)
- [ ] `seal_block` をスナップショット参照に切替
- [x] `seal_block` をスナップショット参照に切替
- 対象: `src/backend/llvm/compiler/codegen/instructions/flow.rs::seal_block`
- 取得: `block_end_values[bid].get(in_vid)` を用いて `val` を取得
- フォールバック: もしスナップショットが無ければ(例外ケース)従来の `vmap` を参照し、警告ログを出す
- ログ: `NYASH_LLVM_TRACE_PHI=1` 時に `[PHI] sealed add pred_bb=.. val=.. ty=.. (snapshot)` と明示
- [ ] 非 sealed 経路の維持(回帰防止)
- [x] 非 sealed 経路の維持(回帰防止)
- `emit_jump/emit_branch` は sealed=OFF の時のみ incoming を追加(現状仕様を維持)
- sealed=ON の時は incoming 配線は一切行わず、`seal_block` のみで完結
- [ ] 型整合coerceの継続強化
- [x] 型整合coerceの継続強化
- 対象: `src/backend/llvm/compiler/codegen/instructions/flow.rs::coerce_to_type`
- 方針: PHI の型は i8* 優先String/Box/Array を含む場合。ptr/int 混在は明示 cast で橋渡し
- 検討: i1 ブリッジboolの zext/trunc の置き場所は PHI 外側に寄せる(必要時)
@ -95,6 +96,30 @@ Note
- フェーズ1では「終端APIと位置ずれの構造化」の最小適用に留め、フォールバック最終unreachableを併用
- フェーズ2で with_block の適用範囲を広げ、余剰なガード・分岐フォールバックを削除していく(ソースは小さくシンプルに)
Progress — 2025-09-12Sealed + RAII 最小導入)
- Sealed: block_end_valuesBB内定義のみをフィルタを導入し、incoming は pred 終端時点の snapshot から配線
- Cast 挿入: pred 終端直前position_beforeに限定、終端後挿入を回避
- BuilderCursor: emit_return/emit_jump/emit_branch を構造化closed ブロックへの挿入を禁止)
- Call/BoxCall: 実引数を callee の型へ coercei2p/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)
- Sealed SSA 入れ替え(安全に段階導入)
- Blockごとに `sealed: bool``incomplete_phis: Map<Var, Phi>` を保持

View File

@ -14,6 +14,25 @@ MIR 13命令の美しさを最大限に活かし、外部コンパイラ依存
4. **エコシステムの自立**: Nyashだけで完結する開発環境
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完了後
- **推定開始**: 2026年前半
- **推定期間**: 6-8ヶ月
- **早期着手**: YAML自動生成は今すぐ開始可能
- **現在進行中**2025年9月
- **Phase 15.2**: LLVM独立化実装中
- **Phase 15.3**: Nyashコンパイラ2025年後半
- **Phase 15.4**: VM層Nyash化2026年前半
- **Phase 15.5**: ABI移行LLVM完成後
## 💡 期待される成果

View File

@ -18,23 +18,29 @@ This roadmap is a living checklist to advance Phase 15 with small, safe boxes. U
## Next (small boxes)
1) Standard Ny std impl (P0→実体化)
- Implement P0 methods for string/array/map in Nyash (keep NyRT primitives minimal)
- Enable via `nyash.toml` `[ny_plugins]` (optin); extend `tools/jit_smoke.sh`
2) Ny compiler MVP (Ny→MIR on JIT path)
- Ny tokenizer + recursivedescent 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 alwayson (JIT, plugins disabled); Plugins as optional job (strict off by default)
5) LLVM Native EXE Generation
1) LLVM Native EXE Generation (Phase 15.2) 🚀
- LLVM backend object → executable pipeline completion
- Separate `nyash-llvm-compiler` crate (reduce main build weight)
- Input: MIR (JSON/binary) → Output: native executable
- Link with nyrt runtime (static/dynamic options)
- 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]` (optin); extend `tools/jit_smoke.sh`
3) Ny compiler MVP (Ny→MIR on JIT path) (Phase 15.3) 🎯
- Ny tokenizer + recursivedescent 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 alwayson (JIT, plugins disabled); Plugins as optional job (strict off by default)
## Later (incremental)

View File

@ -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. 入力MIRJSON/バイナリ)
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プロトタイプ設計**
これが現実的で革新的なロードマップにゃ!

View File

@ -26,6 +26,8 @@ impl<'ctx, 'b> BuilderCursor<'ctx, 'b> {
let prev_bb = self.cur_llbb;
// Preserve previous closed state
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);
let r = body(self);
@ -39,6 +41,13 @@ impl<'ctx, 'b> BuilderCursor<'ctx, 'b> {
if let (Some(pid), Some(closed)) = (prev_bid, prev_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
}

View File

@ -250,8 +250,9 @@ fn coerce_to_type<'ctx>(
}
/// 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>,
cursor: &mut BuilderCursor<'ctx, 'b>,
func: &MirFunction,
bid: BasicBlockId,
succs: &HashMap<BasicBlockId, Vec<BasicBlockId>>,
@ -269,7 +270,8 @@ pub(in super::super) fn seal_block<'ctx>(
for sb in slist {
if let Some(pl) = phis_by_block.get(sb) {
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
let snap_opt = block_end_values
.get(&bid)
@ -304,13 +306,11 @@ pub(in super::super) fn seal_block<'ctx>(
}
}
};
// Ensure any required casts are inserted BEFORE the predecessor's terminator
// Save and restore current insertion point around coercion
// Insert any required casts in the predecessor block, right before its terminator
let saved_block = codegen.builder.get_insert_block();
if let Some(pred_llbb) = bb_map.get(&bid) {
let term = unsafe { pred_llbb.get_terminator() };
if let Some(t) = term {
// Insert casts right before the terminator of predecessor
codegen.builder.position_before(&t);
} else {
codegen.builder.position_at_end(*pred_llbb);
@ -340,8 +340,7 @@ pub(in super::super) fn seal_block<'ctx>(
_ => return Err("unsupported phi incoming value (seal)".to_string()),
}
} else {
// inputs に pred が見つからない場合でも、検証器は「各predに1エントリ」を要求する。
// ゼロ(型に応じた null/0を合成して追加するログ付
// Missing mapping for this predecessor: synthesize a typed zero
let pred_bb = *bb_map.get(&bid).ok_or("pred bb missing")?;
use inkwell::types::BasicTypeEnum as BT;
let bt = phi.as_basic_value().get_type();

View File

@ -125,7 +125,7 @@ impl LLVMCompiler {
> = HashMap::new();
// 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();
// 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();
for (bid, block) in &func.blocks {
let v: Vec<crate::mir::BasicBlockId> = block.successors.iter().copied().collect();
@ -388,7 +388,7 @@ impl LLVMCompiler {
}
}
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.