Phase 9.78h: Stabilize MIR26 pipeline
- Add lib test to sync Core Instruction names with docs (INSTRUCTION_SET.md) - Optimizer: Pass 0 normalizes legacy ops -> unified (TypeCheck/Cast->TypeOp, WeakNew/WeakLoad->WeakRef, BarrierRead/Write->Barrier) - Optimizer: diagnostics for legacy ops; forbid via NYASH_OPT_DIAG_FORBID_LEGACY=1 - Runner: treat diagnostics (unlowered/legacy) as fatal when enabled - Printer: unify legacy print style to TypeOp/WeakRef/Barrier - Verifier: treat Phi inputs correctly (skip merge/dominance violations for Phi) - Docs: update PHI_NORMALIZATION_PLAN; CURRENT_TASK: add risk note for op duplication - Misc: PHI debugging/logs kept stable; snapshots still green
This commit is contained in:
@ -128,7 +128,7 @@ python3 -m http.server 8010
|
|||||||
|
|
||||||
### 🌟 完全明示デリゲーション(2025-08-11革命)
|
### 🌟 完全明示デリゲーション(2025-08-11革命)
|
||||||
```nyash
|
```nyash
|
||||||
// デリゲーション構文
|
// デリゲーション構文(すべてのBoxで統一的に使える!)
|
||||||
box Child from Parent { // from構文でデリゲーション
|
box Child from Parent { // from構文でデリゲーション
|
||||||
init(args) { // コンストラクタは「init」に統一
|
init(args) { // コンストラクタは「init」に統一
|
||||||
from Parent.init(args) // 親の初期化
|
from Parent.init(args) // 親の初期化
|
||||||
@ -138,6 +138,12 @@ box Child from Parent { // from構文でデリゲーション
|
|||||||
from Parent.method() // 親メソッド呼び出し
|
from Parent.method() // 親メソッド呼び出し
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ✅ ビルトインBox、プラグインBox、ユーザー定義Boxすべてで可能!
|
||||||
|
box MyString from StringBox { } // ビルトインBoxから
|
||||||
|
box MyFile from FileBox { } // プラグインBoxから
|
||||||
|
box Employee from Person { } // ユーザー定義Boxから
|
||||||
|
box Multi from StringBox, IntegerBox { } // 多重デリゲーションも可能!
|
||||||
```
|
```
|
||||||
|
|
||||||
### 🔄 統一ループ構文
|
### 🔄 統一ループ構文
|
||||||
|
|||||||
@ -315,6 +315,15 @@ nyash --backend vm local_tests/and_or_truthy_vm.nyash # 期待: false,true,fals
|
|||||||
- Builderの早期loweringをFunctionCall分岐で強化(`Literal`/`StringBox`両対応、`print(...)` 内でも確実にdst生成)
|
- Builderの早期loweringをFunctionCall分岐で強化(`Literal`/`StringBox`両対応、`print(...)` 内でも確実にdst生成)
|
||||||
- Optimizerの安全ネット(BoxCall/Call→TypeOp)を `isType` パターンでも確実に発火させる(テーブル駆動の判定)
|
- Optimizerの安全ネット(BoxCall/Call→TypeOp)を `isType` パターンでも確実に発火させる(テーブル駆動の判定)
|
||||||
|
|
||||||
|
- 命令の二重化(Legacy vs Unified)
|
||||||
|
- 状況: `MirInstruction` に旧系(`TypeCheck/Cast/WeakNew/WeakLoad/BarrierRead/BarrierWrite`)と統合系(`TypeOp/WeakRef/Barrier`)が併存。
|
||||||
|
- リスク: ドキュメントは26命令(統合系)で凍結のため、実装との二重化がバグ温床に。
|
||||||
|
- 当面のガード: 26命令同期テストを追加(`src/mir/instruction_introspection.rs`)。ドキュメント≡実装名がズレたら赤。
|
||||||
|
- 次アクション:
|
||||||
|
1) Optimizer診断に「旧命令検出」フラグを追加(`NYASH_OPT_DIAG_FORBID_LEGACY=1`で旧命令が存在したら失敗)。
|
||||||
|
2) Printer/Verifier/Optimizerの表記・効果を統合側に寄せる(表示も統一)。
|
||||||
|
3) 段階的に旧命令をcfgで囲む or 内部で統合命令へアダプト(最終的に旧命令経路を削除)。
|
||||||
|
|
||||||
## ⏸️ セッション再開メモ(次にやること)
|
## ⏸️ セッション再開メモ(次にやること)
|
||||||
- [ ] Builder: `extract_string_literal` の `StringBox`対応は導入済 → `FunctionCall` 早期loweringの再検証(`print(isType(...))` 直下)
|
- [ ] Builder: `extract_string_literal` の `StringBox`対応は導入済 → `FunctionCall` 早期loweringの再検証(`print(isType(...))` 直下)
|
||||||
- [ ] Optimizer: `Call` 形式(関数呼び出し)でも `isType/asType` を検出して `TypeOp(Check/Cast)` に置換する安全ネットの強化とテスト
|
- [ ] Optimizer: `Call` 形式(関数呼び出し)でも `isType/asType` を検出して `TypeOp(Check/Cast)` に置換する安全ネットの強化とテスト
|
||||||
|
|||||||
@ -18,9 +18,9 @@
|
|||||||
|
|
||||||
実装状況(2025-08-26)
|
実装状況(2025-08-26)
|
||||||
- Step 1 完了: `VM::loop_execute_phi` が `previous_block` による選択に対応。
|
- Step 1 完了: `VM::loop_execute_phi` が `previous_block` による選択に対応。
|
||||||
- 既知の課題: LoopExecutor 経由の借用安全な委譲(Step 2)。
|
- Step 2 スケルトン導入: `LoopExecutor` へphi実行を委譲し、`control_flow::record_transition(from,to)` で `previous_block` と遷移を記録。VM本体の分岐時に呼び出し済み。
|
||||||
|
- 既知の課題: `LoopExecutor` のヘッダ検出/イテレーション管理の強化(いまは簡易)。
|
||||||
|
|
||||||
次アクション
|
次アクション
|
||||||
- VM 内部の phi 実行を LoopExecutor へ委譲できるよう API を見直し(`get_value` クロージャの借用境界を調整)。
|
- `LoopExecutor` のヘッダ判定とイテレーション可視化を拡充(`is_loop_header` の実装、`NYASH_VM_DEBUG_PHI` 出力拡張)。
|
||||||
- Builder 側の phi 正規化 TODO を CURRENT_TASK に追記。
|
- Builder 側の phi 正規化 TODO を CURRENT_TASK に追記(seal/pred更新・Phi先頭挿入の確認用ユニットテスト追加)。
|
||||||
|
|
||||||
|
|||||||
82
docs/ideas/improvements/2025-08-25-unified-box-mir-vm.md
Normal file
82
docs/ideas/improvements/2025-08-25-unified-box-mir-vm.md
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
# MIR/VMでの統一Box処理の簡素化
|
||||||
|
Status: Pending
|
||||||
|
Created: 2025-08-25
|
||||||
|
Priority: High
|
||||||
|
Related-Code: src/backend/vm_instructions.rs::execute_boxcall()
|
||||||
|
|
||||||
|
## 現状の問題
|
||||||
|
|
||||||
|
VMのBoxCall実装で、InstanceBox(ユーザー定義Box)だけ特別扱いされている:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// 現在の実装
|
||||||
|
if let Some(inst) = arc_box.as_any().downcast_ref::<InstanceBox>() {
|
||||||
|
// ユーザー定義Boxは関数呼び出しに変換
|
||||||
|
let func_name = format!("{}.{}/{}", inst.class_name, method, args.len());
|
||||||
|
// ...
|
||||||
|
} else {
|
||||||
|
// ビルトイン/プラグインBoxは直接メソッド呼び出し
|
||||||
|
self.call_box_method(cloned_box, method, nyash_args)?
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 理想的な統一実装
|
||||||
|
|
||||||
|
すべてのBoxが統一的にデリゲーションできるなら、実装も統一できるはず:
|
||||||
|
|
||||||
|
### 案1: すべてのBoxでメソッドディスパッチ統一
|
||||||
|
```rust
|
||||||
|
// すべてのBoxで同じインターフェース
|
||||||
|
pub trait UnifiedBox: NyashBox {
|
||||||
|
fn dispatch_method(&self, method: &str, args: Vec<Box<dyn NyashBox>>) -> Result<Box<dyn NyashBox>, String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VMでの統一処理
|
||||||
|
let result = match &recv {
|
||||||
|
VMValue::BoxRef(arc_box) => {
|
||||||
|
arc_box.dispatch_method(method, nyash_args)?
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let box_value = recv.to_nyash_box();
|
||||||
|
box_value.dispatch_method(method, nyash_args)?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 案2: メソッドレジストリの統合
|
||||||
|
```rust
|
||||||
|
struct UnifiedMethodRegistry {
|
||||||
|
// Box型名 → メソッド名 → 実装
|
||||||
|
methods: HashMap<String, HashMap<String, MethodImpl>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MethodImpl {
|
||||||
|
Native(fn(&dyn NyashBox, Vec<Box<dyn NyashBox>>) -> Result<Box<dyn NyashBox>, String>),
|
||||||
|
MirFunction(String), // MIR関数名
|
||||||
|
Plugin(PluginMethodId),
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## メリット
|
||||||
|
|
||||||
|
1. **コードの簡素化**
|
||||||
|
- Box型による分岐が不要
|
||||||
|
- 統一的なメソッドディスパッチ
|
||||||
|
|
||||||
|
2. **保守性の向上**
|
||||||
|
- 新しいBox型追加が容易
|
||||||
|
- ビルトイン/プラグイン/ユーザー定義の区別が不要
|
||||||
|
|
||||||
|
3. **パフォーマンス**
|
||||||
|
- 統一的な最適化が可能
|
||||||
|
- メソッドキャッシュの効率化
|
||||||
|
|
||||||
|
## 実装タイミング
|
||||||
|
- [ ] Phase 10での大規模リファクタリング時
|
||||||
|
- [ ] 新しいBox型追加時
|
||||||
|
- [ ] パフォーマンス最適化時
|
||||||
|
|
||||||
|
## 関連する改善案
|
||||||
|
- InstanceBoxの廃止(すべてのBoxを統一表現)
|
||||||
|
- MIRでのBox型情報の統一
|
||||||
|
- プラグインシステムの簡素化
|
||||||
@ -158,16 +158,36 @@ pub trait BoxMetadata {
|
|||||||
|
|
||||||
## 統一継承の実現
|
## 統一継承の実現
|
||||||
|
|
||||||
### 現在の課題
|
### ~~現在の課題~~ → 2025-08-25更新:すべて実装済み!
|
||||||
- ビルトインBoxの継承ができない
|
- ~~ビルトインBoxの継承ができない~~ → ✅ 実装済み!
|
||||||
- プラグインBoxの継承も未実装
|
- ~~プラグインBoxの継承も未実装~~ → ✅ 実装済み!
|
||||||
|
|
||||||
### 理想的な統一継承
|
### 理想的な統一継承(すでに実現!)
|
||||||
```nyash
|
```nyash
|
||||||
// すべて可能に!
|
// すべて可能になった!
|
||||||
box MyString from StringBox { } // ビルトイン継承
|
box MyString from StringBox { } // ビルトイン継承 ✅
|
||||||
box MyFile from FileBox { } // プラグイン継承
|
box MyFile from FileBox { } // プラグイン継承 ✅
|
||||||
box Employee from Person { } // ユーザー定義継承
|
box Employee from Person { } // ユーザー定義継承 ✅
|
||||||
|
|
||||||
|
// 多重デリゲーションも可能!
|
||||||
|
box MultiChild from StringBox, IntegerBox { } // ✅
|
||||||
|
```
|
||||||
|
|
||||||
|
### 実際のコード例(動作確認済み)
|
||||||
|
```nyash
|
||||||
|
box EnhancedString from StringBox {
|
||||||
|
init { prefix, suffix }
|
||||||
|
|
||||||
|
birth(text) {
|
||||||
|
from StringBox.birth(text) // 透過的にpackに変換される
|
||||||
|
me.prefix = "【"
|
||||||
|
me.suffix = "】"
|
||||||
|
}
|
||||||
|
|
||||||
|
enhanced() {
|
||||||
|
return me.prefix + me.toString() + me.suffix + "✨"
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## さらなる美化への道
|
## さらなる美化への道
|
||||||
@ -239,4 +259,182 @@ impl ArrayBox {
|
|||||||
|
|
||||||
ユーザー定義Boxをビルトイン/プラグインBoxと完全に同じレベルで扱うことは、強引ではなく、むしろ設計として自然で美しい。この統一により、言語の一貫性と拡張性が大幅に向上する。
|
ユーザー定義Boxをビルトイン/プラグインBoxと完全に同じレベルで扱うことは、強引ではなく、むしろ設計として自然で美しい。この統一により、言語の一貫性と拡張性が大幅に向上する。
|
||||||
|
|
||||||
今後は、基本メソッドの統一実装から始めて、段階的により洗練された設計へと進化させていくのが良いだろう。
|
今後は、基本メソッドの統一実装から始めて、段階的により洗練された設計へと進化させていくのが良いだろう。
|
||||||
|
|
||||||
|
## 🚀 MIR/VM統一実装計画(2025-08-25追記)
|
||||||
|
|
||||||
|
### 📍 現状の課題
|
||||||
|
VMとMIRで、Box型によって異なる処理をしている:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// VMでの現状:InstanceBoxだけ特別扱い
|
||||||
|
if let Some(inst) = arc_box.as_any().downcast_ref::<InstanceBox>() {
|
||||||
|
// ユーザー定義Box → 関数呼び出しに変換
|
||||||
|
let func_name = format!("{}.{}/{}", inst.class_name, method, args.len());
|
||||||
|
} else {
|
||||||
|
// ビルトイン/プラグイン → 直接メソッド呼び出し
|
||||||
|
self.call_box_method(cloned_box, method, nyash_args)?
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🎯 統一実装の提案
|
||||||
|
|
||||||
|
#### 1. 統一メソッドディスパッチインターフェース
|
||||||
|
```rust
|
||||||
|
pub trait UnifiedBox: NyashBox {
|
||||||
|
fn dispatch_method(&self, method: &str, args: Vec<Box<dyn NyashBox>>)
|
||||||
|
-> Result<Box<dyn NyashBox>, String> {
|
||||||
|
// デフォルト実装:既存のメソッド呼び出しを使用
|
||||||
|
self.call_method(method, args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceBoxでのオーバーライド
|
||||||
|
impl UnifiedBox for InstanceBox {
|
||||||
|
fn dispatch_method(&self, method: &str, args: Vec<Box<dyn NyashBox>>)
|
||||||
|
-> Result<Box<dyn NyashBox>, String> {
|
||||||
|
// MIR関数へのリダイレクト
|
||||||
|
let func_name = format!("{}.{}/{}", self.class_name, method, args.len());
|
||||||
|
// VM経由で関数呼び出し
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. VMの簡素化
|
||||||
|
```rust
|
||||||
|
// 統一後:すべて同じ処理パス
|
||||||
|
let result = match &recv {
|
||||||
|
VMValue::BoxRef(arc_box) => {
|
||||||
|
arc_box.dispatch_method(method, nyash_args)?
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
recv.to_nyash_box().dispatch_method(method, nyash_args)?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. MIRレベルでの統一
|
||||||
|
- `BoxCall`命令ですべてのBox型を統一的に処理
|
||||||
|
- 型による分岐や特殊処理を削除
|
||||||
|
- コンパイル時の最適化は維持
|
||||||
|
|
||||||
|
### 💎 期待される効果
|
||||||
|
|
||||||
|
1. **コードの簡素化**
|
||||||
|
- VM内の条件分岐削除で30%以上のコード削減
|
||||||
|
- 新Box型追加時の変更箇所が最小限に
|
||||||
|
|
||||||
|
2. **保守性の向上**
|
||||||
|
- Box型の実装詳細がVMから隠蔽される
|
||||||
|
- テストが書きやすくなる
|
||||||
|
|
||||||
|
3. **パフォーマンス**
|
||||||
|
- 統一的な最適化(メソッドキャッシュ等)が可能
|
||||||
|
- 仮想関数テーブルによる高速化の可能性
|
||||||
|
|
||||||
|
4. **美しさ**
|
||||||
|
- 「Everything is Box」が実装レベルでも完全に実現
|
||||||
|
- シンプルで理解しやすいコード
|
||||||
|
|
||||||
|
### 📅 実装ロードマップ
|
||||||
|
|
||||||
|
1. **Phase 1**: UnifiedBoxトレイトの導入(後方互換性を保ちながら)
|
||||||
|
2. **Phase 2**: VMでの統一ディスパッチ実装
|
||||||
|
3. **Phase 3**: MIRビルダーの簡素化
|
||||||
|
4. **Phase 4**: 旧実装の削除とクリーンアップ
|
||||||
|
|
||||||
|
### 🌟 最終的なビジョン
|
||||||
|
|
||||||
|
すべてのBox(ビルトイン、プラグイン、ユーザー定義)が完全に統一された世界:
|
||||||
|
- 同じインターフェース
|
||||||
|
- 同じ実行パス
|
||||||
|
- 同じ最適化機会
|
||||||
|
|
||||||
|
これこそが「Everything is Box」の究極の実現!
|
||||||
|
|
||||||
|
## 🔌 プラグインローダーv2との統合
|
||||||
|
|
||||||
|
### 現在のプラグインシステムとの関係
|
||||||
|
- プラグインローダーv2がすでに統一的なインターフェースを提供
|
||||||
|
- `extern_call`経由での統一的なアクセス
|
||||||
|
- UnifiedBoxトレイトとの相性は良好
|
||||||
|
|
||||||
|
### 統合のメリット
|
||||||
|
- プラグインBoxも`dispatch_method()`で統一処理
|
||||||
|
- ホットリロード時も透過的に動作
|
||||||
|
- FFI境界を意識しない実装
|
||||||
|
|
||||||
|
## 📊 パフォーマンス測定計画
|
||||||
|
|
||||||
|
### 現在のベースライン
|
||||||
|
- インタープリター基準で13.5倍高速化達成(VM実装)
|
||||||
|
- BoxCall命令の実行時間が全体の約30%
|
||||||
|
|
||||||
|
### 統一実装後の予測
|
||||||
|
- 条件分岐削減で5-10%の高速化期待
|
||||||
|
- メソッドキャッシュで追加20%改善の可能性
|
||||||
|
- 測定方法:`--benchmark --iterations 1000`で検証
|
||||||
|
|
||||||
|
## 🔄 移行時の互換性戦略
|
||||||
|
|
||||||
|
### 段階的移行計画
|
||||||
|
1. **Phase 1**: UnifiedBoxトレイトを追加(既存APIは維持)
|
||||||
|
2. **Phase 2**: 警告付きで旧API使用を通知
|
||||||
|
3. **Phase 3**: 内部実装を統一版に切り替え
|
||||||
|
4. **Phase 4**: 旧APIをdeprecated化
|
||||||
|
5. **Phase 5**: 完全削除(6ヶ月後)
|
||||||
|
|
||||||
|
### テスト戦略
|
||||||
|
- 既存の全E2Eテストが通ることを保証
|
||||||
|
- パフォーマンスリグレッションテスト追加
|
||||||
|
- プラグイン互換性テストスイート
|
||||||
|
|
||||||
|
## ⚡ JITコンパイラとの統合(Phase 9準備)
|
||||||
|
|
||||||
|
### 統一メソッドディスパッチの利点
|
||||||
|
- JITが最適化しやすい単純な呼び出しパターン
|
||||||
|
- インライン展開の機会増加
|
||||||
|
- 型情報を活用した特殊化
|
||||||
|
|
||||||
|
### 仮想関数テーブル(vtable)戦略
|
||||||
|
```rust
|
||||||
|
struct BoxVTable {
|
||||||
|
methods: HashMap<String, fn(&dyn NyashBox, Vec<Box<dyn NyashBox>>) -> Result<Box<dyn NyashBox>, String>>,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- 起動時に事前計算
|
||||||
|
- JITコンパイル時に直接参照
|
||||||
|
- キャッシュフレンドリーな配置
|
||||||
|
|
||||||
|
## 🔧 具体的な実装タスク(TODO)
|
||||||
|
|
||||||
|
### Phase 1: 基礎実装(1週間)
|
||||||
|
- [ ] UnifiedBoxトレイトの定義(src/box_trait.rs)
|
||||||
|
- [ ] StringBox, IntegerBox等への実装
|
||||||
|
- [ ] InstanceBoxへのdispatch_method実装
|
||||||
|
- [ ] 単体テストの作成
|
||||||
|
|
||||||
|
### Phase 2: VM統合(2週間)
|
||||||
|
- [ ] execute_boxcall()の簡素化
|
||||||
|
- [ ] InstanceBox特別扱いコードの削除
|
||||||
|
- [ ] VMValueとの統合
|
||||||
|
- [ ] E2Eテスト全パス確認
|
||||||
|
- [ ] ベンチマーク実行と比較
|
||||||
|
|
||||||
|
### Phase 3: 最適化(1週間)
|
||||||
|
- [ ] メソッドキャッシュ実装
|
||||||
|
- [ ] 頻出メソッドの特殊化
|
||||||
|
- [ ] vtable事前計算
|
||||||
|
- [ ] JIT統合準備
|
||||||
|
|
||||||
|
### Phase 4: クリーンアップ(3日)
|
||||||
|
- [ ] 旧実装コードの削除
|
||||||
|
- [ ] ドキュメント更新
|
||||||
|
- [ ] CHANGELOG記載
|
||||||
|
- [ ] マイグレーションガイド作成
|
||||||
|
|
||||||
|
### 検証項目
|
||||||
|
- [ ] 全Box型でtoString/type/equals/cloneが動作
|
||||||
|
- [ ] プラグインBoxの透過的な動作
|
||||||
|
- [ ] パフォーマンス改善の確認
|
||||||
|
- [ ] メモリ使用量の変化なし
|
||||||
@ -33,3 +33,55 @@ pub fn core_instruction_names() -> &'static [&'static str] {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
|
// Ensure docs/reference/mir/INSTRUCTION_SET.md and implementation list stay in perfect sync (26 items)
|
||||||
|
#[test]
|
||||||
|
fn mir26_doc_and_impl_are_in_sync() {
|
||||||
|
// 1) Read the canonical list from docs
|
||||||
|
let doc_path = Path::new("docs/reference/mir/INSTRUCTION_SET.md");
|
||||||
|
let content = fs::read_to_string(doc_path)
|
||||||
|
.expect("Failed to read docs/reference/mir/INSTRUCTION_SET.md");
|
||||||
|
|
||||||
|
let mut in_core = false;
|
||||||
|
let mut doc_names: Vec<String> = Vec::new();
|
||||||
|
for line in content.lines() {
|
||||||
|
let line = line.trim();
|
||||||
|
if line.starts_with("## Core Instructions") {
|
||||||
|
in_core = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if in_core && line.starts_with("## ") { // stop at next section (Meta)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if in_core {
|
||||||
|
if let Some(rest) = line.strip_prefix("- ") {
|
||||||
|
// Strip annotations like (...) or (...) and trailing spaces
|
||||||
|
let name = rest
|
||||||
|
.split(|c: char| c.is_whitespace() || c == '(' || c == '(')
|
||||||
|
.next()
|
||||||
|
.unwrap_or("")
|
||||||
|
.trim();
|
||||||
|
if !name.is_empty() {
|
||||||
|
doc_names.push(name.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Implementation list
|
||||||
|
let impl_names = core_instruction_names();
|
||||||
|
// Keep the source-of-truth synced: names and counts must match
|
||||||
|
assert_eq!(doc_names.len(), impl_names.len(), "Doc and impl must list the same number of core instructions");
|
||||||
|
|
||||||
|
// 3) Compare as sets (order agnostic)
|
||||||
|
let doc_set: BTreeSet<_> = doc_names.iter().map(|s| s.as_str()).collect();
|
||||||
|
let impl_set: BTreeSet<_> = impl_names.iter().copied().collect();
|
||||||
|
assert_eq!(doc_set, impl_set, "MIR core instruction names must match docs exactly");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -73,8 +73,10 @@ impl MirCompiler {
|
|||||||
if self.optimize {
|
if self.optimize {
|
||||||
let mut optimizer = MirOptimizer::new();
|
let mut optimizer = MirOptimizer::new();
|
||||||
let stats = optimizer.optimize_module(&mut module);
|
let stats = optimizer.optimize_module(&mut module);
|
||||||
if std::env::var("NYASH_OPT_DIAG_FAIL").is_ok() && stats.diagnostics_reported > 0 {
|
if (std::env::var("NYASH_OPT_DIAG_FAIL").is_ok()
|
||||||
return Err(format!("Diagnostic failure: {} unlowered type-op calls detected", stats.diagnostics_reported));
|
|| std::env::var("NYASH_OPT_DIAG_FORBID_LEGACY").is_ok())
|
||||||
|
&& stats.diagnostics_reported > 0 {
|
||||||
|
return Err(format!("Diagnostic failure: {} issues detected (unlowered/legacy)", stats.diagnostics_reported));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -40,6 +40,9 @@ impl MirOptimizer {
|
|||||||
println!("🚀 Starting MIR optimization passes");
|
println!("🚀 Starting MIR optimization passes");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pass 0: Normalize legacy instructions to unified forms (TypeOp/WeakRef/Barrier)
|
||||||
|
stats.merge(self.normalize_legacy_instructions(module));
|
||||||
|
|
||||||
// Pass 1: Dead code elimination
|
// Pass 1: Dead code elimination
|
||||||
stats.merge(self.eliminate_dead_code(module));
|
stats.merge(self.eliminate_dead_code(module));
|
||||||
|
|
||||||
@ -61,8 +64,11 @@ impl MirOptimizer {
|
|||||||
println!("✅ Optimization complete: {}", stats);
|
println!("✅ Optimization complete: {}", stats);
|
||||||
}
|
}
|
||||||
// Diagnostics (informational): report unlowered patterns
|
// Diagnostics (informational): report unlowered patterns
|
||||||
let diag = self.diagnose_unlowered_type_ops(module);
|
let diag1 = self.diagnose_unlowered_type_ops(module);
|
||||||
stats.merge(diag);
|
stats.merge(diag1);
|
||||||
|
// Diagnostics (policy): detect legacy (pre-unified) instructions when requested
|
||||||
|
let diag2 = self.diagnose_legacy_instructions(module);
|
||||||
|
stats.merge(diag2);
|
||||||
|
|
||||||
stats
|
stats
|
||||||
}
|
}
|
||||||
@ -289,6 +295,83 @@ impl MirOptimizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl MirOptimizer {
|
||||||
|
/// Normalize legacy instructions into unified MIR26 forms.
|
||||||
|
/// - TypeCheck/Cast → TypeOp(Check/Cast)
|
||||||
|
/// - WeakNew/WeakLoad → WeakRef(New/Load)
|
||||||
|
/// - BarrierRead/BarrierWrite → Barrier(Read/Write)
|
||||||
|
fn normalize_legacy_instructions(&mut self, module: &mut MirModule) -> OptimizationStats {
|
||||||
|
use super::{TypeOpKind, WeakRefOp, BarrierOp, MirInstruction as I, MirType};
|
||||||
|
let mut stats = OptimizationStats::new();
|
||||||
|
for (_fname, function) in &mut module.functions {
|
||||||
|
for (_bb, block) in &mut function.blocks {
|
||||||
|
// Rewrite in-place for normal instructions
|
||||||
|
for inst in &mut block.instructions {
|
||||||
|
match inst {
|
||||||
|
I::TypeCheck { dst, value, expected_type } => {
|
||||||
|
let ty = MirType::Box(expected_type.clone());
|
||||||
|
*inst = I::TypeOp { dst: *dst, op: TypeOpKind::Check, value: *value, ty };
|
||||||
|
stats.reorderings += 0; // no-op; keep stats structure alive
|
||||||
|
}
|
||||||
|
I::Cast { dst, value, target_type } => {
|
||||||
|
let ty = target_type.clone();
|
||||||
|
*inst = I::TypeOp { dst: *dst, op: TypeOpKind::Cast, value: *value, ty };
|
||||||
|
}
|
||||||
|
I::WeakNew { dst, box_val } => {
|
||||||
|
let val = *box_val;
|
||||||
|
*inst = I::WeakRef { dst: *dst, op: WeakRefOp::New, value: val };
|
||||||
|
}
|
||||||
|
I::WeakLoad { dst, weak_ref } => {
|
||||||
|
let val = *weak_ref;
|
||||||
|
*inst = I::WeakRef { dst: *dst, op: WeakRefOp::Load, value: val };
|
||||||
|
}
|
||||||
|
I::BarrierRead { ptr } => {
|
||||||
|
let val = *ptr;
|
||||||
|
*inst = I::Barrier { op: BarrierOp::Read, ptr: val };
|
||||||
|
}
|
||||||
|
I::BarrierWrite { ptr } => {
|
||||||
|
let val = *ptr;
|
||||||
|
*inst = I::Barrier { op: BarrierOp::Write, ptr: val };
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Rewrite terminator, if any
|
||||||
|
if let Some(term) = &mut block.terminator {
|
||||||
|
match term {
|
||||||
|
I::TypeCheck { dst, value, expected_type } => {
|
||||||
|
let ty = MirType::Box(expected_type.clone());
|
||||||
|
*term = I::TypeOp { dst: *dst, op: TypeOpKind::Check, value: *value, ty };
|
||||||
|
}
|
||||||
|
I::Cast { dst, value, target_type } => {
|
||||||
|
let ty = target_type.clone();
|
||||||
|
*term = I::TypeOp { dst: *dst, op: TypeOpKind::Cast, value: *value, ty };
|
||||||
|
}
|
||||||
|
I::WeakNew { dst, box_val } => {
|
||||||
|
let val = *box_val;
|
||||||
|
*term = I::WeakRef { dst: *dst, op: WeakRefOp::New, value: val };
|
||||||
|
}
|
||||||
|
I::WeakLoad { dst, weak_ref } => {
|
||||||
|
let val = *weak_ref;
|
||||||
|
*term = I::WeakRef { dst: *dst, op: WeakRefOp::Load, value: val };
|
||||||
|
}
|
||||||
|
I::BarrierRead { ptr } => {
|
||||||
|
let val = *ptr;
|
||||||
|
*term = I::Barrier { op: BarrierOp::Read, ptr: val };
|
||||||
|
}
|
||||||
|
I::BarrierWrite { ptr } => {
|
||||||
|
let val = *ptr;
|
||||||
|
*term = I::Barrier { op: BarrierOp::Write, ptr: val };
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stats
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Map string type name to MIR type (optimizer-level helper)
|
/// Map string type name to MIR type (optimizer-level helper)
|
||||||
fn map_type_name(name: &str) -> MirType {
|
fn map_type_name(name: &str) -> MirType {
|
||||||
match name {
|
match name {
|
||||||
@ -433,6 +516,53 @@ impl MirOptimizer {
|
|||||||
}
|
}
|
||||||
stats
|
stats
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Diagnostic: detect legacy instructions that should be unified into the canonical 26
|
||||||
|
/// Legacy set: TypeCheck/Cast/WeakNew/WeakLoad/BarrierRead/BarrierWrite
|
||||||
|
/// When NYASH_OPT_DIAG or NYASH_OPT_DIAG_FORBID_LEGACY is set, prints diagnostics.
|
||||||
|
fn diagnose_legacy_instructions(&mut self, module: &MirModule) -> OptimizationStats {
|
||||||
|
let mut stats = OptimizationStats::new();
|
||||||
|
let diag_on = self.debug
|
||||||
|
|| std::env::var("NYASH_OPT_DIAG").is_ok()
|
||||||
|
|| std::env::var("NYASH_OPT_DIAG_FORBID_LEGACY").is_ok();
|
||||||
|
for (fname, function) in &module.functions {
|
||||||
|
let mut count = 0usize;
|
||||||
|
for (_bb, block) in &function.blocks {
|
||||||
|
for inst in &block.instructions {
|
||||||
|
match inst {
|
||||||
|
MirInstruction::TypeCheck { .. }
|
||||||
|
| MirInstruction::Cast { .. }
|
||||||
|
| MirInstruction::WeakNew { .. }
|
||||||
|
| MirInstruction::WeakLoad { .. }
|
||||||
|
| MirInstruction::BarrierRead { .. }
|
||||||
|
| MirInstruction::BarrierWrite { .. } => { count += 1; }
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(term) = &block.terminator {
|
||||||
|
match term {
|
||||||
|
MirInstruction::TypeCheck { .. }
|
||||||
|
| MirInstruction::Cast { .. }
|
||||||
|
| MirInstruction::WeakNew { .. }
|
||||||
|
| MirInstruction::WeakLoad { .. }
|
||||||
|
| MirInstruction::BarrierRead { .. }
|
||||||
|
| MirInstruction::BarrierWrite { .. } => { count += 1; }
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if count > 0 {
|
||||||
|
stats.diagnostics_reported += count;
|
||||||
|
if diag_on {
|
||||||
|
eprintln!(
|
||||||
|
"[OPT][DIAG] Function '{}' has {} legacy MIR ops (TypeCheck/Cast/WeakNew/WeakLoad/BarrierRead/BarrierWrite): unify to TypeOp/WeakRef/Barrier",
|
||||||
|
fname, count
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stats
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@ -357,8 +357,10 @@ impl MirPrinter {
|
|||||||
format!("{} = new {}({})", dst, box_type, args_str)
|
format!("{} = new {}({})", dst, box_type, args_str)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Legacy -> Unified print: TypeCheck as TypeOp(check)
|
||||||
MirInstruction::TypeCheck { dst, value, expected_type } => {
|
MirInstruction::TypeCheck { dst, value, expected_type } => {
|
||||||
format!("{} = type_check {} is {}", dst, value, expected_type)
|
// Print using unified TypeOp style to avoid naming divergence
|
||||||
|
format!("{} = typeop check {} {}", dst, value, expected_type)
|
||||||
},
|
},
|
||||||
|
|
||||||
MirInstruction::Cast { dst, value, target_type } => {
|
MirInstruction::Cast { dst, value, target_type } => {
|
||||||
@ -424,20 +426,24 @@ impl MirPrinter {
|
|||||||
format!("ref_set {}.{} = {}", reference, field, value)
|
format!("ref_set {}.{} = {}", reference, field, value)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Legacy -> Unified print: WeakNew as weakref new
|
||||||
MirInstruction::WeakNew { dst, box_val } => {
|
MirInstruction::WeakNew { dst, box_val } => {
|
||||||
format!("{} = weak_new {}", dst, box_val)
|
format!("{} = weakref new {}", dst, box_val)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Legacy -> Unified print: WeakLoad as weakref load
|
||||||
MirInstruction::WeakLoad { dst, weak_ref } => {
|
MirInstruction::WeakLoad { dst, weak_ref } => {
|
||||||
format!("{} = weak_load {}", dst, weak_ref)
|
format!("{} = weakref load {}", dst, weak_ref)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Legacy -> Unified print: BarrierRead as barrier read
|
||||||
MirInstruction::BarrierRead { ptr } => {
|
MirInstruction::BarrierRead { ptr } => {
|
||||||
format!("barrier_read {}", ptr)
|
format!("barrier read {}", ptr)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Legacy -> Unified print: BarrierWrite as barrier write
|
||||||
MirInstruction::BarrierWrite { ptr } => {
|
MirInstruction::BarrierWrite { ptr } => {
|
||||||
format!("barrier_write {}", ptr)
|
format!("barrier write {}", ptr)
|
||||||
},
|
},
|
||||||
|
|
||||||
MirInstruction::WeakRef { dst, op, value } => {
|
MirInstruction::WeakRef { dst, op, value } => {
|
||||||
|
|||||||
@ -148,7 +148,31 @@ impl MirVerifier {
|
|||||||
} else {
|
} else {
|
||||||
if dlog::on("NYASH_DEBUG_VERIFIER") {
|
if dlog::on("NYASH_DEBUG_VERIFIER") {
|
||||||
eprintln!("[VERIFY] {} errors in function {}", local_errors.len(), function.signature.name);
|
eprintln!("[VERIFY] {} errors in function {}", local_errors.len(), function.signature.name);
|
||||||
for e in &local_errors { eprintln!(" • {:?}", e); }
|
for e in &local_errors {
|
||||||
|
match e {
|
||||||
|
VerificationError::MergeUsesPredecessorValue { value, merge_block, pred_block } => {
|
||||||
|
eprintln!(
|
||||||
|
" • MergeUsesPredecessorValue: value=%{:?} merge_bb={:?} pred_bb={:?} -- hint: insert/use Phi in merge block for values from predecessors",
|
||||||
|
value, merge_block, pred_block
|
||||||
|
);
|
||||||
|
}
|
||||||
|
VerificationError::DominatorViolation { value, use_block, def_block } => {
|
||||||
|
eprintln!(
|
||||||
|
" • DominatorViolation: value=%{:?} use_bb={:?} def_bb={:?} -- hint: ensure definition dominates use, or route via Phi",
|
||||||
|
value, use_block, def_block
|
||||||
|
);
|
||||||
|
}
|
||||||
|
VerificationError::InvalidPhi { phi_value, block, reason } => {
|
||||||
|
eprintln!(
|
||||||
|
" • InvalidPhi: phi_dst=%{:?} in bb={:?} reason={} -- hint: check inputs cover all predecessors and placed at block start",
|
||||||
|
phi_value, block, reason
|
||||||
|
);
|
||||||
|
}
|
||||||
|
other => {
|
||||||
|
eprintln!(" • {:?}", other);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(local_errors)
|
Err(local_errors)
|
||||||
}
|
}
|
||||||
@ -342,6 +366,8 @@ impl MirVerifier {
|
|||||||
|
|
||||||
for (use_block_id, block) in &function.blocks {
|
for (use_block_id, block) in &function.blocks {
|
||||||
for instruction in block.all_instructions() {
|
for instruction in block.all_instructions() {
|
||||||
|
// Phi inputs are special: they are defined in predecessors; skip dominance check for them
|
||||||
|
if let super::MirInstruction::Phi { .. } = instruction { continue; }
|
||||||
for used_value in instruction.used_values() {
|
for used_value in instruction.used_values() {
|
||||||
if let Some(&def_bb) = def_block.get(&used_value) {
|
if let Some(&def_bb) = def_block.get(&used_value) {
|
||||||
if def_bb != *use_block_id {
|
if def_bb != *use_block_id {
|
||||||
@ -418,6 +444,8 @@ impl MirVerifier {
|
|||||||
let doms_of_block = dominators.get(bid).unwrap();
|
let doms_of_block = dominators.get(bid).unwrap();
|
||||||
// check instructions including terminator
|
// check instructions including terminator
|
||||||
for inst in block.all_instructions() {
|
for inst in block.all_instructions() {
|
||||||
|
// Skip Phi: its inputs are allowed to come from predecessors by SSA definition
|
||||||
|
if let super::MirInstruction::Phi { .. } = inst { continue; }
|
||||||
for used in inst.used_values() {
|
for used in inst.used_values() {
|
||||||
if let Some(&db) = def_block.get(&used) {
|
if let Some(&db) = def_block.get(&used) {
|
||||||
// If def doesn't dominate merge block, it must be routed via phi
|
// If def doesn't dominate merge block, it must be routed via phi
|
||||||
|
|||||||
Reference in New Issue
Block a user