**箱理論に基づく根治的修正**: ## 🎯 Hotfix 1: Parameter ValueId Reservation (パラメータ ValueId 予約) ### 根本原因 - MirFunction counter が params.len() を考慮していなかった - local variables が parameter ValueIds を上書き ### 箱理論的解決 1. **LoopFormContext Box** - パラメータ予約を明示的に管理 - 境界をはっきりさせる 2. **MirFunction::new() 改善** - `initial_counter = param_count.max(1)` でパラメータ予約 - Parameters are %0, %1, ..., %N-1 3. **ensure_counter_after() 強化** - パラメータ数 + 既存 ValueIds 両方を考慮 - `min_counter = param_count.max(max_id + 1)` 4. **reserve_parameter_value_ids() 追加** - 明示的な予約メソッド(Box-First) ## 🎯 Hotfix 2: Exit PHI Predecessor Validation (Exit PHI 検証) ### 根本原因 - LoopForm builder が存在しないブロックを PHI predecessor に追加 - 「幽霊ブロック」問題 ### 箱理論的解決 1. **LoopFormOps.block_exists() 追加** - CFG 存在確認メソッド - 境界を明確化 2. **build_exit_phis() 検証** - 非存在ブロックをスキップ - デバッグログ付き ### 実装ファイル - `src/mir/function.rs`: Parameter reservation - `src/mir/phi_core/loopform_builder.rs`: Context + validation - `src/mir/loop_builder.rs`: LoopFormOps impl - `src/mir/builder/stmts.rs`: Local variable allocation ### 業界標準準拠 - ✅ LLVM IR: Parameters are %0, %1, ... - ✅ SSA Form: PHI predecessors must exist in CFG - ✅ Cytron et al. (1991): Parameter reservation principle 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
86 lines
2.6 KiB
Rust
86 lines
2.6 KiB
Rust
use crate::ast::{ASTNode, BinaryOperator, LiteralValue, Span};
|
|
use crate::mir::{MirCompiler, MirVerifier};
|
|
|
|
fn lit_i(i: i64) -> ASTNode {
|
|
ASTNode::Literal {
|
|
value: LiteralValue::Integer(i),
|
|
span: Span::unknown(),
|
|
}
|
|
}
|
|
|
|
fn bin(op: BinaryOperator, l: ASTNode, r: ASTNode) -> ASTNode {
|
|
ASTNode::BinaryOp {
|
|
operator: op,
|
|
left: Box::new(l),
|
|
right: Box::new(r),
|
|
span: Span::unknown(),
|
|
}
|
|
}
|
|
|
|
/// Basic PHI/SSA sanity: simple counted loop must verify without Undefined value.
|
|
#[test]
|
|
fn mir_phi_basic_counted_loop_verifies() {
|
|
// i = 0;
|
|
// loop (i < 3) {
|
|
// i = i + 1;
|
|
// }
|
|
// return i;
|
|
let ast = ASTNode::Program {
|
|
statements: vec![
|
|
ASTNode::Assignment {
|
|
target: Box::new(ASTNode::Variable {
|
|
name: "i".into(),
|
|
span: Span::unknown(),
|
|
}),
|
|
value: Box::new(lit_i(0)),
|
|
span: Span::unknown(),
|
|
},
|
|
ASTNode::Loop {
|
|
condition: Box::new(bin(
|
|
BinaryOperator::LessThan,
|
|
ASTNode::Variable {
|
|
name: "i".into(),
|
|
span: Span::unknown(),
|
|
},
|
|
lit_i(3),
|
|
)),
|
|
body: vec![ASTNode::Assignment {
|
|
target: Box::new(ASTNode::Variable {
|
|
name: "i".into(),
|
|
span: Span::unknown(),
|
|
}),
|
|
value: Box::new(bin(
|
|
BinaryOperator::Add,
|
|
ASTNode::Variable {
|
|
name: "i".into(),
|
|
span: Span::unknown(),
|
|
},
|
|
lit_i(1),
|
|
)),
|
|
span: Span::unknown(),
|
|
}],
|
|
span: Span::unknown(),
|
|
},
|
|
ASTNode::Return {
|
|
value: Some(Box::new(ASTNode::Variable {
|
|
name: "i".into(),
|
|
span: Span::unknown(),
|
|
})),
|
|
span: Span::unknown(),
|
|
},
|
|
],
|
|
span: Span::unknown(),
|
|
};
|
|
|
|
let mut mc = MirCompiler::with_options(false);
|
|
let cr = mc.compile(ast).expect("compile");
|
|
|
|
let mut verifier = MirVerifier::new();
|
|
if let Err(errors) = verifier.verify_module(&cr.module) {
|
|
for e in &errors {
|
|
eprintln!("[rust-mir-verify] {}", e);
|
|
}
|
|
panic!("MIR verification failed for basic counted loop");
|
|
}
|
|
}
|