diff --git a/CLAUDE.md b/CLAUDE.md index cb3520e3..a2b5366b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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 { } // 多重デリゲーションも可能! ``` ### 🔄 統一ループ構文 diff --git a/docs/development/current/CURRENT_TASK.md b/docs/development/current/CURRENT_TASK.md index 5c798311..4c9987de 100644 --- a/docs/development/current/CURRENT_TASK.md +++ b/docs/development/current/CURRENT_TASK.md @@ -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)` に置換する安全ネットの強化とテスト diff --git a/docs/development/current/PHI_NORMALIZATION_PLAN.md b/docs/development/current/PHI_NORMALIZATION_PLAN.md index 87ce37ee..1023a8e5 100644 --- a/docs/development/current/PHI_NORMALIZATION_PLAN.md +++ b/docs/development/current/PHI_NORMALIZATION_PLAN.md @@ -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先頭挿入の確認用ユニットテスト追加)。 diff --git a/docs/ideas/improvements/2025-08-25-unified-box-mir-vm.md b/docs/ideas/improvements/2025-08-25-unified-box-mir-vm.md new file mode 100644 index 00000000..3ae3c377 --- /dev/null +++ b/docs/ideas/improvements/2025-08-25-unified-box-mir-vm.md @@ -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::() { + // ユーザー定義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>) -> Result, 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>, +} + +enum MethodImpl { + Native(fn(&dyn NyashBox, Vec>) -> Result, String>), + MirFunction(String), // MIR関数名 + Plugin(PluginMethodId), +} +``` + +## メリット + +1. **コードの簡素化** + - Box型による分岐が不要 + - 統一的なメソッドディスパッチ + +2. **保守性の向上** + - 新しいBox型追加が容易 + - ビルトイン/プラグイン/ユーザー定義の区別が不要 + +3. **パフォーマンス** + - 統一的な最適化が可能 + - メソッドキャッシュの効率化 + +## 実装タイミング +- [ ] Phase 10での大規模リファクタリング時 +- [ ] 新しいBox型追加時 +- [ ] パフォーマンス最適化時 + +## 関連する改善案 +- InstanceBoxの廃止(すべてのBoxを統一表現) +- MIRでのBox型情報の統一 +- プラグインシステムの簡素化 \ No newline at end of file diff --git a/docs/ideas/other/2025-08-25-unified-box-design-deep-analysis.md b/docs/ideas/other/2025-08-25-unified-box-design-deep-analysis.md index 48c2989d..28b01bad 100644 --- a/docs/ideas/other/2025-08-25-unified-box-design-deep-analysis.md +++ b/docs/ideas/other/2025-08-25-unified-box-design-deep-analysis.md @@ -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と完全に同じレベルで扱うことは、強引ではなく、むしろ設計として自然で美しい。この統一により、言語の一貫性と拡張性が大幅に向上する。 -今後は、基本メソッドの統一実装から始めて、段階的により洗練された設計へと進化させていくのが良いだろう。 \ No newline at end of file +今後は、基本メソッドの統一実装から始めて、段階的により洗練された設計へと進化させていくのが良いだろう。 + +## 🚀 MIR/VM統一実装計画(2025-08-25追記) + +### 📍 現状の課題 +VMとMIRで、Box型によって異なる処理をしている: + +```rust +// VMでの現状:InstanceBoxだけ特別扱い +if let Some(inst) = arc_box.as_any().downcast_ref::() { + // ユーザー定義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>) + -> Result, String> { + // デフォルト実装:既存のメソッド呼び出しを使用 + self.call_method(method, args) + } +} + +// InstanceBoxでのオーバーライド +impl UnifiedBox for InstanceBox { + fn dispatch_method(&self, method: &str, args: Vec>) + -> Result, 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>) -> Result, 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の透過的な動作 +- [ ] パフォーマンス改善の確認 +- [ ] メモリ使用量の変化なし \ No newline at end of file diff --git a/src/mir/instruction_introspection.rs b/src/mir/instruction_introspection.rs index 4314e43b..270a2b3f 100644 --- a/src/mir/instruction_introspection.rs +++ b/src/mir/instruction_introspection.rs @@ -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 = 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"); + } +} diff --git a/src/mir/mod.rs b/src/mir/mod.rs index 5f432c74..c12f56d9 100644 --- a/src/mir/mod.rs +++ b/src/mir/mod.rs @@ -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)); } } diff --git a/src/mir/optimizer.rs b/src/mir/optimizer.rs index 45737565..121f5001 100644 --- a/src/mir/optimizer.rs +++ b/src/mir/optimizer.rs @@ -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)] diff --git a/src/mir/printer.rs b/src/mir/printer.rs index ced6ac70..2e2e3497 100644 --- a/src/mir/printer.rs +++ b/src/mir/printer.rs @@ -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 } => { diff --git a/src/mir/verification.rs b/src/mir/verification.rs index 34dc1c49..a3d8385d 100644 --- a/src/mir/verification.rs +++ b/src/mir/verification.rs @@ -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