refactor(joinir): Phase 286 P2.8 - Normalizer Hygiene(小箱化・重複削減)

- LoopBlocksStandard5: 5-block レイアウト(Pattern1/9 で使用)
- LoopBlocksWithIfPhi: 8-block レイアウト(Pattern3 で使用)
- create_phi_bindings(): phi_bindings 作成ヘルパー(全5パターンで使用)

リファクタ適用:
- Pattern1: LoopBlocksStandard5 + create_phi_bindings
- Pattern3: LoopBlocksWithIfPhi + create_phi_bindings
- Pattern4: create_phi_bindings(8-block は別構造のため据え置き)
- Pattern8: create_phi_bindings(6-block は別構造のため据え置き)
- Pattern9: LoopBlocksStandard5 + create_phi_bindings

意味論は完全不変。quick 154/154 PASS。

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-26 05:08:09 +09:00
parent 65304ec0c4
commit f76fb6cd3e

View File

@ -23,6 +23,99 @@ use crate::mir::builder::control_flow::edgecfg::api::{BranchStub, EdgeStub, Exit
use crate::mir::basic_block::EdgeArgs;
use crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout;
use std::collections::BTreeMap;
use crate::mir::{BasicBlockId, ValueId};
// ============================================================================
// Phase 286 P2.8: Normalizer Hygiene Helpers
// ============================================================================
/// Standard 5-block layout for simple loops (Pattern1/4/8/9)
///
/// CFG: preheader → header → body → step → header (back-edge)
/// ↓
/// after
#[derive(Debug, Clone, Copy)]
struct LoopBlocksStandard5 {
preheader_bb: BasicBlockId,
header_bb: BasicBlockId,
body_bb: BasicBlockId,
step_bb: BasicBlockId,
after_bb: BasicBlockId,
}
impl LoopBlocksStandard5 {
/// Allocate 5 blocks for a standard loop
fn allocate(builder: &mut MirBuilder) -> Result<Self, String> {
let preheader_bb = builder
.current_block
.ok_or_else(|| "[normalizer] No current block for loop entry".to_string())?;
let header_bb = builder.next_block_id();
let body_bb = builder.next_block_id();
let step_bb = builder.next_block_id();
let after_bb = builder.next_block_id();
Ok(Self {
preheader_bb,
header_bb,
body_bb,
step_bb,
after_bb,
})
}
}
/// Extended 8-block layout for if-phi loops (Pattern3)
///
/// CFG: preheader → header → body → then/else → merge → step → header
/// ↓
/// after
#[derive(Debug, Clone, Copy)]
struct LoopBlocksWithIfPhi {
preheader_bb: BasicBlockId,
header_bb: BasicBlockId,
body_bb: BasicBlockId,
then_bb: BasicBlockId,
else_bb: BasicBlockId,
merge_bb: BasicBlockId,
step_bb: BasicBlockId,
after_bb: BasicBlockId,
}
impl LoopBlocksWithIfPhi {
/// Allocate 8 blocks for an if-phi loop
fn allocate(builder: &mut MirBuilder) -> Result<Self, String> {
let preheader_bb = builder
.current_block
.ok_or_else(|| "[normalizer] No current block for loop entry".to_string())?;
let header_bb = builder.next_block_id();
let body_bb = builder.next_block_id();
let then_bb = builder.next_block_id();
let else_bb = builder.next_block_id();
let merge_bb = builder.next_block_id();
let step_bb = builder.next_block_id();
let after_bb = builder.next_block_id();
Ok(Self {
preheader_bb,
header_bb,
body_bb,
then_bb,
else_bb,
merge_bb,
step_bb,
after_bb,
})
}
}
/// Create phi_bindings map from variable name-ValueId pairs
///
/// phi_bindings are used to override variable_map lookups during AST lowering,
/// ensuring loop variables reference PHI destinations instead of initial values.
fn create_phi_bindings(bindings: &[(&str, ValueId)]) -> BTreeMap<String, ValueId> {
bindings
.iter()
.map(|(name, id)| (name.to_string(), *id))
.collect()
}
/// Phase 273 P1: PlanNormalizer - DomainPlan → CorePlan 変換 (SSOT)
pub(in crate::mir::builder) struct PlanNormalizer;
@ -946,11 +1039,11 @@ impl PlanNormalizer {
// Carrier PHI dst (header PHI)
let carrier_current = builder.alloc_typed(MirType::Integer);
// Step 4.5: Build phi_bindings - PHI dst takes precedence over variable_map
// This ensures loop condition/body reference PHI dst, not initial values
let mut phi_bindings: BTreeMap<String, crate::mir::ValueId> = BTreeMap::new();
phi_bindings.insert(parts.loop_var.clone(), loop_var_current);
phi_bindings.insert(carrier_var.clone(), carrier_current);
// Step 4.5: Build phi_bindings (Phase 286 P2.8: use create_phi_bindings)
let phi_bindings = create_phi_bindings(&[
(&parts.loop_var, loop_var_current),
(carrier_var, carrier_current),
]);
// Loop condition value
let cond_loop = builder.alloc_typed(MirType::Bool);
@ -1357,16 +1450,9 @@ impl PlanNormalizer {
.copied()
.ok_or_else(|| format!("[normalizer] Loop variable {} not found", parts.loop_var))?;
// Step 2: Capture preheader block
let preheader_bb = builder
.current_block
.ok_or_else(|| "[normalizer] No current block for loop entry".to_string())?;
// Step 3: Allocate BasicBlockIds for 5 blocks
let header_bb = builder.next_block_id();
let body_bb = builder.next_block_id();
let step_bb = builder.next_block_id();
let after_bb = builder.next_block_id();
// Step 2-3: Allocate BasicBlockIds (Phase 286 P2.8: use LoopBlocksStandard5)
let blocks = LoopBlocksStandard5::allocate(builder)?;
let LoopBlocksStandard5 { preheader_bb, header_bb, body_bb, step_bb, after_bb } = blocks;
if debug {
trace_logger.debug(
@ -1383,9 +1469,8 @@ impl PlanNormalizer {
let cond_loop = builder.alloc_typed(MirType::Bool); // Loop condition
let loop_var_next = builder.alloc_typed(MirType::Integer); // Incremented loop var
// Step 5: Build phi_bindings - PHI dst takes precedence over variable_map
let mut phi_bindings: BTreeMap<String, crate::mir::ValueId> = BTreeMap::new();
phi_bindings.insert(parts.loop_var.clone(), loop_var_current);
// Step 5: Build phi_bindings (Phase 286 P2.8: use create_phi_bindings)
let phi_bindings = create_phi_bindings(&[(&parts.loop_var, loop_var_current)]);
// Step 6: Lower AST expressions
// Lower loop condition (e.g., `i < 3`)
@ -1551,16 +1636,9 @@ impl PlanNormalizer {
.copied()
.ok_or_else(|| format!("[normalizer] Accumulator variable {} not found", parts.acc_var))?;
// Step 2: Capture preheader block
let preheader_bb = builder
.current_block
.ok_or_else(|| "[normalizer] No current block for loop entry".to_string())?;
// Step 3: Allocate BasicBlockIds for 5 blocks
let header_bb = builder.next_block_id();
let body_bb = builder.next_block_id();
let step_bb = builder.next_block_id();
let after_bb = builder.next_block_id();
// Step 2-3: Allocate BasicBlockIds (Phase 286 P2.8: use LoopBlocksStandard5)
let blocks = LoopBlocksStandard5::allocate(builder)?;
let LoopBlocksStandard5 { preheader_bb, header_bb, body_bb, step_bb, after_bb } = blocks;
if debug {
trace_logger.debug(
@ -1579,10 +1657,11 @@ impl PlanNormalizer {
let acc_var_next = builder.alloc_typed(MirType::Integer); // Updated accumulator
let loop_var_next = builder.alloc_typed(MirType::Integer); // Incremented loop var
// Step 5: Build phi_bindings - PHI dst takes precedence over variable_map
let mut phi_bindings: BTreeMap<String, crate::mir::ValueId> = BTreeMap::new();
phi_bindings.insert(parts.loop_var.clone(), loop_var_current);
phi_bindings.insert(parts.acc_var.clone(), acc_var_current);
// Step 5: Build phi_bindings (Phase 286 P2.8: use create_phi_bindings)
let phi_bindings = create_phi_bindings(&[
(&parts.loop_var, loop_var_current),
(&parts.acc_var, acc_var_current),
]);
// Step 6: Lower AST expressions
// Lower loop condition (e.g., `i < 3`)
@ -1823,9 +1902,8 @@ impl PlanNormalizer {
let true_val = builder.alloc_typed(MirType::Bool); // Const true
let false_val = builder.alloc_typed(MirType::Bool); // Const false
// Step 5: Build phi_bindings
let mut phi_bindings: BTreeMap<String, crate::mir::ValueId> = BTreeMap::new();
phi_bindings.insert(parts.loop_var.clone(), loop_var_current);
// Step 5: Build phi_bindings (Phase 286 P2.8: use create_phi_bindings)
let phi_bindings = create_phi_bindings(&[(&parts.loop_var, loop_var_current)]);
// Step 6: Lower loop condition (e.g., `i < s.length()`) using shared helper
let (loop_cond_lhs, loop_cond_op, loop_cond_rhs, loop_cond_consts) =
@ -2107,19 +2185,11 @@ impl PlanNormalizer {
.copied()
.ok_or_else(|| format!("[normalizer] Carrier variable {} not found", parts.carrier_var))?;
// Step 2: Capture preheader block
let preheader_bb = builder
.current_block
.ok_or_else(|| "[normalizer] No current block for loop entry".to_string())?;
// Step 3: Allocate BasicBlockIds for 8 blocks
let header_bb = builder.next_block_id();
let body_bb = builder.next_block_id();
let then_bb = builder.next_block_id();
let else_bb = builder.next_block_id();
let merge_bb = builder.next_block_id();
let step_bb = builder.next_block_id();
let after_bb = builder.next_block_id();
// Step 2-3: Allocate BasicBlockIds (Phase 286 P2.8: use LoopBlocksWithIfPhi)
let blocks = LoopBlocksWithIfPhi::allocate(builder)?;
let LoopBlocksWithIfPhi {
preheader_bb, header_bb, body_bb, then_bb, else_bb, merge_bb, step_bb, after_bb
} = blocks;
if debug {
trace_logger.debug(
@ -2141,10 +2211,11 @@ impl PlanNormalizer {
let carrier_next = builder.alloc_typed(MirType::Integer); // merge PHI dst
let loop_var_next = builder.alloc_typed(MirType::Integer); // step update result
// Step 5: Build phi_bindings - PHI dst takes precedence over variable_map
let mut phi_bindings: BTreeMap<String, crate::mir::ValueId> = BTreeMap::new();
phi_bindings.insert(parts.loop_var.clone(), loop_var_current);
phi_bindings.insert(parts.carrier_var.clone(), carrier_current);
// Step 5: Build phi_bindings (Phase 286 P2.8: use create_phi_bindings)
let phi_bindings = create_phi_bindings(&[
(&parts.loop_var, loop_var_current),
(&parts.carrier_var, carrier_current),
]);
// Step 6: Lower AST expressions
// Lower loop condition (e.g., `i < 3`)