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:
nyash-codex
2025-12-15 05:36:50 +09:00
parent 18d56d5b88
commit 3f58f34592
22 changed files with 1076 additions and 384 deletions

View File

@ -57,7 +57,7 @@ impl MirBuilder {
trace::trace().varmap("pattern1_start", &self.variable_map);
// Phase 202-A: Create JoinValueSpace for unified ValueId allocation
// Pattern 1 uses Local region only (no Param region needed - no ConditionEnv)
// Pattern 1 uses Param region for boundary input slots (loop var) and Local region for temps.
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
let mut join_value_space = JoinValueSpace::new();
@ -80,6 +80,7 @@ impl MirBuilder {
use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder;
use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding;
use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
use crate::mir::join_ir::lowering::join_value_space::PARAM_MIN;
// Phase 132-Post: Extract k_exit's parameter ValueId from join_module (Box-First)
let k_exit_func = join_module.require_function("k_exit", "Pattern 1");
@ -96,7 +97,7 @@ impl MirBuilder {
let boundary = JoinInlineBoundaryBuilder::new()
.with_inputs(
vec![ValueId(0)], // JoinIR's main() parameter (loop variable)
vec![ValueId(PARAM_MIN)], // JoinIR's main() parameter (loop variable, Param region)
vec![ctx.loop_var_id], // Host's loop variable
)
.with_exit_bindings(vec![exit_binding]) // Phase 132: Enable exit PHI & variable_map update

View File

@ -188,19 +188,11 @@ impl PatternPipelineContext {
// Complex conditions (e.g., i % 2 == 1) → fallback to legacy mode
if let Some(ASTNode::If { condition, .. }) = if_stmt {
use crate::mir::join_ir::lowering::condition_pattern::{
analyze_condition_pattern, normalize_comparison, ConditionPattern,
analyze_condition_capability, ConditionCapability,
};
// (a) Pattern check: must be SimpleComparison
let pattern = analyze_condition_pattern(condition);
if pattern != ConditionPattern::SimpleComparison {
// Complex condition → legacy mode (PoC lowering)
return false;
}
// (b) Normalization check: must be normalizable
if normalize_comparison(condition).is_none() {
// Normalization failed → legacy mode
// Capability check: if-sum lowerer が扱える比較か
if analyze_condition_capability(condition) != ConditionCapability::IfSumComparable {
return false;
}
}