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:
@ -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`)
|
||||
|
||||
Reference in New Issue
Block a user