feat(plan): Phase 273 P3 - Plan Lowering SSOT Finalize
Phase 273 P3 完成: CoreLoopPlan SSOT 化、legacy fallback 撤去 Changes: - CoreLoopPlan: Option<...> → 必須フィールド化(構造で "揺れ" を消す) - block_effects, phis, frag, final_values が必須に - legacy fields 削除 (header_effects, step_effects, carriers, loop_var_name) - Lowerer: lower_loop_legacy() 撤去、generalized 経路のみ - emit_scan_with_init_edgecfg() 参照が完全に消えた - Normalizer: Pattern6/7 両方が generalized fields のみを使用 - Verifier: generalized 専用の不変条件追加 (V7-V9) - V7: PHI non-empty - V8: frag.entry == header_bb - V9: block_effects contains header_bb Acceptance Criteria: - ✅ Lowerer から pattern 固有参照が消えている - ✅ Pattern6/7 が generalized CoreLoopPlan を使用 - ✅ legacy fallback 撤去、欠落時は型エラー (Fail-Fast) - ✅ VM テスト PASS (phase254/256/258) - ✅ LLVM テスト PASS (phase256/258) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
//! Phase 273 P1: PlanLowerer - CorePlan → MIR 生成 (SSOT)
|
||||
//! Phase 273 P3: PlanLowerer - CorePlan → MIR 生成 (SSOT)
|
||||
//!
|
||||
//! # Responsibilities
|
||||
//!
|
||||
@ -10,6 +10,12 @@
|
||||
//!
|
||||
//! Lowerer processes CorePlan ONLY. It does not know about scan, split, or
|
||||
//! any other pattern-specific semantics. All pattern knowledge is in Normalizer.
|
||||
//!
|
||||
//! # Phase 273 P3: SSOT Finalization
|
||||
//!
|
||||
//! - Generalized fields (block_effects/phis/frag/final_values) are now REQUIRED
|
||||
//! - Legacy fallback has been removed (Fail-Fast on missing fields)
|
||||
//! - Pattern-specific emission functions (emit_scan_with_init_edgecfg) no longer used
|
||||
|
||||
use super::{CoreEffectPlan, CoreLoopPlan, CorePlan};
|
||||
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
|
||||
@ -66,7 +72,7 @@ impl PlanLowerer {
|
||||
/// Loop: emit blocks, effects, PHI, and edge CFG
|
||||
///
|
||||
/// This is pattern-agnostic. All pattern knowledge is in Normalizer.
|
||||
/// Phase 273 P2: Now supports generalized fields with fallback to legacy.
|
||||
/// Phase 273 P3: Generalized fields are now REQUIRED (Fail-Fast).
|
||||
fn lower_loop(
|
||||
builder: &mut MirBuilder,
|
||||
loop_plan: CoreLoopPlan,
|
||||
@ -81,32 +87,23 @@ impl PlanLowerer {
|
||||
trace_logger.debug(
|
||||
"lowerer/loop",
|
||||
&format!(
|
||||
"Phase 273 P2: Lowering CoreLoopPlan for {}",
|
||||
"Phase 273 P3: Lowering CoreLoopPlan for {}",
|
||||
ctx.func_name
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Phase 273 P2: Check if using generalized fields
|
||||
let use_generalized = loop_plan.block_effects.is_some()
|
||||
&& loop_plan.phis.is_some()
|
||||
&& loop_plan.frag.is_some()
|
||||
&& loop_plan.final_values.is_some();
|
||||
// Phase 273 P3: Generalized fields are now struct fields (not Option)
|
||||
// No validation needed - type system guarantees presence
|
||||
|
||||
if use_generalized {
|
||||
if debug {
|
||||
trace_logger.debug("lowerer/loop", "Using generalized fields");
|
||||
}
|
||||
Self::lower_loop_generalized(builder, loop_plan, ctx)
|
||||
} else {
|
||||
if debug {
|
||||
trace_logger.debug("lowerer/loop", "Using legacy fields (fallback)");
|
||||
}
|
||||
Self::lower_loop_legacy(builder, loop_plan, ctx)
|
||||
if debug {
|
||||
trace_logger.debug("lowerer/loop", "Processing generalized fields (SSOT)");
|
||||
}
|
||||
|
||||
Self::lower_loop_generalized(builder, loop_plan, ctx)
|
||||
}
|
||||
|
||||
/// Phase 273 P2: Generalized loop lowering
|
||||
/// Phase 273 P3: Generalized loop lowering (SSOT)
|
||||
fn lower_loop_generalized(
|
||||
builder: &mut MirBuilder,
|
||||
loop_plan: CoreLoopPlan,
|
||||
@ -118,10 +115,11 @@ impl PlanLowerer {
|
||||
let trace_logger = trace::trace();
|
||||
let debug = ctx.debug;
|
||||
|
||||
let block_effects = loop_plan.block_effects.as_ref().unwrap();
|
||||
let phis = loop_plan.phis.as_ref().unwrap();
|
||||
let frag = loop_plan.frag.as_ref().unwrap();
|
||||
let final_values = loop_plan.final_values.as_ref().unwrap();
|
||||
// Phase 273 P3: Direct access (not Option - type system guarantees presence)
|
||||
let block_effects = &loop_plan.block_effects;
|
||||
let phis = &loop_plan.phis;
|
||||
let frag = &loop_plan.frag;
|
||||
let final_values = &loop_plan.final_values;
|
||||
|
||||
// Step 1: Emit Jump from current block to loop entry
|
||||
if builder.current_block.is_some() {
|
||||
@ -223,139 +221,9 @@ impl PlanLowerer {
|
||||
Ok(Some(void_val))
|
||||
}
|
||||
|
||||
/// Phase 273 P2: Legacy loop lowering (fallback for old Normalizer)
|
||||
fn lower_loop_legacy(
|
||||
builder: &mut MirBuilder,
|
||||
loop_plan: CoreLoopPlan,
|
||||
ctx: &LoopPatternContext,
|
||||
) -> Result<Option<ValueId>, String> {
|
||||
use crate::mir::builder::control_flow::joinir::trace;
|
||||
|
||||
let trace_logger = trace::trace();
|
||||
let debug = ctx.debug;
|
||||
|
||||
// Step 1: Emit Jump from preheader (current block) to header
|
||||
if builder.current_block.is_some() {
|
||||
builder.emit_instruction(MirInstruction::Jump {
|
||||
target: loop_plan.header_bb,
|
||||
edge_args: None,
|
||||
})?;
|
||||
}
|
||||
|
||||
// Step 2: Start header block and emit header effects
|
||||
builder.start_new_block(loop_plan.header_bb)?;
|
||||
|
||||
for effect in &loop_plan.header_effects {
|
||||
Self::emit_effect(builder, effect)?;
|
||||
}
|
||||
|
||||
if debug {
|
||||
trace_logger.debug(
|
||||
"lowerer/loop",
|
||||
&format!("Header effects emitted: {} effects", loop_plan.header_effects.len()),
|
||||
);
|
||||
}
|
||||
|
||||
// Step 3: Start body block and emit body plans
|
||||
builder.start_new_block(loop_plan.body_bb)?;
|
||||
|
||||
for plan in loop_plan.body {
|
||||
match plan {
|
||||
CorePlan::Effect(effect) => {
|
||||
Self::emit_effect(builder, &effect)?;
|
||||
}
|
||||
_ => {
|
||||
// P1: Only Effect plans in body for now
|
||||
return Err("[lowerer] Non-Effect plan in Loop body not yet supported".to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if debug {
|
||||
trace_logger.debug("lowerer/loop", "Body plans emitted");
|
||||
}
|
||||
|
||||
// Step 4: Start step block and emit step effects
|
||||
builder.start_new_block(loop_plan.step_bb)?;
|
||||
|
||||
for effect in &loop_plan.step_effects {
|
||||
Self::emit_effect(builder, effect)?;
|
||||
}
|
||||
|
||||
if debug {
|
||||
trace_logger.debug(
|
||||
"lowerer/loop",
|
||||
&format!("Step effects emitted: {} effects", loop_plan.step_effects.len()),
|
||||
);
|
||||
}
|
||||
|
||||
// Step 5: Ensure found_bb and after_bb exist
|
||||
builder.ensure_block_exists(loop_plan.found_bb)?;
|
||||
builder.ensure_block_exists(loop_plan.after_bb)?;
|
||||
|
||||
// Step 6: Insert PHI at header for each carrier
|
||||
use crate::mir::builder::emission::phi::insert_loop_phi;
|
||||
|
||||
for carrier in &loop_plan.carriers {
|
||||
insert_loop_phi(
|
||||
builder,
|
||||
loop_plan.header_bb,
|
||||
carrier.phi_dst,
|
||||
vec![
|
||||
(loop_plan.preheader_bb, carrier.init_value),
|
||||
(loop_plan.step_bb, carrier.step_value),
|
||||
],
|
||||
"lowerer/loop_phi",
|
||||
)?;
|
||||
}
|
||||
|
||||
if debug {
|
||||
trace_logger.debug("lowerer/loop", "PHI inserted at header_bb");
|
||||
}
|
||||
|
||||
// Step 7: Emit edge CFG (terminators)
|
||||
use crate::mir::builder::emission::loop_scan_with_init::emit_scan_with_init_edgecfg;
|
||||
|
||||
emit_scan_with_init_edgecfg(
|
||||
builder,
|
||||
loop_plan.header_bb,
|
||||
loop_plan.body_bb,
|
||||
loop_plan.step_bb,
|
||||
loop_plan.after_bb,
|
||||
loop_plan.found_bb,
|
||||
loop_plan.cond_loop,
|
||||
loop_plan.cond_match,
|
||||
loop_plan.carriers.first().map(|c| c.phi_dst).unwrap_or(ValueId(0)),
|
||||
)?;
|
||||
|
||||
if debug {
|
||||
trace_logger.debug("lowerer/loop", "Edge CFG emitted");
|
||||
}
|
||||
|
||||
// Step 8: Update variable_map for carriers
|
||||
for carrier in &loop_plan.carriers {
|
||||
builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.insert(carrier.name.clone(), carrier.phi_dst);
|
||||
}
|
||||
|
||||
// Step 9: Setup after_bb for subsequent AST lowering
|
||||
builder.start_new_block(loop_plan.after_bb)?;
|
||||
|
||||
// Step 10: Return Void (pattern applied successfully)
|
||||
use crate::mir::builder::emission::constant::emit_void;
|
||||
let void_val = emit_void(builder);
|
||||
|
||||
if debug {
|
||||
trace_logger.debug(
|
||||
"lowerer/loop",
|
||||
&format!("Loop complete, returning Void {:?}", void_val),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(Some(void_val))
|
||||
}
|
||||
// Phase 273 P3: lower_loop_legacy() has been REMOVED
|
||||
// All patterns must use generalized fields (block_effects/phis/frag/final_values)
|
||||
// Pattern-specific emission functions (emit_scan_with_init_edgecfg) are no longer used
|
||||
|
||||
/// Emit a single CoreEffectPlan as MirInstruction
|
||||
fn emit_effect(builder: &mut MirBuilder, effect: &CoreEffectPlan) -> Result<(), String> {
|
||||
|
||||
@ -137,7 +137,10 @@ pub(in crate::mir::builder) struct CorePhiInfo {
|
||||
pub tag: String,
|
||||
}
|
||||
|
||||
/// Phase 273 P1: Loop plan with carriers
|
||||
/// Phase 273 P3: Loop plan with generalized fields (SSOT)
|
||||
///
|
||||
/// All fields are now REQUIRED (Option removed for structural SSOT).
|
||||
/// Legacy fields (header_effects, step_effects, carriers) have been removed.
|
||||
#[derive(Debug, Clone)]
|
||||
pub(in crate::mir::builder) struct CoreLoopPlan {
|
||||
// === Block IDs (pre-allocated by Normalizer) ===
|
||||
@ -157,24 +160,15 @@ pub(in crate::mir::builder) struct CoreLoopPlan {
|
||||
/// After block (loop exit)
|
||||
pub after_bb: BasicBlockId,
|
||||
|
||||
/// Found block (early exit on match)
|
||||
/// Found block (early exit on match, or same as after_bb for patterns without early exit)
|
||||
pub found_bb: BasicBlockId,
|
||||
|
||||
// === Effects per block (emitted by Lowerer) ===
|
||||
|
||||
/// Header effects (emitted in header_bb before branch)
|
||||
pub header_effects: Vec<CoreEffectPlan>,
|
||||
// === Body (for lowerer body emission) ===
|
||||
|
||||
/// Body plans (emitted in body_bb, can contain If/Exit)
|
||||
pub body: Vec<CorePlan>,
|
||||
|
||||
/// Step effects (emitted in step_bb before back-edge)
|
||||
pub step_effects: Vec<CoreEffectPlan>,
|
||||
|
||||
// === Loop control ===
|
||||
|
||||
/// Loop carriers (PHI variables)
|
||||
pub carriers: Vec<CoreCarrierInfo>,
|
||||
// === Loop control (ValueId references for Frag) ===
|
||||
|
||||
/// Loop condition (for header→body/after branch)
|
||||
pub cond_loop: ValueId,
|
||||
@ -182,23 +176,20 @@ pub(in crate::mir::builder) struct CoreLoopPlan {
|
||||
/// Match condition (for body→found/step branch)
|
||||
pub cond_match: ValueId,
|
||||
|
||||
/// Loop variable name (for variable_map update after loop)
|
||||
pub loop_var_name: String,
|
||||
|
||||
// === Phase 273 P2: Generalized fields (for Pattern7+ support) ===
|
||||
// === Phase 273 P3: Generalized fields (REQUIRED - SSOT) ===
|
||||
|
||||
/// Block-level effects (generalized for multiple blocks)
|
||||
/// Order: SSOT - preheader, header, body, step
|
||||
pub block_effects: Option<Vec<(BasicBlockId, Vec<CoreEffectPlan>)>>,
|
||||
/// Order: SSOT - preheader, header, body, then, else, step (pattern dependent)
|
||||
pub block_effects: Vec<(BasicBlockId, Vec<CoreEffectPlan>)>,
|
||||
|
||||
/// PHI information (generalized for multiple blocks)
|
||||
pub phis: Option<Vec<CorePhiInfo>>,
|
||||
pub phis: Vec<CorePhiInfo>,
|
||||
|
||||
/// Edge CFG fragment (generalized terminator structure)
|
||||
pub frag: Option<Frag>,
|
||||
pub frag: Frag,
|
||||
|
||||
/// Final values for variable_map update (after loop exit)
|
||||
pub final_values: Option<Vec<(String, ValueId)>>,
|
||||
pub final_values: Vec<(String, ValueId)>,
|
||||
}
|
||||
|
||||
/// Phase 273 P1: Loop carrier (PHI variable)
|
||||
|
||||
@ -336,7 +336,7 @@ impl PlanNormalizer {
|
||||
// Step 11: Build final_values (Phase 273 P2)
|
||||
let final_values = vec![(parts.loop_var.clone(), i_current)];
|
||||
|
||||
// Step 12: Build CoreLoopPlan
|
||||
// Step 12: Build CoreLoopPlan (Phase 273 P3: all generalized fields REQUIRED)
|
||||
let loop_plan = CoreLoopPlan {
|
||||
preheader_bb,
|
||||
header_bb,
|
||||
@ -344,23 +344,14 @@ impl PlanNormalizer {
|
||||
step_bb,
|
||||
after_bb,
|
||||
found_bb,
|
||||
header_effects,
|
||||
body,
|
||||
step_effects,
|
||||
carriers: vec![CoreCarrierInfo {
|
||||
name: parts.loop_var.clone(),
|
||||
init_value: i_init_val,
|
||||
step_value: i_next_val,
|
||||
phi_dst: i_current,
|
||||
}],
|
||||
body, // Keep body for lowerer body emission
|
||||
cond_loop,
|
||||
cond_match,
|
||||
loop_var_name: parts.loop_var,
|
||||
// Phase 273 P2: Generalized fields populated
|
||||
block_effects: Some(block_effects),
|
||||
phis: Some(phis),
|
||||
frag: Some(frag),
|
||||
final_values: Some(final_values),
|
||||
// Phase 273 P3: Generalized fields (REQUIRED - SSOT)
|
||||
block_effects,
|
||||
phis,
|
||||
frag,
|
||||
final_values,
|
||||
};
|
||||
|
||||
if debug {
|
||||
@ -765,18 +756,14 @@ impl PlanNormalizer {
|
||||
step_bb,
|
||||
after_bb,
|
||||
found_bb: after_bb, // No early exit for split pattern
|
||||
header_effects,
|
||||
body,
|
||||
step_effects: vec![], // No step_effects (done in then/else)
|
||||
carriers: vec![], // Legacy field (not used with generalized)
|
||||
cond_loop,
|
||||
cond_match,
|
||||
loop_var_name: parts.i_var,
|
||||
// Phase 273 P2: Generalized fields populated
|
||||
block_effects: Some(block_effects),
|
||||
phis: Some(phis),
|
||||
frag: Some(frag),
|
||||
final_values: Some(final_values),
|
||||
// Phase 273 P3: Generalized fields (REQUIRED - SSOT)
|
||||
block_effects,
|
||||
phis,
|
||||
frag,
|
||||
final_values,
|
||||
};
|
||||
|
||||
if debug {
|
||||
|
||||
@ -57,23 +57,50 @@ impl PlanVerifier {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// V1: Carrier completeness, V2: Condition validity
|
||||
/// Phase 273 P3: Verify loop with generalized fields
|
||||
///
|
||||
/// Invariants:
|
||||
/// - V2: Condition validity (cond_loop, cond_match)
|
||||
/// - V7: PHI non-empty (at least one carrier)
|
||||
/// - V8: Frag entry matches header_bb
|
||||
/// - V9: block_effects contains header_bb
|
||||
fn verify_loop(loop_plan: &CoreLoopPlan, depth: usize) -> Result<(), String> {
|
||||
// V1: Carrier completeness
|
||||
for (i, carrier) in loop_plan.carriers.iter().enumerate() {
|
||||
Self::verify_carrier(carrier, depth, i)?;
|
||||
}
|
||||
|
||||
// V2: Condition validity (basic check - ValueId should be non-zero for safety)
|
||||
// Note: Full ValueId validity check would require builder context
|
||||
Self::verify_value_id_basic(loop_plan.cond_loop, depth, "cond_loop")?;
|
||||
Self::verify_value_id_basic(loop_plan.cond_match, depth, "cond_match")?;
|
||||
|
||||
// Verify header_effects
|
||||
for (i, effect) in loop_plan.header_effects.iter().enumerate() {
|
||||
Self::verify_effect(effect, depth).map_err(|e| {
|
||||
format!("[Loop.header_effects[{}]] {}", i, e)
|
||||
})?;
|
||||
// V7: PHI non-empty (loops must have at least one carrier)
|
||||
if loop_plan.phis.is_empty() {
|
||||
return Err(format!(
|
||||
"[V7] Loop at depth {} has no PHI nodes (loops require at least one carrier)",
|
||||
depth
|
||||
));
|
||||
}
|
||||
|
||||
// V8: Frag entry matches header_bb (loop entry SSOT)
|
||||
if loop_plan.frag.entry != loop_plan.header_bb {
|
||||
return Err(format!(
|
||||
"[V8] Loop at depth {} has frag.entry {:?} != header_bb {:?}",
|
||||
depth, loop_plan.frag.entry, loop_plan.header_bb
|
||||
));
|
||||
}
|
||||
|
||||
// V9: block_effects contains header_bb
|
||||
let has_header = loop_plan.block_effects.iter().any(|(bb, _)| *bb == loop_plan.header_bb);
|
||||
if !has_header {
|
||||
return Err(format!(
|
||||
"[V9] Loop at depth {} block_effects missing header_bb {:?}",
|
||||
depth, loop_plan.header_bb
|
||||
));
|
||||
}
|
||||
|
||||
// Verify block_effects
|
||||
for (i, (bb, effects)) in loop_plan.block_effects.iter().enumerate() {
|
||||
for (j, effect) in effects.iter().enumerate() {
|
||||
Self::verify_effect(effect, depth).map_err(|e| {
|
||||
format!("[Loop.block_effects[{}={:?}][{}]] {}", i, bb, j, e)
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
// Verify body plans (now in_loop = true)
|
||||
@ -83,11 +110,23 @@ impl PlanVerifier {
|
||||
})?;
|
||||
}
|
||||
|
||||
// Verify step_effects
|
||||
for (i, effect) in loop_plan.step_effects.iter().enumerate() {
|
||||
Self::verify_effect(effect, depth).map_err(|e| {
|
||||
format!("[Loop.step_effects[{}]] {}", i, e)
|
||||
})?;
|
||||
// Verify PHIs
|
||||
for (i, phi) in loop_plan.phis.iter().enumerate() {
|
||||
Self::verify_value_id_basic(phi.dst, depth, &format!("phi[{}].dst", i))?;
|
||||
for (j, (_, val)) in phi.inputs.iter().enumerate() {
|
||||
Self::verify_value_id_basic(*val, depth, &format!("phi[{}].inputs[{}]", i, j))?;
|
||||
}
|
||||
}
|
||||
|
||||
// Verify final_values
|
||||
for (i, (name, val)) in loop_plan.final_values.iter().enumerate() {
|
||||
if name.is_empty() {
|
||||
return Err(format!(
|
||||
"[V6] final_values[{}] at depth {} has empty name",
|
||||
i, depth
|
||||
));
|
||||
}
|
||||
Self::verify_value_id_basic(*val, depth, &format!("final_values[{}]", i))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
Reference in New Issue
Block a user