502 lines
18 KiB
Rust
502 lines
18 KiB
Rust
//! Phase 273 P1: DomainPlan/CorePlan 二層構造 + PlanNormalizer + PlanVerifier
|
|
//!
|
|
//! This module provides a two-layer Plan architecture for loop pattern lowering:
|
|
//!
|
|
//! # Architecture
|
|
//!
|
|
//! ```text
|
|
//! DomainPlan (Pattern固有)
|
|
//! ↓ PlanNormalizer (SSOT)
|
|
//! CorePlan (固定語彙 - 構造ノードのみ)
|
|
//! ↓ PlanLowerer
|
|
//! MIR (block/value/phi)
|
|
//! ```
|
|
//!
|
|
//! - **DomainPlan**: Pattern-specific plans (ScanWithInit etc.)
|
|
//! - **PlanNormalizer**: DomainPlan → CorePlan conversion (SSOT, scan knowledge here)
|
|
//! - **CorePlan**: Fixed vocabulary, expressions as ValueId references (no String parsing)
|
|
//! - **PlanVerifier**: Fail-fast validation for CorePlan invariants
|
|
//! - **PlanLowerer**: Processes CorePlan only (no string interpretation)
|
|
//!
|
|
//! # Key Design Decision (String式禁止)
|
|
//!
|
|
//! CorePlan expressions use **ValueId references only** (String expressions forbidden).
|
|
//! This prevents "second language processor" from growing inside Lowerer.
|
|
|
|
use crate::ast::ASTNode;
|
|
use crate::mir::{BasicBlockId, BinaryOp, CompareOp, ConstValue, EffectMask, ValueId};
|
|
use crate::mir::builder::control_flow::edgecfg::api::Frag;
|
|
|
|
pub(in crate::mir::builder) mod lowerer;
|
|
pub(in crate::mir::builder) mod normalizer;
|
|
pub(in crate::mir::builder) mod verifier;
|
|
// Phase 29ai P0: Facts SSOT + Single Planner skeleton (parallel footing, unused in P0)
|
|
pub(in crate::mir::builder) mod facts;
|
|
pub(in crate::mir::builder) mod normalize;
|
|
pub(in crate::mir::builder) mod planner;
|
|
pub(in crate::mir::builder) mod emit;
|
|
// Phase 29ai P5: JoinIR router → single plan extraction entrypoint
|
|
pub(in crate::mir::builder) mod single_planner;
|
|
|
|
// ============================================================================
|
|
// DomainPlan (Pattern固有)
|
|
// ============================================================================
|
|
|
|
/// Phase 273 P1: DomainPlan - Pattern-specific plan vocabulary
|
|
///
|
|
/// DomainPlan contains pattern-specific knowledge (e.g., scan semantics).
|
|
/// Normalizer converts DomainPlan → CorePlan with ValueId generation.
|
|
#[derive(Debug, Clone)]
|
|
pub(in crate::mir::builder) enum DomainPlan {
|
|
/// Pattern6: index_of / find scan
|
|
ScanWithInit(ScanWithInitPlan),
|
|
/// Pattern7: split / tokenization scan
|
|
SplitScan(SplitScanPlan),
|
|
/// Pattern4: Loop with Continue (Phase 286 P2)
|
|
Pattern4Continue(Pattern4ContinuePlan),
|
|
/// Pattern1: Simple While Loop (Phase 286 P2.1)
|
|
Pattern1SimpleWhile(Pattern1SimpleWhilePlan),
|
|
/// Pattern9: Accumulator Const Loop (Phase 286 P2.3)
|
|
Pattern9AccumConstLoop(Pattern9AccumConstLoopPlan),
|
|
/// Pattern8: Boolean Predicate Scan (Phase 286 P2.4)
|
|
Pattern8BoolPredicateScan(Pattern8BoolPredicateScanPlan),
|
|
/// Pattern3: Loop with If-Phi (Phase 286 P2.6)
|
|
Pattern3IfPhi(Pattern3IfPhiPlan),
|
|
/// Pattern2: Loop with Conditional Break (Phase 286 P3.1)
|
|
Pattern2Break(Pattern2BreakPlan),
|
|
/// Pattern5: Infinite Loop with Early Exit (Phase 286 P3.2)
|
|
Pattern5InfiniteEarlyExit(Pattern5InfiniteEarlyExitPlan),
|
|
}
|
|
|
|
/// Phase 273 P0: Scan direction for forward/reverse scan
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub(in crate::mir::builder) enum ScanDirection {
|
|
/// Forward scan: i < s.length(), i = i + 1
|
|
Forward,
|
|
/// Reverse scan: i >= 0, i = i - 1
|
|
Reverse,
|
|
}
|
|
|
|
/// Phase 273 P0: Extracted structure for scan-with-init pattern
|
|
///
|
|
/// This structure contains all the information needed to lower an index_of-style loop.
|
|
/// Moved from pattern6_scan_with_init.rs for centralization.
|
|
#[derive(Debug, Clone)]
|
|
#[allow(dead_code)]
|
|
pub(in crate::mir::builder) struct ScanWithInitPlan {
|
|
/// Loop variable name (e.g., "i")
|
|
pub loop_var: String,
|
|
/// Haystack variable name (e.g., "s")
|
|
pub haystack: String,
|
|
/// Needle variable name (e.g., "ch")
|
|
pub needle: String,
|
|
/// Step literal (Phase 257: can be 1 forward or -1 reverse)
|
|
pub step_lit: i64,
|
|
/// Early return expression (P0: must be Variable(loop_var))
|
|
pub early_return_expr: ASTNode,
|
|
/// Not-found return literal (P0: must be -1)
|
|
pub not_found_return_lit: i64,
|
|
/// Scan direction (Phase 257 P0)
|
|
pub scan_direction: ScanDirection,
|
|
/// Phase 258 P0: True if dynamic needle (substr.length()), false if fixed (ch)
|
|
pub dynamic_needle: bool,
|
|
}
|
|
|
|
/// Phase 273 P2: Extracted structure for split-scan pattern
|
|
///
|
|
/// This structure contains all the information needed to lower a split-style loop.
|
|
#[derive(Debug, Clone)]
|
|
pub(in crate::mir::builder) struct SplitScanPlan {
|
|
/// Haystack variable name (e.g., "s")
|
|
pub s_var: String,
|
|
/// Separator variable name (e.g., "separator")
|
|
pub sep_var: String,
|
|
/// Accumulator variable name (e.g., "result", ArrayBox)
|
|
pub result_var: String,
|
|
/// Loop index variable name (e.g., "i")
|
|
pub i_var: String,
|
|
/// Segment start position variable name (e.g., "start")
|
|
pub start_var: String,
|
|
}
|
|
|
|
/// Phase 286 P2: Extracted structure for Pattern4 (Loop with Continue)
|
|
///
|
|
/// This structure contains all the information needed to lower a continue-style loop.
|
|
#[derive(Debug, Clone)]
|
|
pub(in crate::mir::builder) struct Pattern4ContinuePlan {
|
|
/// Loop variable name (e.g., "i")
|
|
pub loop_var: String,
|
|
/// Carrier variable names (e.g., ["sum"])
|
|
pub carrier_vars: Vec<String>,
|
|
/// Loop condition AST (e.g., `i < 6`)
|
|
pub condition: ASTNode,
|
|
/// Continue condition AST (e.g., `i == 0`)
|
|
pub continue_condition: ASTNode,
|
|
/// Carrier update expressions (var -> update AST)
|
|
pub carrier_updates: std::collections::BTreeMap<String, ASTNode>,
|
|
/// Loop increment expression (e.g., `i + 1`)
|
|
pub loop_increment: ASTNode,
|
|
}
|
|
|
|
/// Phase 286 P2.1: Extracted structure for Pattern1 (Simple While Loop)
|
|
///
|
|
/// This structure contains all the information needed to lower a simple while loop.
|
|
/// Pattern1 is the simplest loop: no break, no continue, no if-else-phi.
|
|
#[derive(Debug, Clone)]
|
|
pub(in crate::mir::builder) struct Pattern1SimpleWhilePlan {
|
|
/// Loop variable name (e.g., "i")
|
|
pub loop_var: String,
|
|
/// Loop condition AST (e.g., `i < 3`)
|
|
pub condition: ASTNode,
|
|
/// Loop increment expression AST (e.g., `i + 1`)
|
|
pub loop_increment: ASTNode,
|
|
}
|
|
|
|
/// Phase 286 P2.3: Extracted structure for Pattern9 (Accumulator Const Loop)
|
|
///
|
|
/// This structure contains all the information needed to lower an accumulator loop.
|
|
/// Pattern9 extends Pattern1 with an accumulator variable (e.g., sum = sum + 1).
|
|
#[derive(Debug, Clone)]
|
|
pub(in crate::mir::builder) struct Pattern9AccumConstLoopPlan {
|
|
/// Loop variable name (e.g., "i")
|
|
pub loop_var: String,
|
|
/// Accumulator variable name (e.g., "sum")
|
|
pub acc_var: String,
|
|
/// Loop condition AST (e.g., `i < 3`)
|
|
pub condition: ASTNode,
|
|
/// Accumulator update expression AST (e.g., `sum + 1` or `sum + i`)
|
|
pub acc_update: ASTNode,
|
|
/// Loop increment expression AST (e.g., `i + 1`)
|
|
pub loop_increment: ASTNode,
|
|
}
|
|
|
|
/// Phase 286 P2.4: Extracted structure for Pattern8 (BoolPredicateScan)
|
|
///
|
|
/// This structure contains all the information needed to lower a boolean predicate scan loop.
|
|
/// Pattern8 scans a string with a predicate check (e.g., is_digit) and returns false on first failure.
|
|
#[derive(Debug, Clone)]
|
|
pub(in crate::mir::builder) struct Pattern8BoolPredicateScanPlan {
|
|
/// Loop variable name (e.g., "i")
|
|
pub loop_var: String,
|
|
/// Haystack variable name (e.g., "s")
|
|
pub haystack: String,
|
|
/// Predicate receiver name (e.g., "me")
|
|
pub predicate_receiver: String,
|
|
/// Predicate method name (e.g., "is_digit")
|
|
pub predicate_method: String,
|
|
/// Loop condition AST (e.g., `i < s.length()`)
|
|
pub condition: ASTNode,
|
|
/// Loop increment literal (P0: must be 1)
|
|
pub step_lit: i64,
|
|
}
|
|
|
|
/// Phase 286 P2.6: Extracted structure for Pattern3 (Loop with If-Phi)
|
|
///
|
|
/// This structure contains all the information needed to lower an if-phi merge loop.
|
|
/// Pattern3 is a loop with conditional carrier update via if-else branching.
|
|
///
|
|
/// # Structure
|
|
/// ```text
|
|
/// loop(i < N) {
|
|
/// if (condition) {
|
|
/// carrier = then_update
|
|
/// } else {
|
|
/// carrier = else_update
|
|
/// }
|
|
/// i = i + step
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// # CFG Layout
|
|
/// ```text
|
|
/// preheader → header(PHI: i, carrier) → body(if_condition)
|
|
/// ↓ ↓
|
|
/// after then | else
|
|
/// ↓ ↓
|
|
/// merge(PHI: carrier)
|
|
/// ↓
|
|
/// step(i_next)
|
|
/// ↓
|
|
/// back-edge to header
|
|
/// ```
|
|
#[derive(Debug, Clone)]
|
|
pub(in crate::mir::builder) struct Pattern3IfPhiPlan {
|
|
/// Loop variable name (e.g., "i")
|
|
pub loop_var: String,
|
|
/// Carrier variable name (e.g., "sum")
|
|
pub carrier_var: String,
|
|
/// Loop condition AST (e.g., `i < 3`)
|
|
pub condition: ASTNode,
|
|
/// If condition AST (e.g., `i > 0`)
|
|
pub if_condition: ASTNode,
|
|
/// Then branch update AST (e.g., `sum + 1`)
|
|
pub then_update: ASTNode,
|
|
/// Else branch update AST (e.g., `sum + 0`)
|
|
pub else_update: ASTNode,
|
|
/// Loop increment expression AST (e.g., `i + 1`)
|
|
pub loop_increment: ASTNode,
|
|
}
|
|
|
|
/// Phase 286 P3.1: Extracted structure for Pattern2 (Loop with Conditional Break)
|
|
///
|
|
/// This structure contains all the information needed to lower a break-style loop.
|
|
///
|
|
/// Key insight: after_bb PHI merges break path and natural exit path carrier values.
|
|
/// - break path: carrier_break = carrier_update_in_break (if Some) or carrier_current (if None)
|
|
/// - natural exit: carrier_out = carrier_current (from header PHI)
|
|
/// - after_bb PHI: carrier_out = PHI(header: carrier_current, break_then: carrier_break)
|
|
///
|
|
/// 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)
|
|
/// ```
|
|
#[derive(Debug, Clone)]
|
|
pub(in crate::mir::builder) struct Pattern2BreakPlan {
|
|
/// Loop variable name (e.g., "i")
|
|
pub loop_var: String,
|
|
/// Carrier variable name (e.g., "sum", "result")
|
|
pub carrier_var: String,
|
|
/// Loop condition AST (e.g., `i < 3`)
|
|
pub loop_condition: ASTNode,
|
|
/// Break condition AST (e.g., `i == 1`)
|
|
pub break_condition: ASTNode,
|
|
/// Carrier update in break path (None if no update before break)
|
|
pub carrier_update_in_break: Option<ASTNode>,
|
|
/// Carrier update in normal body path (e.g., `sum + 1`)
|
|
pub carrier_update_in_body: ASTNode,
|
|
/// Loop increment expression AST (e.g., `i + 1`)
|
|
pub loop_increment: ASTNode,
|
|
}
|
|
|
|
/// Phase 286 P3.2: Exit kind for Pattern5 infinite loop
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub(in crate::mir::builder) enum Pattern5ExitKind {
|
|
/// Early return from function
|
|
Return,
|
|
/// Break from loop
|
|
Break,
|
|
}
|
|
|
|
/// Phase 286 P3.2: Extracted structure for Pattern5 (Infinite Loop with Early Exit)
|
|
///
|
|
/// This structure contains all the information needed to lower a loop(true) pattern
|
|
/// with early exit (return or break).
|
|
///
|
|
/// # PoC Subset
|
|
///
|
|
/// - `loop(true)` literal only (not `loop(1)` or truthy)
|
|
/// - Return version: `if (cond) { return <expr> }` + `i = i + 1`
|
|
/// - Break version: `if (cond) { break }` + `sum = sum + 1` + `i = i + 1` (carrier_update required)
|
|
///
|
|
/// # CFG Structure (Return version)
|
|
/// ```text
|
|
/// preheader → header(PHI: i_current) → body(exit_cond)
|
|
/// ↑ ↓
|
|
/// └───── step ←──────── else path
|
|
/// ↓
|
|
/// then path: CoreExitPlan::Return
|
|
/// ```
|
|
///
|
|
/// # CFG Structure (Break version)
|
|
/// ```text
|
|
/// preheader → header(PHI: i, carrier) → body(exit_cond)
|
|
/// ↑ ↓
|
|
/// └───── step ←────────── else path
|
|
/// ↓
|
|
/// then path → after_bb(PHI: carrier_out)
|
|
/// ```
|
|
#[derive(Debug, Clone)]
|
|
pub(in crate::mir::builder) struct Pattern5InfiniteEarlyExitPlan {
|
|
/// Loop variable name (e.g., "i")
|
|
pub loop_var: String,
|
|
/// Exit kind (Return or Break)
|
|
pub exit_kind: Pattern5ExitKind,
|
|
/// Exit condition AST (e.g., `i == 3`)
|
|
pub exit_condition: ASTNode,
|
|
/// Return value expression (Some for Return, None for Break)
|
|
pub exit_value: Option<ASTNode>,
|
|
/// Carrier variable name (Some for Break with carrier, None for Return)
|
|
pub carrier_var: Option<String>,
|
|
/// Carrier update expression (Some for Break, None for Return)
|
|
pub carrier_update: Option<ASTNode>,
|
|
/// Loop increment expression AST (e.g., `i + 1`)
|
|
pub loop_increment: ASTNode,
|
|
}
|
|
|
|
// ============================================================================
|
|
// CorePlan (固定語彙 - 構造ノードのみ)
|
|
// ============================================================================
|
|
|
|
/// Phase 273 P1: CorePlan - Fixed vocabulary plan (structure nodes only)
|
|
///
|
|
/// CorePlan expressions use **ValueId references only** (no String parsing).
|
|
/// This prevents "second language processor" from growing inside Lowerer.
|
|
#[derive(Debug, Clone)]
|
|
#[allow(dead_code)]
|
|
pub(in crate::mir::builder) enum CorePlan {
|
|
/// Sequence: execute plans in order
|
|
Seq(Vec<CorePlan>),
|
|
|
|
/// Loop with carriers (PHI variables)
|
|
Loop(CoreLoopPlan),
|
|
|
|
/// Conditional branching
|
|
If(CoreIfPlan),
|
|
|
|
/// Side effect (already lowered to ValueId)
|
|
Effect(CoreEffectPlan),
|
|
|
|
/// Control flow exit (Return/Break/Continue)
|
|
Exit(CoreExitPlan),
|
|
}
|
|
|
|
/// Phase 273 P2: PHI information for generalized CoreLoopPlan
|
|
#[derive(Debug, Clone)]
|
|
pub(in crate::mir::builder) struct CorePhiInfo {
|
|
/// Block where PHI is located
|
|
pub block: BasicBlockId,
|
|
/// Destination ValueId for PHI
|
|
pub dst: ValueId,
|
|
/// PHI inputs: (predecessor_block, value)
|
|
pub inputs: Vec<(BasicBlockId, ValueId)>,
|
|
/// Tag for debugging (e.g., "loop_carrier_i")
|
|
pub tag: String,
|
|
}
|
|
|
|
/// Phase 273 P3: Loop plan with generalized fields (SSOT)
|
|
///
|
|
/// All fields are now REQUIRED (Option removed for structural SSOT).
|
|
/// Legacy fields (header_effects, step_effects, carriers) have been removed.
|
|
#[derive(Debug, Clone)]
|
|
#[allow(dead_code)]
|
|
pub(in crate::mir::builder) struct CoreLoopPlan {
|
|
// === Block IDs (pre-allocated by Normalizer) ===
|
|
|
|
/// Preheader block (entry to loop)
|
|
pub preheader_bb: BasicBlockId,
|
|
|
|
/// Header block (loop condition check)
|
|
pub header_bb: BasicBlockId,
|
|
|
|
/// Body block (loop body start)
|
|
pub body_bb: BasicBlockId,
|
|
|
|
/// Step block (increment and back-edge)
|
|
pub step_bb: BasicBlockId,
|
|
|
|
/// After block (loop exit)
|
|
pub after_bb: BasicBlockId,
|
|
|
|
/// Found block (early exit on match, or same as after_bb for patterns without early exit)
|
|
pub found_bb: BasicBlockId,
|
|
|
|
// === Body (for lowerer body emission) ===
|
|
|
|
/// Body plans (emitted in body_bb, can contain If/Exit)
|
|
pub body: Vec<CorePlan>,
|
|
|
|
// === Loop control (ValueId references for Frag) ===
|
|
|
|
/// Loop condition (for header→body/after branch)
|
|
pub cond_loop: ValueId,
|
|
|
|
/// Match condition (for body→found/step branch)
|
|
pub cond_match: ValueId,
|
|
|
|
// === Phase 273 P3: Generalized fields (REQUIRED - SSOT) ===
|
|
|
|
/// Block-level effects (generalized for multiple blocks)
|
|
/// Order: SSOT - preheader, header, body, then, else, step (pattern dependent)
|
|
pub block_effects: Vec<(BasicBlockId, Vec<CoreEffectPlan>)>,
|
|
|
|
/// PHI information (generalized for multiple blocks)
|
|
pub phis: Vec<CorePhiInfo>,
|
|
|
|
/// Edge CFG fragment (generalized terminator structure)
|
|
pub frag: Frag,
|
|
|
|
/// Final values for variable_map update (after loop exit)
|
|
pub final_values: Vec<(String, ValueId)>,
|
|
}
|
|
|
|
// Phase 273 P3: CoreCarrierInfo removed (replaced by CorePhiInfo)
|
|
|
|
/// Phase 273 P1: Conditional plan
|
|
#[derive(Debug, Clone)]
|
|
pub(in crate::mir::builder) struct CoreIfPlan {
|
|
/// Condition (ValueId reference, not String!)
|
|
pub condition: ValueId,
|
|
|
|
/// Then branch plans
|
|
pub then_plans: Vec<CorePlan>,
|
|
|
|
/// Else branch plans (optional)
|
|
pub else_plans: Option<Vec<CorePlan>>,
|
|
}
|
|
|
|
/// Phase 273 P1: Effect plan (side effects already lowered to ValueId)
|
|
///
|
|
/// Effect vocabulary is minimal (scan-specific variants forbidden):
|
|
/// - MethodCall, BinOp, Compare, Const only
|
|
///
|
|
/// Phase 273 P2: MethodCall now supports:
|
|
/// - dst: Option<ValueId> for void methods (e.g., push)
|
|
/// - effects: EffectMask for side effects (e.g., MUT for push)
|
|
#[derive(Debug, Clone)]
|
|
pub(in crate::mir::builder) enum CoreEffectPlan {
|
|
/// Method call (args are ValueIds, not Strings!)
|
|
///
|
|
/// Phase 273 P2: dst is Option for void methods, effects for side effects
|
|
MethodCall {
|
|
dst: Option<ValueId>, // P2: Option for void methods (push)
|
|
object: ValueId,
|
|
method: String, // Method name only (OK as String)
|
|
args: Vec<ValueId>,
|
|
effects: EffectMask, // P2: Side effect mask (PURE+Io or MUT)
|
|
},
|
|
|
|
/// Binary operation
|
|
BinOp {
|
|
dst: ValueId,
|
|
lhs: ValueId,
|
|
op: BinaryOp,
|
|
rhs: ValueId,
|
|
},
|
|
|
|
/// Comparison
|
|
Compare {
|
|
dst: ValueId,
|
|
lhs: ValueId,
|
|
op: CompareOp,
|
|
rhs: ValueId,
|
|
},
|
|
|
|
/// Constant
|
|
Const { dst: ValueId, value: ConstValue },
|
|
}
|
|
|
|
/// Phase 273 P1: Exit plan (control flow exit)
|
|
#[derive(Debug, Clone)]
|
|
#[allow(dead_code)]
|
|
pub(in crate::mir::builder) enum CoreExitPlan {
|
|
/// Return with optional value
|
|
Return(Option<ValueId>),
|
|
|
|
/// Break from loop
|
|
Break,
|
|
|
|
/// Continue to next iteration
|
|
Continue,
|
|
}
|