Previously, CSE's instruction_key() ignored the callee field, which could
cause different method calls on the same receiver to be incorrectly merged:
%r1 = call Method { receiver: %obj, method: "upper" } ()
%r2 = call Method { receiver: %obj, method: "lower" } ()
// Both had key "call_<obj>_" - WRONG!
Now generates unique keys for all Callee variants:
- Global(name) → call_global_{name}_{args}
- Method { box, method, recv } → call_method_{box}.{method}_{recv}_{args}
- Value(vid) → call_value_{vid}_{args}
- Extern(name) → call_extern_{name}_{args}
- Constructor { box_type } → call_ctor_{type}_{args}
- Closure { .. } → call_closure_{func}_{args}
- None (legacy) → call_legacy_{func}_{args}
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
6.8 KiB
6.8 KiB
CSE Pass Callee フィールド対応修正 - 完了報告
日付
2025-12-05
概要
CSE (Common Subexpression Elimination) パスの instruction_key() 関数で Call 命令の callee フィールドを無視していたバグを修正しました。
問題
根本原因
src/mir/passes/cse.rs の instruction_key() 関数が Call 命令のキー生成時に callee フィールドを無視していました:
// 修正前(バグあり)
MirInstruction::Call { func, args, .. } => {
format!("call_{}_{}", func.as_u32(), args_str)
// ← callee フィールドを無視!
}
影響範囲
これにより、以下のような問題が発生する可能性がありました:
-
異なるメソッドの誤統合:
%r1 = call Method { receiver: %obj, method: "upper", ... } () %r2 = call Method { receiver: %obj, method: "lower", ... } () // 両方とも "call_<obj>_" という同じキー → 誤って統合される可能性 -
Global 関数の情報損失:
%r1 = call Global("print") (%msg) // callee 情報が失われ、func の ValueId だけでキー生成
修正内容
コード変更
src/mir/passes/cse.rs の instruction_key() 関数を修正(行 81-126):
MirInstruction::Call { callee, func, args, .. } => {
let args_str = args.iter()
.map(|v| v.as_u32().to_string())
.collect::<Vec<_>>()
.join(",");
// callee 情報を含めて正確なキー生成
if let Some(c) = callee {
use crate::mir::Callee;
match c {
Callee::Global(name) => {
format!("call_global_{}_{}", name, args_str)
}
Callee::Method { box_name, method, receiver, .. } => {
let recv_str = receiver
.map(|r| r.as_u32().to_string())
.unwrap_or_else(|| "static".to_string());
format!("call_method_{}.{}_{}_{}",
box_name, method, recv_str, args_str)
}
Callee::Value(v) => {
format!("call_value_{}_{}", v.as_u32(), args_str)
}
Callee::Extern(name) => {
format!("call_extern_{}_{}", name, args_str)
}
Callee::Constructor { box_type } => {
format!("call_ctor_{}_{}", box_type, args_str)
}
Callee::Closure { .. } => {
// Closures are unique by definition
format!("call_closure_{}_{}", func.as_u32(), args_str)
}
}
} else {
// Legacy path: backward compatibility
format!("call_legacy_{}_{}", func.as_u32(), args_str)
}
}
キー生成方式
| Callee 種類 | キー形式 | 例 |
|---|---|---|
| Global | call_global_{name}_{args} |
call_global_print_200 |
| Method | call_method_{box}.{method}_{receiver}_{args} |
call_method_StringBox.upper_100_ |
| Value | call_value_{vid}_{args} |
call_value_42_200 |
| Extern | call_extern_{name}_{args} |
call_extern_libc.malloc_1024 |
| Constructor | call_ctor_{box_type}_{args} |
call_ctor_StringBox_5 |
| Closure | call_closure_{func}_{args} |
call_closure_99_ |
| None (legacy) | call_legacy_{func}_{args} |
call_legacy_42_200 |
テスト結果
単体テスト(standalone)
✅ Test 1: 同じ receiver、異なる method → 異なるキー
call_method_StringBox.upper_100_ ≠ call_method_StringBox.lower_100_
✅ Test 2: 異なる receiver、同じ method → 異なるキー
call_method_StringBox.upper_100_ ≠ call_method_StringBox.upper_101_
✅ Test 3: 完全に同じ呼び出し → 同じキー(CSE が効く)
call_method_StringBox.upper_100_ == call_method_StringBox.upper_100_
✅ Test 4: Global 関数呼び出し → 正しいキー
call_global_print_200
✅ Test 5: Legacy 呼び出し(callee なし)→ 互換性維持
call_legacy_42_200
統合テスト
✅ apps/tests/loop_if_phi.hako - sum=9(正常)
✅ apps/tests/peek_expr_block.hako - found one(正常)
✅ apps/tests/loop_min_while.hako - 0 1 2(正常)
✅ apps/tests/string_ops_basic.hako - len=5, sub=bcd(正常)
ビルド結果
✅ cargo build --release
Compiling nyash-rust v0.1.0
Finished `release` profile [optimized] target(s) in 0.10s
達成効果
✅ 正確性向上
- 異なるメソッド呼び出しを正しく区別
- receiver と method の両方を考慮したキー生成
- Global/Extern/Constructor 呼び出しを明確に分離
✅ バグ予防
- 同じ receiver でも異なるメソッドは別のキーに
- 異なる receiver でも同じメソッドは別のキーに
- callee 情報の完全活用
✅ 後方互換性
callee: Noneの legacy path で既存コード継続動作- 段階的移行をサポート
✅ パフォーマンス
- キー生成コストはわずか(許容範囲)
- CSE の効果は維持(正確性向上)
技術的洞察
Callee 型の重要性
ChatGPT5 Pro が設計した Callee enum は、MIR Call 命令の型安全性を大幅に向上させました:
- コンパイル時解決: 関数呼び出しの種類を型で表現
- シャドウイング回避: 実行時文字列解決から脱却
- 最適化基盤: CSE などのパスが正確な情報を利用可能
CSE での活用
Callee 情報により、CSE パスは以下を正確に判断できます:
- 同じメソッド呼び出しの検出(receiver + method)
- 異なる呼び出しの区別(Global vs Method vs Extern)
- Constructor と Closure の特別扱い
関連ドキュメント
今後の展開
短期(Phase 33)
- ✅ CSE Pass の callee 対応完了
- 🔄 他の最適化パスでの callee 活用検討
中期(Phase 34)
- 型推論での callee 情報活用
- インライン化での callee 情報利用
長期(Phase 40+)
- Callee ベースの高度な最適化
- JIT での callee 情報活用
結論
CSE Pass の callee 対応修正は完全に成功しました。修正は最小限(約50行)で、テストは全て通過し、期待される効果を達成しています。
この修正により、CSE パスは MIR Call 命令の callee 情報を正確に利用し、異なる呼び出しを正しく区別できるようになりました。これは MIR Callee 型革新(Phase 15.5)の重要な実装成果の一つです。
実装者: Claude Code レビュー: 完了 マージ準備: Ready ✅