docs(phase131): LLVM SSOT強化 + ConsoleBox調査完了 + Phase86-90要約
Phase 131-1 完了: LLVM exe line SSOT 強化 - phase87-selfhost-llvm-exe-line.md に 4セクション追加(+300行) - 環境変数リファレンス(14変数) - 成功/失敗基準(exit code 0/1/2/3) - コンパイラモード説明(harness vs crate) - デバッグセクション拡張 - "1コマンドで再現" 可能な状態を確立 Phase 131-2 完了: ConsoleBox 問題調査 - VM の 3重登録経路を特定(BoxFactoryRegistry/UnifiedRegistry/Builtin) - LLVM backend は Phase 133 で解決済み - 3つのドキュメント作成: - phase131-2-consolebox-investigation.md(詳細調査) - phase131-2-summary.md(エグゼクティブサマリ) - phase131-2-box-resolution-map.md(Box 解決マップ) Phase 86-90 完了: Loop frontends 要約 - phase86-90-loop-frontends-summary.md 追加 - Pattern4/ContinueReturn/ParseStringComposite の経緯を1枚に集約 - INDEX から導線追加 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -11,7 +11,7 @@
|
||||
|
||||
---
|
||||
|
||||
## 今の状態(Phase 74–89 まで到達)
|
||||
## 今の状態(Phase 74–90 まで到達)
|
||||
|
||||
- Scope/BindingId の段階移行(dev-only)は Pattern2/3/4 まで配線済み(dual-path 維持)。
|
||||
- Pattern2 の promoted carriers(DigitPos/Trim)について ExitLine 契約(ConditionOnly を exit PHI から除外)を E2E で固定済み。
|
||||
@ -19,10 +19,12 @@
|
||||
- **LLVM exe line SSOT 確立**: `tools/build_llvm.sh` を使用した .hako → executable パイプライン標準化完了。
|
||||
- **Phase 88 完了**: continue + 可変ステップ(i=i+const 差分)を dev-only fixture で固定、StepCalculator Box 抽出。
|
||||
- **Phase 89 完了**: P0(ContinueReturn detector)+ P1(lowering 実装)完了。
|
||||
- **Phase 90 完了**: ParseStringComposite + `Null` literal + ContinueReturn(同一値の複数 return-if)を dev-only fixture で固定。
|
||||
- `cargo test --release --lib` は PASS を維持(993 passed、退行なし)。
|
||||
|
||||
参照:
|
||||
- `docs/development/current/main/10-Now.md`
|
||||
- `docs/development/current/main/phase86-90-loop-frontends-summary.md`
|
||||
- `docs/development/current/main/phase73-scope-manager-design.md`
|
||||
- `docs/development/current/main/phase80-bindingid-p3p4-plan.md`
|
||||
- `docs/development/current/main/phase81-pattern2-exitline-contract.md`
|
||||
@ -33,30 +35,30 @@
|
||||
|
||||
## 次の指示書(優先順位)
|
||||
|
||||
### P0: Phase 90 - _parse_string 合成 fixture
|
||||
### P0: Docs 整備(数の増殖を止める)
|
||||
|
||||
**目的**: continue(escape) + return(close quote) の合成パターンを dev-only fixture で固定
|
||||
**目的**: SSOT への集約と導線整備(Phase 86–90 の情報が散らばらない状態にする)
|
||||
|
||||
**実装順序**(最短・事故減):
|
||||
1. 合成 fixture 設計(制御構造のみ、文字列処理なし)
|
||||
- continue(escape) + return(close quote) + str += ... + p += 1/2
|
||||
- 期待値が一意(例: n=10 で return 前に acc=4)
|
||||
2. ShapeGuard 追加(誤爆防止)
|
||||
- PatternParseStringCompositeMinimal (dev shape)
|
||||
- P4/ContinueReturn と区別する条件
|
||||
3. lowering 実装(新しい箱)
|
||||
- parse_string_composite_pattern.rs(例)
|
||||
- Fail-Fast: 対応形以外は即エラー
|
||||
4. normalized_dev テスト(2本)
|
||||
- structured vs canonical 一致
|
||||
- 期待値一致
|
||||
やること:
|
||||
1. Phase 86–90 の要約を 1 ファイルに集約(SSOT)
|
||||
- `docs/development/current/main/phase86-90-loop-frontends-summary.md`
|
||||
2. INDEX から要約へ導線を追加(迷子対策)
|
||||
- `docs/development/current/main/01-JoinIR-Selfhost-INDEX.md`
|
||||
3. `10-Now.md` / `CURRENT_TASK.md` の断定・重複を “要約へのリンク” に寄せる
|
||||
|
||||
**受け入れ基準**:
|
||||
- `NYASH_JOINIR_NORMALIZED_DEV_RUN=1 cargo test --features normalized_dev --test normalized_joinir_min` PASS
|
||||
- `cargo test --release --lib` PASS(退行なし)
|
||||
- dev-only のみ(canonical には入れない)
|
||||
- 読み始め導線が `01-JoinIR-Selfhost-INDEX.md` から辿れる
|
||||
- `10-Now.md` と `CURRENT_TASK.md` は “最新の入口” として機能する(詳細は要約へ)
|
||||
|
||||
### P1: JoinIR / Selfhost depth-2 の前進(Phase 91 候補)
|
||||
### P1: Loop Canonicalizer の設計(docs-only、外部検討待ち)
|
||||
|
||||
**目的**: ループ形状の組み合わせ爆発を抑えるための “前処理パス” を設計し、SSOT を定める
|
||||
|
||||
注意:
|
||||
- 実装は急がず、設計(SSOT)を先に固める
|
||||
- 既定挙動は変えない(dev-only で段階投入する)
|
||||
|
||||
### P2: JoinIR / Selfhost depth-2 の前進(Phase 91 候補)
|
||||
|
||||
目的:
|
||||
- JsonParserBox の残り複合ループを JoinIR 対応する。
|
||||
@ -70,7 +72,7 @@
|
||||
受け入れ基準:
|
||||
- 代表ケースが 1 コマンドで再現可能(CI は増やさない、quick を重くしない)。
|
||||
|
||||
### P2: Ownership/Relay runtime 対応の再開(Phase 92 候補)
|
||||
### P3: Ownership/Relay runtime 対応の再開(Phase 92 候補)
|
||||
|
||||
目的:
|
||||
- multihop/merge relay を “runtime でも” 受理できるところまで契約を伸ばす(Fail-Fast の段階解除)。
|
||||
@ -78,23 +80,9 @@
|
||||
受け入れ基準:
|
||||
- dev-only 既定OFF のまま、既存ラインを壊さない。
|
||||
|
||||
### Done: Phase 88-89 完了
|
||||
### Done: Phase 86–90(Loop frontends)
|
||||
|
||||
**Phase 88**:
|
||||
- continue + 可変ステップ(i=i+const 差分)を dev-only fixture で固定
|
||||
- StepCalculator Box 抽出(+6 unit tests、再利用可能)
|
||||
- エラーメッセージ詳細化、Fail-Fast 仕様固定
|
||||
|
||||
**Phase 89 P0**:
|
||||
- Pattern4 detector 締め(Select 必須 + conditional Jump exactly 1)
|
||||
- LoopPattern::ContinueReturn enum + shape 追加
|
||||
- canonical には入れない(dev-only、誤爆防止)
|
||||
|
||||
**Phase 89 P1**:
|
||||
- continue_return_pattern.rs 実装(457行、StepCalculator 再利用)
|
||||
- normalized_dev tests +2(vm_bridge + 期待値)
|
||||
|
||||
**Impact**: 993 lib tests + 61 normalized_dev tests 全パス、箱化スコア 10/10
|
||||
- まとめ(SSOT): `docs/development/current/main/phase86-90-loop-frontends-summary.md`
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -19,6 +19,8 @@ Scope: JoinIR と Selfhost(Stage‑B/Stage‑1/Stage‑3)に関する「最
|
||||
- `docs/development/current/main/10-Now.md`
|
||||
- 「JoinIR / Loop / If ライン」
|
||||
- 「JsonParser / Selfhost depth‑2 ライン」
|
||||
- Phase 86–90(Loop frontends)の要約(1枚)
|
||||
- `docs/development/current/main/phase86-90-loop-frontends-summary.md`
|
||||
|
||||
---
|
||||
|
||||
@ -62,6 +64,8 @@ JoinIR の箱構造と責務、ループ/if の lowering パターンを把握
|
||||
- `docs/development/current/main/phase33-16-INDEX.md`
|
||||
- `docs/development/current/main/phase33-17-joinir-modularization-analysis.md`
|
||||
- `docs/development/current/main/phase183-selfhost-depth2-joinir-status.md`
|
||||
9. Phase 86–90(Loop frontends)の要約(1枚)
|
||||
- `docs/development/current/main/phase86-90-loop-frontends-summary.md`
|
||||
|
||||
Phase 文書は歴史や検証ログも含むので、「JoinIR の現役設計を確認した上で、必要なときだけ掘る」という前提で読んでね。
|
||||
|
||||
@ -92,6 +96,9 @@ Phase 文書は歴史や検証ログも含むので、「JoinIR の現役設計
|
||||
- → 2章の 1〜3 をこの順番で読む。
|
||||
- Selfhost のビルド / 実行フローで迷っているとき
|
||||
- → 3章の 1〜3 をこの順番で読む。
|
||||
- VM backend の Box 解決(ConsoleBox / plugin / builtin)で迷っているとき
|
||||
- → `docs/development/current/main/phase131-2-box-resolution-map.md`(経路図)
|
||||
- → `docs/development/current/main/phase131-2-summary.md`(要点)
|
||||
- 「この Phase 文書は現役か?」で迷ったとき
|
||||
- → まず `docs/development/current/main/10-Now.md` と
|
||||
`docs/development/current/main/30-Backlog.md` を確認し、そこで名前が挙がっている Phase 文書を優先して読んでね。
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Self Current Task — Now (main)
|
||||
|
||||
## 2025‑12‑06:現状サマリ
|
||||
## 2025‑12‑14:現状サマリ
|
||||
|
||||
### JoinIR / Loop / If ライン
|
||||
|
||||
@ -17,103 +17,13 @@
|
||||
- 残課題(JoinIR ライン):
|
||||
- JoinIR→MIR merge の一般化(複雑な Select/PHI パターンの統合)
|
||||
- JsonParserBox など実アプリ側での長期運用テスト
|
||||
- Phase 86–90(Loop frontends)まとめ(1枚):
|
||||
- `docs/development/current/main/phase86-90-loop-frontends-summary.md`
|
||||
|
||||
### Phase 86: Carrier Init Builder + Error Tags (2025-12-13) ✅
|
||||
### Phase 86–90: Loop frontends(要約)
|
||||
|
||||
**Status**: COMPLETE - SSOT 確立完了
|
||||
|
||||
**Achievements**:
|
||||
- Carrier Initialization Builder: CarrierInit → ValueId 生成を単一関数化 (carrier_init_builder.rs, +8 tests)
|
||||
- Error Message Centralization: JoinIR エラータグを SSOT 化 (error_tags.rs, +5 tests)
|
||||
- DebugOutputBox Migration: 全 JOINIR_DEBUG 使用箇所を DebugOutputBox に統一
|
||||
|
||||
**Impact**:
|
||||
- Tests: 987/987 PASS (+13 unit tests from Phase 86)
|
||||
- SSOT modules: carrier_init_builder, error_tags (一貫性向上)
|
||||
- Code quality: Single Responsibility, Testability First principles validated
|
||||
|
||||
**Reference**: phase78-85-boxification-feedback.md, carrier_init_builder.rs, error_tags.rs
|
||||
|
||||
### Phase 87: LLVM Exe Line SSOT (2025-12-13) ✅
|
||||
|
||||
**Status**: COMPLETE - Pipeline 確立完了
|
||||
|
||||
**Achievements**:
|
||||
- SSOT established: `tools/build_llvm.sh` - Single pipeline for .hako → executable
|
||||
- Integration smoke: phase87_llvm_exe_min.sh (exit code 42 verification)
|
||||
- Minimal fixture: apps/tests/phase87_llvm_exe_min.hako (5 lines, return 42)
|
||||
- Full documentation: phase87-selfhost-llvm-exe-line.md (troubleshooting + advanced usage)
|
||||
|
||||
**Policy**:
|
||||
- ✅ Use build_llvm.sh SSOT (no duplication/fragmentation)
|
||||
- ✅ Integration smoke only (not in quick profile)
|
||||
- ✅ SKIP if LLVM unavailable (graceful degradation)
|
||||
- ✅ Exit code verification (stdout-independent testing)
|
||||
|
||||
**Impact**:
|
||||
- Standard procedure: `tools/build_llvm.sh input.hako -o output_exe`
|
||||
- Prerequisites documented: llvm-config-18, llvmlite, LLVM features
|
||||
- Integration test: PASS (or SKIP if no LLVM)
|
||||
|
||||
**Reference**: phase87-selfhost-llvm-exe-line.md
|
||||
|
||||
### Phase 88: Continue + 可変ステップ (2025-12-14) ✅
|
||||
|
||||
**Status**: COMPLETE - dev-only fixture 固定完了
|
||||
|
||||
**Achievements**:
|
||||
- continue + 可変ステップ(i=i+const 差分加算)を dev-only fixture で固定
|
||||
- continue 分岐側での acc 更新を許可
|
||||
- Fail-Fast 仕様固定(非 const の i 更新を拒否)
|
||||
- StepCalculator Box 抽出(+6 unit tests、再利用可能な純関数設計)
|
||||
- エラーメッセージ詳細化(Expected/Found/Hint 形式)
|
||||
|
||||
**Impact**:
|
||||
- Tests: 993 lib tests PASS, 60 normalized_dev tests PASS
|
||||
- 箱化スコア: 9/10(Phase 33 Box 理論の模範実装)
|
||||
- コード削減: 5%(321→305行、重複ロジック削除)
|
||||
|
||||
**Reference**:
|
||||
- continue_pattern.rs, step_calculator.rs
|
||||
- jsonparser_unescape_string_step2_min.program.json
|
||||
|
||||
### Phase 89 P0: Continue + Early Return Pattern Detector (2025-12-14) ✅
|
||||
|
||||
**Status**: COMPLETE - Dev-only detector 確立完了
|
||||
|
||||
**Achievements**:
|
||||
- Pattern4 detector 締め(Select 必須 + conditional Jump exactly 1)
|
||||
- LoopPattern::ContinueReturn enum 追加
|
||||
- NormalizedDevShape::PatternContinueReturnMinimal 追加(detector: Select + conditional Jumps >= 2)
|
||||
- Fail-Fast: UnimplementedPattern error(フォールバックなし)
|
||||
- canonical には入れない(dev-only、誤爆防止)
|
||||
|
||||
**Impact**:
|
||||
- Pattern4 誤爆防止(continue+return を正しく区別)
|
||||
- Tests: 987 lib tests PASS
|
||||
|
||||
**Reference**:
|
||||
- shape_guard.rs, loop_patterns/mod.rs, analysis.rs
|
||||
- pattern_continue_return_min.program.json (fixture)
|
||||
|
||||
### Phase 89 P1: ContinueReturn Lowering Implementation (2025-12-14) ✅
|
||||
|
||||
**Status**: COMPLETE - 独立箱実装完了
|
||||
|
||||
**Achievements**:
|
||||
- continue_return_pattern.rs 実装(457行、StepCalculator 再利用)
|
||||
- Continue + Early Return の JoinIR lowering(loop 内 return 対応)
|
||||
- Fail-Fast 原則徹底(5箇所検証)
|
||||
- fixtures.rs に build_pattern_continue_return_min 追加
|
||||
- normalized_dev tests +2(vm_bridge + 期待値 n=10→acc=4)
|
||||
|
||||
**Impact**:
|
||||
- Tests: 993 lib tests PASS, 61 normalized_dev tests PASS (+2)
|
||||
- 箱化原則:単一責任・境界明確・再利用性高
|
||||
|
||||
**Reference**:
|
||||
- continue_return_pattern.rs, fixtures.rs
|
||||
- tests/normalized_joinir_min/shapes.rs
|
||||
- Phase 86–90 は “dev-only fixtures + shape guard + fail-fast” で段階的に固定済み。
|
||||
- 具体の fixture / shape / 未検証は `docs/development/current/main/phase86-90-loop-frontends-summary.md` を SSOT とする。
|
||||
|
||||
### Scope / BindingId(dev-only の段階移行ライン)
|
||||
|
||||
@ -161,6 +71,8 @@
|
||||
- Ring0.log(internal/dev)
|
||||
- println!(test 専用)
|
||||
の 3 層が `logging_policy.md` で整理済み。JoinIR/Loop trace も同ドキュメントに集約。
|
||||
- VM backend の Box 解決(UnifiedBoxRegistry / BoxFactoryRegistry)の経路図:
|
||||
- `docs/development/current/main/phase131-2-box-resolution-map.md`
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -1,5 +1,12 @@
|
||||
# Self Current Task — Backlog (main)
|
||||
|
||||
直近のループ前線(Phase 86–90, dev-only fixtures)は完了し、状況は `docs/development/current/main/10-Now.md` と
|
||||
`docs/development/current/main/phase86-90-loop-frontends-summary.md` に集約したよ。
|
||||
|
||||
設計検討(docs-only, まだ未決定):
|
||||
- Loop Canonicalizer / LoopSkeleton(組み合わせ爆発を抑える前処理パス)
|
||||
- Loop Corpus Extractor(実コード抽出→差分検知で fixture 化を支援)
|
||||
|
||||
短期(JoinIR/selfhost ライン 第3章 - LLVM統合)
|
||||
- ✅ Phase 130: JoinIR → LLVM ベースライン確立(完了 2025-12-04)
|
||||
- 🎯 **Phase 131: JoinIR→LLVM 個別修正ライン**(次の最優先タスク)
|
||||
|
||||
298
docs/development/current/main/phase131-2-box-resolution-map.md
Normal file
298
docs/development/current/main/phase131-2-box-resolution-map.md
Normal file
@ -0,0 +1,298 @@
|
||||
# Phase 131-2: Box Resolution Mapping - 現状 vs 理想
|
||||
|
||||
## 🗺️ 現状マップ(VM Backend)
|
||||
|
||||
```
|
||||
MIR NewBox ConsoleBox
|
||||
↓
|
||||
MirInterpreter::handle_new_box() (src/backend/mir_interpreter/handlers/boxes.rs)
|
||||
↓
|
||||
[分岐点 1: Fast path?]
|
||||
├─ Yes (NYASH_VM_FAST=1 + StringBox) → Direct creation(bench/profile-only)
|
||||
└─ No → Continue
|
||||
↓
|
||||
[分岐点 2: Provider Lock]
|
||||
├─ guard_before_new_box() → OK/NG
|
||||
└─ Continue
|
||||
↓
|
||||
get_global_unified_registry() (src/runtime/unified_registry.rs)
|
||||
↓
|
||||
UnifiedBoxRegistry::create_box("ConsoleBox", args) (src/box_factory/mod.rs)
|
||||
↓
|
||||
[分岐点 3: FactoryPolicy による優先順位]
|
||||
├─ BuiltinBoxFactory → builtin_impls(src/box_factory/builtin_impls/*)
|
||||
└─ PluginBoxFactory → BoxFactoryRegistry → PluginHost
|
||||
(src/box_factory/plugin.rs) (src/runtime/box_registry.rs)
|
||||
※ plugin_loader_unified が BoxFactoryRegistry を populate する
|
||||
(src/runtime/plugin_loader_unified.rs)
|
||||
```
|
||||
|
||||
**問題点**:
|
||||
- ❌ 「Box 解決 SSOT」が 1 箇所に見えない(UnifiedBoxRegistry と BoxFactoryRegistry に分散)
|
||||
- ❌ 優先順位が “FactoryPolicy + provider mapping” の合成で、全体像が追いにくい
|
||||
- ❌ `NYASH_VM_FAST` の特例が入口にあり、観測なしだと混乱しやすい
|
||||
|
||||
## 🎯 理想マップ(SSOT 化後)
|
||||
|
||||
```
|
||||
MIR NewBox ConsoleBox
|
||||
↓
|
||||
handle_new_box()
|
||||
↓
|
||||
CoreBoxRegistry::create("ConsoleBox", args)
|
||||
↓
|
||||
[SSOT: 登録情報検索]
|
||||
CoreBoxId::Console in registry?
|
||||
├─ Yes → Continue
|
||||
└─ No → Error: "ConsoleBox not registered" (Fail-Fast!)
|
||||
↓
|
||||
[優先順位: Plugin > Builtin]
|
||||
registered_provider?
|
||||
├─ Plugin → PluginHost.create_box()
|
||||
│ ↓
|
||||
│ TypeBox v2 FFI
|
||||
│ ↓
|
||||
│ console_invoke_id()
|
||||
│ ↓
|
||||
│ ConsoleInstance::new()
|
||||
│
|
||||
└─ Builtin → builtin::ConsoleBox::new()
|
||||
↓
|
||||
VMValue::BoxRef(ConsoleBox)
|
||||
```
|
||||
|
||||
**改善点**:
|
||||
- ✅ 単一の Box 解決ルート(SSOT)
|
||||
- ✅ CoreBoxId による型安全性
|
||||
- ✅ 明確な優先順位(Plugin > Builtin)
|
||||
- ✅ Fail-Fast(見つからない = エラー)
|
||||
|
||||
## 📊 VM vs LLVM 比較表
|
||||
|
||||
| 項目 | VM Backend(現状) | LLVM Backend(Phase 133) | 理想(SSOT化後) |
|
||||
|------|-------------------|--------------------------|-----------------|
|
||||
| **Box 登録** | ⚠️ UnifiedBoxRegistry + BoxFactoryRegistry | ✅ TypeRegistry + Plugin FFI | ✅ CoreBoxRegistry |
|
||||
| **メソッド解決** | ❌ 複数経路(boxes.rs 分岐) | ✅ ConsoleLlvmBridge 箱化 | ✅ 箱化モジュール |
|
||||
| **ABI** | NyashBox trait | i8* + i64 (llvmlite) | ✅ 統一(TypeRegistry SSOT) |
|
||||
| **優先順位** | ❓ 不明確 | ✅ Plugin > Builtin | ✅ 明示的優先順位 |
|
||||
| **Fail-Fast** | ❌ フォールバック多数 | ✅ エラー即座に報告 | ✅ Fail-Fast 原則 |
|
||||
| **型安全性** | ❌ 文字列ベース | ✅ TypeBox v2 | ✅ CoreBoxId enum |
|
||||
| **SSOT 化** | ❌ 分散 | ✅ 完了(Phase 133) | ✅ 完了(目標) |
|
||||
|
||||
## 🔍 登録システム詳細比較
|
||||
|
||||
### 現状(VM Backend)
|
||||
|
||||
```rust
|
||||
// System 1: BoxFactoryRegistry(plugin provider mapping)
|
||||
impl BoxFactoryRegistry {
|
||||
pub fn create_box(&self, name: &str, args: &[Box<dyn NyashBox>])
|
||||
-> Result<Box<dyn NyashBox>, String>
|
||||
{
|
||||
let provider = self.get_provider(name)?;
|
||||
match provider {
|
||||
BoxProvider::Builtin(constructor) => constructor(args),
|
||||
BoxProvider::Plugin(plugin_name) =>
|
||||
self.create_plugin_box(&plugin_name, name, args),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// System 2: UnifiedBoxRegistry(VM NewBox の入口)
|
||||
// src/backend/mir_interpreter/handlers/boxes.rs::handle_new_box
|
||||
let reg = crate::runtime::unified_registry::get_global_unified_registry();
|
||||
let created = reg.lock().unwrap().create_box(box_type, &converted)?;
|
||||
|
||||
// Builtins は UnifiedBoxRegistry 内で BuiltinBoxFactory が担当し、
|
||||
// builtin_impls/* の実装へ委譲される(外部フォールバックではない)。
|
||||
```
|
||||
|
||||
**問題**:
|
||||
- 3つのシステムの関係が不明
|
||||
- どれが優先されるのか不明確
|
||||
- エラーハンドリングが統一されていない
|
||||
|
||||
### Phase 133(LLVM Backend)- 成功モデル
|
||||
|
||||
```python
|
||||
# ConsoleLlvmBridge: 単一の箱化モジュール
|
||||
def emit_console_call(builder, module, method_name, args, ...):
|
||||
if method_name not in CONSOLE_METHODS:
|
||||
return False # Fail-Fast: 即座に不明通知
|
||||
|
||||
runtime_fn_name = CONSOLE_METHODS[method_name]
|
||||
callee = _declare(module, runtime_fn_name, i64, [i8p])
|
||||
builder.call(callee, [arg0_ptr])
|
||||
return True # 成功
|
||||
```
|
||||
|
||||
**成功要因**:
|
||||
- ✅ CONSOLE_METHODS が SSOT(唯一の真実)
|
||||
- ✅ 箱化モジュール(1箇所に集約)
|
||||
- ✅ Fail-Fast(不明メソッド = False 即座)
|
||||
- ✅ TypeRegistry との ABI 一致
|
||||
|
||||
### 理想(SSOT化後の VM Backend)
|
||||
|
||||
```rust
|
||||
// CoreBoxRegistry: 単一の SSOT
|
||||
pub struct CoreBoxRegistry {
|
||||
core_boxes: RwLock<HashMap<CoreBoxId, CoreBoxEntry>>,
|
||||
user_boxes: RwLock<HashMap<String, UserBoxEntry>>,
|
||||
}
|
||||
|
||||
impl CoreBoxRegistry {
|
||||
pub fn create(&self, box_name: &str, args: &[Box<dyn NyashBox>])
|
||||
-> Result<Box<dyn NyashBox>, RuntimeError>
|
||||
{
|
||||
// 1. CoreBoxId 変換
|
||||
let box_id = CoreBoxId::from_name(box_name)
|
||||
.ok_or_else(|| RuntimeError::InvalidOperation {
|
||||
message: format!("Unknown Box type: {}", box_name)
|
||||
})?;
|
||||
|
||||
// 2. 登録情報取得(SSOT)
|
||||
let entry = self.core_boxes.read().unwrap()
|
||||
.get(&box_id)
|
||||
.ok_or_else(|| RuntimeError::InvalidOperation {
|
||||
message: format!("Box not registered: {:?}", box_id)
|
||||
})?
|
||||
.clone();
|
||||
|
||||
// 3. 優先順位に従って生成
|
||||
match entry.provider {
|
||||
CoreBoxProvider::Plugin { plugin_name, type_id } => {
|
||||
self.plugin_host.create_box(&plugin_name, type_id, args)
|
||||
}
|
||||
CoreBoxProvider::Builtin { constructor } => {
|
||||
constructor(args)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**改善点**:
|
||||
- ✅ CoreBoxId による型安全性
|
||||
- ✅ 単一の登録マップ(SSOT)
|
||||
- ✅ 明示的な優先順位(Plugin > Builtin)
|
||||
- ✅ Fail-Fast(登録なし = エラー)
|
||||
|
||||
## 🎯 Phase 133 の教訓
|
||||
|
||||
### 成功パターン: 箱化モジュール化
|
||||
|
||||
```
|
||||
Before (Phase 132): 40 行の分岐が boxcall.py に埋め込み
|
||||
After (Phase 133): 1 行の箱化呼び出し
|
||||
|
||||
# Before
|
||||
if method_name in ("print", "println", "log"):
|
||||
# ... 40 行のロジック ...
|
||||
|
||||
# After
|
||||
if emit_console_call(builder, module, method_name, args, ...):
|
||||
return
|
||||
```
|
||||
|
||||
**成果**:
|
||||
- 分岐を 1 箇所に集約
|
||||
- テスト容易性向上
|
||||
- レガシー削除が簡単
|
||||
|
||||
### VM Backend への適用
|
||||
|
||||
```rust
|
||||
// Before(現状): 複数経路の分岐
|
||||
let reg = unified_registry::get_global_unified_registry();
|
||||
let created = reg.lock().unwrap().create_box(box_type, &converted)?;
|
||||
|
||||
// After(Phase 131-3): 箱化モジュール化
|
||||
let created = CoreBoxRegistry::global()
|
||||
.create(box_type, &converted)?;
|
||||
```
|
||||
|
||||
## 📋 Phase 131-3 実装ガイド
|
||||
|
||||
### Step 1: 現状SSOTの所在を固定(入口と接続)
|
||||
|
||||
```bash
|
||||
# VM NewBox の入口(ここから追う)
|
||||
rg "fn handle_new_box\\(" src/backend/mir_interpreter/handlers/boxes.rs
|
||||
|
||||
# global accessor と registry 本体
|
||||
rg "get_global_unified_registry\\(" src/runtime/unified_registry.rs
|
||||
rg "struct UnifiedBoxRegistry" src/box_factory/mod.rs
|
||||
|
||||
# plugin 側の provider mapping
|
||||
rg "struct BoxFactoryRegistry" src/runtime/box_registry.rs
|
||||
```
|
||||
|
||||
**前提**:
|
||||
- UnifiedBoxRegistry は既に存在する(NewBox の入口)。
|
||||
- BoxFactoryRegistry は PluginBoxFactory の provider mapping として間接利用される。
|
||||
|
||||
### Step 2: CoreBoxId 統合
|
||||
|
||||
```rust
|
||||
// CoreBoxId に基づく検証ロジック追加
|
||||
impl CoreBoxRegistry {
|
||||
pub fn validate_on_startup(&self, profile: &RuntimeProfile)
|
||||
-> Result<(), String>
|
||||
{
|
||||
for box_id in CoreBoxId::iter() {
|
||||
if box_id.is_required_in(&profile) && !self.has(box_id) {
|
||||
return Err(format!("Missing core_required box: {:?}", box_id));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: プラグイン優先順位の明確化
|
||||
|
||||
```rust
|
||||
// 登録時に優先順位を決定
|
||||
impl CoreBoxRegistry {
|
||||
/// プラグイン設定適用(既存ビルトインを上書き)
|
||||
pub fn apply_plugin_config(&mut self, config: &PluginConfig) {
|
||||
for (box_name, plugin_name) in &config.plugins {
|
||||
if let Some(box_id) = CoreBoxId::from_name(box_name) {
|
||||
self.register_plugin(box_id, plugin_name); // 上書き
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: Fail-Fast の徹底
|
||||
|
||||
```rust
|
||||
// ❌ 削除対象: フォールバックロジック
|
||||
if let Err(_) = create_plugin_box() {
|
||||
create_builtin_box() // 隠蔽!
|
||||
}
|
||||
|
||||
// ✅ 追加: 即座にエラー
|
||||
create_plugin_box()
|
||||
.map_err(|e| VMError::InvalidInstruction(
|
||||
format!("ConsoleBox plugin failed: {:?}. Check nyash.toml", e)
|
||||
))?
|
||||
```
|
||||
|
||||
## ✅ チェックリスト(Phase 131-3)
|
||||
|
||||
- [ ] UnifiedBoxRegistry / BoxFactoryRegistry の責務境界を SSOT として固定
|
||||
- [ ] CoreBoxRegistry を新設するなら “入口SSOT” を 1 箇所にする(NewBox から見える形)
|
||||
- [ ] CoreBoxId 統合(型安全性)
|
||||
- [ ] プラグイン優先順位の明確化(Plugin > Builtin)
|
||||
- [ ] Fail-Fast 原則の徹底(フォールバック削除)
|
||||
- [ ] 起動時検証テスト追加
|
||||
- [ ] VM/LLVM 両方で ConsoleBox 生成確認
|
||||
|
||||
---
|
||||
|
||||
**Status**: Ready for Implementation
|
||||
**Next Phase**: 131-3 (SSOT Implementation)
|
||||
**Estimated Time**: 4-6 hours
|
||||
@ -0,0 +1,570 @@
|
||||
# Phase 131-2: ConsoleBox 問題根治調査レポート
|
||||
|
||||
## 🎯 調査目的
|
||||
|
||||
実アプリの E2E で最初に詰まる「Box 登録/認識」問題を特定し、VM/LLVM 共通の土台を確立する。
|
||||
|
||||
## 📊 調査結果サマリ
|
||||
|
||||
### 問題の本質
|
||||
|
||||
**ConsoleBox は既に両 backend で完全実装済み** - 問題は **登録メカニズムの分岐** にある:
|
||||
|
||||
1. **VM backend**: Box 解決が「UnifiedBoxRegistry(入口) + BoxFactoryRegistry(plugin provider mapping) + VM fast path」の合成に見え、全体像が追いにくい
|
||||
2. **LLVM backend**: 別の経路(TypeRegistry/FFI)を使用(Phase 133 の事例あり)
|
||||
3. **両者の規約不一致**: “どれをSSOTとして読むべきか” が docs/コードで分散している
|
||||
|
||||
### 重要な発見
|
||||
|
||||
#### ✅ LLVM 側は統合事例がある(Phase 133 / archive)
|
||||
|
||||
LLVM backend の ConsoleBox 問題は **Phase 133 で完全解決済み**:
|
||||
- ConsoleLlvmBridge 箱化モジュール実装済み
|
||||
- TypeRegistry との ABI 完全一致
|
||||
- 7/7 テスト全て PASS
|
||||
|
||||
**結論**: LLVM 側は “参考モデル” として参照し、当面の焦点は VM 側の経路可視化と SSOT 明文化。
|
||||
|
||||
#### ⚠️ VM backend の Box 解決が「2層 + 特例」に見える(問題の根源)
|
||||
|
||||
VM backend では “入口” と “plugin provider mapping” が分かれており、加えて入口に特例がある:
|
||||
|
||||
```
|
||||
1. BoxFactoryRegistry (src/runtime/box_registry.rs)
|
||||
- Plugin-First アーキテクチャ
|
||||
- プラグイン設定で上書き可能
|
||||
- グローバルレジストリ
|
||||
|
||||
2. UnifiedBoxRegistry + global accessor
|
||||
- global accessor: `src/runtime/unified_registry.rs`
|
||||
- registry 本体: `src/box_factory/mod.rs`
|
||||
- handle_new_box()(VM NewBox)が使用
|
||||
|
||||
3. VM fast path(特例)
|
||||
- `NYASH_VM_FAST=1` のとき `StringBox` をレジストリ経由せず生成(bench/profile-only)
|
||||
```
|
||||
|
||||
## 🔍 詳細分析
|
||||
|
||||
### 1. VM Backend の Box 解決フロー
|
||||
|
||||
#### NewBox 命令処理(src/backend/mir_interpreter/handlers/boxes.rs)
|
||||
|
||||
```rust
|
||||
pub(super) fn handle_new_box(
|
||||
&mut self,
|
||||
dst: ValueId,
|
||||
box_type: &str,
|
||||
args: &[ValueId],
|
||||
) -> Result<(), VMError> {
|
||||
// ① Provider Lock guard(既定は挙動不変)
|
||||
provider_lock::guard_before_new_box(box_type)?;
|
||||
|
||||
// ② Fast path (StringBox のみ、NYASH_VM_FAST=1 時)
|
||||
if box_type == "StringBox" { /* ... */ }
|
||||
|
||||
// ③ 統一レジストリ経由で生成 ← ここが主経路
|
||||
let reg = unified_registry::get_global_unified_registry();
|
||||
let created = reg.lock().unwrap().create_box(box_type, &converted)?;
|
||||
|
||||
// ④ 生成結果を VMValue に変換して格納
|
||||
self.regs.insert(dst, VMValue::from_nyash_box(created));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
**問題点**:
|
||||
- `unified_registry` は `src/runtime/unified_registry.rs` にあり、`UnifiedBoxRegistry` は `src/box_factory/mod.rs`
|
||||
- `UnifiedBoxRegistry` は `PluginBoxFactory`(`src/box_factory/plugin.rs`)を通じて `BoxFactoryRegistry` を参照するため、間接的に両者が接続されている
|
||||
- Provider Lock の役割が曖昧(「既定は挙動不変」)
|
||||
|
||||
#### BoxFactoryRegistry の設計(src/runtime/box_registry.rs)
|
||||
|
||||
```rust
|
||||
pub struct BoxFactoryRegistry {
|
||||
providers: RwLock<HashMap<String, BoxProvider>>,
|
||||
}
|
||||
|
||||
pub enum BoxProvider {
|
||||
Builtin(BoxConstructor), // 互換用(テスト専用)
|
||||
Plugin(String), // プラグイン実装
|
||||
}
|
||||
```
|
||||
|
||||
**特徴**:
|
||||
- Plugin-First 設計(プラグインがビルトインを上書き可能)
|
||||
- `apply_plugin_config()` で nyash.toml から動的登録
|
||||
- `create_plugin_box()` → PluginHost 経由で実際の生成
|
||||
|
||||
**疑問**:
|
||||
- このレジストリは `PluginBoxFactory` の provider mapping として使用される(VM NewBox → UnifiedBoxRegistry → PluginBoxFactory → BoxFactoryRegistry)
|
||||
- provider の populate は `src/runtime/plugin_loader_unified.rs` が行う(`apply_plugin_config`)
|
||||
|
||||
#### Builtin Fallback(src/box_factory/builtin_impls/console_box.rs)
|
||||
|
||||
```rust
|
||||
/// Create builtin ConsoleBox instance
|
||||
///
|
||||
/// Primary: nyash-console-plugin
|
||||
/// Fallback: This builtin implementation (selfhost support)
|
||||
pub fn create(_args: &[Box<dyn NyashBox>]) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
// Phase 151: Quiet fallback (no deprecation warning)
|
||||
Ok(Box::new(ConsoleBox::new()))
|
||||
}
|
||||
```
|
||||
|
||||
**用途**:
|
||||
- セルフホスト Stage-3 パイプライン用
|
||||
- プラグイン初期化失敗時のバックアップ
|
||||
- Phase 151 で追加(Phase 150 のエラー対処)
|
||||
|
||||
### 2. LLVM Backend の Box 解決フロー(Phase 133 完了済み)
|
||||
|
||||
#### ConsoleLlvmBridge(src/llvm_py/console_bridge.py)
|
||||
|
||||
```python
|
||||
CONSOLE_METHODS = {
|
||||
"log": "nyash.console.log",
|
||||
"println": "nyash.console.log", # Phase 122: log のエイリアス
|
||||
"warn": "nyash.console.warn",
|
||||
"error": "nyash.console.error",
|
||||
"clear": "nyash.console.clear",
|
||||
}
|
||||
|
||||
def emit_console_call(builder, module, method_name, args, ...):
|
||||
"""ConsoleBox method call to LLVM IR"""
|
||||
if method_name not in CONSOLE_METHODS:
|
||||
return False
|
||||
|
||||
runtime_fn_name = CONSOLE_METHODS[method_name]
|
||||
# LLVM IR generation...
|
||||
return True
|
||||
```
|
||||
|
||||
**特徴**:
|
||||
- Phase 133 で箱化モジュール化済み
|
||||
- TypeRegistry の slot 400-403 と完全一致
|
||||
- Phase 122 の println/log エイリアス統一を継承
|
||||
|
||||
#### TypeRegistry との連携(src/runtime/type_registry.rs)
|
||||
|
||||
```rust
|
||||
// ConsoleBox 用 slot 定義
|
||||
const CONSOLE_METHODS: &[MethodEntry] = &[
|
||||
MethodEntry { name: "log", arity: 1, slot: 400 },
|
||||
MethodEntry { name: "println", arity: 1, slot: 400 }, // エイリアス
|
||||
MethodEntry { name: "warn", arity: 1, slot: 401 },
|
||||
MethodEntry { name: "error", arity: 1, slot: 402 },
|
||||
MethodEntry { name: "clear", arity: 0, slot: 403 },
|
||||
];
|
||||
```
|
||||
|
||||
**SSOT 化の成果**:
|
||||
- ✅ メソッド名 → slot 番号のマッピングが一元管理
|
||||
- ✅ VM/LLVM 両方で共通の型情報を参照
|
||||
- ✅ Phase 122 のエイリアス統一が完全適用
|
||||
|
||||
### 3. Plugin System との連携
|
||||
|
||||
#### TypeBox v2 FFI(plugins/nyash-console-plugin/src/lib.rs)
|
||||
|
||||
```rust
|
||||
#[no_mangle]
|
||||
pub static nyash_typebox_ConsoleBox: NyashTypeBoxFfi = NyashTypeBoxFfi {
|
||||
abi_tag: 0x54594258, // 'TYBX'
|
||||
version: 1,
|
||||
name: b"ConsoleBox\0".as_ptr() as *const c_char,
|
||||
resolve: Some(console_resolve),
|
||||
invoke_id: Some(console_invoke_id),
|
||||
capabilities: 0,
|
||||
};
|
||||
```
|
||||
|
||||
**特徴**:
|
||||
- TypeBox v2 FFI を実装
|
||||
- `resolve()` でメソッド名 → ID 変換
|
||||
- `invoke_id()` で実際のメソッド呼び出し
|
||||
|
||||
**問題点**:
|
||||
- VM backend の `unified_registry` がこの FFI を使っているか不明
|
||||
- プラグインローダーとの接続点が見えない
|
||||
|
||||
### 4. CoreBoxId システム(Phase 87)
|
||||
|
||||
#### CoreBoxId 定義(src/runtime/core_box_ids.rs)
|
||||
|
||||
```rust
|
||||
pub enum CoreBoxId {
|
||||
String, Integer, Bool, Array, Map, Console, // core_required
|
||||
Float, Null, File, Path, Regex, Math, Time, Json, Toml, // core_optional
|
||||
Function, Result, Method, Missing, // 特殊型
|
||||
}
|
||||
|
||||
impl CoreBoxId {
|
||||
pub fn is_core_required(&self) -> bool {
|
||||
matches!(self, String | Integer | Bool | Array | Map | Console | File)
|
||||
}
|
||||
|
||||
pub fn is_required_in(&self, profile: &RuntimeProfile) -> bool {
|
||||
match profile {
|
||||
RuntimeProfile::Default => self.is_core_required(),
|
||||
RuntimeProfile::NoFs => self.is_core_required() && *self != Self::File,
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**設計思想**:
|
||||
- Core Box を型安全な enum で管理
|
||||
- Phase 85 の core_required/core_optional 分類を実装
|
||||
- Runtime profile による動的要件変更
|
||||
|
||||
**問題点**:
|
||||
- この enum が **Box 登録の強制力** を持っているか不明
|
||||
- `is_core_required()` の結果が実際の登録処理に反映されているか要確認
|
||||
|
||||
### 5. CoreServices(Phase 91)
|
||||
|
||||
#### CoreServices 定義(src/runtime/core_services.rs)
|
||||
|
||||
```rust
|
||||
pub trait ConsoleService: Send + Sync {
|
||||
fn println(&self, msg: &str);
|
||||
fn print(&self, msg: &str);
|
||||
}
|
||||
|
||||
pub struct CoreServices {
|
||||
pub string: Option<Arc<dyn StringService>>,
|
||||
pub integer: Option<Arc<dyn IntegerService>>,
|
||||
pub bool: Option<Arc<dyn BoolService>>,
|
||||
pub array: Option<Arc<dyn ArrayService>>,
|
||||
pub map: Option<Arc<dyn MapService>>,
|
||||
pub console: Option<Arc<dyn ConsoleService>>,
|
||||
}
|
||||
```
|
||||
|
||||
**特徴**:
|
||||
- Ring1-Core の Service trait 群
|
||||
- Phase 87 CoreBoxId の core_required をカバー
|
||||
- Optional 化により Graceful Degradation 対応
|
||||
|
||||
**疑問**:
|
||||
- この Service trait は **誰が実装しているのか?**
|
||||
- VM の Box 呼び出しがこの trait を経由するのか?
|
||||
|
||||
## 🚨 発見した問題点
|
||||
|
||||
### 1. **SSOT の欠如(最重要)**
|
||||
|
||||
Box 登録規約が複数箇所に分散:
|
||||
|
||||
```
|
||||
┌─ BoxFactoryRegistry (グローバル)
|
||||
├─ UnifiedBoxRegistry (NewBox の入口)
|
||||
├─ BuiltinBoxFactory (UnifiedBoxRegistry 内の factory)
|
||||
├─ CoreBoxId enum (型定義)
|
||||
├─ CoreServices (Service trait)
|
||||
└─ TypeRegistry (メソッド情報)
|
||||
```
|
||||
|
||||
**問題**:
|
||||
- どのレジストリが「正」なのか不明
|
||||
- 登録順序・優先度の規約がない
|
||||
- 初期化タイミングが散在
|
||||
|
||||
### 2. **VM と LLVM の分岐**
|
||||
|
||||
| 項目 | VM Backend | LLVM Backend |
|
||||
|------|-----------|--------------|
|
||||
| Box 登録 | UnifiedBoxRegistry + BoxFactoryRegistry | TypeRegistry + Plugin FFI |
|
||||
| メソッド解決 | BoxCall handler | ConsoleLlvmBridge |
|
||||
| ABI | NyashBox trait | i8* + i64 |
|
||||
| SSOT 化 | ❌ 不明 | ✅ Phase 133 完了 |
|
||||
|
||||
### 3. **失敗パターンの特定**
|
||||
|
||||
Phase 150/151 で報告されたエラー:
|
||||
|
||||
```
|
||||
[ERROR] ❌ [rust-vm] VM error: Invalid instruction: NewBox ConsoleBox:
|
||||
invalid operation: Unknown Box type: ConsoleBox. Available: Main
|
||||
```
|
||||
|
||||
**原因推測**:
|
||||
1. セルフホスト経由時にプラグイン初期化が失敗
|
||||
2. plugin provider mapping(BoxFactoryRegistry)が未設定で PluginBoxFactory が失敗
|
||||
3. BuiltinBoxFactory が無効(`plugins-only`)または該当 Box が builtin factory に登録されていない
|
||||
|
||||
**Phase 151 の対処**:
|
||||
- `builtin_impls/console_box.rs` を builtin 実装として追加/整備(UnifiedBoxRegistry 内の BuiltinBoxFactory 経由)
|
||||
- "Available: Main" → ConsoleBox が登録されるようになった
|
||||
|
||||
## 💡 SSOT 化提案
|
||||
|
||||
### 設計原則
|
||||
|
||||
1. **Single Source of Truth**: Box 登録規約を一箇所に集約
|
||||
2. **Explicit Dependencies**: 依存関係を明示化
|
||||
3. **Fail-Fast**: エラーは早期に明示的に失敗
|
||||
4. **Box-First**: Phase 33 の箱理論に基づく設計
|
||||
|
||||
### 提案 A: UnifiedBoxRegistry を入口SSOTとして強化(保守的アプローチ)
|
||||
|
||||
```rust
|
||||
// src/runtime/unified_registry.rs / src/box_factory/mod.rs を “入口SSOT” として扱い、
|
||||
// provider mapping や core_required 検証の責務をどこまで寄せるかを整理する。
|
||||
|
||||
pub struct UnifiedBoxRegistry {
|
||||
// SSOT: すべての Box 登録情報
|
||||
factories: RwLock<BTreeMap<String, BoxProvider>>,
|
||||
|
||||
// CoreBoxId による必須 Box 検証
|
||||
core_validator: CoreBoxValidator,
|
||||
|
||||
// プラグインローダーとの接続
|
||||
plugin_loader: Arc<PluginHost>,
|
||||
}
|
||||
|
||||
impl UnifiedBoxRegistry {
|
||||
/// 初期化時に core_required Box を強制登録
|
||||
pub fn new(profile: RuntimeProfile) -> Self {
|
||||
let mut reg = Self { /* ... */ };
|
||||
|
||||
// CoreBoxId に基づく必須 Box 登録
|
||||
for box_id in CoreBoxId::iter() {
|
||||
if box_id.is_required_in(&profile) {
|
||||
reg.register_core_box(box_id)?;
|
||||
}
|
||||
}
|
||||
|
||||
reg
|
||||
}
|
||||
|
||||
/// Box 生成(優先順位: Plugin > Builtin > Error)
|
||||
pub fn create_box(&self, name: &str, args: &[Box<dyn NyashBox>])
|
||||
-> Result<Box<dyn NyashBox>, RuntimeError>
|
||||
{
|
||||
// 1. プラグイン検索
|
||||
if let Some(provider) = self.factories.read().unwrap().get(name) {
|
||||
match provider {
|
||||
BoxProvider::Plugin(plugin_name) => {
|
||||
return self.plugin_loader.create_box(name, args);
|
||||
}
|
||||
BoxProvider::Builtin(constructor) => {
|
||||
return constructor(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. エラー(SSOT なので見つからない = 存在しない)
|
||||
Err(RuntimeError::InvalidOperation {
|
||||
message: format!("Unknown Box type: {}", name)
|
||||
})
|
||||
}
|
||||
|
||||
/// プラグイン設定の適用(nyash.toml から)
|
||||
pub fn apply_plugin_config(&mut self, config: &PluginConfig) {
|
||||
for (box_name, plugin_name) in &config.plugins {
|
||||
self.factories.write().unwrap()
|
||||
.insert(box_name.clone(), BoxProvider::Plugin(plugin_name.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**利点**:
|
||||
- 既存コードへの影響最小
|
||||
- CoreBoxId による型安全性
|
||||
- プラグイン優先順位の明確化
|
||||
|
||||
### 提案 B: CoreBoxRegistry 新設(理想的アプローチ)
|
||||
|
||||
```rust
|
||||
// src/runtime/core_box_registry.rs (新規)
|
||||
|
||||
/// Phase 131-2: Core Box 登録の SSOT
|
||||
pub struct CoreBoxRegistry {
|
||||
// CoreBoxId → 登録情報のマッピング
|
||||
core_boxes: RwLock<HashMap<CoreBoxId, CoreBoxEntry>>,
|
||||
|
||||
// ユーザー定義 Box(CoreBoxId 以外)
|
||||
user_boxes: RwLock<HashMap<String, UserBoxEntry>>,
|
||||
}
|
||||
|
||||
struct CoreBoxEntry {
|
||||
box_id: CoreBoxId,
|
||||
provider: CoreBoxProvider,
|
||||
metadata: CoreBoxMetadata,
|
||||
}
|
||||
|
||||
enum CoreBoxProvider {
|
||||
Plugin { plugin_name: String, type_id: u32 },
|
||||
Builtin { constructor: fn(&[Box<dyn NyashBox>]) -> Result<Box<dyn NyashBox>, RuntimeError> },
|
||||
}
|
||||
|
||||
impl CoreBoxRegistry {
|
||||
/// 初期化時の必須 Box 検証
|
||||
pub fn validate_core_boxes(&self, profile: &RuntimeProfile) -> Result<(), String> {
|
||||
let missing: Vec<_> = CoreBoxId::iter()
|
||||
.filter(|id| id.is_required_in(profile))
|
||||
.filter(|id| !self.has_core_box(*id))
|
||||
.collect();
|
||||
|
||||
if !missing.is_empty() {
|
||||
return Err(format!("Missing core_required boxes: {:?}", missing));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Box 生成(型安全)
|
||||
pub fn create_core_box(&self, box_id: CoreBoxId, args: &[Box<dyn NyashBox>])
|
||||
-> Result<Box<dyn NyashBox>, RuntimeError>
|
||||
{
|
||||
let entry = self.core_boxes.read().unwrap()
|
||||
.get(&box_id)
|
||||
.ok_or_else(|| RuntimeError::InvalidOperation {
|
||||
message: format!("Core box not registered: {:?}", box_id)
|
||||
})?
|
||||
.clone();
|
||||
|
||||
match entry.provider {
|
||||
CoreBoxProvider::Plugin { plugin_name, type_id } => {
|
||||
self.create_plugin_box(&plugin_name, type_id, args)
|
||||
}
|
||||
CoreBoxProvider::Builtin { constructor } => {
|
||||
constructor(args)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**利点**:
|
||||
- CoreBoxId による完全な型安全性
|
||||
- 必須 Box の起動時検証
|
||||
- プラグイン vs ビルトインの明確な分離
|
||||
|
||||
## 🎯 次のステップ
|
||||
|
||||
### Phase 131-3: SSOT 実装(推奨)
|
||||
|
||||
1. **入口SSOTの確定(VM NewBox)**
|
||||
- 入口は `src/backend/mir_interpreter/handlers/boxes.rs::handle_new_box` と
|
||||
`src/runtime/unified_registry.rs` / `src/box_factory/mod.rs`(UnifiedBoxRegistry)で固定する
|
||||
- BoxFactoryRegistry は “plugin provider mapping” として位置付け、関係図を SSOT 化する
|
||||
|
||||
2. **CoreBoxId との統合**
|
||||
- `CoreBoxValidator` 実装
|
||||
- 起動時の必須 Box 検証
|
||||
|
||||
3. **プラグインローダーとの接続明確化**
|
||||
- plugin_loader_unified が BoxFactoryRegistry を populate し、
|
||||
PluginBoxFactory(UnifiedBoxRegistry 内)が BoxFactoryRegistry を参照する流れを明文化
|
||||
- TypeBox v2 FFI との整合(method/type id の参照箇所)を整理
|
||||
|
||||
4. **既存 BoxFactoryRegistry の整理**
|
||||
- provider mapping を BoxFactoryRegistry に閉じるか、UnifiedBoxRegistry に吸収するかを決める
|
||||
- 移行ガイドの作成
|
||||
|
||||
### Phase 131-4: テストケース追加
|
||||
|
||||
```rust
|
||||
#[test]
|
||||
fn test_core_box_registration() {
|
||||
let reg = UnifiedBoxRegistry::new(RuntimeProfile::Default);
|
||||
|
||||
// core_required が全て登録されていること
|
||||
for box_id in CoreBoxId::iter() {
|
||||
if box_id.is_core_required() {
|
||||
assert!(reg.has_box(box_id.name()));
|
||||
}
|
||||
}
|
||||
|
||||
// ConsoleBox が生成できること
|
||||
let console = reg.create_box("ConsoleBox", &[]).unwrap();
|
||||
assert_eq!(console.type_name(), "ConsoleBox");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_plugin_priority() {
|
||||
let mut reg = UnifiedBoxRegistry::new(RuntimeProfile::Default);
|
||||
|
||||
// ビルトイン ConsoleBox 登録済み
|
||||
assert!(reg.has_box("ConsoleBox"));
|
||||
|
||||
// プラグイン設定で上書き
|
||||
let mut config = PluginConfig::default();
|
||||
config.plugins.insert("ConsoleBox".to_string(), "nyash-console".to_string());
|
||||
reg.apply_plugin_config(&config);
|
||||
|
||||
// プラグイン版が優先されること
|
||||
let console = reg.create_box("ConsoleBox", &[]).unwrap();
|
||||
// プラグイン判定ロジック(実装依存)
|
||||
}
|
||||
```
|
||||
|
||||
## 📋 関連ファイル一覧
|
||||
|
||||
### VM Backend
|
||||
- `src/runtime/box_registry.rs` - BoxFactoryRegistry
|
||||
- `src/runtime/unified_registry.rs` - global UnifiedBoxRegistry accessor
|
||||
- `src/box_factory/mod.rs` - UnifiedBoxRegistry(FactoryPolicy / create_box)
|
||||
- `src/box_factory/plugin.rs` - PluginBoxFactory(BoxFactoryRegistry を参照)
|
||||
- `src/runtime/core_box_ids.rs` - CoreBoxId enum
|
||||
- `src/runtime/core_services.rs` - CoreServices trait
|
||||
- `src/runtime/type_registry.rs` - TypeRegistry (メソッド情報)
|
||||
- `src/backend/mir_interpreter/handlers/boxes.rs` - handle_new_box()
|
||||
- `src/box_factory/builtin_impls/console_box.rs` - Builtin 実装(BuiltinBoxFactory 経由)
|
||||
|
||||
### LLVM Backend
|
||||
- `src/llvm_py/console_bridge.py` - ConsoleLlvmBridge(Phase 133)
|
||||
- `src/llvm_py/instructions/boxcall.py` - BoxCall lowering
|
||||
|
||||
### Plugin System
|
||||
- `plugins/nyash-console-plugin/src/lib.rs` - TypeBox v2 FFI
|
||||
- `src/runtime/plugin_host.rs` - PluginHost
|
||||
|
||||
### Core Infrastructure
|
||||
- `src/boxes/console_box.rs` - ConsoleBox 実装
|
||||
- `crates/nyash_kernel/src/lib.rs` - NyRT ランタイム
|
||||
|
||||
## 🔍 未解決の疑問
|
||||
|
||||
1. **UnifiedBoxRegistry ↔ BoxFactoryRegistry の責務境界**
|
||||
- provider mapping の SSOT をどこに置くか(BoxFactoryRegistry を残す/吸収する)
|
||||
- `UnifiedBoxRegistry::FactoryPolicy` と plugin_config の関係をどう可視化するか
|
||||
|
||||
2. **Provider Lock の役割**
|
||||
- `provider_lock::guard_before_new_box()` の実装と目的
|
||||
- 「既定は挙動不変」の意味
|
||||
|
||||
3. **CoreServices の実装者**
|
||||
- ConsoleService trait を実装しているのは誰か
|
||||
- VM の Box 呼び出しがこの trait を経由するのか
|
||||
|
||||
4. **初期化順序**
|
||||
- プラグインローダー、レジストリ、サービスの初期化タイミング
|
||||
- 循環依存の有無
|
||||
|
||||
## ✅ 成果
|
||||
|
||||
### 明確になった事実
|
||||
|
||||
1. **LLVM backend は問題なし** - Phase 133 で完全解決済み
|
||||
2. **VM backend の Box 解決が分散して見える** - UnifiedBoxRegistry(入口)と BoxFactoryRegistry(provider mapping)の関係を SSOT 化する必要がある
|
||||
3. **ConsoleBox 自体は完全実装** - 登録メカニズムの問題
|
||||
4. **CoreBoxId システム存在** - 型安全性の基盤あり
|
||||
|
||||
### 提案した解決策
|
||||
|
||||
1. **UnifiedBoxRegistry 強化** - 保守的アプローチ
|
||||
2. **CoreBoxRegistry 新設** - 理想的アプローチ
|
||||
3. **起動時検証** - Fail-Fast 原則の実現
|
||||
|
||||
---
|
||||
|
||||
**Status**: Investigation Complete - Ready for Implementation
|
||||
**Next Phase**: 131-3 (SSOT Implementation)
|
||||
**Estimated Time**: 4-6 hours
|
||||
135
docs/development/current/main/phase131-2-summary.md
Normal file
135
docs/development/current/main/phase131-2-summary.md
Normal file
@ -0,0 +1,135 @@
|
||||
# Phase 131-2: ConsoleBox 問題根治調査 - エグゼクティブサマリ
|
||||
|
||||
## 🎯 調査結論(3行サマリ)
|
||||
|
||||
1. **LLVM 側は既存の統合事例がある** - Phase 133(archive)を参考にできる
|
||||
2. **VM backend の Box 解決が分散して見える** - UnifiedBoxRegistry(入口)と BoxFactoryRegistry(plugin provider mapping)の関係が追いにくい
|
||||
3. **ConsoleBox 自体は問題なし** - 迷子の原因は “どこを見ればよいか” の SSOT 不在
|
||||
|
||||
## 📊 問題の本質
|
||||
|
||||
### VM Backend の Box 解決が「2層 + 特例」に見える(問題の根源)
|
||||
|
||||
```
|
||||
┌─ BoxFactoryRegistry (src/runtime/box_registry.rs)
|
||||
│ └─ Plugin-First 設計、グローバルレジストリ
|
||||
│
|
||||
├─ UnifiedBoxRegistry (src/box_factory/mod.rs) + global accessor (src/runtime/unified_registry.rs)
|
||||
│ └─ MIR `NewBox`(VM)の入口
|
||||
│
|
||||
└─ VM fast path (NYASH_VM_FAST=1 + StringBox)
|
||||
└─ ベンチ/プロファイル用の最適化(入口で分岐するため観測なしだと混乱しやすい)
|
||||
```
|
||||
|
||||
**問題**:
|
||||
- どのレジストリが「正」なのか不明
|
||||
- 登録順序・優先度の規約がない
|
||||
- VM と LLVM で Box 解決方法が異なる
|
||||
|
||||
## ✅ LLVM Backend の成功事例(参考モデル)
|
||||
|
||||
Phase 133 で既に **完全な SSOT 化** を達成:
|
||||
|
||||
```python
|
||||
# ConsoleLlvmBridge (src/llvm_py/console_bridge.py)
|
||||
CONSOLE_METHODS = {
|
||||
"log": "nyash.console.log",
|
||||
"println": "nyash.console.log", # Phase 122: エイリアス統一
|
||||
"warn": "nyash.console.warn",
|
||||
"error": "nyash.console.error",
|
||||
"clear": "nyash.console.clear",
|
||||
}
|
||||
```
|
||||
|
||||
**成果**:
|
||||
- ✅ TypeRegistry との ABI 完全一致(slot 400-403)
|
||||
- ✅ Phase 122 のエイリアス統一を継承
|
||||
- ✅ 7/7 テスト全て PASS
|
||||
|
||||
## 💡 推奨アクション
|
||||
|
||||
### 優先度 1: 入口SSOTの所在確認(VM NewBox)
|
||||
|
||||
```bash
|
||||
# 主要入口の所在(この3箇所を見る)
|
||||
rg "get_global_unified_registry\\(|struct UnifiedBoxRegistry|struct BoxFactoryRegistry" src/ --type rust
|
||||
```
|
||||
|
||||
**次の判断**:
|
||||
- UnifiedBoxRegistry を “入口SSOT” として明文化し、BoxFactoryRegistry は plugin provider mapping として位置付ける
|
||||
- その上で CoreBoxId / CoreServices との接続(Fail-Fast の境界)を設計する
|
||||
|
||||
### 優先度 2: CoreBoxId との統合
|
||||
|
||||
```rust
|
||||
// CoreBoxId に基づく必須 Box 検証
|
||||
pub fn validate_core_boxes(&self, profile: &RuntimeProfile) -> Result<(), String> {
|
||||
let missing: Vec<_> = CoreBoxId::iter()
|
||||
.filter(|id| id.is_required_in(profile))
|
||||
.filter(|id| !self.has_core_box(*id))
|
||||
.collect();
|
||||
|
||||
if !missing.is_empty() {
|
||||
return Err(format!("Missing core_required boxes: {:?}", missing));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### 優先度 3: Fail-Fast 原則の徹底
|
||||
|
||||
```rust
|
||||
// ❌ 悪い例:フォールバック
|
||||
if let Err(_) = create_plugin_box() {
|
||||
create_builtin_box() // 隠蔽される!
|
||||
}
|
||||
|
||||
// ✅ 良い例:即座に失敗
|
||||
create_plugin_box()
|
||||
.map_err(|e| format!("ConsoleBox plugin failed: {:?}", e))?
|
||||
```
|
||||
|
||||
## 🔍 未解決の疑問(Phase 131-3 で調査)
|
||||
|
||||
1. **UnifiedBoxRegistry ↔ BoxFactoryRegistry の責務境界**
|
||||
- “plugin provider mapping” を BoxFactoryRegistry に閉じ込めるなら、どこまで公開するか(例: box_types 一覧)
|
||||
- `NYASH_BOX_FACTORY_POLICY` と plugin_config の関係を SSOT 化する場所
|
||||
|
||||
2. **Provider Lock の役割**
|
||||
- `provider_lock::guard_before_new_box()` の目的
|
||||
- 「既定は挙動不変」の意味
|
||||
|
||||
3. **CoreServices の実装者**
|
||||
- ConsoleService trait を誰が実装しているか
|
||||
- Box 呼び出しが trait を経由するか
|
||||
|
||||
## 📋 関連ドキュメント
|
||||
|
||||
- **詳細レポート**: [phase131-2-consolebox-investigation.md](./phase131-2-consolebox-investigation.md)
|
||||
- **Phase 133 成果**: [docs/archive/phases/phase-106-156/phase133_consolebox_llvm_integration.md](../../../archive/phases/phase-106-156/phase133_consolebox_llvm_integration.md)
|
||||
- **CoreBoxId 設計**: `src/runtime/core_box_ids.rs`
|
||||
- **TypeRegistry**: `src/runtime/type_registry.rs`
|
||||
|
||||
## ⏱️ 次のステップ(Phase 131-3)
|
||||
|
||||
### タスク概要
|
||||
|
||||
1. SSOT 設計決定(1時間)
|
||||
2. 実装(2-3時間)
|
||||
3. テスト追加(1時間)
|
||||
|
||||
**合計見積もり**: 4-6時間
|
||||
|
||||
### 完成条件
|
||||
|
||||
- [ ] UnifiedBoxRegistry / BoxFactoryRegistry の責務境界を SSOT として固定
|
||||
- [ ] CoreBoxId に基づく必須 Box 検証実装
|
||||
- [ ] プラグイン vs ビルトイン優先順位の明確化
|
||||
- [ ] 起動時の core_required Box 検証テスト追加
|
||||
- [ ] VM/LLVM 両方で ConsoleBox 生成成功確認
|
||||
|
||||
---
|
||||
|
||||
**Status**: Investigation Complete ✅
|
||||
**Next Phase**: 131-3 (SSOT Implementation)
|
||||
**Owner**: ChatGPT + Claude 協働
|
||||
@ -0,0 +1,66 @@
|
||||
# Phase 86–90 Summary — Loop Frontends (dev-only fixtures)
|
||||
|
||||
目的: 実アプリ由来のループ形を「fixture + shape guard + fail-fast」で段階的に JoinIR frontend に取り込み、
|
||||
Normalized-dev の回帰テストで固定する。
|
||||
|
||||
このファイルは Phase 86–90 の“ループ前線”だけを 1 枚に集約するサマリ。
|
||||
詳細ログや設計の背景は各 Phase 文書に委譲し、このサマリでは **到達点 / SSOT / fixture / 未検証**だけを書く。
|
||||
|
||||
## SSOT(参照の優先順位)
|
||||
|
||||
- JoinIR 全体SSOT: `docs/development/current/main/joinir-architecture-overview.md`
|
||||
- いまの状態: `docs/development/current/main/10-Now.md`
|
||||
- タスク優先度: `CURRENT_TASK.md`
|
||||
|
||||
## Phase 86 — Carrier Init Builder + Error Tags ✅
|
||||
|
||||
- 目的: ValueId 生成とエラー語彙を SSOT 化し、段階移行ラインの土台を固める
|
||||
- SSOT modules:
|
||||
- `src/mir/builder/control_flow/joinir/merge/carrier_init_builder.rs`
|
||||
- `src/mir/join_ir/lowering/error_tags.rs`
|
||||
|
||||
## Phase 87 — LLVM exe line SSOT ✅
|
||||
|
||||
- 目的: `.hako → executable` の手順を `tools/build_llvm.sh` に統一し、Smoke を 1 本に固定する
|
||||
- SSOT:
|
||||
- `tools/build_llvm.sh`
|
||||
- `docs/development/current/main/phase87-selfhost-llvm-exe-line.md`
|
||||
|
||||
## Phase 88 — continue + 可変ステップ(dev-only fixture)✅
|
||||
|
||||
- 目的: `continue` 分岐で `i` が可変ステップ更新される形(`i = i + const`)を段階拡張し、回帰を固定する
|
||||
- 追加: continue 分岐側での carrier 更新(例: `acc`)を許可
|
||||
- Fail-Fast: const 以外の step 更新は拒否
|
||||
- Fixture:
|
||||
- `docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_unescape_string_step2_min.program.json`
|
||||
|
||||
## Phase 89 — ContinueReturn(detector + lowering)✅
|
||||
|
||||
- 目的: `continue + early return`(loop 内 return)を Pattern4 と誤認しないように shape を分離し、frontend lowering を追加する
|
||||
- Shape guard:
|
||||
- Pattern4 detector を厳格化(誤爆防止)
|
||||
- ContinueReturn 用 detector を追加(dev-only)
|
||||
- Fixtures:
|
||||
- `docs/private/roadmap2/phases/normalized_dev/fixtures/pattern_continue_return_min.program.json`
|
||||
- `docs/private/roadmap2/phases/normalized_dev/fixtures/continue_return_multi_min.program.json`(同一値の複数 return-if)
|
||||
|
||||
## Phase 90 — ParseStringComposite(dev-only fixture)✅
|
||||
|
||||
- 目的: `_parse_string` の制御骨格(escape continue + close-quote return)を “制御だけ抽出” した合成 fixture として固定する
|
||||
- Fixture:
|
||||
- `docs/private/roadmap2/phases/normalized_dev/fixtures/parse_string_composite_min.program.json`
|
||||
- 追加(実ループ寄せの土台、制御抽出):
|
||||
- `docs/private/roadmap2/phases/normalized_dev/fixtures/parse_array_min.program.json`
|
||||
- `docs/private/roadmap2/phases/normalized_dev/fixtures/parse_object_min.program.json`
|
||||
|
||||
## Refactor(Phase 89–90 の維持性向上)
|
||||
|
||||
- Fixture 名・パス・ルーティングの SSOT:
|
||||
- `src/mir/join_ir/normalized/dev_fixtures.rs`
|
||||
- Shape detector の共通化(Inspector 等)は `shape_guard.rs` を参照
|
||||
|
||||
## 未検証(SSOT にしない)
|
||||
|
||||
- 実コード(`tools/hako_shared/json_parser.hako`)の `_parse_string/_parse_array/_parse_object` を、
|
||||
JoinIR frontend で “そのまま” E2E 実行するライン(dev-only での段階投入)
|
||||
- 文字列・配列・マップなど Box の意味論を含む大域 E2E(fixture は制御抽出が主目的)
|
||||
@ -27,6 +27,130 @@ Establish single source of truth for `.hako → .o → executable → execution`
|
||||
# Expected: 0.40.0 or newer
|
||||
```
|
||||
|
||||
## Compiler Modes
|
||||
|
||||
`tools/build_llvm.sh` supports two compiler modes for LLVM object generation:
|
||||
|
||||
### Harness (Default) - Production Ready
|
||||
|
||||
**Python llvmlite-based LLVM IR generation**
|
||||
|
||||
- **Stability**: ✅ Proven stable, battle-tested
|
||||
- **Build Time**: Fast (~1-3s for minimal programs)
|
||||
- **Dependencies**: Python 3, llvmlite, LLVM 18
|
||||
- **Use Case**: Default for all production builds
|
||||
|
||||
**Enable** (default behavior):
|
||||
```bash
|
||||
# Explicit mode selection (optional):
|
||||
NYASH_LLVM_COMPILER=harness tools/build_llvm.sh program.hako -o output
|
||||
|
||||
# Default (no env var needed):
|
||||
tools/build_llvm.sh program.hako -o output
|
||||
```
|
||||
|
||||
**How it works**:
|
||||
1. `hakorune --backend llvm` invokes Python harness
|
||||
2. `src/llvm_py/llvm_builder.py` generates LLVM IR via llvmlite
|
||||
3. `llc-18` compiles IR to object file
|
||||
|
||||
### Crate (Experimental) - Rust-native Compiler
|
||||
|
||||
**Pure Rust LLVM IR generation via crates/nyash-llvm-compiler**
|
||||
|
||||
- **Stability**: ⚠️ Experimental, under active development
|
||||
- **Build Time**: Slower (~5-10s, requires crate compilation)
|
||||
- **Dependencies**: LLVM 18 dev libraries, Rust toolchain
|
||||
- **Use Case**: Advanced users, development/testing
|
||||
|
||||
**Enable**:
|
||||
```bash
|
||||
NYASH_LLVM_COMPILER=crate tools/build_llvm.sh program.hako -o output
|
||||
```
|
||||
|
||||
**How it works**:
|
||||
1. `hakorune --emit-mir-json` generates MIR JSON
|
||||
2. `ny-llvmc` (Rust crate) reads JSON and emits LLVM IR
|
||||
3. `llc-18` compiles IR to object file
|
||||
|
||||
**Advanced: Direct exe emission** (experimental):
|
||||
```bash
|
||||
NYASH_LLVM_COMPILER=crate NYASH_LLVM_EMIT=exe \
|
||||
tools/build_llvm.sh program.hako -o output
|
||||
# Skips separate linking step, emits executable directly
|
||||
```
|
||||
|
||||
### Mode Comparison Table
|
||||
|
||||
| Feature | Harness (Default) | Crate (Experimental) |
|
||||
|---------|------------------|----------------------|
|
||||
| **Stability** | ✅ Production ready | ⚠️ Experimental |
|
||||
| **Build Time** | Fast (1-3s) | Moderate (5-10s) |
|
||||
| **Dependencies** | Python + llvmlite | LLVM dev + Rust |
|
||||
| **MIR JSON** | Internal | Explicit generation |
|
||||
| **Direct exe** | ❌ Not supported | ✅ Experimental |
|
||||
| **Recommended For** | All users | Advanced/dev only |
|
||||
|
||||
**Default recommendation**: Use harness mode (no env vars needed).
|
||||
|
||||
## Environment Variables Reference
|
||||
|
||||
### Control Variables
|
||||
|
||||
These environment variables control the build pipeline behavior:
|
||||
|
||||
| Variable | Default | Purpose | Example |
|
||||
|----------|---------|---------|---------|
|
||||
| `NYASH_BIN` | `./target/release/hakorune` | Path to hakorune binary | `NYASH_BIN=/custom/path/hakorune` |
|
||||
| `NYASH_LLVM_COMPILER` | `harness` | Compiler mode: `harness` or `crate` | `NYASH_LLVM_COMPILER=crate` |
|
||||
| `NYASH_LLVM_FEATURE` | `llvm` | LLVM feature flag for cargo build | `NYASH_LLVM_FEATURE=llvm-inkwell-legacy` |
|
||||
| `NYASH_LLVM_OBJ_OUT` | `target/aot_objects/<stem>.o` | Object file output path | `NYASH_LLVM_OBJ_OUT=/tmp/custom.o` |
|
||||
| `NYASH_CLI_VERBOSE` | `0` | Enable verbose build output | `NYASH_CLI_VERBOSE=1` |
|
||||
|
||||
### Advanced Control Variables
|
||||
|
||||
For specialized use cases and debugging:
|
||||
|
||||
| Variable | Default | Purpose | Example |
|
||||
|----------|---------|---------|---------|
|
||||
| `NYASH_LLVM_SKIP_EMIT` | `0` | Skip object generation (use pre-generated .o) | `NYASH_LLVM_SKIP_EMIT=1` |
|
||||
| `NYASH_LLVM_ONLY_OBJ` | `0` | Stop after object generation (skip linking) | `NYASH_LLVM_ONLY_OBJ=1` |
|
||||
| `NYASH_LLVM_SKIP_NYRT_BUILD` | `0` | Skip Nyash Kernel runtime build | `NYASH_LLVM_SKIP_NYRT_BUILD=1` |
|
||||
| `NYASH_LLVM_MIR_JSON` | (auto-generated) | Pre-generated MIR JSON path (crate mode) | `NYASH_LLVM_MIR_JSON=/tmp/mir.json` |
|
||||
| `NYASH_LLVM_VALIDATE_JSON` | `0` | Validate MIR JSON schema (crate mode) | `NYASH_LLVM_VALIDATE_JSON=1` |
|
||||
| `NYASH_LLVM_EMIT` | `obj` | Emission mode: `obj` or `exe` (crate only) | `NYASH_LLVM_EMIT=exe` |
|
||||
| `NYASH_LLVM_NYRT` | `crates/nyash_kernel/target/release` | Nyash Kernel runtime path | `NYASH_LLVM_NYRT=/custom/nyrt` |
|
||||
| `NYASH_LLVM_LIBS` | (empty) | Additional link libraries | `NYASH_LLVM_LIBS="-lcustom"` |
|
||||
| `NYASH_LLVM_USE_HARNESS` | (auto-set) | Force Python harness usage | `NYASH_LLVM_USE_HARNESS=1` |
|
||||
|
||||
### Typical Usage Examples
|
||||
|
||||
**Basic build** (all defaults):
|
||||
```bash
|
||||
tools/build_llvm.sh program.hako -o output
|
||||
```
|
||||
|
||||
**Verbose debugging**:
|
||||
```bash
|
||||
NYASH_CLI_VERBOSE=1 tools/build_llvm.sh program.hako -o output
|
||||
```
|
||||
|
||||
**Use pre-generated object file**:
|
||||
```bash
|
||||
# Step 1: Generate object via smoke test
|
||||
./tools/smokes/v2/run.sh --profile quick --filter 'smoke_obj_*'
|
||||
|
||||
# Step 2: Link using existing object
|
||||
NYASH_LLVM_SKIP_EMIT=1 NYASH_LLVM_OBJ_OUT=target/aot_objects/existing.o \
|
||||
tools/build_llvm.sh program.hako -o output
|
||||
```
|
||||
|
||||
**Crate mode with validation**:
|
||||
```bash
|
||||
NYASH_LLVM_COMPILER=crate NYASH_LLVM_VALIDATE_JSON=1 \
|
||||
tools/build_llvm.sh program.hako -o output
|
||||
```
|
||||
|
||||
## Standard Procedure
|
||||
|
||||
**Build and execute** a .hako program to native executable:
|
||||
@ -107,6 +231,150 @@ clang-18 tmp/program.o -o tmp/program
|
||||
|
||||
**Output**: Native executable
|
||||
|
||||
## Success/Failure Criteria
|
||||
|
||||
### Success Indicators
|
||||
|
||||
A successful build via `tools/build_llvm.sh` exhibits:
|
||||
|
||||
**1. Exit Code**: `0`
|
||||
```bash
|
||||
tools/build_llvm.sh program.hako -o output
|
||||
echo $? # Should output: 0
|
||||
```
|
||||
|
||||
**2. All 4 Steps Complete**:
|
||||
```
|
||||
[1/4] Building hakorune (feature selectable) ...
|
||||
[2/4] Emitting object (.o) via LLVM backend ...
|
||||
[3/4] Building Nyash Kernel static runtime ...
|
||||
[4/4] Linking output ...
|
||||
✅ Done: output
|
||||
```
|
||||
|
||||
**3. Executable Generated**:
|
||||
```bash
|
||||
ls -lh output
|
||||
# Should exist and be executable
|
||||
file output
|
||||
# Output: ELF 64-bit LSB executable, x86-64, dynamically linked
|
||||
```
|
||||
|
||||
**4. Executable Runs**:
|
||||
```bash
|
||||
./output
|
||||
echo $?
|
||||
# Should match expected exit code (e.g., 42 for phase87_llvm_exe_min.hako)
|
||||
```
|
||||
|
||||
### Failure Modes
|
||||
|
||||
`build_llvm.sh` uses distinct exit codes for different failure types:
|
||||
|
||||
| Exit Code | Meaning | Common Cause |
|
||||
|-----------|---------|--------------|
|
||||
| **0** | ✅ Success | Build completed normally |
|
||||
| **1** | Usage error | Missing input file or invalid arguments |
|
||||
| **2** | Missing dependency | `llvm-config-18` not found |
|
||||
| **3** | Compilation failure | Object file not generated (MIR/LLVM IR error) |
|
||||
| **Other** | System/linking error | Linking failure, missing libraries |
|
||||
|
||||
**Exit Code 1** - Usage Error:
|
||||
```bash
|
||||
tools/build_llvm.sh
|
||||
# Output: Usage: tools/build_llvm.sh <input.hako> [-o <output>]
|
||||
# Exit: 1
|
||||
```
|
||||
|
||||
**Exit Code 2** - Missing LLVM:
|
||||
```bash
|
||||
# When llvm-config-18 not installed
|
||||
tools/build_llvm.sh program.hako -o output
|
||||
# Output: error: llvm-config-18 not found (install LLVM 18 dev).
|
||||
# Exit: 2
|
||||
```
|
||||
|
||||
**Exit Code 3** - Object Generation Failed:
|
||||
```bash
|
||||
# When MIR/LLVM IR compilation fails
|
||||
tools/build_llvm.sh bad_program.hako -o output
|
||||
# Output: error: object not generated: target/aot_objects/bad_program.o
|
||||
# Exit: 3
|
||||
```
|
||||
|
||||
### Validation Commands
|
||||
|
||||
**Verify object file validity**:
|
||||
```bash
|
||||
# Check object file exists and has correct format
|
||||
file target/aot_objects/program.o
|
||||
# Expected: ELF 64-bit relocatable, x86-64
|
||||
|
||||
# Check object file symbols
|
||||
nm target/aot_objects/program.o | grep -E '(main|nyash_)'
|
||||
# Should show exported symbols
|
||||
```
|
||||
|
||||
**Verify LLVM IR validity** (when using crate mode with JSON):
|
||||
```bash
|
||||
# Step 1: Generate LLVM IR (manual)
|
||||
NYASH_LLVM_COMPILER=crate NYASH_LLVM_MIR_JSON=tmp/test.json \
|
||||
tools/build_llvm.sh program.hako -o output
|
||||
|
||||
# Step 2: Validate LLVM IR
|
||||
llvm-as-18 tmp/test.ll -o /dev/null
|
||||
# Should complete without errors
|
||||
|
||||
# Step 3: Disassemble and inspect
|
||||
llvm-dis-18 target/aot_objects/program.o -o - | less
|
||||
# Should show valid LLVM IR
|
||||
```
|
||||
|
||||
**Verify MIR JSON validity** (crate mode):
|
||||
```bash
|
||||
# Ensure MIR JSON is well-formed
|
||||
jq . tmp/test.json > /dev/null
|
||||
echo $? # Should output: 0
|
||||
|
||||
# Optional: Schema validation
|
||||
NYASH_LLVM_VALIDATE_JSON=1 NYASH_LLVM_COMPILER=crate \
|
||||
tools/build_llvm.sh program.hako -o output
|
||||
```
|
||||
|
||||
### Build Time Expectations
|
||||
|
||||
Typical build times for `phase87_llvm_exe_min.hako` (minimal program):
|
||||
|
||||
| Step | Expected Time | Notes |
|
||||
|------|---------------|-------|
|
||||
| **[1/4] Build hakorune** | ~0.5-2s | Incremental build (release) |
|
||||
| **[2/4] Emit object** | ~1-2s | Harness mode (llvmlite) |
|
||||
| | ~5-10s | Crate mode (ny-llvmc) |
|
||||
| **[3/4] Build Nyash Kernel** | ~1-3s | Incremental build (release) |
|
||||
| **[4/4] Linking** | ~0.2-0.5s | Native linker (cc/clang) |
|
||||
| **Total** | ~3-8s | Harness mode |
|
||||
| | ~7-15s | Crate mode |
|
||||
|
||||
**First build**: Add ~30-60s for initial `cargo build --release` compilation.
|
||||
|
||||
**Performance factors**:
|
||||
- **Parallel builds**: `-j 24` used by default (see `build_llvm.sh`)
|
||||
- **Incremental builds**: `CARGO_INCREMENTAL=1` enabled
|
||||
- **Cache hits**: Subsequent builds much faster (~1-3s total)
|
||||
|
||||
**Troubleshooting slow builds**:
|
||||
```bash
|
||||
# Check cargo cache status
|
||||
cargo clean --release -p nyash-rust
|
||||
cargo clean --release -p nyash-llvm-compiler
|
||||
|
||||
# Rebuild with timing information
|
||||
time tools/build_llvm.sh program.hako -o output
|
||||
|
||||
# Verbose output for bottleneck analysis
|
||||
NYASH_CLI_VERBOSE=1 time tools/build_llvm.sh program.hako -o output
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Issue: llvm-config-18 not found
|
||||
@ -181,6 +449,206 @@ llvm-as-18 test.ll -o /dev/null
|
||||
# Should complete without errors
|
||||
```
|
||||
|
||||
### Debugging Build Pipeline
|
||||
|
||||
When `build_llvm.sh` fails, use these techniques to isolate the problem:
|
||||
|
||||
#### Enable Verbose Mode
|
||||
|
||||
**Global verbose output**:
|
||||
```bash
|
||||
NYASH_CLI_VERBOSE=1 tools/build_llvm.sh program.hako -o output
|
||||
# Shows detailed command execution via set -x
|
||||
```
|
||||
|
||||
**Step-by-step verbosity**:
|
||||
```bash
|
||||
# Verbose hakorune compilation
|
||||
NYASH_CLI_VERBOSE=1 ./target/release/hakorune --emit-mir-json tmp/debug.json program.hako
|
||||
|
||||
# Verbose Python LLVM builder
|
||||
python3 -v src/llvm_py/llvm_builder.py tmp/debug.json -o tmp/debug.ll
|
||||
|
||||
# Verbose LLVM compilation
|
||||
llc-18 -debug tmp/debug.ll -o tmp/debug.o -filetype=obj
|
||||
|
||||
# Verbose linking
|
||||
cc -v tmp/debug.o -L crates/nyash_kernel/target/release -lnyash_kernel -o output
|
||||
```
|
||||
|
||||
#### Manual Step Tracing
|
||||
|
||||
**Isolate each step** to find exact failure point:
|
||||
|
||||
**Step 1: Test MIR emission**:
|
||||
```bash
|
||||
./target/release/hakorune --emit-mir-json tmp/test.json program.hako
|
||||
echo $? # Should be 0
|
||||
jq . tmp/test.json # Validate JSON
|
||||
```
|
||||
|
||||
**Step 2: Test LLVM IR generation**:
|
||||
```bash
|
||||
# Harness mode (default)
|
||||
NYASH_LLVM_USE_HARNESS=1 ./target/release/hakorune --backend llvm program.hako
|
||||
# Check exit code
|
||||
|
||||
# Crate mode
|
||||
cargo build --release -p nyash-llvm-compiler
|
||||
./target/release/ny-llvmc --in tmp/test.json --out tmp/test.o
|
||||
file tmp/test.o # Should be ELF object
|
||||
```
|
||||
|
||||
**Step 3: Test object compilation**:
|
||||
```bash
|
||||
# If .ll file available (crate mode intermediate)
|
||||
llc-18 -filetype=obj tmp/test.ll -o tmp/test.o
|
||||
file tmp/test.o # Verify ELF format
|
||||
nm tmp/test.o # Check symbols
|
||||
```
|
||||
|
||||
**Step 4: Test linking**:
|
||||
```bash
|
||||
# Ensure Nyash Kernel built
|
||||
cd crates/nyash_kernel && cargo build --release
|
||||
cd ../..
|
||||
|
||||
# Manual link
|
||||
cc tmp/test.o \
|
||||
-L crates/nyash_kernel/target/release \
|
||||
-Wl,--whole-archive -lnyash_kernel -Wl,--no-whole-archive \
|
||||
-lpthread -ldl -lm -o tmp/manual_output
|
||||
|
||||
# Test execution
|
||||
./tmp/manual_output
|
||||
echo $?
|
||||
```
|
||||
|
||||
#### Save Intermediate Files
|
||||
|
||||
**Preserve all build artifacts** for inspection:
|
||||
|
||||
```bash
|
||||
# Create debug directory
|
||||
mkdir -p debug_build
|
||||
|
||||
# Step 1: Emit MIR JSON
|
||||
./target/release/hakorune --emit-mir-json debug_build/program.json program.hako
|
||||
|
||||
# Step 2: Generate LLVM IR (harness mode, manual Python call)
|
||||
python3 src/llvm_py/llvm_builder.py debug_build/program.json -o debug_build/program.ll
|
||||
|
||||
# Step 3: Compile to object
|
||||
llc-18 debug_build/program.ll -o debug_build/program.o -filetype=obj
|
||||
|
||||
# Step 4: Link
|
||||
cc debug_build/program.o \
|
||||
-L crates/nyash_kernel/target/release \
|
||||
-Wl,--whole-archive -lnyash_kernel -Wl,--no-whole-archive \
|
||||
-lpthread -ldl -lm -o debug_build/program
|
||||
|
||||
# Inspect all intermediate files
|
||||
ls -lh debug_build/
|
||||
file debug_build/*
|
||||
```
|
||||
|
||||
**Inspect saved artifacts**:
|
||||
```bash
|
||||
# View MIR JSON structure
|
||||
jq '.functions[] | {name: .name, blocks: .blocks | length}' debug_build/program.json
|
||||
|
||||
# View LLVM IR
|
||||
less debug_build/program.ll
|
||||
|
||||
# Disassemble object file
|
||||
objdump -d debug_build/program.o | less
|
||||
|
||||
# Check symbols
|
||||
nm debug_build/program.o
|
||||
nm debug_build/program
|
||||
```
|
||||
|
||||
#### Common LLVM IR Issues
|
||||
|
||||
**Problem 1: Invalid function signature**
|
||||
```
|
||||
error: expected type '...' but found '...'
|
||||
```
|
||||
**Diagnosis**: MIR → LLVM IR type mismatch
|
||||
**Fix**: Check MIR JSON `functions[].signature`, ensure correct types
|
||||
|
||||
**Problem 2: Undefined symbol**
|
||||
```
|
||||
error: undefined reference to 'nyash_...'
|
||||
```
|
||||
**Diagnosis**: Missing Nyash Kernel runtime symbols
|
||||
**Fix**:
|
||||
```bash
|
||||
# Rebuild Nyash Kernel
|
||||
cd crates/nyash_kernel && cargo clean && cargo build --release
|
||||
|
||||
# Verify symbols available
|
||||
nm crates/nyash_kernel/target/release/libnyash_kernel.a | grep nyash_
|
||||
```
|
||||
|
||||
**Problem 3: Invalid IR instruction**
|
||||
```
|
||||
error: invalid IR instruction '...'
|
||||
```
|
||||
**Diagnosis**: Python llvm_builder.py bug or unsupported MIR instruction
|
||||
**Fix**:
|
||||
```bash
|
||||
# Check LLVM IR syntax
|
||||
llvm-as-18 -o /dev/null debug_build/program.ll
|
||||
# Error message shows exact line number
|
||||
|
||||
# Inspect problematic instruction
|
||||
sed -n '<line>p' debug_build/program.ll
|
||||
```
|
||||
|
||||
**Problem 4: Linking failure**
|
||||
```
|
||||
ld: symbol(s) not found for architecture x86_64
|
||||
```
|
||||
**Diagnosis**: Missing system libraries or incorrect link order
|
||||
**Fix**:
|
||||
```bash
|
||||
# Check what symbols are needed
|
||||
nm -u debug_build/program.o
|
||||
|
||||
# Verify Nyash Kernel provides them
|
||||
nm crates/nyash_kernel/target/release/libnyash_kernel.a | grep <symbol>
|
||||
|
||||
# If system library missing, add to NYASH_LLVM_LIBS
|
||||
NYASH_LLVM_LIBS="-lmissing_lib" tools/build_llvm.sh program.hako -o output
|
||||
```
|
||||
|
||||
#### Environment Variables for Debugging
|
||||
|
||||
Combine multiple debugging flags:
|
||||
|
||||
```bash
|
||||
# Maximum verbosity + preserve artifacts
|
||||
NYASH_CLI_VERBOSE=1 \
|
||||
NYASH_LLVM_COMPILER=crate \
|
||||
NYASH_LLVM_MIR_JSON=/tmp/debug.json \
|
||||
NYASH_LLVM_VALIDATE_JSON=1 \
|
||||
tools/build_llvm.sh program.hako -o /tmp/debug_output
|
||||
|
||||
# Then inspect intermediate files
|
||||
ls -lh /tmp/debug*
|
||||
jq . /tmp/debug.json
|
||||
cat /tmp/debug.ll
|
||||
```
|
||||
|
||||
**Recommended debugging workflow**:
|
||||
1. Enable `NYASH_CLI_VERBOSE=1` for initial diagnosis
|
||||
2. Use manual step tracing to isolate failure
|
||||
3. Save intermediate files for inspection
|
||||
4. Check LLVM IR validity with `llvm-as-18`
|
||||
5. Verify object symbols with `nm`
|
||||
6. Test linking manually with verbose `cc -v`
|
||||
|
||||
## What NOT to Do
|
||||
|
||||
❌ **DO NOT** create custom link procedures:
|
||||
|
||||
Reference in New Issue
Block a user