- Fix: Call with Callee::Method now includes receiver in used_values() - Prevents DCE from eliminating Copy instructions that define receivers - Pattern 3 (loop_if_phi.hako) now works correctly (sum=9) - Add: NYASH_DCE_TRACE=1 for debugging eliminated instructions - Shows which pure instructions DCE removes and from which block - Cleanup: Consolidate Call used_values to single source of truth - Early return in methods.rs handles all Call variants - Removed duplicate match arm (now unreachable!()) - ChatGPT's suggestion for cleaner architecture - Docs: Phase 166 analysis of inst_meta layer architecture - Identified CSE pass callee bug (to be fixed next) - Improvement proposals for CallLikeInst 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
12 KiB
inst_meta層とused_values()の設計分析レポート
概要
MIRのメタデータシステム(inst_meta層)と使用値判定(used_values())に関する設計的な問題と改善機会について分析します。
現状の構造
1. Call命令の特殊扱い(methods.rs lines 148-170)
// methods.rs: used_values()
if let MirInstruction::Call { callee, func, args, .. } = self {
// Callee::Method { receiver: Some(r), .. } を特殊処理
match callee {
Some(Callee::Method { receiver: Some(r), .. }) => {
used.push(*r); // ← receiver を明示的に抽出
}
None => {
used.push(*func); // ← legacy path
}
_ => {}
}
used.extend(args.iter().copied());
return used; // ← Early return: inst_meta をバイパス
}
特徴:
- Call命令のみ methods.rs で完結
- CallLikeInst に callee フィールドがないため inst_meta をバイパス
- receiver を含む unified 経路と legacy 経路を統一的に処理
2. CallLikeInst の部分実装(instruction_kinds/mod.rs lines 718-803)
pub enum CallLikeInst {
Call {
dst: Option<ValueId>,
func: ValueId, // ← callee フィールドがない!
args: Vec<ValueId>,
},
BoxCall { dst, box_val, args },
PluginInvoke { dst, box_val, args },
ExternCall { dst, args },
}
impl CallLikeInst {
pub fn used(&self) -> Vec<ValueId> {
match self {
CallLikeInst::Call { func, args, .. } => {
let mut v = Vec::new();
if *func != ValueId::INVALID { // ← INVALID チェック?
v.push(*func);
}
v.extend(args.iter().copied());
v
}
// ... BoxCall, PluginInvoke, ExternCall ...
}
}
}
問題:
- CallLikeInst::Call は callee フィールドを持たない
- unified 経路(Callee::Method)のreceiver 処理が欠落
- ValueId::INVALID チェックは方針がはっきりしない
3. inst_meta の統合パス(instruction_kinds/mod.rs lines 275-352)
pub fn used_via_meta(i: &MirInstruction) -> Option<Vec<ValueId>> {
// ... 多くの instruction の処理 ...
if let Some(k) = CallLikeInst::from_mir(i) {
return Some(k.used()); // ← CallLikeInst::used() を呼び出す
}
// ... rest ...
None
}
現状:
- used_via_meta() は CallLikeInst::used() を呼び出す
- しかし methods.rs の used_values() は early return で inst_meta をバイパス
- 結果: CallLikeInst::used() は実質的に使われていない(DCE等では methods.rs 経路)
問題分析
P1: inst_meta層の役割あいまいさ
症状:
- Call命令: methods.rs で early return(inst_meta 経由でない)
- BoxCall/PluginInvoke: inst_meta 経由で CallLikeInst を使用
- ExternCall: inst_meta 経由で CallLikeInst を使用
根本原因:
- inst_meta はPoC(Proof of Concept)段階の不完全な実装
- Call命令の callee フィールド対応が遅れている
- CallLikeInst に callee を追加できない設計的理由がない
P2: CallLikeInst::Call の不完全な used()
症状:
// CallLikeInst::Call::used()
if *func != ValueId::INVALID {
v.push(*func);
}
- INVALID チェックは unified 経路(callee: Some(_))を前提?
- しかし CallLikeInst には callee フィールドがない
- どちらの経路か判定不可
結論: 設計的に矛盾している
P3: methods.rs の early return がもたらす非対称性
症状:
- Call: methods.rs の manual match(callee 対応)
- BoxCall/PluginInvoke: inst_meta 経由(CallLikeInst 経由)
問題:
- 新しい Call の用途が追加されたとき、methods.rs と CallLikeInst の両方を修正しないといけない
- 意図的な分離か偶発的な分割か不明確
P4: DCE の信頼性
症状(dce.rs lines 60-87):
let mut used_values: HashSet<ValueId> = HashSet::new();
// Mark values used by side-effecting instructions and terminators
for instruction in &block.instructions {
if !instruction.effects().is_pure() {
for u in instruction.used_values() { // ← used_values() を使用
used_values.insert(u);
}
}
}
// Backward propagation
for instruction in &block.instructions {
if used_values.contains(&dst) {
for u in instruction.used_values() {
if used_values.insert(u) { changed = true; }
}
}
}
潜在的リスク:
- Call命令で Callee::Method { receiver: Some(r), .. } の receiver が使用値に含まれるか?
- YES (methods.rs の early return で处理)
- だが、inst_meta::used_via_meta() から入った場合は?
- NO (CallLikeInst::Call は callee を知らない)
結論: 処理経路によって結果が異なる可能性
設計的な分岐点
Option A: CallLikeInst に callee を追加
メリット:
- inst_meta を完全統一化できる
- methods.rs の early return を削除可能
- CallLikeInst::Call が unified 経路に対応
デメリット:
- CallLikeInst が大きくなる(Box-heavy)
- Clone/from_mir の複雑性増加
- Callee enum 自体が methods.rs との結合度を上げる
実装量: 中程度
Option B: methods.rs を強化(inst_meta 側は軽量に保つ)
メリット:
- inst_meta をPoC段階のまま保つことができる
- methods.rs が「Call系の単一ソース・オブ・トゥルース」になる
- 将来 inst_meta を削除しても影響ない
デメリット:
- inst_meta の役割があいまい(不完全なPoC)
- ドキュメント化が重要になる
実装量: 少ない(コメント追加程度)
Option C: inst_meta を CallLikeInst から分離(Method層として実装)
メリット:
- inst_meta と methods.rs の役割を完全に分離
- 将来の拡張に柔軟
デメリット:
- コード複製が増える
- 維持が大変
実装量: 高い
パターンスキャン結果
他の同じ問題がある箇所
1. CSE pass(passes/cse.rs lines 72-91):
fn instruction_key(i: &MirInstruction) -> String {
match i {
// ...
MirInstruction::Call { func, args, .. } => {
format!("call_{}_{}", func.as_u32(), args_str)
// ← callee を無視している!
}
// ...
}
}
問題: Call命令が Callee::Method { receiver: Some(r), .. } を持つ場合、receiver を含めずにキーを生成
影響: 異なる receiver を持つ同じメソッド呼び出しを「同一」と判定する可能性
例:
%r1 = call Method { receiver: Some(%obj1), ... } "upper"()
%r2 = call Method { receiver: Some(%obj2), ... } "upper"()
→ 同じキーになる → CSE で不正な最適化?
他に detected する可能性のある問題
2. 新しい instruction_kinds 追加時:
- inst_meta に追加する人は effects/dst/used の3つを実装する
- methods.rs との同期漏れリスク
3. BoxCall/PluginInvoke の method_id:
- instruction_kinds/mod.rs は method_id を無視している
- methods.rs は method_id を見ていない(フィールドがない)
改善提案(優先度順)
【優先度1】docs: inst_meta の役割と制約をドキュメント化
内容:
- inst_meta は PoC であること
- methods.rs が「単一ソース・オブ・トゥルース」であること
- CallLikeInst は callee フィールドがないこと(意図的)
- 将来統一する際の手順
ファイル: docs/development/current/main/inst-meta-layer-design.md
実装量: 1-2時間
効果: 中(開発者の混乱を減らす)
【優先度2】fix: CSE の instruction_key に callee を含める
内容:
fn instruction_key(i: &MirInstruction) -> String {
match i {
MirInstruction::Call { callee, args, .. } => {
// callee をキーに含める
let callee_str = format!("{:?}", callee); // or structured key
let args_str = args.iter()...
format!("call_{}_{}_{}", callee_str, ...)
}
// ...
}
}
ファイル: src/mir/passes/cse.rs
実装量: 1-2時間
効果: 高(CSEの正確性向上)
【優先度3】refactor: CallLikeInst に callee を追加(段階的)
Phase 1: CallLikeInst::Call に callee: Option を追加
pub enum CallLikeInst {
Call {
dst: Option<ValueId>,
func: ValueId,
callee: Option<Callee>, // 新規
args: Vec<ValueId>,
},
// ...
}
Phase 2: CallLikeInst::used() を更新して receiver を処理
CallLikeInst::Call { func, callee, args, .. } => {
let mut v = Vec::new();
if let Some(Callee::Method { receiver: Some(r), .. }) = callee {
v.push(*r);
} else if *func != ValueId::INVALID {
v.push(*func);
}
v.extend(args.iter().copied());
v
}
Phase 3: methods.rs の early return を削除
// methods.rs: Remove early return for Call
// Let inst_meta::used_via_meta handle it
ファイル:
src/mir/instruction_kinds/mod.rssrc/mir/instruction/methods.rs
実装量: 4-6時間
効果: 高(inst_meta 統一化)
【優先度4】test: 統合テスト(Callee::Method の receiver 判定)
内容: DCE/CSE で receiver を含む Call を正確に処理することを確認
テストケース:
// Case 1: Method call with receiver
%obj = new StringBox()
%r1 = call Method { receiver: Some(%obj), ... } "upper"()
// ↑ obj は使用値に含まれるべき
// Case 2: Different receivers
%s1 = new StringBox()
%s2 = new StringBox()
%r1 = call Method { receiver: Some(%s1), ... } "upper"()
%r2 = call Method { receiver: Some(%s2), ... } "upper"()
// ↑ CSE key は異なるべき
ファイル:
src/mir/instruction_kinds/tests.rs(新規)- または既存テストに統合
実装量: 2-3時間
効果: 中(回帰テスト)
設計原則の推奨
「箱化」の視点から見た改善
現状の問題:
- inst_meta 層が「箱」として完全ではない
- methods.rs との責任の分離がはっきりしていない
推奨アーキテクチャ:
┌─────────────────────────────────┐
│ MIRInstruction │
│ (Callee enum を含む確定形) │
└─────────────────────────────────┘
↓
┌─────────────────────────────────┐
│ methods.rs │
│ effects() / dst_value() │
│ used_values() (single source) │
└─────────────────────────────────┘
↓
┌─────────────────────────────────┐
│ inst_meta (PoC: optional) │
│ 高速化用スキップ層 │
│ (検証/デバッグ用) │
└─────────────────────────────────┘
原則:
- methods.rs が SSOT(Single Source of Truth)
- inst_meta は最適化用レイヤー(将来削除可)
- CallLikeInst は methods.rs を完全ミラー
まとめ
| 問題 | 影響 | 優先度 | 改善方針 |
|---|---|---|---|
| inst_meta 役割あいまい | 開発者混乱 | 1 | ドキュメント化 |
| CSE の callee 無視 | 最適化誤り可能 | 2 | fix CSE |
| CallLikeInst::Call 不完全 | 潜在バグ | 3 | callee 追加 |
| DCE 処理経路の非対称 | テスト困難 | 3 | 統合テスト追加 |