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:
Moe Charm
2025-08-26 06:30:01 +09:00
parent ff53fc90b1
commit 391a095f4c
10 changed files with 537 additions and 24 deletions

View File

@ -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 { } // 多重デリゲーションも可能!
```
### 🔄 統一ループ構文

View File

@ -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)` に置換する安全ネットの強化とテスト

View File

@ -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先頭挿入の確認用ユニットテスト追加

View 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型情報の統一
- プラグインシステムの簡素化

View File

@ -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の透過的な動作
- [ ] パフォーマンス改善の確認
- [ ] メモリ使用量の変化なし

View File

@ -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");
}
}

View File

@ -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));
}
}

View File

@ -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)]

View File

@ -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 } => {

View File

@ -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