feat(joinir): Phase 286 P2/P2.1/P2.2 - JoinIR Line Absorption (Pattern1/4 Plan化 PoC + hygiene)
Phase 286 P2: Pattern4 (Loop with Continue) を Plan/Frag SSOT に移行 - DomainPlan::Pattern4Continue 追加 - PlanNormalizer::normalize_pattern4_continue() 実装(phi_bindings による PHI dst 優先参照) - Router integration(Plan line routing → legacy fallback) - Integration test PASS (output: 6), quick smoke 154/154 PASS Phase 286 P2.1: Pattern1 (SimpleWhile) を Plan/Frag SSOT に移行 - DomainPlan::Pattern1SimpleWhile 追加 - PlanNormalizer::normalize_pattern1_simple_while() 実装(4ブロック、1 PHI、phi_bindings 流用) - Router integration(Plan line routing → legacy fallback) - Integration test PASS (return: 3), quick smoke 154/154 PASS Phase 286 P2.2: hygiene(extractor重複排除 + router小整理) - extractor helper化: extract_loop_increment_plan を common_helpers.rs に統一 - Pattern1/Pattern4 が呼ぶだけに変更(重複排除 ~25行) - router helper化: lower_via_plan() を追加し Pattern6/7/4/1 で共用 - 3行パターン(normalize→verify→lower)を1関数に集約(ボイラープレート削減 ~40行) 成果物: - DomainPlan 2パターン新規追加(Pattern1SimpleWhile, Pattern4Continue) - Normalizer 2つの normalize 関数追加 - Router に Plan line ブロック追加 + lower_via_plan() helper - Extractor に extract_pattern1_plan() 追加 - Integration fixtures 2個 + smoke tests 2個 検証: - quick smoke: 154/154 PASS - integration: phase286_pattern1_frag_poc PASS, phase286_pattern4_frag_poc PASS - Plan line routing: route=plan strategy=extract で Pattern1/4 検出確認 🤖 Generated with Claude Code Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -45,7 +45,10 @@ pub(in crate::mir::builder) enum DomainPlan {
|
||||
ScanWithInit(ScanWithInitPlan),
|
||||
/// Pattern7: split / tokenization scan
|
||||
SplitScan(SplitScanPlan),
|
||||
// P2+: BoolPredicate(BoolPredicatePlan), etc.
|
||||
/// Pattern4: Loop with Continue (Phase 286 P2)
|
||||
Pattern4Continue(Pattern4ContinuePlan),
|
||||
/// Pattern1: Simple While Loop (Phase 286 P2.1)
|
||||
Pattern1SimpleWhile(Pattern1SimpleWhilePlan),
|
||||
}
|
||||
|
||||
/// Phase 273 P0: Scan direction for forward/reverse scan
|
||||
@ -98,6 +101,39 @@ pub(in crate::mir::builder) struct SplitScanPlan {
|
||||
pub start_var: String,
|
||||
}
|
||||
|
||||
/// Phase 286 P2: Extracted structure for Pattern4 (Loop with Continue)
|
||||
///
|
||||
/// This structure contains all the information needed to lower a continue-style loop.
|
||||
#[derive(Debug, Clone)]
|
||||
pub(in crate::mir::builder) struct Pattern4ContinuePlan {
|
||||
/// Loop variable name (e.g., "i")
|
||||
pub loop_var: String,
|
||||
/// Carrier variable names (e.g., ["sum"])
|
||||
pub carrier_vars: Vec<String>,
|
||||
/// Loop condition AST (e.g., `i < 6`)
|
||||
pub condition: ASTNode,
|
||||
/// Continue condition AST (e.g., `i == 0`)
|
||||
pub continue_condition: ASTNode,
|
||||
/// Carrier update expressions (var -> update AST)
|
||||
pub carrier_updates: std::collections::BTreeMap<String, ASTNode>,
|
||||
/// Loop increment expression (e.g., `i + 1`)
|
||||
pub loop_increment: ASTNode,
|
||||
}
|
||||
|
||||
/// Phase 286 P2.1: Extracted structure for Pattern1 (Simple While Loop)
|
||||
///
|
||||
/// This structure contains all the information needed to lower a simple while loop.
|
||||
/// Pattern1 is the simplest loop: no break, no continue, no if-else-phi.
|
||||
#[derive(Debug, Clone)]
|
||||
pub(in crate::mir::builder) struct Pattern1SimpleWhilePlan {
|
||||
/// Loop variable name (e.g., "i")
|
||||
pub loop_var: String,
|
||||
/// Loop condition AST (e.g., `i < 3`)
|
||||
pub condition: ASTNode,
|
||||
/// Loop increment expression AST (e.g., `i + 1`)
|
||||
pub loop_increment: ASTNode,
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// CorePlan (固定語彙 - 構造ノードのみ)
|
||||
// ============================================================================
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
|
||||
use super::{
|
||||
CoreEffectPlan, CoreLoopPlan, CorePhiInfo, CorePlan, DomainPlan,
|
||||
ScanWithInitPlan, SplitScanPlan,
|
||||
ScanWithInitPlan, SplitScanPlan, Pattern1SimpleWhilePlan,
|
||||
};
|
||||
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
|
||||
use crate::mir::builder::MirBuilder;
|
||||
@ -39,6 +39,8 @@ impl PlanNormalizer {
|
||||
match domain {
|
||||
DomainPlan::ScanWithInit(parts) => Self::normalize_scan_with_init(builder, parts, ctx),
|
||||
DomainPlan::SplitScan(parts) => Self::normalize_split_scan(builder, parts, ctx),
|
||||
DomainPlan::Pattern4Continue(parts) => Self::normalize_pattern4_continue(builder, parts, ctx),
|
||||
DomainPlan::Pattern1SimpleWhile(parts) => Self::normalize_pattern1_simple_while(builder, parts, ctx),
|
||||
}
|
||||
}
|
||||
|
||||
@ -856,4 +858,610 @@ impl PlanNormalizer {
|
||||
|
||||
Ok(CorePlan::Loop(loop_plan))
|
||||
}
|
||||
|
||||
/// Phase 286 P2: Pattern4Continue → CorePlan 変換
|
||||
///
|
||||
/// Expands Pattern4 (Loop with Continue) semantics into generic CorePlan:
|
||||
/// - CFG structure: 2-step branching + header PHI merge
|
||||
/// - NO Select instruction (not in CoreEffectPlan)
|
||||
/// - NO after PHI (header PHI handles all paths)
|
||||
fn normalize_pattern4_continue(
|
||||
builder: &mut MirBuilder,
|
||||
parts: crate::mir::builder::control_flow::plan::Pattern4ContinuePlan,
|
||||
ctx: &LoopPatternContext,
|
||||
) -> Result<CorePlan, String> {
|
||||
use crate::mir::builder::control_flow::joinir::trace;
|
||||
|
||||
let trace_logger = trace::trace();
|
||||
let debug = ctx.debug;
|
||||
|
||||
if debug {
|
||||
trace_logger.debug(
|
||||
"normalizer/pattern4_continue",
|
||||
&format!(
|
||||
"Phase 286 P2: Normalizing Pattern4Continue for {} (loop_var: {}, carriers: {:?})",
|
||||
ctx.func_name, parts.loop_var, parts.carrier_vars
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// P2 PoC Scope: Single carrier only
|
||||
if parts.carrier_vars.len() != 1 {
|
||||
return Err(format!(
|
||||
"[normalizer] P2 PoC scope: only single carrier supported (found: {})",
|
||||
parts.carrier_vars.len()
|
||||
));
|
||||
}
|
||||
|
||||
let carrier_var = &parts.carrier_vars[0];
|
||||
|
||||
// Step 1: Get host ValueIds for variables
|
||||
let loop_var_init = builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.get(&parts.loop_var)
|
||||
.copied()
|
||||
.ok_or_else(|| format!("[normalizer] Loop variable {} not found", parts.loop_var))?;
|
||||
|
||||
let carrier_init = builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.get(carrier_var)
|
||||
.copied()
|
||||
.ok_or_else(|| format!("[normalizer] Carrier variable {} not found", 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 (2-step branching architecture)
|
||||
let header_bb = builder.next_block_id();
|
||||
let body_bb = builder.next_block_id();
|
||||
let continue_path_bb = builder.next_block_id();
|
||||
let normal_path_bb = builder.next_block_id();
|
||||
let step_continue_bb = builder.next_block_id();
|
||||
let step_normal_bb = builder.next_block_id();
|
||||
let after_bb = builder.next_block_id();
|
||||
|
||||
if debug {
|
||||
trace_logger.debug(
|
||||
"normalizer/pattern4_continue",
|
||||
&format!(
|
||||
"Allocated: preheader={:?}, header={:?}, body={:?}, continue_path={:?}, normal_path={:?}, step_continue={:?}, step_normal={:?}, after={:?}",
|
||||
preheader_bb, header_bb, body_bb, continue_path_bb, normal_path_bb, step_continue_bb, step_normal_bb, after_bb
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Step 4: Allocate ValueIds for loop control and carriers
|
||||
|
||||
// Loop variable PHI dst (header PHI)
|
||||
let loop_var_current = builder.alloc_typed(MirType::Integer);
|
||||
|
||||
// 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);
|
||||
|
||||
// Loop condition value
|
||||
let cond_loop = builder.alloc_typed(MirType::Bool);
|
||||
|
||||
// Continue condition value
|
||||
let cond_continue = builder.alloc_typed(MirType::Bool);
|
||||
|
||||
// Loop increment values (continue path)
|
||||
let loop_var_cont_next = builder.alloc_typed(MirType::Integer);
|
||||
|
||||
// Carrier update value (normal path)
|
||||
let carrier_updated = builder.alloc_typed(MirType::Integer);
|
||||
|
||||
// Loop increment values (normal path)
|
||||
let loop_var_norm_next = builder.alloc_typed(MirType::Integer);
|
||||
|
||||
// Step 5: Lower AST expressions to get operands and const definitions
|
||||
// Pass phi_bindings to ensure loop variables reference PHI dst
|
||||
|
||||
// Lower loop condition (e.g., `i < 6`)
|
||||
let (loop_cond_lhs, loop_cond_op, loop_cond_rhs, loop_cond_consts) =
|
||||
Self::lower_compare_ast(&parts.condition, builder, &phi_bindings)?;
|
||||
|
||||
// Lower continue condition (e.g., `i == 0`)
|
||||
let (cont_cond_lhs, cont_cond_op, cont_cond_rhs, cont_cond_consts) =
|
||||
Self::lower_compare_ast(&parts.continue_condition, builder, &phi_bindings)?;
|
||||
|
||||
// Lower carrier update expression (e.g., `sum = sum + i`)
|
||||
let carrier_update_ast = parts.carrier_updates.get(carrier_var)
|
||||
.ok_or_else(|| format!("[normalizer] Carrier update for {} not found", carrier_var))?;
|
||||
let (carrier_update_lhs, carrier_update_op, carrier_update_rhs, carrier_update_consts) =
|
||||
Self::lower_binop_ast(carrier_update_ast, builder, &phi_bindings)?;
|
||||
|
||||
// Lower loop increment (e.g., `i = i + 1`)
|
||||
let (loop_inc_lhs, loop_inc_op, loop_inc_rhs, loop_inc_consts) =
|
||||
Self::lower_binop_ast(&parts.loop_increment, builder, &phi_bindings)?;
|
||||
|
||||
// Step 6: Build header_effects (const definitions + loop condition check)
|
||||
let mut header_effects = loop_cond_consts;
|
||||
header_effects.push(CoreEffectPlan::Compare {
|
||||
dst: cond_loop,
|
||||
lhs: loop_cond_lhs,
|
||||
op: loop_cond_op,
|
||||
rhs: loop_cond_rhs,
|
||||
});
|
||||
|
||||
// Step 7: Build body (as CorePlan items, NOT block_effects)
|
||||
let mut body = Vec::new();
|
||||
// Add const definitions
|
||||
for const_effect in cont_cond_consts {
|
||||
body.push(CorePlan::Effect(const_effect));
|
||||
}
|
||||
// Add continue condition check
|
||||
body.push(CorePlan::Effect(CoreEffectPlan::Compare {
|
||||
dst: cond_continue,
|
||||
lhs: cont_cond_lhs,
|
||||
op: cont_cond_op,
|
||||
rhs: cont_cond_rhs,
|
||||
}));
|
||||
|
||||
// Step 8: Build continue_path effects (const definitions + increment loop var + passthrough carrier)
|
||||
let mut continue_path_effects = loop_inc_consts.clone();
|
||||
continue_path_effects.push(CoreEffectPlan::BinOp {
|
||||
dst: loop_var_cont_next,
|
||||
lhs: loop_inc_lhs,
|
||||
op: loop_inc_op,
|
||||
rhs: loop_inc_rhs,
|
||||
});
|
||||
|
||||
// Step 8.5: step_continue has NO effects
|
||||
// Carrier passthrough: carrier PHI input directly references carrier_current
|
||||
// No Add 0 workaround needed - PHI inputs can reference the PHI dst from another path
|
||||
|
||||
// Step 9: Build normal_path effects (const definitions + carrier update + loop increment)
|
||||
let mut normal_path_effects = carrier_update_consts;
|
||||
normal_path_effects.push(CoreEffectPlan::BinOp {
|
||||
dst: carrier_updated,
|
||||
lhs: carrier_update_lhs,
|
||||
op: carrier_update_op,
|
||||
rhs: carrier_update_rhs,
|
||||
});
|
||||
normal_path_effects.extend(loop_inc_consts);
|
||||
normal_path_effects.push(CoreEffectPlan::BinOp {
|
||||
dst: loop_var_norm_next,
|
||||
lhs: loop_inc_lhs,
|
||||
op: loop_inc_op,
|
||||
rhs: loop_inc_rhs,
|
||||
});
|
||||
|
||||
// Step 10: Build block_effects
|
||||
let block_effects = vec![
|
||||
(preheader_bb, vec![]),
|
||||
(header_bb, header_effects),
|
||||
(body_bb, vec![]), // Body effects are in body CorePlan field
|
||||
(continue_path_bb, continue_path_effects),
|
||||
(normal_path_bb, normal_path_effects),
|
||||
(step_continue_bb, vec![]), // No effects - carrier passthrough is handled by PHI
|
||||
(step_normal_bb, vec![]),
|
||||
];
|
||||
|
||||
// Step 11: Build PHIs (header PHI with 3 inputs for 2-step branching)
|
||||
let phis = vec![
|
||||
// Loop variable PHI: (preheader, init), (step_continue, cont), (step_normal, norm)
|
||||
CorePhiInfo {
|
||||
block: header_bb,
|
||||
dst: loop_var_current,
|
||||
inputs: vec![
|
||||
(preheader_bb, loop_var_init),
|
||||
(step_continue_bb, loop_var_cont_next),
|
||||
(step_normal_bb, loop_var_norm_next),
|
||||
],
|
||||
tag: format!("loop_var_{}", parts.loop_var),
|
||||
},
|
||||
// Carrier PHI: (preheader, init), (step_continue, current), (step_normal, updated)
|
||||
// Continue path: carrier unchanged - directly reference carrier_current (no intermediate value)
|
||||
CorePhiInfo {
|
||||
block: header_bb,
|
||||
dst: carrier_current,
|
||||
inputs: vec![
|
||||
(preheader_bb, carrier_init),
|
||||
(step_continue_bb, carrier_current), // Continue path: carrier unchanged (self-reference OK)
|
||||
(step_normal_bb, carrier_updated), // Normal path: carrier updated
|
||||
],
|
||||
tag: format!("carrier_{}", carrier_var),
|
||||
},
|
||||
];
|
||||
|
||||
// Step 12: Build Frag (2-step branching structure)
|
||||
let empty_args = EdgeArgs {
|
||||
layout: JumpArgsLayout::CarriersOnly,
|
||||
values: vec![],
|
||||
};
|
||||
|
||||
let branches = vec![
|
||||
// Header: loop condition check
|
||||
BranchStub {
|
||||
from: header_bb,
|
||||
cond: cond_loop,
|
||||
then_target: body_bb,
|
||||
then_args: empty_args.clone(),
|
||||
else_target: after_bb,
|
||||
else_args: empty_args.clone(),
|
||||
},
|
||||
// Body: continue condition check (2-step branching starts here)
|
||||
BranchStub {
|
||||
from: body_bb,
|
||||
cond: cond_continue,
|
||||
then_target: continue_path_bb, // Continue path
|
||||
then_args: empty_args.clone(),
|
||||
else_target: normal_path_bb, // Normal path
|
||||
else_args: empty_args.clone(),
|
||||
},
|
||||
];
|
||||
|
||||
let wires = vec![
|
||||
// Continue path: continue_path → step_continue → header
|
||||
EdgeStub {
|
||||
from: continue_path_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: Some(step_continue_bb),
|
||||
args: empty_args.clone(),
|
||||
},
|
||||
EdgeStub {
|
||||
from: step_continue_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: Some(header_bb),
|
||||
args: empty_args.clone(),
|
||||
},
|
||||
// Normal path: normal_path → step_normal → header
|
||||
EdgeStub {
|
||||
from: normal_path_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: Some(step_normal_bb),
|
||||
args: empty_args.clone(),
|
||||
},
|
||||
EdgeStub {
|
||||
from: step_normal_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: Some(header_bb),
|
||||
args: empty_args.clone(),
|
||||
},
|
||||
];
|
||||
|
||||
let frag = Frag {
|
||||
entry: header_bb,
|
||||
exits: BTreeMap::new(),
|
||||
wires,
|
||||
branches,
|
||||
};
|
||||
|
||||
// Step 13: Build final_values
|
||||
let final_values = vec![
|
||||
(parts.loop_var.clone(), loop_var_current),
|
||||
(carrier_var.clone(), carrier_current),
|
||||
];
|
||||
|
||||
// Step 14: Build CoreLoopPlan
|
||||
let loop_plan = CoreLoopPlan {
|
||||
preheader_bb,
|
||||
header_bb,
|
||||
body_bb,
|
||||
step_bb: step_normal_bb, // Use step_normal as primary step block
|
||||
after_bb,
|
||||
found_bb: after_bb, // No early exit, so found = after
|
||||
body, // Body CorePlan with continue condition check
|
||||
cond_loop,
|
||||
cond_match: cond_continue, // Use continue condition as match condition
|
||||
block_effects,
|
||||
phis,
|
||||
frag,
|
||||
final_values,
|
||||
};
|
||||
|
||||
if debug {
|
||||
trace_logger.debug(
|
||||
"normalizer/pattern4_continue",
|
||||
"CorePlan construction complete (2-step branching with header PHI merge)",
|
||||
);
|
||||
}
|
||||
|
||||
Ok(CorePlan::Loop(loop_plan))
|
||||
}
|
||||
|
||||
/// Helper: Lower Compare AST to (lhs ValueId, op, rhs ValueId, const_effects)
|
||||
fn lower_compare_ast(
|
||||
ast: &crate::ast::ASTNode,
|
||||
builder: &mut MirBuilder,
|
||||
phi_bindings: &BTreeMap<String, crate::mir::ValueId>,
|
||||
) -> Result<(crate::mir::ValueId, CompareOp, crate::mir::ValueId, Vec<CoreEffectPlan>), String> {
|
||||
use crate::ast::{ASTNode, BinaryOperator};
|
||||
|
||||
match ast {
|
||||
ASTNode::BinaryOp { operator, left, right, .. } => {
|
||||
let op = match operator {
|
||||
BinaryOperator::Less => CompareOp::Lt,
|
||||
BinaryOperator::LessEqual => CompareOp::Le,
|
||||
BinaryOperator::Greater => CompareOp::Gt,
|
||||
BinaryOperator::GreaterEqual => CompareOp::Ge,
|
||||
BinaryOperator::Equal => CompareOp::Eq,
|
||||
BinaryOperator::NotEqual => CompareOp::Ne,
|
||||
_ => return Err(format!("[normalizer] Unsupported compare operator: {:?}", operator)),
|
||||
};
|
||||
|
||||
let (lhs, mut lhs_consts) = Self::lower_value_ast(left, builder, phi_bindings)?;
|
||||
let (rhs, rhs_consts) = Self::lower_value_ast(right, builder, phi_bindings)?;
|
||||
|
||||
lhs_consts.extend(rhs_consts);
|
||||
|
||||
Ok((lhs, op, rhs, lhs_consts))
|
||||
}
|
||||
_ => Err(format!("[normalizer] Expected BinaryOp for compare, got: {:?}", ast)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper: Lower BinOp AST to (lhs ValueId, op, rhs ValueId, const_effects)
|
||||
fn lower_binop_ast(
|
||||
ast: &crate::ast::ASTNode,
|
||||
builder: &mut MirBuilder,
|
||||
phi_bindings: &BTreeMap<String, crate::mir::ValueId>,
|
||||
) -> Result<(crate::mir::ValueId, BinaryOp, crate::mir::ValueId, Vec<CoreEffectPlan>), String> {
|
||||
use crate::ast::{ASTNode, BinaryOperator};
|
||||
|
||||
match ast {
|
||||
ASTNode::BinaryOp { operator, left, right, .. } => {
|
||||
let op = match operator {
|
||||
BinaryOperator::Add => BinaryOp::Add,
|
||||
BinaryOperator::Subtract => BinaryOp::Sub,
|
||||
BinaryOperator::Multiply => BinaryOp::Mul,
|
||||
BinaryOperator::Divide => BinaryOp::Div,
|
||||
_ => return Err(format!("[normalizer] Unsupported binary operator: {:?}", operator)),
|
||||
};
|
||||
|
||||
let (lhs, mut lhs_consts) = Self::lower_value_ast(left, builder, phi_bindings)?;
|
||||
let (rhs, rhs_consts) = Self::lower_value_ast(right, builder, phi_bindings)?;
|
||||
|
||||
lhs_consts.extend(rhs_consts);
|
||||
|
||||
Ok((lhs, op, rhs, lhs_consts))
|
||||
}
|
||||
_ => Err(format!("[normalizer] Expected BinOp, got: {:?}", ast)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper: Lower value AST to (ValueId, const_effects)
|
||||
/// Returns the ValueId and any Const instructions needed to define literals
|
||||
///
|
||||
/// phi_bindings: PHI dst for loop variables (takes precedence over variable_map)
|
||||
fn lower_value_ast(
|
||||
ast: &crate::ast::ASTNode,
|
||||
builder: &mut MirBuilder,
|
||||
phi_bindings: &BTreeMap<String, crate::mir::ValueId>,
|
||||
) -> Result<(crate::mir::ValueId, Vec<CoreEffectPlan>), String> {
|
||||
use crate::ast::{ASTNode, LiteralValue};
|
||||
|
||||
match ast {
|
||||
ASTNode::Variable { name, .. } => {
|
||||
// PHI bindings take precedence (for loop variables in header/body)
|
||||
if let Some(&phi_dst) = phi_bindings.get(name) {
|
||||
return Ok((phi_dst, vec![]));
|
||||
}
|
||||
// Fallback to variable_map (for pre-loop init values)
|
||||
if let Some(&value_id) = builder.variable_ctx.variable_map.get(name) {
|
||||
Ok((value_id, vec![]))
|
||||
} else {
|
||||
Err(format!("[normalizer] Variable {} not found", name))
|
||||
}
|
||||
}
|
||||
ASTNode::Literal { value, .. } => {
|
||||
// Allocate ValueId for literal and create Const instruction
|
||||
let value_id = builder.next_value_id();
|
||||
let const_value = match value {
|
||||
LiteralValue::Integer(n) => ConstValue::Integer(*n),
|
||||
LiteralValue::String(s) => ConstValue::String(s.clone()),
|
||||
LiteralValue::Bool(b) => ConstValue::Bool(*b),
|
||||
_ => return Err(format!("[normalizer] Unsupported literal type: {:?}", value)),
|
||||
};
|
||||
|
||||
builder.type_ctx.value_types.insert(value_id, MirType::Integer);
|
||||
|
||||
// Return the ValueId and the Const instruction
|
||||
let const_effect = CoreEffectPlan::Const {
|
||||
dst: value_id,
|
||||
value: const_value,
|
||||
};
|
||||
|
||||
Ok((value_id, vec![const_effect]))
|
||||
}
|
||||
_ => Err(format!("[normalizer] Unsupported value AST: {:?}", ast)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 286 P2.1: Pattern1SimpleWhile → CorePlan 変換
|
||||
///
|
||||
/// Expands Pattern1 (Simple While Loop) semantics into generic CorePlan:
|
||||
/// - CFG structure: preheader → header → body → step → header (back-edge)
|
||||
/// - 1 PHI for loop variable in header
|
||||
/// - No 2-step branching (simpler than Pattern4)
|
||||
fn normalize_pattern1_simple_while(
|
||||
builder: &mut MirBuilder,
|
||||
parts: Pattern1SimpleWhilePlan,
|
||||
ctx: &LoopPatternContext,
|
||||
) -> Result<CorePlan, String> {
|
||||
use crate::mir::builder::control_flow::joinir::trace;
|
||||
|
||||
let trace_logger = trace::trace();
|
||||
let debug = ctx.debug;
|
||||
|
||||
if debug {
|
||||
trace_logger.debug(
|
||||
"normalizer/pattern1_simple_while",
|
||||
&format!(
|
||||
"Phase 286 P2.1: Normalizing Pattern1SimpleWhile for {} (loop_var: {})",
|
||||
ctx.func_name, parts.loop_var
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Step 1: Get host ValueId for loop variable
|
||||
let loop_var_init = builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.get(&parts.loop_var)
|
||||
.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();
|
||||
|
||||
if debug {
|
||||
trace_logger.debug(
|
||||
"normalizer/pattern1_simple_while",
|
||||
&format!(
|
||||
"Allocated: preheader={:?}, header={:?}, body={:?}, step={:?}, after={:?}",
|
||||
preheader_bb, header_bb, body_bb, step_bb, after_bb
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Step 4: Allocate ValueIds for loop control
|
||||
let loop_var_current = builder.alloc_typed(MirType::Integer); // PHI destination
|
||||
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 6: Lower AST expressions
|
||||
// Lower loop condition (e.g., `i < 3`)
|
||||
let (loop_cond_lhs, loop_cond_op, loop_cond_rhs, loop_cond_consts) =
|
||||
Self::lower_compare_ast(&parts.condition, builder, &phi_bindings)?;
|
||||
|
||||
// Lower loop increment (e.g., `i + 1`)
|
||||
let (loop_inc_lhs, loop_inc_op, loop_inc_rhs, loop_inc_consts) =
|
||||
Self::lower_binop_ast(&parts.loop_increment, builder, &phi_bindings)?;
|
||||
|
||||
// Step 7: Build header_effects (const definitions + loop condition check)
|
||||
let mut header_effects = loop_cond_consts;
|
||||
header_effects.push(CoreEffectPlan::Compare {
|
||||
dst: cond_loop,
|
||||
lhs: loop_cond_lhs,
|
||||
op: loop_cond_op,
|
||||
rhs: loop_cond_rhs,
|
||||
});
|
||||
|
||||
// Step 8: Build step_effects (const definitions + increment)
|
||||
let mut step_effects = loop_inc_consts;
|
||||
step_effects.push(CoreEffectPlan::BinOp {
|
||||
dst: loop_var_next,
|
||||
lhs: loop_inc_lhs,
|
||||
op: loop_inc_op,
|
||||
rhs: loop_inc_rhs,
|
||||
});
|
||||
|
||||
// Step 9: Build block_effects
|
||||
let block_effects = vec![
|
||||
(preheader_bb, vec![]),
|
||||
(header_bb, header_effects),
|
||||
(body_bb, vec![]), // Body is empty for simple increment-only loop
|
||||
(step_bb, step_effects),
|
||||
];
|
||||
|
||||
// Step 10: Build PHI (single PHI for loop variable)
|
||||
let phis = vec![
|
||||
CorePhiInfo {
|
||||
block: header_bb,
|
||||
dst: loop_var_current,
|
||||
inputs: vec![
|
||||
(preheader_bb, loop_var_init),
|
||||
(step_bb, loop_var_next),
|
||||
],
|
||||
tag: format!("loop_var_{}", parts.loop_var),
|
||||
},
|
||||
];
|
||||
|
||||
// Step 11: Build Frag
|
||||
let empty_args = EdgeArgs {
|
||||
layout: JumpArgsLayout::CarriersOnly,
|
||||
values: vec![],
|
||||
};
|
||||
|
||||
let branches = vec![
|
||||
BranchStub {
|
||||
from: header_bb,
|
||||
cond: cond_loop,
|
||||
then_target: body_bb,
|
||||
then_args: empty_args.clone(),
|
||||
else_target: after_bb,
|
||||
else_args: empty_args.clone(),
|
||||
},
|
||||
];
|
||||
|
||||
let wires = vec![
|
||||
// body → step
|
||||
EdgeStub {
|
||||
from: body_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: Some(step_bb),
|
||||
args: empty_args.clone(),
|
||||
},
|
||||
// step → header (back-edge)
|
||||
EdgeStub {
|
||||
from: step_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: Some(header_bb),
|
||||
args: empty_args.clone(),
|
||||
},
|
||||
];
|
||||
|
||||
let frag = Frag {
|
||||
entry: header_bb,
|
||||
exits: BTreeMap::new(),
|
||||
wires,
|
||||
branches,
|
||||
};
|
||||
|
||||
// Step 12: Build final_values
|
||||
let final_values = vec![
|
||||
(parts.loop_var.clone(), loop_var_current),
|
||||
];
|
||||
|
||||
// Step 13: Build CoreLoopPlan
|
||||
// Note: found_bb = after_bb (no early exit in Pattern1)
|
||||
// Note: cond_match unused but required by struct, use cond_loop as placeholder
|
||||
let loop_plan = CoreLoopPlan {
|
||||
preheader_bb,
|
||||
header_bb,
|
||||
body_bb,
|
||||
step_bb,
|
||||
after_bb,
|
||||
found_bb: after_bb, // No early exit
|
||||
body: vec![], // Body is empty for simple loop
|
||||
cond_loop,
|
||||
cond_match: cond_loop, // Placeholder (unused in Pattern1)
|
||||
block_effects,
|
||||
phis,
|
||||
frag,
|
||||
final_values,
|
||||
};
|
||||
|
||||
if debug {
|
||||
trace_logger.debug(
|
||||
"normalizer/pattern1_simple_while",
|
||||
"CorePlan construction complete (4 blocks, 1 PHI)",
|
||||
);
|
||||
}
|
||||
|
||||
Ok(CorePlan::Loop(loop_plan))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user