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

@ -0,0 +1,232 @@
//! Phase 33-3: If/Else → Select lowering integration tests
//!
//! Tests the pattern matching and lowering of if/else to JoinIR Select instruction.
#[cfg(test)]
mod tests {
use crate::mir::join_ir::lowering::try_lower_if_to_joinir;
use crate::mir::join_ir::JoinInst;
use crate::mir::{
BasicBlock, BasicBlockId, MirFunction, MirInstruction, MirModule, ValueId,
};
use std::collections::BTreeMap;
/// Helper to create a simple if/else function matching the "simple" pattern
fn create_simple_pattern_mir() -> MirFunction {
let mut blocks = BTreeMap::new();
// Entry block (bb0): branch on cond
let mut entry = BasicBlock::new(BasicBlockId::new(0));
entry.terminator = Some(MirInstruction::Branch {
condition: ValueId(0), // cond parameter
then_bb: BasicBlockId::new(1),
else_bb: BasicBlockId::new(2),
});
blocks.insert(BasicBlockId::new(0), entry);
// Then block (bb1): return 10
// NOTE: Pattern matcher expects empty blocks (Return only)
let mut then_block = BasicBlock::new(BasicBlockId::new(1));
then_block.terminator = Some(MirInstruction::Return {
value: Some(ValueId(1)), // Assumes ValueId(1) is const 10
});
blocks.insert(BasicBlockId::new(1), then_block);
// Else block (bb2): return 20
// NOTE: Pattern matcher expects empty blocks (Return only)
let mut else_block = BasicBlock::new(BasicBlockId::new(2));
else_block.terminator = Some(MirInstruction::Return {
value: Some(ValueId(2)), // Assumes ValueId(2) is const 20
});
blocks.insert(BasicBlockId::new(2), else_block);
use crate::mir::{EffectMask, MirType};
use crate::mir::function::FunctionMetadata;
use std::collections::HashMap;
MirFunction {
signature: crate::mir::FunctionSignature {
name: "IfSelectTest.test/1".to_string(),
params: vec![MirType::Unknown],
return_type: MirType::Integer,
effects: EffectMask::PURE,
},
entry_block: BasicBlockId::new(0),
blocks: blocks.into_iter().collect(),
locals: vec![],
params: vec![ValueId(0)],
next_value_id: 3,
metadata: FunctionMetadata::default(),
}
}
/// Helper to create a local pattern function
fn create_local_pattern_mir() -> MirFunction {
let mut blocks = BTreeMap::new();
// Entry block (bb0): branch on cond
let mut entry = BasicBlock::new(BasicBlockId::new(0));
entry.terminator = Some(MirInstruction::Branch {
condition: ValueId(0), // cond
then_bb: BasicBlockId::new(1),
else_bb: BasicBlockId::new(2),
});
blocks.insert(BasicBlockId::new(0), entry);
// Then block (bb1): x = 100; jump merge
// NOTE: Pattern matcher expects exactly 1 Copy instruction
let mut then_block = BasicBlock::new(BasicBlockId::new(1));
then_block.instructions.push(MirInstruction::Copy {
dst: ValueId(3), // x
src: ValueId(10), // Assumes ValueId(10) is const 100
});
then_block.terminator = Some(MirInstruction::Jump {
target: BasicBlockId::new(3),
});
blocks.insert(BasicBlockId::new(1), then_block);
// Else block (bb2): x = 200; jump merge
// NOTE: Pattern matcher expects exactly 1 Copy instruction
let mut else_block = BasicBlock::new(BasicBlockId::new(2));
else_block.instructions.push(MirInstruction::Copy {
dst: ValueId(3), // x
src: ValueId(20), // Assumes ValueId(20) is const 200
});
else_block.terminator = Some(MirInstruction::Jump {
target: BasicBlockId::new(3),
});
blocks.insert(BasicBlockId::new(2), else_block);
// Merge block (bb3): return x
let mut merge_block = BasicBlock::new(BasicBlockId::new(3));
merge_block.terminator = Some(MirInstruction::Return {
value: Some(ValueId(3)),
});
blocks.insert(BasicBlockId::new(3), merge_block);
use crate::mir::{EffectMask, MirType};
use crate::mir::function::FunctionMetadata;
use std::collections::HashMap;
MirFunction {
signature: crate::mir::FunctionSignature {
name: "IfSelectTest.main/0".to_string(),
params: vec![],
return_type: MirType::Integer,
effects: EffectMask::PURE,
},
entry_block: BasicBlockId::new(0),
blocks: blocks.into_iter().collect(),
locals: vec![],
params: vec![],
next_value_id: 21,
metadata: FunctionMetadata::default(),
}
}
#[test]
fn test_if_select_simple_pattern() {
// Set environment variable for this test
std::env::set_var("NYASH_JOINIR_IF_SELECT", "1");
let func = create_simple_pattern_mir();
let entry_block = func.entry_block;
// Try to lower to JoinIR
let result = try_lower_if_to_joinir(&func, entry_block, true);
assert!(
result.is_some(),
"Expected simple pattern to be lowered to Select"
);
if let Some(JoinInst::Select {
dst,
cond,
then_val,
else_val,
}) = result
{
eprintln!("✅ Simple pattern successfully lowered to Select");
eprintln!(" dst: {:?}, cond: {:?}, then: {:?}, else: {:?}", dst, cond, then_val, else_val);
} else {
panic!("Expected JoinInst::Select, got {:?}", result);
}
// Clean up
std::env::remove_var("NYASH_JOINIR_IF_SELECT");
}
#[test]
fn test_if_select_local_pattern() {
std::env::set_var("NYASH_JOINIR_IF_SELECT", "1");
let func = create_local_pattern_mir();
let entry_block = func.entry_block;
// Try to lower to JoinIR
let result = try_lower_if_to_joinir(&func, entry_block, true);
assert!(
result.is_some(),
"Expected local pattern to be lowered to Select"
);
if let Some(JoinInst::Select {
dst,
cond,
then_val,
else_val,
}) = result
{
eprintln!("✅ Local pattern successfully lowered to Select");
eprintln!(" dst: {:?}, cond: {:?}, then: {:?}, else: {:?}", dst, cond, then_val, else_val);
} else {
panic!("Expected JoinInst::Select, got {:?}", result);
}
std::env::remove_var("NYASH_JOINIR_IF_SELECT");
}
#[test]
fn test_if_select_disabled_by_default() {
// Ensure environment variable is NOT set
std::env::remove_var("NYASH_JOINIR_IF_SELECT");
let func = create_simple_pattern_mir();
let entry_block = func.entry_block;
// Should return None when disabled
let result = try_lower_if_to_joinir(&func, entry_block, false);
assert!(
result.is_none(),
"Expected None when NYASH_JOINIR_IF_SELECT is not set"
);
eprintln!("✅ If/Select lowering correctly disabled by default");
}
#[test]
fn test_if_select_wrong_function_name() {
std::env::set_var("NYASH_JOINIR_IF_SELECT", "1");
// Create function with wrong name (not IfSelectTest.*)
let mut func = create_simple_pattern_mir();
func.signature.name = "WrongName.test/1".to_string();
let entry_block = func.entry_block;
// Should return None for non-IfSelectTest functions
let result = try_lower_if_to_joinir(&func, entry_block, true);
assert!(
result.is_none(),
"Expected None for non-IfSelectTest functions"
);
eprintln!("✅ Function name filter working correctly");
std::env::remove_var("NYASH_JOINIR_IF_SELECT");
}
}

View File

@ -20,6 +20,7 @@ pub mod mir_funcscanner_ssa;
pub mod mir_funcscanner_trim_min;
pub mod mir_joinir_funcscanner_append_defs; // Phase 27.14: FuncScannerBox._append_defs JoinIR変換
pub mod mir_joinir_funcscanner_trim; // Phase 27.1: FuncScannerBox.trim JoinIR変換
pub mod mir_joinir_if_select; // Phase 33-3: If/Else → Select lowering tests
pub mod mir_joinir_min; // Phase 26-H: JoinIR型定義妥当性確認
pub mod mir_joinir_skip_ws; // Phase 27.0: minimal_ssa_skip_ws JoinIR変換
pub mod mir_joinir_stage1_using_resolver_min; // Phase 27.12: Stage1UsingResolverBox.resolve_for_source JoinIR変換