LoopBuilder: bind variable_map to Phi result on seal
- After inserting Phi at loop header, update variable_map so that subsequent uses (after the loop) refer to the Phi result instead of the latch/body value. This fixes dominance issues in verifier. - Add tests: loop phi normalization and loop+nested-if phi; both pass.
This commit is contained in:
@ -437,4 +437,94 @@ struct BoxVTable {
|
|||||||
- [ ] 全Box型でtoString/type/equals/cloneが動作
|
- [ ] 全Box型でtoString/type/equals/cloneが動作
|
||||||
- [ ] プラグインBoxの透過的な動作
|
- [ ] プラグインBoxの透過的な動作
|
||||||
- [ ] パフォーマンス改善の確認
|
- [ ] パフォーマンス改善の確認
|
||||||
- [ ] メモリ使用量の変化なし
|
- [ ] メモリ使用量の変化なし
|
||||||
|
|
||||||
|
## 🚀 究極の統一:ビルトインBox完全プラグイン化構想
|
||||||
|
|
||||||
|
### 現状の二重実装問題
|
||||||
|
- **plugin_loader.rs** (1217行) - ビルトインBoxの動的ライブラリ化
|
||||||
|
- **plugin_loader_v2.rs** (906行) - プラグインBoxシステム
|
||||||
|
- 合計2000行以上の重複!
|
||||||
|
|
||||||
|
### 完全プラグイン化の提案
|
||||||
|
|
||||||
|
#### すべてをプラグインに統一
|
||||||
|
```rust
|
||||||
|
// 現在
|
||||||
|
ビルトインFileBox → 静的リンク
|
||||||
|
プラグインFileBox → 動的ロード(.so)
|
||||||
|
|
||||||
|
// 統一後
|
||||||
|
すべてのBox → プラグイン(.so)として実装
|
||||||
|
```
|
||||||
|
|
||||||
|
#### コアBoxの自動ロード戦略
|
||||||
|
```rust
|
||||||
|
const CORE_BOXES: &[&str] = &[
|
||||||
|
"libnyash_string_box.so", // StringBox(必須)
|
||||||
|
"libnyash_integer_box.so", // IntegerBox(必須)
|
||||||
|
"libnyash_bool_box.so", // BoolBox(必須)
|
||||||
|
"libnyash_console_box.so", // ConsoleBox(print用)
|
||||||
|
];
|
||||||
|
|
||||||
|
// 起動時に自動ロード
|
||||||
|
fn init_core_boxes() {
|
||||||
|
for plugin in CORE_BOXES {
|
||||||
|
plugin_loader.load_required(plugin)
|
||||||
|
.expect("Core box loading failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### メリット
|
||||||
|
1. **コード削減**: plugin_loader.rs (1217行) を完全削除
|
||||||
|
2. **統一性**: Everything is Boxの究極の実現
|
||||||
|
3. **柔軟性**: StringBoxすら置き換え可能
|
||||||
|
4. **ビルド高速化**: 本体が軽量に
|
||||||
|
5. **配布の柔軟性**: 必要なBoxだけ選択可能
|
||||||
|
|
||||||
|
### 考慮事項
|
||||||
|
|
||||||
|
#### パフォーマンス
|
||||||
|
- FFI境界のオーバーヘッドは**ナノ秒レベル**
|
||||||
|
- 実用上の影響なし
|
||||||
|
|
||||||
|
#### デバッグの課題と対策
|
||||||
|
```rust
|
||||||
|
// 課題:エラー時のスタックトレース
|
||||||
|
thread 'main' panicked at 'FFI boundary: 0x7f8b2c001234'
|
||||||
|
|
||||||
|
// 対策1:プラグイン側でのロギング
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn box_method_toString() {
|
||||||
|
eprintln!("[StringBox::toString] called from {:?}", std::thread::current().id());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 対策2:デバッグシンボル保持
|
||||||
|
cargo build --features debug-symbols
|
||||||
|
|
||||||
|
// 対策3:プラグイン単体テストの充実
|
||||||
|
#[test]
|
||||||
|
fn test_string_box_methods() { /* ... */ }
|
||||||
|
```
|
||||||
|
|
||||||
|
### 実装ロードマップ
|
||||||
|
1. **Phase A**: コアBoxのプラグイン化
|
||||||
|
- StringBox, IntegerBox, BoolBox, ConsoleBox
|
||||||
|
2. **Phase B**: 起動時自動ロード機構
|
||||||
|
3. **Phase C**: plugin_loader.rs削除
|
||||||
|
4. **Phase D**: ドキュメント・テスト整備
|
||||||
|
|
||||||
|
### 設定ファイル案
|
||||||
|
```toml
|
||||||
|
# ~/.nyash/config.toml
|
||||||
|
[plugins]
|
||||||
|
core_path = "./plugins/core/"
|
||||||
|
search_paths = ["./plugins", "/usr/lib/nyash/plugins"]
|
||||||
|
|
||||||
|
[core_boxes]
|
||||||
|
required = ["string", "integer", "bool", "console"]
|
||||||
|
optional = ["file", "math", "time"]
|
||||||
|
```
|
||||||
|
|
||||||
|
これにより、「Everything is Box」哲学が実装レベルでも完全に実現される!
|
||||||
@ -162,6 +162,8 @@ impl<'a> LoopBuilder<'a> {
|
|||||||
|
|
||||||
// 完成したPhi nodeを発行
|
// 完成したPhi nodeを発行
|
||||||
self.emit_phi_at_block_start(block_id, phi.phi_id, phi.known_inputs)?;
|
self.emit_phi_at_block_start(block_id, phi.phi_id, phi.known_inputs)?;
|
||||||
|
// 重要: ループ外から参照される変数はPhi結果に束縛し直す
|
||||||
|
self.update_variable(phi.var_name.clone(), phi.phi_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -753,4 +753,95 @@ mod tests {
|
|||||||
assert!(errs.iter().any(|e| matches!(e, VerificationError::MergeUsesPredecessorValue{..} | VerificationError::DominatorViolation{..})),
|
assert!(errs.iter().any(|e| matches!(e, VerificationError::MergeUsesPredecessorValue{..} | VerificationError::DominatorViolation{..})),
|
||||||
"Expected merge/dominator error, got: {:?}", errs);
|
"Expected merge/dominator error, got: {:?}", errs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_loop_phi_normalization() {
|
||||||
|
// Program:
|
||||||
|
// local i = 0
|
||||||
|
// loop(false) { i = 1 }
|
||||||
|
// i
|
||||||
|
let ast = ASTNode::Program {
|
||||||
|
statements: vec![
|
||||||
|
ASTNode::Local {
|
||||||
|
variables: vec!["i".to_string()],
|
||||||
|
initial_values: vec![Some(Box::new(ASTNode::Literal { value: LiteralValue::Integer(0), span: Span::unknown() }))],
|
||||||
|
span: Span::unknown(),
|
||||||
|
},
|
||||||
|
ASTNode::Loop {
|
||||||
|
condition: Box::new(ASTNode::Literal { value: LiteralValue::Bool(false), span: Span::unknown() }),
|
||||||
|
body: vec![ ASTNode::Assignment {
|
||||||
|
target: Box::new(ASTNode::Variable { name: "i".to_string(), span: Span::unknown() }),
|
||||||
|
value: Box::new(ASTNode::Literal { value: LiteralValue::Integer(1), span: Span::unknown() }),
|
||||||
|
span: Span::unknown(),
|
||||||
|
}],
|
||||||
|
span: Span::unknown(),
|
||||||
|
},
|
||||||
|
ASTNode::Variable { name: "i".to_string(), span: Span::unknown() },
|
||||||
|
],
|
||||||
|
span: Span::unknown(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut builder = MirBuilder::new();
|
||||||
|
let module = builder.build_module(ast).expect("build mir");
|
||||||
|
|
||||||
|
// Verify SSA/dominance: should pass
|
||||||
|
let mut verifier = MirVerifier::new();
|
||||||
|
let res = verifier.verify_module(&module);
|
||||||
|
if let Err(errs) = &res { eprintln!("Verifier errors: {:?}", errs); }
|
||||||
|
assert!(res.is_ok(), "MIR loop with phi normalization should pass verification");
|
||||||
|
|
||||||
|
// Ensure phi is printed (header phi for variable i)
|
||||||
|
let printer = MirPrinter::verbose();
|
||||||
|
let mir_text = printer.print_module(&module);
|
||||||
|
assert!(mir_text.contains("phi"), "Printed MIR should contain a phi for loop header\n{}", mir_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_loop_nested_if_phi() {
|
||||||
|
// Program:
|
||||||
|
// local x = 0
|
||||||
|
// loop(false) { if true { x = 1 } else { x = 2 } }
|
||||||
|
// x
|
||||||
|
let ast = ASTNode::Program {
|
||||||
|
statements: vec![
|
||||||
|
ASTNode::Local {
|
||||||
|
variables: vec!["x".to_string()],
|
||||||
|
initial_values: vec![Some(Box::new(ASTNode::Literal { value: LiteralValue::Integer(0), span: Span::unknown() }))],
|
||||||
|
span: Span::unknown(),
|
||||||
|
},
|
||||||
|
ASTNode::Loop {
|
||||||
|
condition: Box::new(ASTNode::Literal { value: LiteralValue::Bool(false), span: Span::unknown() }),
|
||||||
|
body: vec![ ASTNode::If {
|
||||||
|
condition: Box::new(ASTNode::Literal { value: LiteralValue::Bool(true), span: Span::unknown() }),
|
||||||
|
then_body: vec![ ASTNode::Assignment {
|
||||||
|
target: Box::new(ASTNode::Variable { name: "x".to_string(), span: Span::unknown() }),
|
||||||
|
value: Box::new(ASTNode::Literal { value: LiteralValue::Integer(1), span: Span::unknown() }),
|
||||||
|
span: Span::unknown(),
|
||||||
|
}],
|
||||||
|
else_body: Some(vec![ ASTNode::Assignment {
|
||||||
|
target: Box::new(ASTNode::Variable { name: "x".to_string(), span: Span::unknown() }),
|
||||||
|
value: Box::new(ASTNode::Literal { value: LiteralValue::Integer(2), span: Span::unknown() }),
|
||||||
|
span: Span::unknown(),
|
||||||
|
}]),
|
||||||
|
span: Span::unknown(),
|
||||||
|
}],
|
||||||
|
span: Span::unknown(),
|
||||||
|
},
|
||||||
|
ASTNode::Variable { name: "x".to_string(), span: Span::unknown() },
|
||||||
|
],
|
||||||
|
span: Span::unknown(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut builder = MirBuilder::new();
|
||||||
|
let module = builder.build_module(ast).expect("build mir");
|
||||||
|
|
||||||
|
let mut verifier = MirVerifier::new();
|
||||||
|
let res = verifier.verify_module(&module);
|
||||||
|
if let Err(errs) = &res { eprintln!("Verifier errors: {:?}", errs); }
|
||||||
|
assert!(res.is_ok(), "Nested if in loop should pass verification with proper phis");
|
||||||
|
|
||||||
|
let printer = MirPrinter::verbose();
|
||||||
|
let mir_text = printer.print_module(&module);
|
||||||
|
assert!(mir_text.contains("phi"), "Printed MIR should contain phi nodes for nested if/loop\n{}", mir_text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user