refactor(joinir): Phase 286 P3.2 cleanup + normalizer modularization
## Legacy Pattern5 削除 (488行) - pattern5_infinite_early_exit.rs 完全削除 - LOOP_PATTERNS テーブルからエントリ削除 - Plan extractor が SSOT ## Normalizer 分割 (3294行 → 12ファイル) - helpers.rs: 共通ヘルパー関数 - pattern*.rs: 各パターン専用ファイル - mod.rs: ディスパッチャ ## ドキュメント更新 - Phase 286 README: クリーンアップ完了・Fail-Fast方針記載 - Phase 287 README: 将来計画 quick smoke 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:
@ -79,7 +79,6 @@ pub(in crate::mir::builder) mod pattern2_with_break;
|
||||
pub(in crate::mir::builder) mod pattern3_with_if_phi;
|
||||
pub(in crate::mir::builder) mod pattern4_carrier_analyzer;
|
||||
pub(in crate::mir::builder) mod pattern4_with_continue;
|
||||
pub(in crate::mir::builder) mod pattern5_infinite_early_exit; // Phase 131-11
|
||||
pub(in crate::mir::builder) mod pattern6_scan_with_init; // Phase 254 P0: index_of/find/contains pattern
|
||||
pub(in crate::mir::builder) mod pattern7_split_scan; // Phase 256 P0: split/tokenization with variable step
|
||||
pub(in crate::mir::builder) mod pattern8_scan_bool_predicate; // Phase 259 P0: boolean predicate scan (is_integer/is_valid)
|
||||
|
||||
@ -1,488 +0,0 @@
|
||||
//! Pattern 5: Infinite Loop with Early Exit (Phase 131-11)
|
||||
//!
|
||||
//! # Pattern Overview
|
||||
//!
|
||||
//! Handles `loop(true)` with both `break` and `continue` statements.
|
||||
//! This is a specialized pattern for infinite loops with early exit conditions.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```nyash
|
||||
//! local counter = 0
|
||||
//! loop (true) {
|
||||
//! counter = counter + 1
|
||||
//! if counter == 3 { break }
|
||||
//! continue
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! # Shape Guard (Fail-Fast)
|
||||
//!
|
||||
//! - Condition MUST be `true` literal (infinite loop)
|
||||
//! - MUST have exactly 1 break statement
|
||||
//! - MUST have exactly 1 continue statement
|
||||
//! - Carriers: MUST be exactly 1 counter-like variable
|
||||
//! - No nested loops (not yet supported)
|
||||
//!
|
||||
//! # Implementation Status
|
||||
//!
|
||||
//! Phase 131-11: Fully implemented
|
||||
//! - Shape validation with strict break/continue guards
|
||||
//! - Lowering via JoinIRConversionPipeline with counter carrier tracking
|
||||
|
||||
use super::super::trace;
|
||||
use crate::ast::ASTNode;
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::loop_pattern_detection::LoopPatternKind;
|
||||
use crate::mir::ValueId;
|
||||
|
||||
use super::router::LoopPatternContext;
|
||||
|
||||
/// Phase 131-11-D: Enhanced shape guard with real count + position constraints
|
||||
///
|
||||
/// This function validates the loop structure for Pattern 5 with strict requirements:
|
||||
/// - Exactly 1 break inside `if counter == N { break }`
|
||||
/// Phase 282 P7: Pattern detection for InfiniteEarlyExit (ExtractionBased)
|
||||
///
|
||||
/// This function checks if the loop matches Pattern 5 characteristics.
|
||||
/// Uses ExtractionBased strategy with extractor as SSOT.
|
||||
pub(crate) fn can_lower(_builder: &MirBuilder, ctx: &LoopPatternContext) -> bool {
|
||||
let debug = ctx.debug;
|
||||
|
||||
// Phase 282 P7 Step 1: Pattern kind safety valve (O(1) guard)
|
||||
if ctx.pattern_kind != LoopPatternKind::InfiniteEarlyExit {
|
||||
if debug {
|
||||
trace::trace().debug(
|
||||
"pattern5/can_lower",
|
||||
&format!(
|
||||
"Pattern kind mismatch: expected InfiniteEarlyExit, got {:?}",
|
||||
ctx.pattern_kind
|
||||
),
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Phase 282 P7 Step 2: ExtractionBased detection (SSOT)
|
||||
use super::extractors::pattern5::extract_infinite_early_exit_parts;
|
||||
|
||||
match extract_infinite_early_exit_parts(ctx.condition, ctx.body) {
|
||||
Ok(Some(parts)) => {
|
||||
if debug {
|
||||
trace::trace().debug(
|
||||
"pattern5/can_lower",
|
||||
&format!(
|
||||
"✅ Pattern5 detected: break={}, continue={}, return={}, nested={}, continue_at_end={}, break_in_if={}",
|
||||
parts.break_count,
|
||||
parts.continue_count,
|
||||
parts.return_count,
|
||||
parts.has_nested_loop,
|
||||
parts.continue_at_end,
|
||||
parts.break_in_simple_if
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Phase 282 P7 Step 3: Carrier validation (existing logic preserved)
|
||||
// Pattern5 requires exactly 1 carrier (counter-like variable)
|
||||
if ctx.features.carrier_count != 1 {
|
||||
if debug {
|
||||
trace::trace().debug(
|
||||
"pattern5/can_lower",
|
||||
&format!(
|
||||
"Carrier count mismatch: expected 1, got {}",
|
||||
ctx.features.carrier_count
|
||||
),
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
Ok(None) => {
|
||||
if debug {
|
||||
trace::trace().debug(
|
||||
"pattern5/can_lower",
|
||||
"Not Pattern5 (extraction returned None - structural mismatch)",
|
||||
);
|
||||
}
|
||||
false
|
||||
}
|
||||
Err(e) => {
|
||||
// USER GUIDANCE: Log "unsupported" for Err cases (e.g., return found)
|
||||
if debug {
|
||||
trace::trace().debug(
|
||||
"pattern5/can_lower",
|
||||
&format!("Pattern5 unsupported: {}", e),
|
||||
);
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 131-11-D: Extract counter variable name from break condition
|
||||
///
|
||||
/// Looks for pattern: `if counter == N { break }`
|
||||
/// Returns the counter variable name
|
||||
fn extract_counter_name(body: &[ASTNode]) -> Result<String, String> {
|
||||
use crate::ast::BinaryOperator;
|
||||
|
||||
for stmt in body {
|
||||
if let ASTNode::If {
|
||||
condition,
|
||||
then_body,
|
||||
..
|
||||
} = stmt
|
||||
{
|
||||
// Check if then_body contains just break
|
||||
if then_body.len() == 1 && matches!(then_body[0], ASTNode::Break { .. }) {
|
||||
// Extract counter from condition
|
||||
if let ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Equal,
|
||||
left,
|
||||
..
|
||||
} = condition.as_ref()
|
||||
{
|
||||
if let ASTNode::Variable { name, .. } = left.as_ref() {
|
||||
return Ok(name.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err("Could not extract counter variable from break condition".to_string())
|
||||
}
|
||||
|
||||
/// Phase 131-11-D: Extract limit constant from break condition
|
||||
///
|
||||
/// Looks for pattern: `if counter == LIMIT { break }`
|
||||
/// Returns the LIMIT value
|
||||
fn extract_limit_value(body: &[ASTNode]) -> Result<i64, String> {
|
||||
use crate::ast::{BinaryOperator, LiteralValue};
|
||||
|
||||
for stmt in body {
|
||||
if let ASTNode::If {
|
||||
condition,
|
||||
then_body,
|
||||
..
|
||||
} = stmt
|
||||
{
|
||||
// Check if then_body contains just break
|
||||
if then_body.len() == 1 && matches!(then_body[0], ASTNode::Break { .. }) {
|
||||
// Extract limit from condition
|
||||
if let ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Equal,
|
||||
right,
|
||||
..
|
||||
} = condition.as_ref()
|
||||
{
|
||||
if let ASTNode::Literal {
|
||||
value: LiteralValue::Integer(limit),
|
||||
..
|
||||
} = right.as_ref()
|
||||
{
|
||||
return Ok(*limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err("Could not extract limit constant from break condition".to_string())
|
||||
}
|
||||
|
||||
/// Phase 282 P7: Lower InfiniteEarlyExit pattern to JoinIR (ExtractionBased)
|
||||
///
|
||||
/// # Implementation Status
|
||||
///
|
||||
/// Phase 282 P7: Re-extraction for SSOT enforcement
|
||||
///
|
||||
/// # JoinIR Structure (post-increment pattern)
|
||||
///
|
||||
/// ```text
|
||||
/// fn main(counter_init):
|
||||
/// result = loop_step(counter_init)
|
||||
/// return Void
|
||||
///
|
||||
/// fn loop_step(counter):
|
||||
/// counter_next = counter + 1
|
||||
/// break_cond = (counter_next == LIMIT)
|
||||
/// Jump(k_exit, [counter_next], cond=break_cond)
|
||||
/// Call(loop_step, [counter_next]) // tail call
|
||||
///
|
||||
/// fn k_exit(counter_exit):
|
||||
/// return Void
|
||||
/// ```
|
||||
pub(crate) fn lower(
|
||||
builder: &mut MirBuilder,
|
||||
ctx: &LoopPatternContext,
|
||||
) -> Result<Option<ValueId>, String> {
|
||||
let debug = ctx.debug;
|
||||
|
||||
// Phase 282 P7 Step 4: Re-extract to enforce SSOT
|
||||
use super::extractors::pattern5::extract_infinite_early_exit_parts;
|
||||
|
||||
let parts = match extract_infinite_early_exit_parts(ctx.condition, ctx.body) {
|
||||
Ok(Some(p)) => p,
|
||||
Ok(None) => {
|
||||
return Err(
|
||||
"[pattern5/lower] Extraction returned None (should not happen - can_lower() passed)"
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(format!("[pattern5/lower] Extraction failed: {}", e));
|
||||
}
|
||||
};
|
||||
|
||||
if debug {
|
||||
trace::trace().debug(
|
||||
"pattern5/lower",
|
||||
&format!(
|
||||
"Pattern5 lowering: break={}, continue={}, return={}",
|
||||
parts.break_count, parts.continue_count, parts.return_count
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Existing lowering logic (zero behavior change)
|
||||
// Step 1: Extract counter variable name (existing helper)
|
||||
let counter_name = extract_counter_name(ctx.body)?;
|
||||
if debug {
|
||||
trace::trace().debug(
|
||||
"pattern5/lower",
|
||||
&format!("Extracted counter variable: '{}'", counter_name),
|
||||
);
|
||||
}
|
||||
|
||||
// Step 2: Get counter ValueId from variable_ctx.variable_map
|
||||
let counter_id = builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.get(&counter_name)
|
||||
.copied()
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"Counter variable '{}' not found in variable_ctx.variable_map",
|
||||
counter_name
|
||||
)
|
||||
})?;
|
||||
|
||||
if debug {
|
||||
trace::trace().debug(
|
||||
"pattern5/lower",
|
||||
&format!("Counter ValueId: {:?}", counter_id),
|
||||
);
|
||||
}
|
||||
|
||||
// Step 3: Extract limit value
|
||||
let limit = extract_limit_value(ctx.body)?;
|
||||
if debug {
|
||||
trace::trace().debug(
|
||||
"pattern5/lower",
|
||||
&format!("Extracted limit value: {}", limit),
|
||||
);
|
||||
}
|
||||
|
||||
// Step 4: Generate JoinIR
|
||||
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
|
||||
use crate::mir::join_ir::{
|
||||
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule,
|
||||
MirLikeInst,
|
||||
};
|
||||
|
||||
let mut join_value_space = JoinValueSpace::new();
|
||||
|
||||
let mut join_module = JoinModule::new();
|
||||
|
||||
// Function IDs
|
||||
let main_id = JoinFuncId::new(0);
|
||||
let loop_step_id = JoinFuncId::new(1);
|
||||
let k_exit_id = JoinFuncId::new(2);
|
||||
|
||||
// ValueId allocation
|
||||
// Phase 286 P1: Use alloc_param() for function parameters (Param region: 100-999)
|
||||
// Use alloc_local() for local variables (Local region: 1000+)
|
||||
|
||||
// main() params/locals
|
||||
let counter_init = join_value_space.alloc_param(); // ValueId(100) - initial counter (PARAM)
|
||||
let loop_result = join_value_space.alloc_local(); // ValueId(1000) - result from loop_step (LOCAL)
|
||||
|
||||
// loop_step params/locals
|
||||
let counter_param = join_value_space.alloc_param(); // ValueId(101) - parameter (PARAM)
|
||||
let const_1 = join_value_space.alloc_local(); // ValueId(1001) - increment constant (LOCAL)
|
||||
let counter_next = join_value_space.alloc_local(); // ValueId(1002) - counter + 1 (LOCAL)
|
||||
let const_limit = join_value_space.alloc_local(); // ValueId(1003) - limit constant (LOCAL)
|
||||
let break_cond = join_value_space.alloc_local(); // ValueId(1004) - counter_next == LIMIT (LOCAL)
|
||||
|
||||
// k_exit params/locals
|
||||
let counter_exit = join_value_space.alloc_param(); // ValueId(102) - exit parameter (PARAM)
|
||||
|
||||
// return 0 constant
|
||||
let const_0 = join_value_space.alloc_local(); // Local constant (LOCAL region)
|
||||
|
||||
// ==================================================================
|
||||
// main() function
|
||||
// ==================================================================
|
||||
let mut main_func = JoinFunction::new(main_id, "main".to_string(), vec![counter_init]);
|
||||
|
||||
// result = loop_step(counter_init)
|
||||
main_func.body.push(JoinInst::Call {
|
||||
func: loop_step_id,
|
||||
args: vec![counter_init],
|
||||
k_next: None,
|
||||
dst: Some(loop_result),
|
||||
});
|
||||
|
||||
// return 0 (Pattern 5 doesn't produce a value, but we return 0 by convention)
|
||||
main_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: const_0,
|
||||
value: ConstValue::Integer(0),
|
||||
}));
|
||||
|
||||
main_func.body.push(JoinInst::Ret {
|
||||
value: Some(const_0),
|
||||
});
|
||||
|
||||
join_module.add_function(main_func);
|
||||
|
||||
// ==================================================================
|
||||
// loop_step(counter) function - post-increment pattern
|
||||
// ==================================================================
|
||||
let mut loop_step_func =
|
||||
JoinFunction::new(loop_step_id, "loop_step".to_string(), vec![counter_param]);
|
||||
|
||||
// counter_next = counter + 1
|
||||
loop_step_func
|
||||
.body
|
||||
.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: const_1,
|
||||
value: ConstValue::Integer(1),
|
||||
}));
|
||||
|
||||
loop_step_func
|
||||
.body
|
||||
.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||
dst: counter_next,
|
||||
op: BinOpKind::Add,
|
||||
lhs: counter_param,
|
||||
rhs: const_1,
|
||||
}));
|
||||
|
||||
// break_cond = (counter_next == LIMIT)
|
||||
loop_step_func
|
||||
.body
|
||||
.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: const_limit,
|
||||
value: ConstValue::Integer(limit),
|
||||
}));
|
||||
|
||||
loop_step_func
|
||||
.body
|
||||
.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||
dst: break_cond,
|
||||
op: CompareOp::Eq,
|
||||
lhs: counter_next,
|
||||
rhs: const_limit,
|
||||
}));
|
||||
|
||||
// Jump(k_exit, [counter_next], cond=break_cond)
|
||||
loop_step_func.body.push(JoinInst::Jump {
|
||||
cont: k_exit_id.as_cont(),
|
||||
args: vec![counter_next],
|
||||
cond: Some(break_cond),
|
||||
});
|
||||
|
||||
// Call(loop_step, [counter_next]) - tail recursion
|
||||
loop_step_func.body.push(JoinInst::Call {
|
||||
func: loop_step_id,
|
||||
args: vec![counter_next],
|
||||
k_next: None, // tail call
|
||||
dst: None,
|
||||
});
|
||||
|
||||
join_module.add_function(loop_step_func);
|
||||
|
||||
// ==================================================================
|
||||
// k_exit(counter_exit) function
|
||||
// ==================================================================
|
||||
let mut k_exit_func = JoinFunction::new(k_exit_id, "k_exit".to_string(), vec![counter_exit]);
|
||||
|
||||
// Return counter_exit (the final counter value) instead of const 0
|
||||
k_exit_func.body.push(JoinInst::Ret {
|
||||
value: Some(counter_exit),
|
||||
});
|
||||
|
||||
join_module.add_function(k_exit_func);
|
||||
|
||||
// Set entry point
|
||||
join_module.entry = Some(main_id);
|
||||
|
||||
if debug {
|
||||
trace::trace().debug(
|
||||
"pattern5/lower",
|
||||
"Generated JoinIR: main, loop_step, k_exit",
|
||||
);
|
||||
}
|
||||
|
||||
// Step 5: Create CarrierInfo for the counter (Phase 131-11-D)
|
||||
// Note: counter is the loop variable, NOT a separate carrier
|
||||
use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, ExitMeta};
|
||||
|
||||
let carrier_info = CarrierInfo {
|
||||
loop_var_name: counter_name.clone(),
|
||||
loop_var_id: counter_id,
|
||||
carriers: vec![], // No separate carriers - counter is the loop variable itself
|
||||
trim_helper: None,
|
||||
promoted_loopbodylocals: Vec::new(),
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
promoted_bindings: std::collections::BTreeMap::new(),
|
||||
};
|
||||
|
||||
// Step 6: Create ExitMeta with counter as exit value
|
||||
// Phase 131-11-D: The counter flows through jump_args and must be registered in exit_values
|
||||
let exit_meta = ExitMeta {
|
||||
exit_values: vec![(counter_name.clone(), counter_exit)],
|
||||
};
|
||||
|
||||
// Step 7: Build boundary and merge
|
||||
use crate::mir::builder::control_flow::joinir::merge::exit_line::ExitMetaCollector;
|
||||
use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder;
|
||||
|
||||
let exit_bindings = ExitMetaCollector::collect(builder, &exit_meta, Some(&carrier_info), debug);
|
||||
|
||||
let boundary = JoinInlineBoundaryBuilder::new()
|
||||
.with_inputs(
|
||||
vec![counter_init], // JoinIR main() parameter
|
||||
vec![counter_id], // Host counter value
|
||||
)
|
||||
.with_exit_bindings(exit_bindings)
|
||||
.with_loop_var_name(Some(counter_name.clone())) // Phase 131-11-D: For LoopHeaderPhiBuilder
|
||||
.with_carrier_info(carrier_info) // Phase 131-11-D: For exit line reconnection
|
||||
.build();
|
||||
|
||||
// Step 7: Execute conversion pipeline
|
||||
use super::conversion_pipeline::JoinIRConversionPipeline;
|
||||
let _ = JoinIRConversionPipeline::execute(
|
||||
builder,
|
||||
join_module,
|
||||
Some(&boundary),
|
||||
"pattern5",
|
||||
debug,
|
||||
)?;
|
||||
|
||||
// Step 8: Return Void (loops don't produce values)
|
||||
let void_val = crate::mir::builder::emission::constant::emit_void(builder);
|
||||
|
||||
if debug {
|
||||
trace::trace().debug(
|
||||
"pattern5/lower",
|
||||
&format!("Pattern 5 lowering complete, returning Void {:?}", void_val),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(Some(void_val))
|
||||
}
|
||||
@ -358,18 +358,14 @@ pub(crate) struct LoopPatternEntry {
|
||||
///
|
||||
/// **IMPORTANT**: Patterns are tried in array order (SSOT).
|
||||
/// Array order defines priority - earlier entries are tried first.
|
||||
/// Pattern5 (most specific) → Pattern4 → Pattern3 → Pattern1 → Pattern2
|
||||
/// Pattern4 → Pattern8 → Pattern9 → Pattern3 → Pattern1 → Pattern2
|
||||
///
|
||||
/// # Current Patterns (Structure-based detection, established Phase 131-11+)
|
||||
///
|
||||
/// Pattern detection strategies (updated Phase 282 P0):
|
||||
/// Pattern detection strategies (updated Phase 286):
|
||||
/// - Pattern6/7: ExtractionBased (Plan line, Phase 273+)
|
||||
/// - Pattern8/9: ExtractionBased (extraction functions, already implemented)
|
||||
/// - Pattern1-5: StructureBased (LoopFeatures classification, legacy)
|
||||
///
|
||||
/// - Pattern 5: Infinite Loop with Early Exit (llvm_stage3_loop_only.hako) [Phase 131-11]
|
||||
/// - Detection: pattern_kind == InfiniteEarlyExit
|
||||
/// - Structure: is_infinite_loop && has_break && has_continue
|
||||
/// - Pattern5/8/9: ExtractionBased (extraction functions, Plan line Phase 286+)
|
||||
/// - Pattern1-4: StructureBased (LoopFeatures classification, legacy)
|
||||
///
|
||||
/// - Pattern 4: Loop with Continue (loop_continue_pattern4.hako)
|
||||
/// - Detection: pattern_kind == Pattern4Continue
|
||||
@ -388,12 +384,8 @@ pub(crate) struct LoopPatternEntry {
|
||||
/// - Structure: has_break && !has_continue
|
||||
///
|
||||
/// Note: func_name is now only used for debug logging, not pattern detection
|
||||
/// Phase 286: Pattern5 removed (migrated to Plan-based routing)
|
||||
pub(crate) static LOOP_PATTERNS: &[LoopPatternEntry] = &[
|
||||
LoopPatternEntry {
|
||||
name: "Pattern5_InfiniteEarlyExit",
|
||||
detect: super::pattern5_infinite_early_exit::can_lower,
|
||||
lower: super::pattern5_infinite_early_exit::lower,
|
||||
},
|
||||
LoopPatternEntry {
|
||||
name: "Pattern4_WithContinue",
|
||||
detect: super::pattern4_with_continue::can_lower,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
20
src/mir/builder/control_flow/plan/normalizer/README.md
Normal file
20
src/mir/builder/control_flow/plan/normalizer/README.md
Normal file
@ -0,0 +1,20 @@
|
||||
# Plan Normalizer Modules
|
||||
|
||||
This directory splits the DomainPlan -> CorePlan normalization into small modules.
|
||||
|
||||
Responsibilities:
|
||||
- Keep pattern-specific knowledge localized per module.
|
||||
- Share small, stable helpers (AST lowering, block layouts, phi bindings).
|
||||
- Preserve PlanNormalizer as the single SSOT entry point.
|
||||
|
||||
Modules:
|
||||
- helpers.rs: shared block layouts, phi bindings, AST-to-ValueId lowering helpers.
|
||||
- pattern_scan_with_init.rs: Pattern6 scan-with-init normalization.
|
||||
- pattern_split_scan.rs: Pattern7 split-scan normalization.
|
||||
- pattern4_continue.rs: Pattern4 continue-loop normalization.
|
||||
- pattern1_simple_while.rs: Pattern1 simple while normalization.
|
||||
- pattern9_accum_const_loop.rs: Pattern9 accumulator-loop normalization.
|
||||
- pattern8_bool_predicate_scan.rs: Pattern8 predicate-scan normalization.
|
||||
- pattern3_if_phi.rs: Pattern3 if-phi normalization.
|
||||
- pattern2_break.rs: Pattern2 break-loop normalization.
|
||||
- pattern5_infinite_early_exit.rs: Pattern5 infinite loop with early exit normalization.
|
||||
263
src/mir/builder/control_flow/plan/normalizer/helpers.rs
Normal file
263
src/mir/builder/control_flow/plan/normalizer/helpers.rs
Normal file
@ -0,0 +1,263 @@
|
||||
use super::CoreEffectPlan;
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::{BasicBlockId, BinaryOp, CompareOp, ConstValue, Effect, EffectMask, MirType, ValueId};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
// ============================================================================
|
||||
// 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)]
|
||||
pub(super) struct LoopBlocksStandard5 {
|
||||
pub(super) preheader_bb: BasicBlockId,
|
||||
pub(super) header_bb: BasicBlockId,
|
||||
pub(super) body_bb: BasicBlockId,
|
||||
pub(super) step_bb: BasicBlockId,
|
||||
pub(super) after_bb: BasicBlockId,
|
||||
}
|
||||
|
||||
impl LoopBlocksStandard5 {
|
||||
/// Allocate 5 blocks for a standard loop
|
||||
pub(super) 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)]
|
||||
pub(super) struct LoopBlocksWithIfPhi {
|
||||
pub(super) preheader_bb: BasicBlockId,
|
||||
pub(super) header_bb: BasicBlockId,
|
||||
pub(super) body_bb: BasicBlockId,
|
||||
pub(super) then_bb: BasicBlockId,
|
||||
pub(super) else_bb: BasicBlockId,
|
||||
pub(super) merge_bb: BasicBlockId,
|
||||
pub(super) step_bb: BasicBlockId,
|
||||
pub(super) after_bb: BasicBlockId,
|
||||
}
|
||||
|
||||
impl LoopBlocksWithIfPhi {
|
||||
/// Allocate 8 blocks for an if-phi loop
|
||||
pub(super) 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.
|
||||
pub(super) fn create_phi_bindings(bindings: &[(&str, ValueId)]) -> BTreeMap<String, ValueId> {
|
||||
bindings
|
||||
.iter()
|
||||
.map(|(name, id)| (name.to_string(), *id))
|
||||
.collect()
|
||||
}
|
||||
|
||||
impl super::PlanNormalizer {
|
||||
/// Helper: Lower Compare AST to (lhs ValueId, op, rhs ValueId, const_effects)
|
||||
pub(super) fn lower_compare_ast(
|
||||
ast: &crate::ast::ASTNode,
|
||||
builder: &mut MirBuilder,
|
||||
phi_bindings: &BTreeMap<String, ValueId>,
|
||||
) -> Result<(ValueId, CompareOp, 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)
|
||||
pub(super) fn lower_binop_ast(
|
||||
ast: &crate::ast::ASTNode,
|
||||
builder: &mut MirBuilder,
|
||||
phi_bindings: &BTreeMap<String, ValueId>,
|
||||
) -> Result<(ValueId, BinaryOp, 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)
|
||||
pub(super) fn lower_value_ast(
|
||||
ast: &crate::ast::ASTNode,
|
||||
builder: &mut MirBuilder,
|
||||
phi_bindings: &BTreeMap<String, ValueId>,
|
||||
) -> Result<(ValueId, Vec<CoreEffectPlan>), String> {
|
||||
use crate::ast::{ASTNode, LiteralValue};
|
||||
|
||||
match ast {
|
||||
ASTNode::Variable { name, .. } => {
|
||||
if let Some(&phi_dst) = phi_bindings.get(name) {
|
||||
return Ok((phi_dst, vec![]));
|
||||
}
|
||||
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, .. } => {
|
||||
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);
|
||||
|
||||
let const_effect = CoreEffectPlan::Const {
|
||||
dst: value_id,
|
||||
value: const_value,
|
||||
};
|
||||
|
||||
Ok((value_id, vec![const_effect]))
|
||||
}
|
||||
ASTNode::MethodCall { object, method, arguments, .. } => {
|
||||
let object_name = match object.as_ref() {
|
||||
ASTNode::Variable { name, .. } => name.clone(),
|
||||
_ => {
|
||||
return Err(format!(
|
||||
"[normalizer] Method call on non-variable object: {:?}",
|
||||
object
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
let object_id = if let Some(&phi_dst) = phi_bindings.get(&object_name) {
|
||||
phi_dst
|
||||
} else if let Some(&value_id) = builder.variable_ctx.variable_map.get(&object_name) {
|
||||
value_id
|
||||
} else {
|
||||
return Err(format!(
|
||||
"[normalizer] Method call object {} not found",
|
||||
object_name
|
||||
));
|
||||
};
|
||||
|
||||
let mut arg_ids = Vec::new();
|
||||
let mut arg_effects = Vec::new();
|
||||
for arg in arguments {
|
||||
let (arg_id, mut effects) = Self::lower_value_ast(arg, builder, phi_bindings)?;
|
||||
arg_ids.push(arg_id);
|
||||
arg_effects.append(&mut effects);
|
||||
}
|
||||
|
||||
let result_id = builder.next_value_id();
|
||||
builder.type_ctx.value_types.insert(result_id, MirType::Integer);
|
||||
|
||||
arg_effects.push(CoreEffectPlan::MethodCall {
|
||||
dst: Some(result_id),
|
||||
object: object_id,
|
||||
method: method.clone(),
|
||||
args: arg_ids,
|
||||
effects: EffectMask::PURE.add(Effect::Io),
|
||||
});
|
||||
|
||||
Ok((result_id, arg_effects))
|
||||
}
|
||||
_ => Err(format!("[normalizer] Unsupported value AST: {:?}", ast)),
|
||||
}
|
||||
}
|
||||
}
|
||||
69
src/mir/builder/control_flow/plan/normalizer/mod.rs
Normal file
69
src/mir/builder/control_flow/plan/normalizer/mod.rs
Normal file
@ -0,0 +1,69 @@
|
||||
//! Phase 273 P1: PlanNormalizer - DomainPlan → CorePlan 変換 (SSOT)
|
||||
//!
|
||||
//! # Responsibilities
|
||||
//!
|
||||
//! - Convert DomainPlan to CorePlan (SSOT for pattern-specific knowledge)
|
||||
//! - Generate ValueIds for CorePlan expressions
|
||||
//! - Expand pattern-specific operations into generic CoreEffectPlan
|
||||
//!
|
||||
//! # Key Design Decision
|
||||
//!
|
||||
//! Normalizer is the ONLY place that knows pattern-specific semantics.
|
||||
//! Lowerer processes CorePlan without any pattern knowledge.
|
||||
|
||||
mod helpers;
|
||||
mod pattern1_simple_while;
|
||||
mod pattern2_break;
|
||||
mod pattern3_if_phi;
|
||||
mod pattern4_continue;
|
||||
mod pattern5_infinite_early_exit;
|
||||
mod pattern8_bool_predicate_scan;
|
||||
mod pattern9_accum_const_loop;
|
||||
mod pattern_scan_with_init;
|
||||
mod pattern_split_scan;
|
||||
|
||||
use super::{
|
||||
CoreEffectPlan, CoreLoopPlan, CorePhiInfo, CorePlan, DomainPlan, Pattern1SimpleWhilePlan,
|
||||
Pattern2BreakPlan, Pattern3IfPhiPlan, Pattern4ContinuePlan, Pattern5ExitKind,
|
||||
Pattern5InfiniteEarlyExitPlan, Pattern8BoolPredicateScanPlan, Pattern9AccumConstLoopPlan,
|
||||
ScanWithInitPlan, SplitScanPlan,
|
||||
};
|
||||
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
|
||||
use crate::mir::builder::MirBuilder;
|
||||
|
||||
/// Phase 273 P1: PlanNormalizer - DomainPlan → CorePlan 変換 (SSOT)
|
||||
pub(in crate::mir::builder) struct PlanNormalizer;
|
||||
|
||||
impl PlanNormalizer {
|
||||
/// Normalize DomainPlan to CorePlan
|
||||
///
|
||||
/// This is the SSOT for pattern-specific knowledge expansion.
|
||||
/// All pattern semantics (scan, split, etc.) are expanded here.
|
||||
pub(in crate::mir::builder) fn normalize(
|
||||
builder: &mut MirBuilder,
|
||||
domain: DomainPlan,
|
||||
ctx: &LoopPatternContext,
|
||||
) -> Result<CorePlan, String> {
|
||||
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)
|
||||
}
|
||||
DomainPlan::Pattern9AccumConstLoop(parts) => {
|
||||
Self::normalize_pattern9_accum_const_loop(builder, parts, ctx)
|
||||
}
|
||||
DomainPlan::Pattern8BoolPredicateScan(parts) => {
|
||||
Self::normalize_pattern8_bool_predicate_scan(builder, parts, ctx)
|
||||
}
|
||||
DomainPlan::Pattern3IfPhi(parts) => Self::normalize_pattern3_if_phi(builder, parts, ctx),
|
||||
DomainPlan::Pattern2Break(parts) => Self::normalize_pattern2_break(builder, parts, ctx),
|
||||
DomainPlan::Pattern5InfiniteEarlyExit(parts) => {
|
||||
Self::normalize_pattern5_infinite_early_exit(builder, parts, ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,181 @@
|
||||
use super::helpers::{create_phi_bindings, LoopBlocksStandard5};
|
||||
use super::{CoreEffectPlan, CoreLoopPlan, CorePhiInfo, CorePlan, Pattern1SimpleWhilePlan};
|
||||
use crate::mir::basic_block::EdgeArgs;
|
||||
use crate::mir::builder::control_flow::edgecfg::api::{BranchStub, EdgeStub, ExitKind, Frag};
|
||||
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout;
|
||||
use crate::mir::MirType;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
impl super::PlanNormalizer {
|
||||
/// 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)
|
||||
pub(super) 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-3: Allocate BasicBlockIds
|
||||
let blocks = LoopBlocksStandard5::allocate(builder)?;
|
||||
let LoopBlocksStandard5 {
|
||||
preheader_bb,
|
||||
header_bb,
|
||||
body_bb,
|
||||
step_bb,
|
||||
after_bb,
|
||||
} = blocks;
|
||||
|
||||
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);
|
||||
let cond_loop = builder.alloc_typed(MirType::Bool);
|
||||
let loop_var_next = builder.alloc_typed(MirType::Integer);
|
||||
|
||||
// Step 5: Build phi_bindings
|
||||
let phi_bindings = create_phi_bindings(&[(&parts.loop_var, loop_var_current)]);
|
||||
|
||||
// Step 6: Lower AST expressions
|
||||
let (loop_cond_lhs, loop_cond_op, loop_cond_rhs, loop_cond_consts) =
|
||||
Self::lower_compare_ast(&parts.condition, builder, &phi_bindings)?;
|
||||
|
||||
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
|
||||
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
|
||||
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![]),
|
||||
(step_bb, step_effects),
|
||||
];
|
||||
|
||||
// Step 10: Build PHI
|
||||
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![
|
||||
EdgeStub {
|
||||
from: body_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: Some(step_bb),
|
||||
args: empty_args.clone(),
|
||||
},
|
||||
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
|
||||
let loop_plan = CoreLoopPlan {
|
||||
preheader_bb,
|
||||
header_bb,
|
||||
body_bb,
|
||||
step_bb,
|
||||
after_bb,
|
||||
found_bb: after_bb,
|
||||
body: vec![],
|
||||
cond_loop,
|
||||
cond_match: cond_loop,
|
||||
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))
|
||||
}
|
||||
}
|
||||
299
src/mir/builder/control_flow/plan/normalizer/pattern2_break.rs
Normal file
299
src/mir/builder/control_flow/plan/normalizer/pattern2_break.rs
Normal file
@ -0,0 +1,299 @@
|
||||
use super::helpers::create_phi_bindings;
|
||||
use super::{CoreEffectPlan, CoreLoopPlan, CorePhiInfo, CorePlan, Pattern2BreakPlan};
|
||||
use crate::mir::basic_block::EdgeArgs;
|
||||
use crate::mir::builder::control_flow::edgecfg::api::{BranchStub, EdgeStub, ExitKind, Frag};
|
||||
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout;
|
||||
use crate::mir::{BinaryOp, ConstValue, MirType};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
impl super::PlanNormalizer {
|
||||
/// Pattern2Break → CorePlan 変換
|
||||
///
|
||||
/// CFG structure (6 blocks):
|
||||
/// ```
|
||||
/// preheader → header(PHI: i_current, carrier_current)
|
||||
/// ↓
|
||||
/// body(break_cond check)
|
||||
/// ↓
|
||||
/// ┌────┴────┐
|
||||
/// break_then step
|
||||
/// (optional ↓
|
||||
/// update) header (back-edge)
|
||||
/// ↓
|
||||
/// after_bb(PHI: carrier_out)
|
||||
/// ↑
|
||||
/// header (natural exit when !cond_loop)
|
||||
/// ```
|
||||
///
|
||||
/// Key: after_bb PHI merges break path and natural exit path carrier values.
|
||||
pub(super) fn normalize_pattern2_break(
|
||||
builder: &mut MirBuilder,
|
||||
parts: Pattern2BreakPlan,
|
||||
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/pattern2_break",
|
||||
&format!(
|
||||
"Phase 286 P3.1: Normalizing Pattern2Break for {} (loop_var={}, carrier_var={})",
|
||||
ctx.func_name, parts.loop_var, parts.carrier_var
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Step 1: Block allocation (6 blocks)
|
||||
let preheader_bb = builder
|
||||
.current_block
|
||||
.ok_or_else(|| "Pattern2Break: no current block".to_string())?;
|
||||
let header_bb = builder.next_block_id();
|
||||
let body_bb = builder.next_block_id();
|
||||
let break_then_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/pattern2_break",
|
||||
&format!(
|
||||
"Block allocation: preheader={:?}, header={:?}, body={:?}, break_then={:?}, step={:?}, after={:?}",
|
||||
preheader_bb, header_bb, body_bb, break_then_bb, step_bb, after_bb
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Step 2: Get initial values from variable_map
|
||||
let loop_var_init = builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.get(&parts.loop_var)
|
||||
.copied()
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"Pattern2Break: loop_var '{}' not in variable_map",
|
||||
parts.loop_var
|
||||
)
|
||||
})?;
|
||||
|
||||
let carrier_init = builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.get(&parts.carrier_var)
|
||||
.copied()
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"Pattern2Break: carrier_var '{}' not in variable_map",
|
||||
parts.carrier_var
|
||||
)
|
||||
})?;
|
||||
|
||||
// Step 3: ValueId allocation
|
||||
let loop_var_current = builder.alloc_typed(MirType::Integer);
|
||||
let carrier_current = builder.alloc_typed(MirType::Integer);
|
||||
let cond_loop = builder.alloc_typed(MirType::Bool);
|
||||
let cond_break = builder.alloc_typed(MirType::Bool);
|
||||
let carrier_break = builder.alloc_typed(MirType::Integer);
|
||||
let carrier_step = builder.alloc_typed(MirType::Integer);
|
||||
let loop_var_next = builder.alloc_typed(MirType::Integer);
|
||||
let carrier_out = builder.alloc_typed(MirType::Integer);
|
||||
|
||||
// Step 4: phi_bindings for AST lowering
|
||||
let phi_bindings = create_phi_bindings(&[
|
||||
(&parts.loop_var, loop_var_current),
|
||||
(&parts.carrier_var, carrier_current),
|
||||
]);
|
||||
|
||||
// Step 5: Lower AST expressions
|
||||
let (loop_cond_lhs, loop_cond_op, loop_cond_rhs, loop_cond_consts) =
|
||||
Self::lower_compare_ast(&parts.loop_condition, builder, &phi_bindings)?;
|
||||
|
||||
let (break_cond_lhs, break_cond_op, break_cond_rhs, break_cond_consts) =
|
||||
Self::lower_compare_ast(&parts.break_condition, builder, &phi_bindings)?;
|
||||
|
||||
let break_then_effects = if let Some(ref break_update_ast) = parts.carrier_update_in_break {
|
||||
let (lhs, op, rhs, consts) =
|
||||
Self::lower_binop_ast(break_update_ast, builder, &phi_bindings)?;
|
||||
let mut effects = consts;
|
||||
effects.push(CoreEffectPlan::BinOp {
|
||||
dst: carrier_break,
|
||||
lhs,
|
||||
op,
|
||||
rhs,
|
||||
});
|
||||
effects
|
||||
} else {
|
||||
let zero = builder.alloc_typed(MirType::Integer);
|
||||
vec![
|
||||
CoreEffectPlan::Const {
|
||||
dst: zero,
|
||||
value: ConstValue::Integer(0),
|
||||
},
|
||||
CoreEffectPlan::BinOp {
|
||||
dst: carrier_break,
|
||||
lhs: carrier_current,
|
||||
op: BinaryOp::Add,
|
||||
rhs: zero,
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
let (carrier_lhs, carrier_op, carrier_rhs, carrier_consts) =
|
||||
Self::lower_binop_ast(&parts.carrier_update_in_body, builder, &phi_bindings)?;
|
||||
|
||||
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
|
||||
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 plans (break condition check)
|
||||
let mut body_plans: Vec<CorePlan> = Vec::new();
|
||||
for const_effect in break_cond_consts {
|
||||
body_plans.push(CorePlan::Effect(const_effect));
|
||||
}
|
||||
body_plans.push(CorePlan::Effect(CoreEffectPlan::Compare {
|
||||
dst: cond_break,
|
||||
lhs: break_cond_lhs,
|
||||
op: break_cond_op,
|
||||
rhs: break_cond_rhs,
|
||||
}));
|
||||
|
||||
// Step 8: Build step_effects
|
||||
let mut step_effects = carrier_consts;
|
||||
step_effects.push(CoreEffectPlan::BinOp {
|
||||
dst: carrier_step,
|
||||
lhs: carrier_lhs,
|
||||
op: carrier_op,
|
||||
rhs: carrier_rhs,
|
||||
});
|
||||
step_effects.extend(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![]),
|
||||
(break_then_bb, break_then_effects),
|
||||
(step_bb, step_effects),
|
||||
];
|
||||
|
||||
// Step 10: Build PHIs
|
||||
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),
|
||||
},
|
||||
CorePhiInfo {
|
||||
block: header_bb,
|
||||
dst: carrier_current,
|
||||
inputs: vec![(preheader_bb, carrier_init), (step_bb, carrier_step)],
|
||||
tag: format!("carrier_{}", parts.carrier_var),
|
||||
},
|
||||
CorePhiInfo {
|
||||
block: after_bb,
|
||||
dst: carrier_out,
|
||||
inputs: vec![(header_bb, carrier_current), (break_then_bb, carrier_break)],
|
||||
tag: format!("after_{}", parts.carrier_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,
|
||||
else_target: after_bb,
|
||||
then_args: empty_args.clone(),
|
||||
else_args: empty_args.clone(),
|
||||
},
|
||||
BranchStub {
|
||||
from: body_bb,
|
||||
cond: cond_break,
|
||||
then_target: break_then_bb,
|
||||
else_target: step_bb,
|
||||
then_args: empty_args.clone(),
|
||||
else_args: empty_args.clone(),
|
||||
},
|
||||
];
|
||||
|
||||
let wires = vec![
|
||||
EdgeStub {
|
||||
from: break_then_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: Some(after_bb),
|
||||
args: empty_args.clone(),
|
||||
},
|
||||
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),
|
||||
(parts.carrier_var.clone(), carrier_out),
|
||||
];
|
||||
|
||||
// Step 13: Build CoreLoopPlan
|
||||
let loop_plan = CoreLoopPlan {
|
||||
preheader_bb,
|
||||
header_bb,
|
||||
body_bb,
|
||||
step_bb,
|
||||
after_bb,
|
||||
found_bb: after_bb,
|
||||
body: body_plans,
|
||||
cond_loop,
|
||||
cond_match: cond_break,
|
||||
block_effects,
|
||||
phis,
|
||||
frag,
|
||||
final_values,
|
||||
};
|
||||
|
||||
if debug {
|
||||
trace_logger.debug(
|
||||
"normalizer/pattern2_break",
|
||||
"CorePlan construction complete (6 blocks, 3 PHIs, after_bb PHI is key!)",
|
||||
);
|
||||
}
|
||||
|
||||
Ok(CorePlan::Loop(loop_plan))
|
||||
}
|
||||
}
|
||||
287
src/mir/builder/control_flow/plan/normalizer/pattern3_if_phi.rs
Normal file
287
src/mir/builder/control_flow/plan/normalizer/pattern3_if_phi.rs
Normal file
@ -0,0 +1,287 @@
|
||||
use super::helpers::{create_phi_bindings, LoopBlocksWithIfPhi};
|
||||
use super::{CoreEffectPlan, CoreLoopPlan, CorePhiInfo, CorePlan, Pattern3IfPhiPlan};
|
||||
use crate::mir::basic_block::EdgeArgs;
|
||||
use crate::mir::builder::control_flow::edgecfg::api::{BranchStub, EdgeStub, ExitKind, Frag};
|
||||
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout;
|
||||
use crate::mir::MirType;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
impl super::PlanNormalizer {
|
||||
/// Phase 286 P2.6.1: Pattern3IfPhi → CorePlan 変換
|
||||
///
|
||||
/// Expands Pattern3 (Loop with If-Phi) semantics into generic CorePlan:
|
||||
/// - CFG structure: preheader → header → body → then/else → merge → step → header
|
||||
/// - 3 PHIs: 2 in header (loop_var, carrier), 1 in merge (carrier_next)
|
||||
/// - If-else branching with PHI merge (no Select instruction)
|
||||
pub(super) fn normalize_pattern3_if_phi(
|
||||
builder: &mut MirBuilder,
|
||||
parts: Pattern3IfPhiPlan,
|
||||
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/pattern3_if_phi",
|
||||
&format!(
|
||||
"Phase 286 P2.6.1: Normalizing Pattern3IfPhi for {} (loop_var: {}, carrier_var: {})",
|
||||
ctx.func_name, parts.loop_var, parts.carrier_var
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 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(&parts.carrier_var)
|
||||
.copied()
|
||||
.ok_or_else(|| format!("[normalizer] Carrier variable {} not found", parts.carrier_var))?;
|
||||
|
||||
// Step 2-3: Allocate BasicBlockIds
|
||||
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(
|
||||
"normalizer/pattern3_if_phi",
|
||||
&format!(
|
||||
"Allocated: preheader={:?}, header={:?}, body={:?}, then={:?}, else={:?}, merge={:?}, step={:?}, after={:?}",
|
||||
preheader_bb,
|
||||
header_bb,
|
||||
body_bb,
|
||||
then_bb,
|
||||
else_bb,
|
||||
merge_bb,
|
||||
step_bb,
|
||||
after_bb
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Step 4: Allocate ValueIds for loop control
|
||||
let loop_var_current = builder.alloc_typed(MirType::Integer);
|
||||
let carrier_current = builder.alloc_typed(MirType::Integer);
|
||||
let cond_loop = builder.alloc_typed(MirType::Bool);
|
||||
let cond_if = builder.alloc_typed(MirType::Bool);
|
||||
let carrier_then = builder.alloc_typed(MirType::Integer);
|
||||
let carrier_else = builder.alloc_typed(MirType::Integer);
|
||||
let carrier_next = builder.alloc_typed(MirType::Integer);
|
||||
let loop_var_next = builder.alloc_typed(MirType::Integer);
|
||||
|
||||
// Step 5: Build phi_bindings
|
||||
let phi_bindings = create_phi_bindings(&[
|
||||
(&parts.loop_var, loop_var_current),
|
||||
(&parts.carrier_var, carrier_current),
|
||||
]);
|
||||
|
||||
// Step 6: Lower AST expressions
|
||||
let (loop_cond_lhs, loop_cond_op, loop_cond_rhs, loop_cond_consts) =
|
||||
Self::lower_compare_ast(&parts.condition, builder, &phi_bindings)?;
|
||||
|
||||
let (if_cond_lhs, if_cond_op, if_cond_rhs, if_cond_consts) =
|
||||
Self::lower_compare_ast(&parts.if_condition, builder, &phi_bindings)?;
|
||||
|
||||
let (then_lhs, then_op, then_rhs, then_consts) =
|
||||
Self::lower_binop_ast(&parts.then_update, builder, &phi_bindings)?;
|
||||
|
||||
let (else_lhs, else_op, else_rhs, else_consts) =
|
||||
Self::lower_binop_ast(&parts.else_update, builder, &phi_bindings)?;
|
||||
|
||||
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
|
||||
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 body plans
|
||||
let mut body_plans: Vec<CorePlan> = Vec::new();
|
||||
for const_effect in if_cond_consts {
|
||||
body_plans.push(CorePlan::Effect(const_effect));
|
||||
}
|
||||
body_plans.push(CorePlan::Effect(CoreEffectPlan::Compare {
|
||||
dst: cond_if,
|
||||
lhs: if_cond_lhs,
|
||||
op: if_cond_op,
|
||||
rhs: if_cond_rhs,
|
||||
}));
|
||||
|
||||
// Step 9: Build then_effects
|
||||
let mut then_effects = then_consts;
|
||||
then_effects.push(CoreEffectPlan::BinOp {
|
||||
dst: carrier_then,
|
||||
lhs: then_lhs,
|
||||
op: then_op,
|
||||
rhs: then_rhs,
|
||||
});
|
||||
|
||||
// Step 10: Build else_effects
|
||||
let mut else_effects = else_consts;
|
||||
else_effects.push(CoreEffectPlan::BinOp {
|
||||
dst: carrier_else,
|
||||
lhs: else_lhs,
|
||||
op: else_op,
|
||||
rhs: else_rhs,
|
||||
});
|
||||
|
||||
// Step 11: Build step_effects
|
||||
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 12: Build block_effects
|
||||
let block_effects = vec![
|
||||
(preheader_bb, vec![]),
|
||||
(header_bb, header_effects),
|
||||
(body_bb, vec![]),
|
||||
(then_bb, then_effects),
|
||||
(else_bb, else_effects),
|
||||
(merge_bb, vec![]),
|
||||
(step_bb, step_effects),
|
||||
];
|
||||
|
||||
// Step 13: Build PHIs
|
||||
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),
|
||||
},
|
||||
CorePhiInfo {
|
||||
block: header_bb,
|
||||
dst: carrier_current,
|
||||
inputs: vec![(preheader_bb, carrier_init), (step_bb, carrier_next)],
|
||||
tag: format!("carrier_{}", parts.carrier_var),
|
||||
},
|
||||
CorePhiInfo {
|
||||
block: merge_bb,
|
||||
dst: carrier_next,
|
||||
inputs: vec![(then_bb, carrier_then), (else_bb, carrier_else)],
|
||||
tag: format!("merge_{}", parts.carrier_var),
|
||||
},
|
||||
];
|
||||
|
||||
// Step 14: 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(),
|
||||
},
|
||||
BranchStub {
|
||||
from: body_bb,
|
||||
cond: cond_if,
|
||||
then_target: then_bb,
|
||||
then_args: empty_args.clone(),
|
||||
else_target: else_bb,
|
||||
else_args: empty_args.clone(),
|
||||
},
|
||||
];
|
||||
|
||||
let wires = vec![
|
||||
EdgeStub {
|
||||
from: then_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: Some(merge_bb),
|
||||
args: empty_args.clone(),
|
||||
},
|
||||
EdgeStub {
|
||||
from: else_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: Some(merge_bb),
|
||||
args: empty_args.clone(),
|
||||
},
|
||||
EdgeStub {
|
||||
from: merge_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: Some(step_bb),
|
||||
args: empty_args.clone(),
|
||||
},
|
||||
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 15: Build final_values
|
||||
let final_values = vec![
|
||||
(parts.loop_var.clone(), loop_var_current),
|
||||
(parts.carrier_var.clone(), carrier_current),
|
||||
];
|
||||
|
||||
// Step 16: Build CoreLoopPlan
|
||||
let loop_plan = CoreLoopPlan {
|
||||
preheader_bb,
|
||||
header_bb,
|
||||
body_bb,
|
||||
step_bb,
|
||||
after_bb,
|
||||
found_bb: after_bb,
|
||||
body: body_plans,
|
||||
cond_loop,
|
||||
cond_match: cond_if,
|
||||
block_effects,
|
||||
phis,
|
||||
frag,
|
||||
final_values,
|
||||
};
|
||||
|
||||
if debug {
|
||||
trace_logger.debug(
|
||||
"normalizer/pattern3_if_phi",
|
||||
"CorePlan construction complete (7 blocks, 3 PHIs)",
|
||||
);
|
||||
}
|
||||
|
||||
Ok(CorePlan::Loop(loop_plan))
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,299 @@
|
||||
use super::helpers::create_phi_bindings;
|
||||
use super::{CoreEffectPlan, CoreLoopPlan, CorePhiInfo, CorePlan, Pattern4ContinuePlan};
|
||||
use crate::mir::basic_block::EdgeArgs;
|
||||
use crate::mir::builder::control_flow::edgecfg::api::{BranchStub, EdgeStub, ExitKind, Frag};
|
||||
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout;
|
||||
use crate::mir::MirType;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
impl super::PlanNormalizer {
|
||||
/// 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)
|
||||
pub(super) fn normalize_pattern4_continue(
|
||||
builder: &mut MirBuilder,
|
||||
parts: 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
|
||||
let loop_var_current = builder.alloc_typed(MirType::Integer);
|
||||
let carrier_current = builder.alloc_typed(MirType::Integer);
|
||||
|
||||
// Step 4.5: Build phi_bindings
|
||||
let phi_bindings = create_phi_bindings(&[
|
||||
(&parts.loop_var, loop_var_current),
|
||||
(carrier_var, carrier_current),
|
||||
]);
|
||||
|
||||
let cond_loop = builder.alloc_typed(MirType::Bool);
|
||||
let cond_continue = builder.alloc_typed(MirType::Bool);
|
||||
let loop_var_cont_next = builder.alloc_typed(MirType::Integer);
|
||||
let carrier_updated = builder.alloc_typed(MirType::Integer);
|
||||
let loop_var_norm_next = builder.alloc_typed(MirType::Integer);
|
||||
|
||||
// Step 5: Lower AST expressions to get operands and const definitions
|
||||
let (loop_cond_lhs, loop_cond_op, loop_cond_rhs, loop_cond_consts) =
|
||||
Self::lower_compare_ast(&parts.condition, builder, &phi_bindings)?;
|
||||
|
||||
let (cont_cond_lhs, cont_cond_op, cont_cond_rhs, cont_cond_consts) =
|
||||
Self::lower_compare_ast(&parts.continue_condition, builder, &phi_bindings)?;
|
||||
|
||||
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)?;
|
||||
|
||||
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();
|
||||
for const_effect in cont_cond_consts {
|
||||
body.push(CorePlan::Effect(const_effect));
|
||||
}
|
||||
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
|
||||
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 9: Build normal_path effects
|
||||
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![]),
|
||||
(continue_path_bb, continue_path_effects),
|
||||
(normal_path_bb, normal_path_effects),
|
||||
(step_continue_bb, vec![]),
|
||||
(step_normal_bb, vec![]),
|
||||
];
|
||||
|
||||
// Step 11: Build PHIs
|
||||
let phis = vec![
|
||||
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),
|
||||
},
|
||||
CorePhiInfo {
|
||||
block: header_bb,
|
||||
dst: carrier_current,
|
||||
inputs: vec![
|
||||
(preheader_bb, carrier_init),
|
||||
(step_continue_bb, carrier_current),
|
||||
(step_normal_bb, carrier_updated),
|
||||
],
|
||||
tag: format!("carrier_{}", carrier_var),
|
||||
},
|
||||
];
|
||||
|
||||
// Step 12: 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(),
|
||||
},
|
||||
BranchStub {
|
||||
from: body_bb,
|
||||
cond: cond_continue,
|
||||
then_target: continue_path_bb,
|
||||
then_args: empty_args.clone(),
|
||||
else_target: normal_path_bb,
|
||||
else_args: empty_args.clone(),
|
||||
},
|
||||
];
|
||||
|
||||
let wires = vec![
|
||||
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(),
|
||||
},
|
||||
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,
|
||||
after_bb,
|
||||
found_bb: after_bb,
|
||||
body,
|
||||
cond_loop,
|
||||
cond_match: cond_continue,
|
||||
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))
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,542 @@
|
||||
use super::helpers::create_phi_bindings;
|
||||
use super::{
|
||||
CoreEffectPlan, CoreLoopPlan, CorePhiInfo, CorePlan, Pattern5ExitKind,
|
||||
Pattern5InfiniteEarlyExitPlan,
|
||||
};
|
||||
use crate::mir::basic_block::EdgeArgs;
|
||||
use crate::mir::builder::control_flow::edgecfg::api::{BranchStub, EdgeStub, ExitKind, Frag};
|
||||
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout;
|
||||
use crate::mir::{ConstValue, MirType, ValueId};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
impl super::PlanNormalizer {
|
||||
/// Pattern5InfiniteEarlyExit → CorePlan 変換 (Phase 286 P3.2)
|
||||
///
|
||||
/// Expands infinite loop with early exit into CorePlan.
|
||||
///
|
||||
/// # Return version CFG (5 blocks)
|
||||
/// ```text
|
||||
/// preheader → header(PHI: i_current) → body(exit_cond)
|
||||
/// ↑ ↓
|
||||
/// └───── step ←──────── else path
|
||||
/// ↓
|
||||
/// then path: Return (found_bb)
|
||||
/// ```
|
||||
///
|
||||
/// # Break version CFG (6 blocks)
|
||||
/// ```text
|
||||
/// preheader → header(PHI: i, carrier) → body(exit_cond)
|
||||
/// ↑ ↓
|
||||
/// └───── step ←────────── else path
|
||||
/// ↓
|
||||
/// then path → after_bb(PHI: carrier_out)
|
||||
/// ```
|
||||
pub(super) fn normalize_pattern5_infinite_early_exit(
|
||||
builder: &mut MirBuilder,
|
||||
parts: Pattern5InfiniteEarlyExitPlan,
|
||||
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/pattern5_infinite_early_exit",
|
||||
&format!(
|
||||
"Phase 286 P3.2: Normalizing Pattern5 for {} (loop_var={}, exit_kind={:?})",
|
||||
ctx.func_name, parts.loop_var, parts.exit_kind
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
match parts.exit_kind {
|
||||
Pattern5ExitKind::Return => {
|
||||
Self::normalize_pattern5_return(builder, parts, ctx, debug, &trace_logger)
|
||||
}
|
||||
Pattern5ExitKind::Break => {
|
||||
Self::normalize_pattern5_break(builder, parts, ctx, debug, &trace_logger)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern5 Return version normalizer
|
||||
///
|
||||
/// CFG: preheader → header → body → found_bb (return) / step → header
|
||||
fn normalize_pattern5_return(
|
||||
builder: &mut MirBuilder,
|
||||
parts: Pattern5InfiniteEarlyExitPlan,
|
||||
_ctx: &LoopPatternContext,
|
||||
debug: bool,
|
||||
trace_logger: &crate::mir::builder::control_flow::joinir::trace::JoinLoopTrace,
|
||||
) -> Result<CorePlan, String> {
|
||||
// Step 1: Block allocation (5 blocks)
|
||||
let preheader_bb = builder
|
||||
.current_block
|
||||
.ok_or_else(|| "Pattern5Return: no current block".to_string())?;
|
||||
let header_bb = builder.next_block_id();
|
||||
let body_bb = builder.next_block_id();
|
||||
let found_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/pattern5_return",
|
||||
&format!(
|
||||
"Block allocation: preheader={:?}, header={:?}, body={:?}, found={:?}, step={:?}, after={:?}",
|
||||
preheader_bb, header_bb, body_bb, found_bb, step_bb, after_bb
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Step 2: Get initial values from variable_map
|
||||
let loop_var_init = builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.get(&parts.loop_var)
|
||||
.copied()
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"Pattern5Return: loop_var '{}' not in variable_map",
|
||||
parts.loop_var
|
||||
)
|
||||
})?;
|
||||
|
||||
// Step 3: ValueId allocation
|
||||
let loop_var_current = builder.alloc_typed(MirType::Integer);
|
||||
let cond_exit = builder.alloc_typed(MirType::Bool);
|
||||
let loop_var_next = builder.alloc_typed(MirType::Integer);
|
||||
let true_val = builder.alloc_typed(MirType::Bool);
|
||||
|
||||
// Step 4: phi_bindings for AST lowering
|
||||
let phi_bindings = create_phi_bindings(&[(&parts.loop_var, loop_var_current)]);
|
||||
|
||||
// Step 5: Lower AST expressions
|
||||
let (exit_cond_lhs, exit_cond_op, exit_cond_rhs, exit_cond_consts) =
|
||||
Self::lower_compare_ast(&parts.exit_condition, builder, &phi_bindings)?;
|
||||
|
||||
let return_value: Option<(ValueId, crate::ast::ASTNode)> =
|
||||
if let Some(ref exit_val_ast) = parts.exit_value {
|
||||
let return_val_id = builder.alloc_typed(MirType::Integer);
|
||||
let return_val_ast = exit_val_ast.clone();
|
||||
Some((return_val_id, return_val_ast))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let (inc_lhs, inc_op, inc_rhs, inc_consts) =
|
||||
Self::lower_binop_ast(&parts.loop_increment, builder, &phi_bindings)?;
|
||||
|
||||
// Step 6: Build header_effects
|
||||
let header_effects = vec![CoreEffectPlan::Const {
|
||||
dst: true_val,
|
||||
value: ConstValue::Bool(true),
|
||||
}];
|
||||
|
||||
// Step 7: Build body plans
|
||||
let mut body_plans: Vec<CorePlan> = Vec::new();
|
||||
for eff in exit_cond_consts {
|
||||
body_plans.push(CorePlan::Effect(eff));
|
||||
}
|
||||
body_plans.push(CorePlan::Effect(CoreEffectPlan::Compare {
|
||||
dst: cond_exit,
|
||||
lhs: exit_cond_lhs,
|
||||
op: exit_cond_op,
|
||||
rhs: exit_cond_rhs,
|
||||
}));
|
||||
|
||||
// Step 8: Build found_bb effects (return value computation + Return)
|
||||
let mut found_effects: Vec<CoreEffectPlan> = Vec::new();
|
||||
let return_val_id = if let Some((ret_val_id, ref ret_val_ast)) = return_value {
|
||||
if let crate::ast::ASTNode::Literal {
|
||||
value: crate::ast::LiteralValue::Integer(n),
|
||||
..
|
||||
} = ret_val_ast
|
||||
{
|
||||
found_effects.push(CoreEffectPlan::Const {
|
||||
dst: ret_val_id,
|
||||
value: ConstValue::Integer(*n),
|
||||
});
|
||||
} else {
|
||||
let (lhs, op, rhs, consts) =
|
||||
Self::lower_binop_ast(ret_val_ast, builder, &phi_bindings)?;
|
||||
for c in consts {
|
||||
found_effects.push(c);
|
||||
}
|
||||
found_effects.push(CoreEffectPlan::BinOp {
|
||||
dst: ret_val_id,
|
||||
lhs,
|
||||
op,
|
||||
rhs,
|
||||
});
|
||||
}
|
||||
Some(ret_val_id)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Step 9: Build step_effects (increment loop var)
|
||||
let mut step_effects = inc_consts;
|
||||
step_effects.push(CoreEffectPlan::BinOp {
|
||||
dst: loop_var_next,
|
||||
lhs: inc_lhs,
|
||||
op: inc_op,
|
||||
rhs: inc_rhs,
|
||||
});
|
||||
|
||||
// Step 10: Build PHIs
|
||||
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_carrier_{}", parts.loop_var),
|
||||
}];
|
||||
|
||||
// Step 11: Build block_effects
|
||||
let block_effects = vec![
|
||||
(preheader_bb, vec![]),
|
||||
(header_bb, header_effects),
|
||||
(body_bb, vec![]),
|
||||
(found_bb, found_effects),
|
||||
(step_bb, step_effects),
|
||||
(after_bb, vec![]),
|
||||
];
|
||||
|
||||
// Step 12: Build Frag with Return exit
|
||||
let empty_args = EdgeArgs {
|
||||
layout: JumpArgsLayout::CarriersOnly,
|
||||
values: vec![],
|
||||
};
|
||||
|
||||
let branches = vec![
|
||||
BranchStub {
|
||||
from: header_bb,
|
||||
cond: true_val,
|
||||
then_target: body_bb,
|
||||
else_target: after_bb,
|
||||
then_args: empty_args.clone(),
|
||||
else_args: empty_args.clone(),
|
||||
},
|
||||
BranchStub {
|
||||
from: body_bb,
|
||||
cond: cond_exit,
|
||||
then_target: found_bb,
|
||||
else_target: step_bb,
|
||||
then_args: empty_args.clone(),
|
||||
else_args: empty_args.clone(),
|
||||
},
|
||||
];
|
||||
|
||||
let return_args = EdgeArgs {
|
||||
layout: JumpArgsLayout::CarriersOnly,
|
||||
values: if let Some(val_id) = return_val_id {
|
||||
vec![val_id]
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
};
|
||||
|
||||
let wires = vec![
|
||||
EdgeStub {
|
||||
from: step_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: Some(header_bb),
|
||||
args: empty_args.clone(),
|
||||
},
|
||||
EdgeStub {
|
||||
from: found_bb,
|
||||
kind: ExitKind::Return,
|
||||
target: None,
|
||||
args: return_args,
|
||||
},
|
||||
EdgeStub {
|
||||
from: after_bb,
|
||||
kind: ExitKind::Return,
|
||||
target: None,
|
||||
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)];
|
||||
|
||||
// Step 14: Build CoreLoopPlan
|
||||
let loop_plan = CoreLoopPlan {
|
||||
preheader_bb,
|
||||
header_bb,
|
||||
body_bb,
|
||||
step_bb,
|
||||
after_bb,
|
||||
found_bb,
|
||||
body: body_plans,
|
||||
cond_loop: true_val,
|
||||
cond_match: cond_exit,
|
||||
block_effects,
|
||||
phis,
|
||||
frag,
|
||||
final_values,
|
||||
};
|
||||
|
||||
if debug {
|
||||
trace_logger.debug(
|
||||
"normalizer/pattern5_return",
|
||||
"CorePlan construction complete (5 blocks, 1 PHI, Return exit)",
|
||||
);
|
||||
}
|
||||
|
||||
Ok(CorePlan::Loop(loop_plan))
|
||||
}
|
||||
|
||||
/// Pattern5 Break version normalizer
|
||||
///
|
||||
/// CFG: preheader → header → body → break_then → after / step → header
|
||||
fn normalize_pattern5_break(
|
||||
builder: &mut MirBuilder,
|
||||
parts: Pattern5InfiniteEarlyExitPlan,
|
||||
_ctx: &LoopPatternContext,
|
||||
debug: bool,
|
||||
trace_logger: &crate::mir::builder::control_flow::joinir::trace::JoinLoopTrace,
|
||||
) -> Result<CorePlan, String> {
|
||||
// Step 1: Block allocation (6 blocks)
|
||||
let preheader_bb = builder
|
||||
.current_block
|
||||
.ok_or_else(|| "Pattern5Break: no current block".to_string())?;
|
||||
let header_bb = builder.next_block_id();
|
||||
let body_bb = builder.next_block_id();
|
||||
let break_then_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/pattern5_break",
|
||||
&format!(
|
||||
"Block allocation: preheader={:?}, header={:?}, body={:?}, break_then={:?}, step={:?}, after={:?}",
|
||||
preheader_bb, header_bb, body_bb, break_then_bb, step_bb, after_bb
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Step 2: Get carrier_var (required for Break version)
|
||||
let carrier_var = parts
|
||||
.carrier_var
|
||||
.as_ref()
|
||||
.ok_or_else(|| "Pattern5Break: carrier_var required for Break version".to_string())?;
|
||||
let carrier_update_ast = parts
|
||||
.carrier_update
|
||||
.as_ref()
|
||||
.ok_or_else(|| "Pattern5Break: carrier_update required for Break version".to_string())?;
|
||||
|
||||
// Step 3: Get initial values from variable_map
|
||||
let loop_var_init = builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.get(&parts.loop_var)
|
||||
.copied()
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"Pattern5Break: loop_var '{}' not in variable_map",
|
||||
parts.loop_var
|
||||
)
|
||||
})?;
|
||||
|
||||
let carrier_init = builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.get(carrier_var)
|
||||
.copied()
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"Pattern5Break: carrier_var '{}' not in variable_map",
|
||||
carrier_var
|
||||
)
|
||||
})?;
|
||||
|
||||
// Step 4: ValueId allocation
|
||||
let loop_var_current = builder.alloc_typed(MirType::Integer);
|
||||
let carrier_current = builder.alloc_typed(MirType::Integer);
|
||||
let cond_exit = builder.alloc_typed(MirType::Bool);
|
||||
let carrier_step = builder.alloc_typed(MirType::Integer);
|
||||
let loop_var_next = builder.alloc_typed(MirType::Integer);
|
||||
let carrier_out = builder.alloc_typed(MirType::Integer);
|
||||
let true_val = builder.alloc_typed(MirType::Bool);
|
||||
|
||||
// Step 5: phi_bindings for AST lowering
|
||||
let phi_bindings = create_phi_bindings(&[
|
||||
(&parts.loop_var, loop_var_current),
|
||||
(carrier_var, carrier_current),
|
||||
]);
|
||||
|
||||
// Step 6: Lower AST expressions
|
||||
let (exit_cond_lhs, exit_cond_op, exit_cond_rhs, exit_cond_consts) =
|
||||
Self::lower_compare_ast(&parts.exit_condition, builder, &phi_bindings)?;
|
||||
|
||||
let (carrier_lhs, carrier_op, carrier_rhs, carrier_consts) =
|
||||
Self::lower_binop_ast(carrier_update_ast, builder, &phi_bindings)?;
|
||||
|
||||
let (inc_lhs, inc_op, inc_rhs, inc_consts) =
|
||||
Self::lower_binop_ast(&parts.loop_increment, builder, &phi_bindings)?;
|
||||
|
||||
// Step 7: Build header_effects
|
||||
let header_effects = vec![CoreEffectPlan::Const {
|
||||
dst: true_val,
|
||||
value: ConstValue::Bool(true),
|
||||
}];
|
||||
|
||||
// Step 8: Build body plans (exit condition check)
|
||||
let mut body_plans: Vec<CorePlan> = Vec::new();
|
||||
for eff in exit_cond_consts {
|
||||
body_plans.push(CorePlan::Effect(eff));
|
||||
}
|
||||
body_plans.push(CorePlan::Effect(CoreEffectPlan::Compare {
|
||||
dst: cond_exit,
|
||||
lhs: exit_cond_lhs,
|
||||
op: exit_cond_op,
|
||||
rhs: exit_cond_rhs,
|
||||
}));
|
||||
|
||||
// Step 9: Build break_then_effects (empty)
|
||||
let break_then_effects = vec![];
|
||||
|
||||
// Step 10: Build step_effects (carrier update + loop increment)
|
||||
let mut step_effects = carrier_consts;
|
||||
step_effects.push(CoreEffectPlan::BinOp {
|
||||
dst: carrier_step,
|
||||
lhs: carrier_lhs,
|
||||
op: carrier_op,
|
||||
rhs: carrier_rhs,
|
||||
});
|
||||
for c in inc_consts {
|
||||
step_effects.push(c);
|
||||
}
|
||||
step_effects.push(CoreEffectPlan::BinOp {
|
||||
dst: loop_var_next,
|
||||
lhs: inc_lhs,
|
||||
op: inc_op,
|
||||
rhs: inc_rhs,
|
||||
});
|
||||
|
||||
// Step 11: Build PHIs
|
||||
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_carrier_{}", parts.loop_var),
|
||||
},
|
||||
CorePhiInfo {
|
||||
block: header_bb,
|
||||
dst: carrier_current,
|
||||
inputs: vec![(preheader_bb, carrier_init), (step_bb, carrier_step)],
|
||||
tag: format!("loop_carrier_{}", carrier_var),
|
||||
},
|
||||
CorePhiInfo {
|
||||
block: after_bb,
|
||||
dst: carrier_out,
|
||||
inputs: vec![(break_then_bb, carrier_current)],
|
||||
tag: format!("exit_carrier_{}", carrier_var),
|
||||
},
|
||||
];
|
||||
|
||||
// Step 12: Build block_effects
|
||||
let block_effects = vec![
|
||||
(preheader_bb, vec![]),
|
||||
(header_bb, header_effects),
|
||||
(body_bb, vec![]),
|
||||
(break_then_bb, break_then_effects),
|
||||
(step_bb, step_effects),
|
||||
];
|
||||
|
||||
// Step 13: Build Frag
|
||||
let empty_args = EdgeArgs {
|
||||
layout: JumpArgsLayout::CarriersOnly,
|
||||
values: vec![],
|
||||
};
|
||||
|
||||
let branches = vec![
|
||||
BranchStub {
|
||||
from: header_bb,
|
||||
cond: true_val,
|
||||
then_target: body_bb,
|
||||
else_target: after_bb,
|
||||
then_args: empty_args.clone(),
|
||||
else_args: empty_args.clone(),
|
||||
},
|
||||
BranchStub {
|
||||
from: body_bb,
|
||||
cond: cond_exit,
|
||||
then_target: break_then_bb,
|
||||
else_target: step_bb,
|
||||
then_args: empty_args.clone(),
|
||||
else_args: empty_args.clone(),
|
||||
},
|
||||
];
|
||||
|
||||
let loop_id = crate::mir::control_form::LoopId(0);
|
||||
|
||||
let wires = vec![
|
||||
EdgeStub {
|
||||
from: break_then_bb,
|
||||
kind: ExitKind::Break(loop_id),
|
||||
target: Some(after_bb),
|
||||
args: empty_args.clone(),
|
||||
},
|
||||
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 14: Build final_values
|
||||
let final_values = vec![
|
||||
(parts.loop_var.clone(), loop_var_current),
|
||||
(carrier_var.clone(), carrier_out),
|
||||
];
|
||||
|
||||
// Step 15: Build CoreLoopPlan
|
||||
let loop_plan = CoreLoopPlan {
|
||||
preheader_bb,
|
||||
header_bb,
|
||||
body_bb,
|
||||
step_bb,
|
||||
after_bb,
|
||||
found_bb: after_bb,
|
||||
body: body_plans,
|
||||
cond_loop: true_val,
|
||||
cond_match: cond_exit,
|
||||
block_effects,
|
||||
phis,
|
||||
frag,
|
||||
final_values,
|
||||
};
|
||||
|
||||
if debug {
|
||||
trace_logger.debug(
|
||||
"normalizer/pattern5_break",
|
||||
"CorePlan construction complete (6 blocks, 3 PHIs, after_bb PHI for carrier)",
|
||||
);
|
||||
}
|
||||
|
||||
Ok(CorePlan::Loop(loop_plan))
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,301 @@
|
||||
use super::helpers::create_phi_bindings;
|
||||
use super::{CoreEffectPlan, CoreLoopPlan, CorePhiInfo, CorePlan, Pattern8BoolPredicateScanPlan};
|
||||
use crate::mir::basic_block::EdgeArgs;
|
||||
use crate::mir::builder::control_flow::edgecfg::api::{BranchStub, EdgeStub, ExitKind, Frag};
|
||||
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout;
|
||||
use crate::mir::{BinaryOp, CompareOp, ConstValue, Effect, EffectMask, MirType};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
impl super::PlanNormalizer {
|
||||
/// Pattern8BoolPredicateScan → CorePlan 変換 (Phase 286 P2.4.1)
|
||||
///
|
||||
/// Expands Pattern8 (Bool Predicate Scan) semantics into generic CorePlan:
|
||||
/// - CFG structure: preheader → header → body → found (return false) / step → header
|
||||
/// - 1 PHI for loop variable (i)
|
||||
/// - Early exit: return false in found_bb
|
||||
/// - Post-loop: return true in after_bb
|
||||
pub(super) fn normalize_pattern8_bool_predicate_scan(
|
||||
builder: &mut MirBuilder,
|
||||
parts: Pattern8BoolPredicateScanPlan,
|
||||
ctx: &LoopPatternContext,
|
||||
) -> Result<CorePlan, String> {
|
||||
use crate::mir::builder::control_flow::edgecfg::api::compose;
|
||||
use crate::mir::builder::control_flow::joinir::trace;
|
||||
|
||||
let trace_logger = trace::trace();
|
||||
let debug = ctx.debug;
|
||||
|
||||
if debug {
|
||||
trace_logger.debug(
|
||||
"normalizer/pattern8_bool_predicate_scan",
|
||||
&format!(
|
||||
"Phase 286 P2.4.1: Normalizing Pattern8 for {} (loop_var: {}, predicate: {}.{})",
|
||||
ctx.func_name, parts.loop_var, parts.predicate_receiver, parts.predicate_method
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// P0 Scope: Forward scan (step=1) only
|
||||
if parts.step_lit != 1 {
|
||||
return Err(format!(
|
||||
"[normalizer/pattern8] P0 scope: only forward scan supported (step={})",
|
||||
parts.step_lit
|
||||
));
|
||||
}
|
||||
|
||||
// Step 1: Get host ValueIds for variables
|
||||
let haystack_host = builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.get(&parts.haystack)
|
||||
.copied()
|
||||
.ok_or_else(|| format!("[normalizer/pattern8] Variable {} not found", parts.haystack))?;
|
||||
|
||||
let predicate_receiver_host = builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.get(&parts.predicate_receiver)
|
||||
.copied()
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"[normalizer/pattern8] Variable {} not found",
|
||||
parts.predicate_receiver
|
||||
)
|
||||
})?;
|
||||
|
||||
let loop_var_init = builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.get(&parts.loop_var)
|
||||
.copied()
|
||||
.ok_or_else(|| format!("[normalizer/pattern8] Loop variable {} not found", parts.loop_var))?;
|
||||
|
||||
// Step 2: Capture preheader block
|
||||
let preheader_bb = builder
|
||||
.current_block
|
||||
.ok_or_else(|| "[normalizer/pattern8] No current block for loop entry".to_string())?;
|
||||
|
||||
// Step 3: Allocate BasicBlockIds for 6 blocks
|
||||
let header_bb = builder.next_block_id();
|
||||
let body_bb = builder.next_block_id();
|
||||
let found_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/pattern8_bool_predicate_scan",
|
||||
&format!(
|
||||
"Allocated: preheader={:?}, header={:?}, body={:?}, found={:?}, step={:?}, after={:?}",
|
||||
preheader_bb, header_bb, body_bb, found_bb, step_bb, after_bb
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Step 4: Allocate ValueIds
|
||||
let loop_var_current = builder.alloc_typed(MirType::Integer);
|
||||
let cond_loop = builder.alloc_typed(MirType::Bool);
|
||||
let one_val = builder.alloc_typed(MirType::Integer);
|
||||
let i_plus_one = builder.alloc_typed(MirType::Integer);
|
||||
let ch = builder.alloc_typed(MirType::String);
|
||||
let cond_predicate = builder.alloc_typed(MirType::Bool);
|
||||
let cond_not_predicate = builder.alloc_typed(MirType::Bool);
|
||||
let loop_var_next = builder.alloc_typed(MirType::Integer);
|
||||
let true_val = builder.alloc_typed(MirType::Bool);
|
||||
let false_val = builder.alloc_typed(MirType::Bool);
|
||||
|
||||
// Step 5: Build phi_bindings
|
||||
let phi_bindings = create_phi_bindings(&[(&parts.loop_var, loop_var_current)]);
|
||||
|
||||
// Step 6: Lower loop condition
|
||||
let (loop_cond_lhs, loop_cond_op, loop_cond_rhs, loop_cond_consts) =
|
||||
Self::lower_compare_ast(&parts.condition, builder, &phi_bindings)?;
|
||||
|
||||
// Step 7: Build header_effects
|
||||
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 body (predicate check)
|
||||
let body = vec![
|
||||
CorePlan::Effect(CoreEffectPlan::Const {
|
||||
dst: one_val,
|
||||
value: ConstValue::Integer(1),
|
||||
}),
|
||||
CorePlan::Effect(CoreEffectPlan::Const {
|
||||
dst: false_val,
|
||||
value: ConstValue::Bool(false),
|
||||
}),
|
||||
CorePlan::Effect(CoreEffectPlan::BinOp {
|
||||
dst: i_plus_one,
|
||||
lhs: loop_var_current,
|
||||
op: BinaryOp::Add,
|
||||
rhs: one_val,
|
||||
}),
|
||||
CorePlan::Effect(CoreEffectPlan::MethodCall {
|
||||
dst: Some(ch),
|
||||
object: haystack_host,
|
||||
method: "substring".to_string(),
|
||||
args: vec![loop_var_current, i_plus_one],
|
||||
effects: EffectMask::PURE.add(Effect::Io),
|
||||
}),
|
||||
CorePlan::Effect(CoreEffectPlan::MethodCall {
|
||||
dst: Some(cond_predicate),
|
||||
object: predicate_receiver_host,
|
||||
method: parts.predicate_method.clone(),
|
||||
args: vec![ch],
|
||||
effects: EffectMask::PURE.add(Effect::Io),
|
||||
}),
|
||||
CorePlan::Effect(CoreEffectPlan::Compare {
|
||||
dst: cond_not_predicate,
|
||||
lhs: cond_predicate,
|
||||
op: CompareOp::Eq,
|
||||
rhs: false_val,
|
||||
}),
|
||||
];
|
||||
|
||||
// Step 9: Build step_effects
|
||||
let step_effects = vec![CoreEffectPlan::BinOp {
|
||||
dst: loop_var_next,
|
||||
lhs: loop_var_current,
|
||||
op: BinaryOp::Add,
|
||||
rhs: one_val,
|
||||
}];
|
||||
|
||||
// Step 10: Build found_bb effects (empty)
|
||||
let found_effects = vec![];
|
||||
|
||||
// Step 11: Build after_bb effects
|
||||
let after_effects = vec![CoreEffectPlan::Const {
|
||||
dst: true_val,
|
||||
value: ConstValue::Bool(true),
|
||||
}];
|
||||
|
||||
// Step 12: Build block_effects
|
||||
let block_effects = vec![
|
||||
(preheader_bb, vec![]),
|
||||
(header_bb, header_effects),
|
||||
(body_bb, vec![]),
|
||||
(found_bb, found_effects),
|
||||
(step_bb, step_effects),
|
||||
(after_bb, after_effects),
|
||||
];
|
||||
|
||||
// Step 13: Build PHI
|
||||
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 14: Build Frag using compose::cleanup for early exit
|
||||
let empty_args = EdgeArgs {
|
||||
layout: JumpArgsLayout::CarriersOnly,
|
||||
values: vec![],
|
||||
};
|
||||
|
||||
let ret_false_args = EdgeArgs {
|
||||
layout: JumpArgsLayout::CarriersOnly,
|
||||
values: vec![false_val],
|
||||
};
|
||||
|
||||
let ret_true_args = EdgeArgs {
|
||||
layout: JumpArgsLayout::CarriersOnly,
|
||||
values: vec![true_val],
|
||||
};
|
||||
|
||||
let main_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(),
|
||||
},
|
||||
BranchStub {
|
||||
from: body_bb,
|
||||
cond: cond_not_predicate,
|
||||
then_target: found_bb,
|
||||
then_args: empty_args.clone(),
|
||||
else_target: step_bb,
|
||||
else_args: empty_args.clone(),
|
||||
},
|
||||
];
|
||||
|
||||
let main_wires = vec![EdgeStub {
|
||||
from: step_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: Some(header_bb),
|
||||
args: empty_args.clone(),
|
||||
}];
|
||||
|
||||
let main_frag = Frag {
|
||||
entry: header_bb,
|
||||
exits: BTreeMap::new(),
|
||||
wires: main_wires,
|
||||
branches: main_branches,
|
||||
};
|
||||
|
||||
let cleanup_exits = vec![
|
||||
EdgeStub {
|
||||
from: found_bb,
|
||||
kind: ExitKind::Return,
|
||||
target: None,
|
||||
args: ret_false_args,
|
||||
},
|
||||
EdgeStub {
|
||||
from: after_bb,
|
||||
kind: ExitKind::Return,
|
||||
target: None,
|
||||
args: ret_true_args,
|
||||
},
|
||||
];
|
||||
|
||||
let cleanup_frag = Frag {
|
||||
entry: found_bb,
|
||||
exits: BTreeMap::from([(ExitKind::Return, cleanup_exits)]),
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
};
|
||||
|
||||
let frag = compose::cleanup(main_frag, cleanup_frag, None, None)
|
||||
.expect("compose::cleanup() failed in normalize_pattern8_bool_predicate_scan");
|
||||
|
||||
// Step 15: Build final_values
|
||||
let final_values = vec![(parts.loop_var.clone(), loop_var_current)];
|
||||
|
||||
// Step 16: Build CoreLoopPlan
|
||||
let loop_plan = CoreLoopPlan {
|
||||
preheader_bb,
|
||||
header_bb,
|
||||
body_bb,
|
||||
step_bb,
|
||||
after_bb,
|
||||
found_bb,
|
||||
body,
|
||||
cond_loop,
|
||||
cond_match: cond_not_predicate,
|
||||
block_effects,
|
||||
phis,
|
||||
frag,
|
||||
final_values,
|
||||
};
|
||||
|
||||
if debug {
|
||||
trace_logger.debug(
|
||||
"normalizer/pattern8_bool_predicate_scan",
|
||||
"CorePlan construction complete (5 blocks, 1 PHI, 2 Return exits)",
|
||||
);
|
||||
}
|
||||
|
||||
Ok(CorePlan::Loop(loop_plan))
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,214 @@
|
||||
use super::helpers::{create_phi_bindings, LoopBlocksStandard5};
|
||||
use super::{CoreEffectPlan, CoreLoopPlan, CorePhiInfo, CorePlan, Pattern9AccumConstLoopPlan};
|
||||
use crate::mir::basic_block::EdgeArgs;
|
||||
use crate::mir::builder::control_flow::edgecfg::api::{BranchStub, EdgeStub, ExitKind, Frag};
|
||||
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout;
|
||||
use crate::mir::MirType;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
impl super::PlanNormalizer {
|
||||
/// Phase 286 P2.3: Pattern9AccumConstLoop → CorePlan 変換
|
||||
///
|
||||
/// Expands Pattern9 (Accumulator Const Loop) semantics into generic CorePlan:
|
||||
/// - CFG structure: preheader → header → body → step → header (back-edge)
|
||||
/// - 2 PHIs in header: loop variable (i) and accumulator (sum)
|
||||
/// - Similar to Pattern1 but with an additional carrier
|
||||
pub(super) fn normalize_pattern9_accum_const_loop(
|
||||
builder: &mut MirBuilder,
|
||||
parts: Pattern9AccumConstLoopPlan,
|
||||
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/pattern9_accum_const_loop",
|
||||
&format!(
|
||||
"Phase 286 P2.3: Normalizing Pattern9AccumConstLoop for {} (loop_var: {}, acc_var: {})",
|
||||
ctx.func_name, parts.loop_var, parts.acc_var
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 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 acc_var_init = builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.get(&parts.acc_var)
|
||||
.copied()
|
||||
.ok_or_else(|| format!("[normalizer] Accumulator variable {} not found", parts.acc_var))?;
|
||||
|
||||
// Step 2-3: Allocate BasicBlockIds
|
||||
let blocks = LoopBlocksStandard5::allocate(builder)?;
|
||||
let LoopBlocksStandard5 {
|
||||
preheader_bb,
|
||||
header_bb,
|
||||
body_bb,
|
||||
step_bb,
|
||||
after_bb,
|
||||
} = blocks;
|
||||
|
||||
if debug {
|
||||
trace_logger.debug(
|
||||
"normalizer/pattern9_accum_const_loop",
|
||||
&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);
|
||||
let acc_var_current = builder.alloc_typed(MirType::Integer);
|
||||
let cond_loop = builder.alloc_typed(MirType::Bool);
|
||||
let acc_var_next = builder.alloc_typed(MirType::Integer);
|
||||
let loop_var_next = builder.alloc_typed(MirType::Integer);
|
||||
|
||||
// Step 5: Build 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
|
||||
let (loop_cond_lhs, loop_cond_op, loop_cond_rhs, loop_cond_consts) =
|
||||
Self::lower_compare_ast(&parts.condition, builder, &phi_bindings)?;
|
||||
|
||||
let (acc_update_lhs, acc_update_op, acc_update_rhs, acc_update_consts) =
|
||||
Self::lower_binop_ast(&parts.acc_update, builder, &phi_bindings)?;
|
||||
|
||||
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
|
||||
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
|
||||
let mut step_effects = acc_update_consts;
|
||||
step_effects.push(CoreEffectPlan::BinOp {
|
||||
dst: acc_var_next,
|
||||
lhs: acc_update_lhs,
|
||||
op: acc_update_op,
|
||||
rhs: acc_update_rhs,
|
||||
});
|
||||
step_effects.extend(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![]),
|
||||
(step_bb, step_effects),
|
||||
];
|
||||
|
||||
// Step 10: Build PHIs
|
||||
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),
|
||||
},
|
||||
CorePhiInfo {
|
||||
block: header_bb,
|
||||
dst: acc_var_current,
|
||||
inputs: vec![(preheader_bb, acc_var_init), (step_bb, acc_var_next)],
|
||||
tag: format!("acc_var_{}", parts.acc_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![
|
||||
EdgeStub {
|
||||
from: body_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: Some(step_bb),
|
||||
args: empty_args.clone(),
|
||||
},
|
||||
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),
|
||||
(parts.acc_var.clone(), acc_var_current),
|
||||
];
|
||||
|
||||
// Step 13: Build CoreLoopPlan
|
||||
let loop_plan = CoreLoopPlan {
|
||||
preheader_bb,
|
||||
header_bb,
|
||||
body_bb,
|
||||
step_bb,
|
||||
after_bb,
|
||||
found_bb: after_bb,
|
||||
body: vec![],
|
||||
cond_loop,
|
||||
cond_match: cond_loop,
|
||||
block_effects,
|
||||
phis,
|
||||
frag,
|
||||
final_values,
|
||||
};
|
||||
|
||||
if debug {
|
||||
trace_logger.debug(
|
||||
"normalizer/pattern9_accum_const_loop",
|
||||
"CorePlan construction complete (4 blocks, 2 PHIs)",
|
||||
);
|
||||
}
|
||||
|
||||
Ok(CorePlan::Loop(loop_plan))
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,342 @@
|
||||
use super::{CoreEffectPlan, CoreLoopPlan, CorePhiInfo, CorePlan, ScanWithInitPlan};
|
||||
use crate::mir::basic_block::EdgeArgs;
|
||||
use crate::mir::builder::control_flow::edgecfg::api::{BranchStub, EdgeStub, ExitKind, Frag};
|
||||
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout;
|
||||
use crate::mir::{BinaryOp, CompareOp, ConstValue, Effect, EffectMask, MirType};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
impl super::PlanNormalizer {
|
||||
/// ScanWithInit → CorePlan 変換
|
||||
///
|
||||
/// Expands scan-specific semantics into generic CorePlan:
|
||||
/// - header_effects: one=1, needle_len, len, bound, cond_loop
|
||||
/// - body: i+needle_len, substring, cond_match
|
||||
/// - step_effects: i_next = i + 1
|
||||
pub(super) fn normalize_scan_with_init(
|
||||
builder: &mut MirBuilder,
|
||||
parts: ScanWithInitPlan,
|
||||
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/scan_with_init",
|
||||
&format!(
|
||||
"Phase 273 P1: Normalizing ScanWithInit for {}",
|
||||
ctx.func_name
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// P1 Scope: Forward scan (step=1) only
|
||||
if parts.step_lit != 1 {
|
||||
return Err(format!(
|
||||
"[normalizer] P1 scope: only forward scan supported (step={})",
|
||||
parts.step_lit
|
||||
));
|
||||
}
|
||||
|
||||
// Step 1: Get host ValueIds for variables
|
||||
let s_host = builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.get(&parts.haystack)
|
||||
.copied()
|
||||
.ok_or_else(|| format!("[normalizer] Variable {} not found", parts.haystack))?;
|
||||
|
||||
let needle_host = builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.get(&parts.needle)
|
||||
.copied()
|
||||
.ok_or_else(|| format!("[normalizer] Variable {} not found", parts.needle))?;
|
||||
|
||||
let i_init_val = 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 (entry to loop) for PHI input
|
||||
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();
|
||||
let found_bb = builder.next_block_id();
|
||||
|
||||
// Step 4: Allocate ValueIds for CorePlan
|
||||
let i_current = builder.next_value_id(); // PHI destination
|
||||
builder
|
||||
.type_ctx
|
||||
.value_types
|
||||
.insert(i_current, MirType::Integer);
|
||||
|
||||
let one_val = builder.next_value_id();
|
||||
builder
|
||||
.type_ctx
|
||||
.value_types
|
||||
.insert(one_val, MirType::Integer);
|
||||
|
||||
let needle_len_val = if parts.dynamic_needle {
|
||||
let v = builder.next_value_id();
|
||||
builder.type_ctx.value_types.insert(v, MirType::Integer);
|
||||
v
|
||||
} else {
|
||||
one_val // reuse one_val for fixed needle
|
||||
};
|
||||
|
||||
let len_val = builder.next_value_id();
|
||||
builder
|
||||
.type_ctx
|
||||
.value_types
|
||||
.insert(len_val, MirType::Integer);
|
||||
|
||||
let bound_val = builder.next_value_id();
|
||||
builder
|
||||
.type_ctx
|
||||
.value_types
|
||||
.insert(bound_val, MirType::Integer);
|
||||
|
||||
let cond_loop = builder.next_value_id();
|
||||
builder
|
||||
.type_ctx
|
||||
.value_types
|
||||
.insert(cond_loop, MirType::Bool);
|
||||
|
||||
let i_plus_needle_len = builder.next_value_id();
|
||||
builder
|
||||
.type_ctx
|
||||
.value_types
|
||||
.insert(i_plus_needle_len, MirType::Integer);
|
||||
|
||||
let window_val = builder.next_value_id();
|
||||
builder
|
||||
.type_ctx
|
||||
.value_types
|
||||
.insert(window_val, MirType::String);
|
||||
|
||||
let cond_match = builder.next_value_id();
|
||||
builder
|
||||
.type_ctx
|
||||
.value_types
|
||||
.insert(cond_match, MirType::Bool);
|
||||
|
||||
let i_next_val = builder.next_value_id();
|
||||
builder
|
||||
.type_ctx
|
||||
.value_types
|
||||
.insert(i_next_val, MirType::Integer);
|
||||
|
||||
if debug {
|
||||
trace_logger.debug(
|
||||
"normalizer/scan_with_init",
|
||||
&format!(
|
||||
"Allocated: preheader={:?}, header={:?}, body={:?}, step={:?}, after={:?}, found={:?}",
|
||||
preheader_bb, header_bb, body_bb, step_bb, after_bb, found_bb
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Step 5: Build header_effects (emitted in header_bb)
|
||||
let mut header_effects = vec![
|
||||
// one = 1
|
||||
CoreEffectPlan::Const {
|
||||
dst: one_val,
|
||||
value: ConstValue::Integer(1),
|
||||
},
|
||||
];
|
||||
|
||||
// needle_len = needle.length() if dynamic, else reuse one_val
|
||||
if parts.dynamic_needle {
|
||||
header_effects.push(CoreEffectPlan::MethodCall {
|
||||
dst: Some(needle_len_val),
|
||||
object: needle_host,
|
||||
method: "length".to_string(),
|
||||
args: vec![],
|
||||
effects: EffectMask::PURE.add(Effect::Io),
|
||||
});
|
||||
}
|
||||
|
||||
// len = s.length()
|
||||
header_effects.push(CoreEffectPlan::MethodCall {
|
||||
dst: Some(len_val),
|
||||
object: s_host,
|
||||
method: "length".to_string(),
|
||||
args: vec![],
|
||||
effects: EffectMask::PURE.add(Effect::Io),
|
||||
});
|
||||
|
||||
// bound = len - needle_len
|
||||
header_effects.push(CoreEffectPlan::BinOp {
|
||||
dst: bound_val,
|
||||
lhs: len_val,
|
||||
op: BinaryOp::Sub,
|
||||
rhs: needle_len_val,
|
||||
});
|
||||
|
||||
// cond_loop = i <= bound
|
||||
header_effects.push(CoreEffectPlan::Compare {
|
||||
dst: cond_loop,
|
||||
lhs: i_current,
|
||||
op: CompareOp::Le,
|
||||
rhs: bound_val,
|
||||
});
|
||||
|
||||
// Step 6: Build body (emitted in body_bb)
|
||||
let body = vec![
|
||||
// i_plus_needle_len = i + needle_len
|
||||
CorePlan::Effect(CoreEffectPlan::BinOp {
|
||||
dst: i_plus_needle_len,
|
||||
lhs: i_current,
|
||||
op: BinaryOp::Add,
|
||||
rhs: needle_len_val,
|
||||
}),
|
||||
// window = s.substring(i, i + needle_len)
|
||||
CorePlan::Effect(CoreEffectPlan::MethodCall {
|
||||
dst: Some(window_val),
|
||||
object: s_host,
|
||||
method: "substring".to_string(),
|
||||
args: vec![i_current, i_plus_needle_len],
|
||||
effects: EffectMask::PURE.add(Effect::Io),
|
||||
}),
|
||||
// cond_match = window == needle
|
||||
CorePlan::Effect(CoreEffectPlan::Compare {
|
||||
dst: cond_match,
|
||||
lhs: window_val,
|
||||
op: CompareOp::Eq,
|
||||
rhs: needle_host,
|
||||
}),
|
||||
];
|
||||
|
||||
// Step 7: Build step_effects (emitted in step_bb)
|
||||
let step_effects = vec![
|
||||
// i_next = i + 1
|
||||
CoreEffectPlan::BinOp {
|
||||
dst: i_next_val,
|
||||
lhs: i_current,
|
||||
op: BinaryOp::Add,
|
||||
rhs: one_val,
|
||||
},
|
||||
];
|
||||
|
||||
// Step 8: Build block_effects (Phase 273 P2)
|
||||
let block_effects = vec![
|
||||
(preheader_bb, vec![]),
|
||||
(header_bb, header_effects.clone()),
|
||||
(body_bb, vec![]),
|
||||
(step_bb, step_effects.clone()),
|
||||
];
|
||||
|
||||
// Step 9: Build phis (Phase 273 P2)
|
||||
let phis = vec![CorePhiInfo {
|
||||
block: header_bb,
|
||||
dst: i_current,
|
||||
inputs: vec![(preheader_bb, i_init_val), (step_bb, i_next_val)],
|
||||
tag: format!("loop_carrier_{}", parts.loop_var),
|
||||
}];
|
||||
|
||||
// Step 10: Build Frag (Phase 273 P2)
|
||||
let empty_args = EdgeArgs {
|
||||
layout: JumpArgsLayout::CarriersOnly,
|
||||
values: vec![],
|
||||
};
|
||||
|
||||
let ret_found_args = EdgeArgs {
|
||||
layout: JumpArgsLayout::CarriersOnly,
|
||||
values: vec![i_current],
|
||||
};
|
||||
|
||||
use crate::mir::builder::control_flow::edgecfg::api::compose;
|
||||
|
||||
let main_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(),
|
||||
},
|
||||
BranchStub {
|
||||
from: body_bb,
|
||||
cond: cond_match,
|
||||
then_target: found_bb,
|
||||
then_args: empty_args.clone(),
|
||||
else_target: step_bb,
|
||||
else_args: empty_args.clone(),
|
||||
},
|
||||
];
|
||||
|
||||
let main_wires = vec![EdgeStub {
|
||||
from: step_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: Some(header_bb),
|
||||
args: empty_args.clone(),
|
||||
}];
|
||||
|
||||
let main_frag = Frag {
|
||||
entry: header_bb,
|
||||
exits: BTreeMap::new(),
|
||||
wires: main_wires,
|
||||
branches: main_branches,
|
||||
};
|
||||
|
||||
let cleanup_exits = vec![EdgeStub {
|
||||
from: found_bb,
|
||||
kind: ExitKind::Return,
|
||||
target: None,
|
||||
args: ret_found_args,
|
||||
}];
|
||||
|
||||
let cleanup_frag = Frag {
|
||||
entry: found_bb,
|
||||
exits: BTreeMap::from([(ExitKind::Return, cleanup_exits)]),
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
};
|
||||
|
||||
let frag = compose::cleanup(main_frag, cleanup_frag, None, None)
|
||||
.expect("compose::cleanup() failed in normalize_scan_with_init");
|
||||
|
||||
// Step 11: Build final_values (Phase 273 P2)
|
||||
let final_values = vec![(parts.loop_var.clone(), i_current)];
|
||||
|
||||
// Step 12: Build CoreLoopPlan (Phase 273 P3: all generalized fields REQUIRED)
|
||||
let loop_plan = CoreLoopPlan {
|
||||
preheader_bb,
|
||||
header_bb,
|
||||
body_bb,
|
||||
step_bb,
|
||||
after_bb,
|
||||
found_bb,
|
||||
body,
|
||||
cond_loop,
|
||||
cond_match,
|
||||
block_effects,
|
||||
phis,
|
||||
frag,
|
||||
final_values,
|
||||
};
|
||||
|
||||
if debug {
|
||||
trace_logger.debug(
|
||||
"normalizer/scan_with_init",
|
||||
"CorePlan construction complete",
|
||||
);
|
||||
}
|
||||
|
||||
Ok(CorePlan::Loop(loop_plan))
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,439 @@
|
||||
use super::{CoreEffectPlan, CoreLoopPlan, CorePhiInfo, CorePlan, SplitScanPlan};
|
||||
use crate::mir::basic_block::EdgeArgs;
|
||||
use crate::mir::builder::control_flow::edgecfg::api::{BranchStub, EdgeStub, ExitKind, Frag};
|
||||
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout;
|
||||
use crate::mir::{BinaryOp, CompareOp, ConstValue, Effect, EffectMask, MirType};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
impl super::PlanNormalizer {
|
||||
/// SplitScan → CorePlan 変換
|
||||
///
|
||||
/// Expands split-specific semantics into generic CorePlan:
|
||||
/// - 2 carriers: i (loop index), start (segment start)
|
||||
/// - 6 blocks: preheader, header, body, then, else, step, after
|
||||
/// - 4 PHI nodes: header (i_current, start_current) + step (i_next, start_next)
|
||||
/// - Side effect: result.push(segment) in then_bb
|
||||
pub(super) fn normalize_split_scan(
|
||||
builder: &mut MirBuilder,
|
||||
parts: SplitScanPlan,
|
||||
ctx: &LoopPatternContext,
|
||||
) -> Result<CorePlan, String> {
|
||||
use crate::mir::builder::control_flow::joinir::trace;
|
||||
use crate::mir::builder::control_flow::edgecfg::api::compose;
|
||||
|
||||
let trace_logger = trace::trace();
|
||||
let debug = ctx.debug;
|
||||
|
||||
if debug {
|
||||
trace_logger.debug(
|
||||
"normalizer/split_scan",
|
||||
&format!(
|
||||
"Phase 273 P2: Normalizing SplitScan for {}",
|
||||
ctx.func_name
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Step 1: Get host ValueIds for variables
|
||||
let s_host = builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.get(&parts.s_var)
|
||||
.copied()
|
||||
.ok_or_else(|| format!("[normalizer] Variable {} not found", parts.s_var))?;
|
||||
|
||||
let sep_host = builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.get(&parts.sep_var)
|
||||
.copied()
|
||||
.ok_or_else(|| format!("[normalizer] Variable {} not found", parts.sep_var))?;
|
||||
|
||||
let result_host = builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.get(&parts.result_var)
|
||||
.copied()
|
||||
.ok_or_else(|| format!("[normalizer] Variable {} not found", parts.result_var))?;
|
||||
|
||||
let i_init_val = builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.get(&parts.i_var)
|
||||
.copied()
|
||||
.ok_or_else(|| format!("[normalizer] Variable {} not found", parts.i_var))?;
|
||||
|
||||
let start_init_val = builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.get(&parts.start_var)
|
||||
.copied()
|
||||
.ok_or_else(|| format!("[normalizer] Variable {} not found", parts.start_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 6 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 step_bb = builder.next_block_id();
|
||||
let after_bb = builder.next_block_id();
|
||||
|
||||
// Step 4: Allocate ValueIds for PHI destinations (before blocks)
|
||||
let i_current = builder.next_value_id();
|
||||
builder
|
||||
.type_ctx
|
||||
.value_types
|
||||
.insert(i_current, MirType::Integer);
|
||||
|
||||
let start_current = builder.next_value_id();
|
||||
builder
|
||||
.type_ctx
|
||||
.value_types
|
||||
.insert(start_current, MirType::Integer);
|
||||
|
||||
let i_next = builder.next_value_id();
|
||||
builder
|
||||
.type_ctx
|
||||
.value_types
|
||||
.insert(i_next, MirType::Integer);
|
||||
|
||||
let start_next = builder.next_value_id();
|
||||
builder
|
||||
.type_ctx
|
||||
.value_types
|
||||
.insert(start_next, MirType::Integer);
|
||||
|
||||
// Step 5: Allocate ValueIds for expressions
|
||||
let sep_len = builder.next_value_id();
|
||||
builder
|
||||
.type_ctx
|
||||
.value_types
|
||||
.insert(sep_len, MirType::Integer);
|
||||
|
||||
let s_len = builder.next_value_id();
|
||||
builder.type_ctx.value_types.insert(s_len, MirType::Integer);
|
||||
|
||||
let limit = builder.next_value_id();
|
||||
builder.type_ctx.value_types.insert(limit, MirType::Integer);
|
||||
|
||||
let cond_loop = builder.next_value_id();
|
||||
builder
|
||||
.type_ctx
|
||||
.value_types
|
||||
.insert(cond_loop, MirType::Bool);
|
||||
|
||||
let i_plus_sep = builder.next_value_id();
|
||||
builder
|
||||
.type_ctx
|
||||
.value_types
|
||||
.insert(i_plus_sep, MirType::Integer);
|
||||
|
||||
let chunk = builder.next_value_id();
|
||||
builder.type_ctx.value_types.insert(chunk, MirType::String);
|
||||
|
||||
let cond_match = builder.next_value_id();
|
||||
builder
|
||||
.type_ctx
|
||||
.value_types
|
||||
.insert(cond_match, MirType::Bool);
|
||||
|
||||
let segment = builder.next_value_id();
|
||||
builder
|
||||
.type_ctx
|
||||
.value_types
|
||||
.insert(segment, MirType::String);
|
||||
|
||||
let start_next_then = builder.next_value_id();
|
||||
builder
|
||||
.type_ctx
|
||||
.value_types
|
||||
.insert(start_next_then, MirType::Integer);
|
||||
|
||||
let one = builder.next_value_id();
|
||||
builder.type_ctx.value_types.insert(one, MirType::Integer);
|
||||
|
||||
let i_next_else = builder.next_value_id();
|
||||
builder
|
||||
.type_ctx
|
||||
.value_types
|
||||
.insert(i_next_else, MirType::Integer);
|
||||
|
||||
if debug {
|
||||
trace_logger.debug(
|
||||
"normalizer/split_scan",
|
||||
&format!(
|
||||
"Allocated: preheader={:?}, header={:?}, body={:?}, then={:?}, else={:?}, step={:?}, after={:?}",
|
||||
preheader_bb, header_bb, body_bb, then_bb, else_bb, step_bb, after_bb
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Step 6: Build header_effects
|
||||
let header_effects = vec![
|
||||
// sep_len = sep.length()
|
||||
CoreEffectPlan::MethodCall {
|
||||
dst: Some(sep_len),
|
||||
object: sep_host,
|
||||
method: "length".to_string(),
|
||||
args: vec![],
|
||||
effects: EffectMask::PURE.add(Effect::Io),
|
||||
},
|
||||
// s_len = s.length()
|
||||
CoreEffectPlan::MethodCall {
|
||||
dst: Some(s_len),
|
||||
object: s_host,
|
||||
method: "length".to_string(),
|
||||
args: vec![],
|
||||
effects: EffectMask::PURE.add(Effect::Io),
|
||||
},
|
||||
// limit = s_len - sep_len
|
||||
CoreEffectPlan::BinOp {
|
||||
dst: limit,
|
||||
lhs: s_len,
|
||||
op: BinaryOp::Sub,
|
||||
rhs: sep_len,
|
||||
},
|
||||
// cond_loop = i <= limit
|
||||
CoreEffectPlan::Compare {
|
||||
dst: cond_loop,
|
||||
lhs: i_current,
|
||||
op: CompareOp::Le,
|
||||
rhs: limit,
|
||||
},
|
||||
];
|
||||
|
||||
// Step 7: Build body effects and plans
|
||||
let body = vec![
|
||||
// i_plus_sep = i + sep_len
|
||||
CorePlan::Effect(CoreEffectPlan::BinOp {
|
||||
dst: i_plus_sep,
|
||||
lhs: i_current,
|
||||
op: BinaryOp::Add,
|
||||
rhs: sep_len,
|
||||
}),
|
||||
// chunk = s.substring(i, i_plus_sep)
|
||||
CorePlan::Effect(CoreEffectPlan::MethodCall {
|
||||
dst: Some(chunk),
|
||||
object: s_host,
|
||||
method: "substring".to_string(),
|
||||
args: vec![i_current, i_plus_sep],
|
||||
effects: EffectMask::PURE.add(Effect::Io),
|
||||
}),
|
||||
// cond_match = chunk == sep
|
||||
CorePlan::Effect(CoreEffectPlan::Compare {
|
||||
dst: cond_match,
|
||||
lhs: chunk,
|
||||
op: CompareOp::Eq,
|
||||
rhs: sep_host,
|
||||
}),
|
||||
];
|
||||
|
||||
// Step 8: Build then_effects (push + updates)
|
||||
let then_effects = vec![
|
||||
// segment = s.substring(start, i)
|
||||
CoreEffectPlan::MethodCall {
|
||||
dst: Some(segment),
|
||||
object: s_host,
|
||||
method: "substring".to_string(),
|
||||
args: vec![start_current, i_current],
|
||||
effects: EffectMask::PURE.add(Effect::Io),
|
||||
},
|
||||
// result.push(segment) - Side effect!
|
||||
CoreEffectPlan::MethodCall {
|
||||
dst: None,
|
||||
object: result_host,
|
||||
method: "push".to_string(),
|
||||
args: vec![segment],
|
||||
effects: EffectMask::MUT,
|
||||
},
|
||||
// start_next_then = i + sep_len
|
||||
CoreEffectPlan::BinOp {
|
||||
dst: start_next_then,
|
||||
lhs: i_current,
|
||||
op: BinaryOp::Add,
|
||||
rhs: sep_len,
|
||||
},
|
||||
];
|
||||
|
||||
// Step 9: Build else_effects (increment i)
|
||||
let else_effects = vec![
|
||||
// one = const 1
|
||||
CoreEffectPlan::Const {
|
||||
dst: one,
|
||||
value: ConstValue::Integer(1),
|
||||
},
|
||||
// i_next_else = i + 1
|
||||
CoreEffectPlan::BinOp {
|
||||
dst: i_next_else,
|
||||
lhs: i_current,
|
||||
op: BinaryOp::Add,
|
||||
rhs: one,
|
||||
},
|
||||
];
|
||||
|
||||
// Step 9.5: Build Frags for compose::if_() (Phase 281 P0)
|
||||
let empty_args = EdgeArgs {
|
||||
layout: JumpArgsLayout::CarriersOnly,
|
||||
values: vec![],
|
||||
};
|
||||
|
||||
let mut then_exits = BTreeMap::new();
|
||||
then_exits.insert(
|
||||
ExitKind::Normal,
|
||||
vec![EdgeStub {
|
||||
from: then_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: None,
|
||||
args: empty_args.clone(),
|
||||
}],
|
||||
);
|
||||
let then_frag = Frag {
|
||||
entry: then_bb,
|
||||
exits: then_exits,
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
};
|
||||
|
||||
let mut else_exits = BTreeMap::new();
|
||||
else_exits.insert(
|
||||
ExitKind::Normal,
|
||||
vec![EdgeStub {
|
||||
from: else_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: None,
|
||||
args: empty_args.clone(),
|
||||
}],
|
||||
);
|
||||
let else_frag = Frag {
|
||||
entry: else_bb,
|
||||
exits: else_exits,
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
};
|
||||
|
||||
let step_frag = Frag {
|
||||
entry: step_bb,
|
||||
exits: BTreeMap::new(),
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
};
|
||||
|
||||
let body_if_frag = compose::if_(
|
||||
body_bb,
|
||||
cond_match,
|
||||
then_frag,
|
||||
empty_args.clone(),
|
||||
else_frag,
|
||||
empty_args.clone(),
|
||||
step_frag,
|
||||
);
|
||||
|
||||
// Step 10: Build block_effects
|
||||
let block_effects = vec![
|
||||
(preheader_bb, vec![]),
|
||||
(header_bb, header_effects.clone()),
|
||||
(body_bb, vec![]),
|
||||
(then_bb, then_effects),
|
||||
(else_bb, else_effects),
|
||||
(step_bb, vec![]),
|
||||
];
|
||||
|
||||
// Step 11: Build phis (4 PHIs: 2 in header + 2 in step)
|
||||
let phis = vec![
|
||||
CorePhiInfo {
|
||||
block: header_bb,
|
||||
dst: i_current,
|
||||
inputs: vec![(preheader_bb, i_init_val), (step_bb, i_next)],
|
||||
tag: format!("loop_carrier_i_{}", parts.i_var),
|
||||
},
|
||||
CorePhiInfo {
|
||||
block: header_bb,
|
||||
dst: start_current,
|
||||
inputs: vec![(preheader_bb, start_init_val), (step_bb, start_next)],
|
||||
tag: format!("loop_carrier_start_{}", parts.start_var),
|
||||
},
|
||||
CorePhiInfo {
|
||||
block: step_bb,
|
||||
dst: i_next,
|
||||
inputs: vec![(then_bb, start_next_then), (else_bb, i_next_else)],
|
||||
tag: format!("step_phi_i_{}", parts.i_var),
|
||||
},
|
||||
CorePhiInfo {
|
||||
block: step_bb,
|
||||
dst: start_next,
|
||||
inputs: vec![(then_bb, start_next_then), (else_bb, start_current)],
|
||||
tag: format!("step_phi_start_{}", parts.start_var),
|
||||
},
|
||||
];
|
||||
|
||||
// Step 12: Build Frag
|
||||
let mut 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(),
|
||||
}];
|
||||
|
||||
branches.extend(body_if_frag.branches);
|
||||
|
||||
let mut wires = Vec::new();
|
||||
wires.extend(body_if_frag.wires);
|
||||
|
||||
wires.push(EdgeStub {
|
||||
from: step_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: Some(header_bb),
|
||||
args: empty_args.clone(),
|
||||
});
|
||||
|
||||
let mut exits = BTreeMap::new();
|
||||
for (kind, stubs) in body_if_frag.exits {
|
||||
exits.insert(kind, stubs);
|
||||
}
|
||||
|
||||
let frag = Frag {
|
||||
entry: header_bb,
|
||||
exits,
|
||||
wires,
|
||||
branches,
|
||||
};
|
||||
|
||||
// Step 13: Build final_values (i, start for post-loop)
|
||||
let final_values = vec![(parts.i_var.clone(), i_current), (parts.start_var.clone(), start_current)];
|
||||
|
||||
// Step 14: Build CoreLoopPlan
|
||||
let loop_plan = CoreLoopPlan {
|
||||
preheader_bb,
|
||||
header_bb,
|
||||
body_bb,
|
||||
step_bb,
|
||||
after_bb,
|
||||
found_bb: after_bb,
|
||||
body,
|
||||
cond_loop,
|
||||
cond_match,
|
||||
block_effects,
|
||||
phis,
|
||||
frag,
|
||||
final_values,
|
||||
};
|
||||
|
||||
if debug {
|
||||
trace_logger.debug(
|
||||
"normalizer/split_scan",
|
||||
"CorePlan construction complete (6 blocks, 4 PHIs)",
|
||||
);
|
||||
}
|
||||
|
||||
Ok(CorePlan::Loop(loop_plan))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user