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:
2025-12-23 00:01:58 +09:00
parent e4b5a8e832
commit 0664526d99
4 changed files with 105 additions and 220 deletions

View File

@ -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> {

View File

@ -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)

View File

@ -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 {

View File

@ -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(())