Extended PatternPipelineContext and CarrierUpdateInfo for Pattern 3 AST-based generalization. Changes: 1. PatternPipelineContext: - Added loop_condition: Option<ASTNode> - Added loop_body: Option<Vec<ASTNode>> - Added loop_update_summary: Option<LoopUpdateSummary> - Updated build_pattern_context() for Pattern 3 2. CarrierUpdateInfo: - Added then_expr: Option<ASTNode> - Added else_expr: Option<ASTNode> - Updated analyze_loop_updates() with None defaults Status: Phase 213-2 Steps 2-2 & 2-3 complete Next: Create Pattern3IfAnalyzer to extract if statement and populate update summary
377 lines
13 KiB
Rust
377 lines
13 KiB
Rust
//! Phase 33: If/Else の Select 命令への lowering
|
||
//!
|
||
//! 最小の if/else(副作用なし、単純な値選択)を JoinInst::Select に変換する。
|
||
//!
|
||
//! ## 責務分離(Phase 33-9.1)
|
||
//!
|
||
//! **IfSelectLowerer の責務**:
|
||
//! - 単純 if/else(副作用なし、単一変数)を Select 命令に変換する
|
||
//!
|
||
//! **非責務**:
|
||
//! - Loop の PHI には触らない(Loop lowering の責務)
|
||
|
||
use crate::mir::join_ir::JoinInst;
|
||
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
|
||
// Phase 63-2: Type hint inference from MIR
|
||
|
||
// Phase 61-1: If-in-loop context support
|
||
use super::if_phi_context::IfPhiContext;
|
||
|
||
pub struct IfSelectLowerer {
|
||
debug_level: u8,
|
||
// Phase 61-1: If-in-loop context (None = Pure If)
|
||
#[allow(dead_code)]
|
||
context: Option<IfPhiContext>,
|
||
}
|
||
|
||
/// If/Else パターンの分類
|
||
#[derive(Debug, Clone, Copy)]
|
||
enum IfPatternType {
|
||
/// Simple pattern: if cond { return 1 } else { return 2 }
|
||
Simple,
|
||
/// Local pattern: if cond { x = a } else { x = b }; return x
|
||
Local,
|
||
}
|
||
|
||
/// 検出された If/Else パターン情報
|
||
#[derive(Debug, Clone)]
|
||
struct IfPattern {
|
||
pattern_type: IfPatternType,
|
||
cond: ValueId,
|
||
then_val: ValueId,
|
||
else_val: ValueId,
|
||
dst: Option<ValueId>,
|
||
}
|
||
|
||
/// Branch 命令の情報
|
||
#[derive(Debug, Clone)]
|
||
struct IfBranch {
|
||
cond: ValueId,
|
||
then_block: BasicBlockId,
|
||
else_block: BasicBlockId,
|
||
}
|
||
|
||
impl IfSelectLowerer {
|
||
pub fn new(debug_level: u8) -> Self {
|
||
Self {
|
||
debug_level,
|
||
context: None, // Phase 61-1: デフォルトは Pure If
|
||
}
|
||
}
|
||
|
||
/// Phase 33-8: debug-level backward compat wrapper
|
||
#[allow(dead_code)]
|
||
pub fn with_debug(debug: bool) -> Self {
|
||
Self {
|
||
debug_level: if debug { 1 } else { 0 },
|
||
context: None, // Phase 61-1: デフォルトは Pure If
|
||
}
|
||
}
|
||
|
||
/// Phase 61-1: If-in-loop 用コンストラクタ
|
||
///
|
||
/// # Arguments
|
||
///
|
||
/// * `debug_level` - デバッグログレベル (0-3)
|
||
/// * `context` - If-in-loop コンテキスト(carrier_names 情報を含む)
|
||
///
|
||
/// # Example
|
||
///
|
||
/// ```ignore
|
||
/// let context = IfPhiContext::for_loop_body(carrier_names);
|
||
/// let lowerer = IfSelectLowerer::with_context(debug_level, context);
|
||
/// ```
|
||
pub fn with_context(debug_level: u8, context: IfPhiContext) -> Self {
|
||
Self {
|
||
debug_level,
|
||
context: Some(context),
|
||
}
|
||
}
|
||
|
||
/// if/else が Select に lowering できるかチェック
|
||
pub fn can_lower_to_select(&self, func: &MirFunction, if_block_id: BasicBlockId) -> bool {
|
||
self.find_if_pattern(func, if_block_id).is_some()
|
||
}
|
||
|
||
/// if/else を Select に変換
|
||
pub fn lower_if_to_select(
|
||
&self,
|
||
func: &MirFunction,
|
||
if_block_id: BasicBlockId,
|
||
) -> Option<JoinInst> {
|
||
let pattern = self.find_if_pattern(func, if_block_id)?;
|
||
|
||
// Phase 33-8: Level 1 - Basic lowering info
|
||
if self.debug_level >= 1 {
|
||
eprintln!(
|
||
"[IfSelectLowerer] ✅ lowering {:?} pattern to Select",
|
||
pattern.pattern_type
|
||
);
|
||
}
|
||
|
||
// Phase 33-8: Level 3 - Full pattern details
|
||
if self.debug_level >= 3 {
|
||
eprintln!("[IfSelectLowerer] cond: {:?}", pattern.cond);
|
||
eprintln!("[IfSelectLowerer] then_val: {:?}", pattern.then_val);
|
||
eprintln!("[IfSelectLowerer] else_val: {:?}", pattern.else_val);
|
||
eprintln!("[IfSelectLowerer] dst: {:?}", pattern.dst);
|
||
}
|
||
|
||
// Select 命令を生成
|
||
let dst = pattern.dst.unwrap_or(pattern.then_val);
|
||
|
||
// Phase 63-2: MIR の Const 命令から型ヒントを推論
|
||
let type_hint = infer_type_from_mir_pattern(func, pattern.then_val)
|
||
.or_else(|| infer_type_from_mir_pattern(func, pattern.else_val));
|
||
|
||
Some(JoinInst::Select {
|
||
dst,
|
||
cond: pattern.cond,
|
||
then_val: pattern.then_val,
|
||
else_val: pattern.else_val,
|
||
type_hint, // Phase 63-2: Const 命令から推論した型(Integer/Bool/String など)
|
||
})
|
||
}
|
||
|
||
/// MIR 関数から if/else パターンを探す
|
||
fn find_if_pattern(&self, func: &MirFunction, block_id: BasicBlockId) -> Option<IfPattern> {
|
||
// 1. Block が Branch 命令で終わっているか確認
|
||
let block = func.blocks.get(&block_id)?;
|
||
let branch = match block.terminator.as_ref()? {
|
||
MirInstruction::Branch {
|
||
condition,
|
||
then_bb,
|
||
else_bb,
|
||
} => IfBranch {
|
||
cond: *condition,
|
||
then_block: *then_bb,
|
||
else_block: *else_bb,
|
||
},
|
||
_ => return None,
|
||
};
|
||
|
||
// 2. then/else ブロックの構造を確認
|
||
let then_block = func.blocks.get(&branch.then_block)?;
|
||
let else_block = func.blocks.get(&branch.else_block)?;
|
||
|
||
// Phase 33-10: PHI早期チェック(パターンマッチング前)
|
||
// JoinIRは「PHI生成器」であり「PHI変換器」ではない
|
||
// then/elseがJumpで終わる場合、merge blockにPHI命令があるか早期確認
|
||
if let Some(merge_block_id) =
|
||
self.get_merge_block_if_jump_pattern(&branch, then_block, else_block)
|
||
{
|
||
let merge_block = func.blocks.get(&merge_block_id)?;
|
||
if merge_block
|
||
.instructions
|
||
.iter()
|
||
.any(|inst| matches!(inst, MirInstruction::Phi { .. }))
|
||
{
|
||
if self.debug_level >= 2 {
|
||
eprintln!("[IfSelectLowerer] ⏭️ PHI already exists in merge block, skipping");
|
||
}
|
||
return None;
|
||
}
|
||
}
|
||
|
||
// 3. simple パターンのチェック
|
||
if let Some(pattern) = self.try_match_simple_pattern(&branch, then_block, else_block) {
|
||
// Phase 33-8: Level 2 - Pattern matching details
|
||
if self.debug_level >= 2 {
|
||
eprintln!("[IfSelectLowerer] ✅ matched simple pattern");
|
||
}
|
||
return Some(pattern);
|
||
}
|
||
|
||
// 4. local パターンのチェック
|
||
if let Some(pattern) = self.try_match_local_pattern(func, &branch, then_block, else_block) {
|
||
// Phase 33-8: Level 2 - Pattern matching details
|
||
if self.debug_level >= 2 {
|
||
eprintln!("[IfSelectLowerer] ✅ matched local pattern");
|
||
}
|
||
return Some(pattern);
|
||
}
|
||
|
||
// Phase 33-8: Level 2 - Pattern matching details
|
||
if self.debug_level >= 2 {
|
||
eprintln!("[IfSelectLowerer] ❌ no pattern matched");
|
||
}
|
||
None
|
||
}
|
||
|
||
/// simple パターン: if cond { return 1 } else { return 2 }
|
||
///
|
||
/// Phase 33-9.2: 実用MIR対応 - 副作用なし命令(Const/Copy)を許容
|
||
/// - 旧: Return のみ(instructions empty)
|
||
/// - 新: Const/Copy → Return を許容(実MIRパターン)
|
||
fn try_match_simple_pattern(
|
||
&self,
|
||
branch: &IfBranch,
|
||
then_block: &crate::mir::BasicBlock,
|
||
else_block: &crate::mir::BasicBlock,
|
||
) -> Option<IfPattern> {
|
||
// then ブロックが Return だけか確認
|
||
let then_val = match then_block.terminator.as_ref()? {
|
||
MirInstruction::Return { value: Some(v) } => *v,
|
||
_ => return None,
|
||
};
|
||
|
||
// else ブロックが Return だけか確認
|
||
let else_val = match else_block.terminator.as_ref()? {
|
||
MirInstruction::Return { value: Some(v) } => *v,
|
||
_ => return None,
|
||
};
|
||
|
||
// Phase 33-9.2: 副作用なし命令(Const/Copy)のみを許容
|
||
// - ユニットテスト(empty)も通過(空配列 → all() = true)
|
||
// - 実用MIR(const + ret)も通過
|
||
if !self.is_side_effect_free(&then_block.instructions)
|
||
|| !self.is_side_effect_free(&else_block.instructions)
|
||
{
|
||
return None;
|
||
}
|
||
|
||
Some(IfPattern {
|
||
pattern_type: IfPatternType::Simple,
|
||
cond: branch.cond,
|
||
then_val,
|
||
else_val,
|
||
dst: None,
|
||
})
|
||
}
|
||
|
||
/// Phase 33-9.2: 副作用なし命令判定ヘルパー
|
||
///
|
||
/// Const/Copy のみを許容(分岐・call・書き込み等は除外)
|
||
fn is_side_effect_free(&self, instructions: &[MirInstruction]) -> bool {
|
||
instructions.iter().all(|inst| {
|
||
matches!(
|
||
inst,
|
||
MirInstruction::Const { .. } | MirInstruction::Copy { .. }
|
||
)
|
||
})
|
||
}
|
||
|
||
/// Phase 33-10: Jump pattern 検出ヘルパー
|
||
///
|
||
/// then/else 両方が Jump で終わり、同じ merge block に飛んでいる場合、
|
||
/// その merge block IDを返す
|
||
fn get_merge_block_if_jump_pattern(
|
||
&self,
|
||
_branch: &IfBranch,
|
||
then_block: &crate::mir::BasicBlock,
|
||
else_block: &crate::mir::BasicBlock,
|
||
) -> Option<BasicBlockId> {
|
||
// then が Jump で終わるか確認
|
||
let then_target = match then_block.terminator.as_ref()? {
|
||
MirInstruction::Jump { target } => *target,
|
||
_ => return None,
|
||
};
|
||
|
||
// else が Jump で終わるか確認
|
||
let else_target = match else_block.terminator.as_ref()? {
|
||
MirInstruction::Jump { target } => *target,
|
||
_ => return None,
|
||
};
|
||
|
||
// 両方が同じ merge block に飛んでいるか確認
|
||
if then_target == else_target {
|
||
Some(then_target)
|
||
} else {
|
||
None
|
||
}
|
||
}
|
||
|
||
/// local パターン: if cond { x = a } else { x = b }; return x
|
||
///
|
||
/// Phase 33-10: 実用MIR対応 - Const命令を許容
|
||
/// - 旧: Copy命令のみ(ユニットテスト想定)
|
||
/// - 新: Const/Copy命令を許容(実MIR対応、Simple patternと同じ修正)
|
||
fn try_match_local_pattern(
|
||
&self,
|
||
func: &MirFunction,
|
||
branch: &IfBranch,
|
||
then_block: &crate::mir::BasicBlock,
|
||
else_block: &crate::mir::BasicBlock,
|
||
) -> Option<IfPattern> {
|
||
// Phase 33-10: 副作用なし命令のみを許容
|
||
if !self.is_side_effect_free(&then_block.instructions) {
|
||
return None;
|
||
}
|
||
|
||
// then ブロックの最後の値を取得
|
||
// Phase 33-10: Const命令も許容(実MIR対応)
|
||
let (dst_then, val_then) = if then_block.instructions.len() == 1 {
|
||
match &then_block.instructions[0] {
|
||
MirInstruction::Copy { dst, src } => (*dst, *src),
|
||
MirInstruction::Const { dst, .. } => (*dst, *dst), // Constの場合、dst自身が値
|
||
_ => return None,
|
||
}
|
||
} else {
|
||
return None;
|
||
};
|
||
|
||
// then ブロックが Jump で終わるか確認
|
||
let merge_block_id = match then_block.terminator.as_ref()? {
|
||
MirInstruction::Jump { target } => *target,
|
||
_ => return None,
|
||
};
|
||
|
||
// Phase 33-10: else ブロックも副作用なし命令のみを許容
|
||
if !self.is_side_effect_free(&else_block.instructions) {
|
||
return None;
|
||
}
|
||
|
||
// else ブロックの最後の値を取得
|
||
let (dst_else, val_else) = if else_block.instructions.len() == 1 {
|
||
match &else_block.instructions[0] {
|
||
MirInstruction::Copy { dst, src } => (*dst, *src),
|
||
MirInstruction::Const { dst, .. } => (*dst, *dst), // Constの場合、dst自身が値
|
||
_ => return None,
|
||
}
|
||
} else {
|
||
return None;
|
||
};
|
||
|
||
// 代入先が同じ変数か確認
|
||
if dst_then != dst_else {
|
||
return None;
|
||
}
|
||
|
||
// else ブロックも同じ merge ブロックに Jump するか確認
|
||
let else_merge = match else_block.terminator.as_ref()? {
|
||
MirInstruction::Jump { target } => *target,
|
||
_ => return None,
|
||
};
|
||
|
||
if merge_block_id != else_merge {
|
||
return None;
|
||
}
|
||
|
||
// merge ブロックが「return dst」だけか確認
|
||
let merge_block = func.blocks.get(&merge_block_id)?;
|
||
// Phase 33-10: PHIチェックは find_if_pattern() で早期実行済み
|
||
|
||
match merge_block.terminator.as_ref()? {
|
||
MirInstruction::Return { value: Some(v) } if *v == dst_then => {
|
||
// OK
|
||
}
|
||
_ => return None,
|
||
}
|
||
|
||
if !merge_block.instructions.is_empty() {
|
||
return None;
|
||
}
|
||
|
||
Some(IfPattern {
|
||
pattern_type: IfPatternType::Local,
|
||
cond: branch.cond,
|
||
then_val: val_then,
|
||
else_val: val_else,
|
||
dst: Some(dst_then),
|
||
})
|
||
}
|
||
}
|
||
|
||
// Phase 185: infer_type_from_mir_pattern() moved to common.rs
|
||
use super::common::infer_type_from_mir_pattern;
|