feat(llvm): Phase 132-P0 - block_end_values tuple-key fix for cross-function isolation
## Problem `block_end_values` used block ID only as key, causing collisions when multiple functions share the same block IDs (e.g., bb0 in both condition_fn and main). ## Root Cause - condition_fn's bb0 → block_end_values[0] - main's bb0 → block_end_values[0] (OVERWRITES!) - PHI resolution gets wrong snapshot → dominance error ## Solution (Box-First principle) Change key from `int` to `Tuple[str, int]` (func_name, block_id): ```python # Before block_end_values: Dict[int, Dict[int, ir.Value]] # After block_end_values: Dict[Tuple[str, int], Dict[int, ir.Value]] ``` ## Files Modified (Python - 6 files) 1. `llvm_builder.py` - Type annotation update 2. `function_lower.py` - Pass func_name to lower_blocks 3. `block_lower.py` - Use tuple keys for snapshot save/load 4. `resolver.py` - Add func_name parameter to resolve_incoming 5. `wiring.py` - Thread func_name through PHI wiring 6. `phi_manager.py` - Debug traces ## Files Modified (Rust - cleanup) - Removed deprecated `loop_to_join.rs` (297 lines deleted) - Updated pattern lowerers for cleaner exit handling - Added lifecycle management improvements ## Verification - ✅ Pattern 1: VM RC: 3, LLVM Result: 3 (no regression) - ⚠️ Case C: Still has dominance error (separate root cause) - Needs additional scope fixes (phi_manager, resolver caches) ## Design Principles - **Box-First**: Each function is an isolated Box with scoped state - **SSOT**: (func_name, block_id) uniquely identifies block snapshots - **Fail-Fast**: No cross-function state contamination ## Known Issues (Phase 132-P1) Other function-local state needs same treatment: - phi_manager.predeclared - resolver caches (i64_cache, ptr_cache, etc.) - builder._jump_only_blocks ## Documentation - docs/development/current/main/investigations/phase132-p0-case-c-root-cause.md - docs/development/current/main/investigations/phase132-p0-tuple-key-implementation.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -14,7 +14,8 @@
|
||||
//! ## 解決策
|
||||
//!
|
||||
//! ConditionPatternBox を導入し、if条件が「単純比較」かどうかを判定する。
|
||||
//! AST-based lowerer は単純比較のみ処理可能とし、複雑条件はlegacy modeへフォールバック。
|
||||
//! ただし「単純/複雑/legacy」の語彙は混線しやすいので、routing 用には
|
||||
//! `ConditionCapability` を使って「どの経路で扱うか」を明示する。
|
||||
//!
|
||||
//! Phase 222: 左右反転(literal on left → var on left)と変数同士の比較をサポート。
|
||||
//!
|
||||
@ -35,6 +36,64 @@
|
||||
use crate::ast::{ASTNode, BinaryOperator, LiteralValue};
|
||||
use crate::mir::CompareOp;
|
||||
|
||||
/// ConditionCapability: 条件式をどの戦略で扱えるか(routing 用)
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ConditionCapability {
|
||||
/// Pattern3 if-sum AST-based lowerer で比較として扱える
|
||||
IfSumComparable,
|
||||
/// 上記以外(caller が別経路を選ぶ)
|
||||
Unsupported,
|
||||
}
|
||||
|
||||
fn is_if_sum_value_expr(expr: &ASTNode) -> bool {
|
||||
match expr {
|
||||
ASTNode::Variable { .. } | ASTNode::Literal { .. } => true,
|
||||
ASTNode::BinaryOp {
|
||||
operator, left, right, ..
|
||||
} => matches!(
|
||||
operator,
|
||||
BinaryOperator::Add
|
||||
| BinaryOperator::Subtract
|
||||
| BinaryOperator::Multiply
|
||||
| BinaryOperator::Divide
|
||||
| BinaryOperator::Modulo
|
||||
) && is_if_sum_value_expr(left.as_ref())
|
||||
&& is_if_sum_value_expr(right.as_ref()),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// 条件式の“能力”を判定(routing のための入口)
|
||||
pub fn analyze_condition_capability(cond: &ASTNode) -> ConditionCapability {
|
||||
match cond {
|
||||
ASTNode::BinaryOp {
|
||||
operator,
|
||||
left,
|
||||
right,
|
||||
..
|
||||
} => {
|
||||
let is_comparison = matches!(
|
||||
operator,
|
||||
BinaryOperator::Equal
|
||||
| BinaryOperator::NotEqual
|
||||
| BinaryOperator::Less
|
||||
| BinaryOperator::Greater
|
||||
| BinaryOperator::LessEqual
|
||||
| BinaryOperator::GreaterEqual
|
||||
);
|
||||
if !is_comparison {
|
||||
return ConditionCapability::Unsupported;
|
||||
}
|
||||
if is_if_sum_value_expr(left.as_ref()) && is_if_sum_value_expr(right.as_ref()) {
|
||||
ConditionCapability::IfSumComparable
|
||||
} else {
|
||||
ConditionCapability::Unsupported
|
||||
}
|
||||
}
|
||||
_ => ConditionCapability::Unsupported,
|
||||
}
|
||||
}
|
||||
|
||||
/// if条件のパターン種別
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ConditionPattern {
|
||||
@ -176,8 +235,8 @@ pub fn analyze_condition_pattern(cond: &ASTNode) -> ConditionPattern {
|
||||
/// // i > 0 → true
|
||||
/// assert!(is_simple_comparison(&simple_condition));
|
||||
///
|
||||
/// // i % 2 == 1 → false
|
||||
/// assert!(!is_simple_comparison(&complex_condition));
|
||||
/// // i % 2 == 1 → true(Phase 242-EX-A で比較のオペランドに算術式を許可)
|
||||
/// assert!(is_simple_comparison(&complex_condition));
|
||||
/// ```
|
||||
pub fn is_simple_comparison(cond: &ASTNode) -> bool {
|
||||
analyze_condition_pattern(cond) == ConditionPattern::SimpleComparison
|
||||
@ -605,4 +664,51 @@ mod tests {
|
||||
);
|
||||
assert!(is_simple_comparison(&cond));
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// ConditionCapability (routing) Tests
|
||||
// ========================================================================
|
||||
|
||||
#[test]
|
||||
fn test_capability_if_sum_comparable_simple() {
|
||||
let cond = binop(BinaryOperator::Greater, var("i"), int_lit(0));
|
||||
assert_eq!(
|
||||
analyze_condition_capability(&cond),
|
||||
ConditionCapability::IfSumComparable
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_capability_if_sum_comparable_binop_operand() {
|
||||
let lhs = binop(BinaryOperator::Modulo, var("i"), int_lit(2));
|
||||
let cond = binop(BinaryOperator::Equal, lhs, int_lit(1));
|
||||
assert_eq!(
|
||||
analyze_condition_capability(&cond),
|
||||
ConditionCapability::IfSumComparable
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_capability_rejects_logical_and() {
|
||||
let cond = binop(BinaryOperator::And, var("a"), var("b"));
|
||||
assert_eq!(
|
||||
analyze_condition_capability(&cond),
|
||||
ConditionCapability::Unsupported
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_capability_rejects_method_call_operand() {
|
||||
let method_call = ASTNode::MethodCall {
|
||||
object: Box::new(var("obj")),
|
||||
method: "get".to_string(),
|
||||
arguments: vec![],
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let cond = binop(BinaryOperator::Greater, method_call, int_lit(0));
|
||||
assert_eq!(
|
||||
analyze_condition_capability(&cond),
|
||||
ConditionCapability::Unsupported
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user