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革命)
|
||||
```nyash
|
||||
// デリゲーション構文
|
||||
// デリゲーション構文(すべてのBoxで統一的に使える!)
|
||||
box Child from Parent { // from構文でデリゲーション
|
||||
init(args) { // コンストラクタは「init」に統一
|
||||
from Parent.init(args) // 親の初期化
|
||||
@ -138,6 +138,12 @@ box Child from Parent { // from構文でデリゲーション
|
||||
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生成)
|
||||
- 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(...))` 直下)
|
||||
- [ ] Optimizer: `Call` 形式(関数呼び出し)でも `isType/asType` を検出して `TypeOp(Check/Cast)` に置換する安全ネットの強化とテスト
|
||||
|
||||
@ -18,9 +18,9 @@
|
||||
|
||||
実装状況(2025-08-26)
|
||||
- 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` クロージャの借用境界を調整)。
|
||||
- Builder 側の phi 正規化 TODO を CURRENT_TASK に追記。
|
||||
|
||||
- `LoopExecutor` のヘッダ判定とイテレーション可視化を拡充(`is_loop_header` の実装、`NYASH_VM_DEBUG_PHI` 出力拡張)。
|
||||
- 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 {
|
||||
|
||||
## 統一継承の実現
|
||||
|
||||
### 現在の課題
|
||||
- ビルトインBoxの継承ができない
|
||||
- プラグインBoxの継承も未実装
|
||||
### ~~現在の課題~~ → 2025-08-25更新:すべて実装済み!
|
||||
- ~~ビルトインBoxの継承ができない~~ → ✅ 実装済み!
|
||||
- ~~プラグインBoxの継承も未実装~~ → ✅ 実装済み!
|
||||
|
||||
### 理想的な統一継承
|
||||
### 理想的な統一継承(すでに実現!)
|
||||
```nyash
|
||||
// すべて可能に!
|
||||
box MyString from StringBox { } // ビルトイン継承
|
||||
box MyFile from FileBox { } // プラグイン継承
|
||||
box Employee from Person { } // ユーザー定義継承
|
||||
// すべて可能になった!
|
||||
box MyString from StringBox { } // ビルトイン継承 ✅
|
||||
box MyFile from FileBox { } // プラグイン継承 ✅
|
||||
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と完全に同じレベルで扱うことは、強引ではなく、むしろ設計として自然で美しい。この統一により、言語の一貫性と拡張性が大幅に向上する。
|
||||
|
||||
今後は、基本メソッドの統一実装から始めて、段階的により洗練された設計へと進化させていくのが良いだろう。
|
||||
今後は、基本メソッドの統一実装から始めて、段階的により洗練された設計へと進化させていくのが良いだろう。
|
||||
|
||||
## 🚀 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 {
|
||||
let mut optimizer = MirOptimizer::new();
|
||||
let stats = optimizer.optimize_module(&mut module);
|
||||
if std::env::var("NYASH_OPT_DIAG_FAIL").is_ok() && stats.diagnostics_reported > 0 {
|
||||
return Err(format!("Diagnostic failure: {} unlowered type-op calls detected", stats.diagnostics_reported));
|
||||
if (std::env::var("NYASH_OPT_DIAG_FAIL").is_ok()
|
||||
|| 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");
|
||||
}
|
||||
|
||||
// Pass 0: Normalize legacy instructions to unified forms (TypeOp/WeakRef/Barrier)
|
||||
stats.merge(self.normalize_legacy_instructions(module));
|
||||
|
||||
// Pass 1: Dead code elimination
|
||||
stats.merge(self.eliminate_dead_code(module));
|
||||
|
||||
@ -61,8 +64,11 @@ impl MirOptimizer {
|
||||
println!("✅ Optimization complete: {}", stats);
|
||||
}
|
||||
// Diagnostics (informational): report unlowered patterns
|
||||
let diag = self.diagnose_unlowered_type_ops(module);
|
||||
stats.merge(diag);
|
||||
let diag1 = self.diagnose_unlowered_type_ops(module);
|
||||
stats.merge(diag1);
|
||||
// Diagnostics (policy): detect legacy (pre-unified) instructions when requested
|
||||
let diag2 = self.diagnose_legacy_instructions(module);
|
||||
stats.merge(diag2);
|
||||
|
||||
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)
|
||||
fn map_type_name(name: &str) -> MirType {
|
||||
match name {
|
||||
@ -433,6 +516,53 @@ impl MirOptimizer {
|
||||
}
|
||||
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)]
|
||||
|
||||
@ -357,8 +357,10 @@ impl MirPrinter {
|
||||
format!("{} = new {}({})", dst, box_type, args_str)
|
||||
},
|
||||
|
||||
// Legacy -> Unified print: TypeCheck as TypeOp(check)
|
||||
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 } => {
|
||||
@ -424,20 +426,24 @@ impl MirPrinter {
|
||||
format!("ref_set {}.{} = {}", reference, field, value)
|
||||
},
|
||||
|
||||
// Legacy -> Unified print: WeakNew as weakref new
|
||||
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 } => {
|
||||
format!("{} = weak_load {}", dst, weak_ref)
|
||||
format!("{} = weakref load {}", dst, weak_ref)
|
||||
},
|
||||
|
||||
// Legacy -> Unified print: BarrierRead as barrier read
|
||||
MirInstruction::BarrierRead { ptr } => {
|
||||
format!("barrier_read {}", ptr)
|
||||
format!("barrier read {}", ptr)
|
||||
},
|
||||
|
||||
// Legacy -> Unified print: BarrierWrite as barrier write
|
||||
MirInstruction::BarrierWrite { ptr } => {
|
||||
format!("barrier_write {}", ptr)
|
||||
format!("barrier write {}", ptr)
|
||||
},
|
||||
|
||||
MirInstruction::WeakRef { dst, op, value } => {
|
||||
|
||||
@ -148,7 +148,31 @@ impl MirVerifier {
|
||||
} else {
|
||||
if dlog::on("NYASH_DEBUG_VERIFIER") {
|
||||
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)
|
||||
}
|
||||
@ -342,6 +366,8 @@ impl MirVerifier {
|
||||
|
||||
for (use_block_id, block) in &function.blocks {
|
||||
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() {
|
||||
if let Some(&def_bb) = def_block.get(&used_value) {
|
||||
if def_bb != *use_block_id {
|
||||
@ -418,6 +444,8 @@ impl MirVerifier {
|
||||
let doms_of_block = dominators.get(bid).unwrap();
|
||||
// check instructions including terminator
|
||||
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() {
|
||||
if let Some(&db) = def_block.get(&used) {
|
||||
// If def doesn't dominate merge block, it must be routed via phi
|
||||
|
||||
Reference in New Issue
Block a user