Phase 33-3: If/PHI MIR pattern matching + Select lowering (minimal patterns)

Implementation:
- Implement MIR pattern matching in if_select.rs (simple/local patterns)
- Add try_lower_if_to_joinir() entry point in lowering/mod.rs
- Create comprehensive integration tests (4/4 passing)

Pattern Support:
- Simple pattern: if cond { return 1 } else { return 2 }
  - Both blocks must have Return only (no instructions)
- Local pattern: if cond { x = a } else { x = b }; return x
  - Each branch has exactly 1 Copy instruction
  - Both branches jump to same merge block
  - Merge block Returns the assigned variable

Safety Mechanisms:
- Dev toggle: NYASH_JOINIR_IF_SELECT=1 required
- Function name filter: Only IfSelectTest.* functions
- Fallback: Returns None on pattern mismatch
- Zero breaking changes: Existing if_phi path untouched

Tests (4/4 PASS):
- test_if_select_simple_pattern
- test_if_select_local_pattern
- test_if_select_disabled_by_default
- test_if_select_wrong_function_name

Files:
- Modified: src/mir/join_ir/lowering/if_select.rs (pattern matching)
- Modified: src/mir/join_ir/lowering/mod.rs (entry point)
- New: src/tests/mir_joinir_if_select.rs (integration tests)
- Modified: src/tests/mod.rs (module registration)

Phase 33-3.2/3.3 (legacy removal) pending

🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-27 03:28:32 +09:00
parent cbb9c0ade9
commit 5cfb0e1d5b
4 changed files with 493 additions and 19 deletions

View File

@ -3,44 +3,227 @@
//! 最小の if/else副作用なし、単純な値選択を JoinInst::Select に変換する。
use crate::mir::join_ir::JoinInst;
use crate::mir::{BasicBlockId, MirFunction};
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
pub struct IfSelectLowerer {
debug: bool,
}
/// 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: bool) -> Self {
Self { debug }
}
/// if/else が Select に lowering できるかチェック
pub fn can_lower_to_select(&self, _func: &MirFunction, _if_block_id: BasicBlockId) -> bool {
// パターン:
// 1. if_block に Branch がある
// 2. then/else ブロックが存在
// 3. merge ブロックに 1 つの PHI がある
// 4. PHI の incoming が then/else から来ている
// 実装: Phase 33-2 では保守的に false を返す(フォールバック)
// Phase 33-3 で実装予定
if self.debug {
eprintln!("[joinir/if_select] can_lower_to_select: Phase 33-2 stub (always false)");
}
false
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,
func: &MirFunction,
if_block_id: BasicBlockId,
) -> Option<JoinInst> {
// 実装: Phase 33-2 では None を返す(フォールバック)
// Phase 33-3 で実装予定
let pattern = self.find_if_pattern(func, if_block_id)?;
if self.debug {
eprintln!("[joinir/if_select] lower_if_to_select: Phase 33-2 stub (always None)");
eprintln!(
"[IfSelectLowerer] lowering {:?} pattern to Select",
pattern.pattern_type
);
}
// Select 命令を生成
let dst = pattern.dst.unwrap_or(pattern.then_val);
Some(JoinInst::Select {
dst,
cond: pattern.cond,
then_val: pattern.then_val,
else_val: pattern.else_val,
})
}
/// 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)?;
// 3. simple パターンのチェック
if let Some(pattern) = self.try_match_simple_pattern(&branch, then_block, else_block) {
if self.debug {
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)
{
if self.debug {
eprintln!("[IfSelectLowerer] matched local pattern");
}
return Some(pattern);
}
if self.debug {
eprintln!("[IfSelectLowerer] no pattern matched");
}
None
}
/// simple パターン: if cond { return 1 } else { return 2 }
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,
};
// 両方のブロックが命令を持たないReturn のみ)ことを確認
if !then_block.instructions.is_empty() || !else_block.instructions.is_empty() {
return None;
}
Some(IfPattern {
pattern_type: IfPatternType::Simple,
cond: branch.cond,
then_val,
else_val,
dst: None,
})
}
/// local パターン: if cond { x = a } else { x = b }; return x
fn try_match_local_pattern(
&self,
func: &MirFunction,
branch: &IfBranch,
then_block: &crate::mir::BasicBlock,
else_block: &crate::mir::BasicBlock,
) -> Option<IfPattern> {
// then ブロックが「1命令 + Jump」の形か確認
if then_block.instructions.len() != 1 {
return None;
}
// then ブロックの命令が代入Copyか確認
let (dst_then, val_then) = match &then_block.instructions[0] {
MirInstruction::Copy { dst, src } => (*dst, *src),
_ => return None,
};
// then ブロックが Jump で終わるか確認
let merge_block_id = match then_block.terminator.as_ref()? {
MirInstruction::Jump { target } => *target,
_ => return None,
};
// else ブロックも同じ構造か確認
if else_block.instructions.len() != 1 {
return None;
}
let (dst_else, val_else) = match &else_block.instructions[0] {
MirInstruction::Copy { dst, src } => (*dst, *src),
_ => 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)?;
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),
})
}
}

View File

@ -41,3 +41,61 @@ pub use skip_ws::lower_skip_ws_to_joinir;
pub use stage1_using_resolver::lower_stage1_usingresolver_to_joinir;
pub use stageb_body::lower_stageb_body_to_joinir;
pub use stageb_funcscanner::lower_stageb_funcscanner_to_joinir;
// Phase 33: If/Else → Select lowering entry point
use crate::mir::join_ir::JoinInst;
use crate::mir::{BasicBlockId, MirFunction};
/// Phase 33-3: Try to lower if/else to JoinIR Select instruction
///
/// Scope:
/// - Only applies to functions matching "IfSelectTest.*"
/// - Requires NYASH_JOINIR_IF_SELECT=1 environment variable
/// - Falls back to traditional if_phi on pattern mismatch
///
/// Returns Some(JoinInst::Select) if pattern matched, None otherwise.
pub fn try_lower_if_to_joinir(
func: &MirFunction,
block_id: BasicBlockId,
debug: bool,
) -> Option<JoinInst> {
// dev トグルチェック
if !crate::config::env::joinir_if_select_enabled() {
return None;
}
// 関数名で制限IfSelectTest.* のみ)
if !func.signature.name.starts_with("IfSelectTest.") {
if debug {
eprintln!(
"[try_lower_if_to_joinir] skipping non-test function: {}",
func.signature.name
);
}
return None;
}
// if_select lowering を試行
let lowerer = if_select::IfSelectLowerer::new(debug);
if !lowerer.can_lower_to_select(func, block_id) {
if debug {
eprintln!(
"[try_lower_if_to_joinir] pattern not matched for {}",
func.signature.name
);
}
return None;
}
let result = lowerer.lower_if_to_select(func, block_id);
if result.is_some() && debug {
eprintln!(
"[try_lower_if_to_joinir] if_select lowering used for {}",
func.signature.name
);
}
result
}