feat(joinir): Phase 33-7 IfMerge lowering for multiple-variable PHI

Implements IfMerge instruction lowering to support multiple variables
merging from if/else branches (Phase 33-7: return pattern only).

## Changes

- Add src/mir/join_ir/lowering/if_merge.rs (232 lines)
  - IfMergeLowerer with pattern matching for common variables
  - extract_written_vars() / find_written_value() helpers
  - Phase 33-7 constraint: return pattern only (k_next=None)

- Update src/mir/join_ir/lowering/mod.rs
  - Unified entry point: try_lower_if_to_joinir()
  - Priority: IfMerge → Select → if_phi fallback
  - Add IfMergeTest.* to whitelist

- Add unit tests in src/tests/mir_joinir_if_select.rs
  - test_if_merge_simple_pattern (2 variables)
  - test_if_merge_multiple_pattern (3 variables)
  - All 7/7 tests PASS 

- Add reference test cases
  - apps/tests/joinir_if_merge_simple.hako (2-var pattern)
  - apps/tests/joinir_if_merge_multiple.hako (3-var pattern)

## Test Results

 Simple pattern (2 vars): merges=2, k_next=None
 Multiple pattern (3 vars): merges=3, k_next=None
 test result: ok. 7 passed; 0 failed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-27 08:18:09 +09:00
parent 2d379692e3
commit 5b7818f5c9
5 changed files with 602 additions and 9 deletions

View File

@ -19,6 +19,7 @@ pub mod exit_args_resolver;
pub mod funcscanner_append_defs;
pub mod funcscanner_trim;
pub mod generic_case_a;
pub mod if_merge; // Phase 33-7
pub mod if_select; // Phase 33
pub mod loop_form_intake;
pub mod loop_scope_shape;
@ -46,29 +47,35 @@ pub use stageb_funcscanner::lower_stageb_funcscanner_to_joinir;
use crate::mir::join_ir::JoinInst;
use crate::mir::{BasicBlockId, MirFunction};
/// Phase 33-3: Try to lower if/else to JoinIR Select instruction
/// Phase 33-7: Try to lower if/else to JoinIR Select/IfMerge instruction
///
/// Scope:
/// - Only applies to whitelisted functions:
/// - IfSelectTest.* (Phase 33-2/33-3)
/// - IfMergeTest.* (Phase 33-7)
/// - JsonShapeToMap._read_value_from_pair/1 (Phase 33-4 Stage-1)
/// - Stage1JsonScannerBox.value_start_after_key_pos/2 (Phase 33-4 Stage-B)
/// - 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.
/// Pattern selection:
/// - 1 variable → Select
/// - 2+ variables → IfMerge
///
/// Returns Some(JoinInst::Select) or Some(JoinInst::IfMerge) if pattern matched, None otherwise.
pub fn try_lower_if_to_joinir(
func: &MirFunction,
block_id: BasicBlockId,
debug: bool,
) -> Option<JoinInst> {
// dev トグルチェック
// 1. dev トグルチェック
if !crate::config::env::joinir_if_select_enabled() {
return None;
}
// Phase 33-4/33-5: 関数名ガード拡張(テスト + Stage-1/Stage-B 候補)
// 2. Phase 33-7: 関数名ガード拡張(テスト + Stage-1/Stage-B 候補)
let is_allowed = func.signature.name.starts_with("IfSelectTest.")
|| func.signature.name.starts_with("IfMergeTest.") // Phase 33-7
|| func.signature.name.starts_with("Stage1JsonScannerTestBox.") // Phase 33-5 test
|| func.signature.name == "JsonShapeToMap._read_value_from_pair/1"
|| func.signature.name == "Stage1JsonScannerBox.value_start_after_key_pos/2";
@ -83,10 +90,26 @@ pub fn try_lower_if_to_joinir(
return None;
}
// if_select lowering を試行
let lowerer = if_select::IfSelectLowerer::new(debug);
// 3. Phase 33-7: IfMerge を優先的に試行(複数変数パターン)
// IfMerge が成功すればそれを返す、失敗したら Select を試行
let if_merge_lowerer = if_merge::IfMergeLowerer::new(debug);
if !lowerer.can_lower_to_select(func, block_id) {
if if_merge_lowerer.can_lower_to_if_merge(func, block_id) {
if let Some(result) = if_merge_lowerer.lower_if_to_if_merge(func, block_id) {
if debug {
eprintln!(
"[try_lower_if_to_joinir] IfMerge lowering used for {}",
func.signature.name
);
}
return Some(result);
}
}
// 4. IfMerge が失敗したら Select を試行(単一変数パターン)
let if_select_lowerer = if_select::IfSelectLowerer::new(debug);
if !if_select_lowerer.can_lower_to_select(func, block_id) {
if debug {
eprintln!(
"[try_lower_if_to_joinir] pattern not matched for {}",
@ -96,11 +119,11 @@ pub fn try_lower_if_to_joinir(
return None;
}
let result = lowerer.lower_if_to_select(func, block_id);
let result = if_select_lowerer.lower_if_to_select(func, block_id);
if result.is_some() && debug {
eprintln!(
"[try_lower_if_to_joinir] if_select lowering used for {}",
"[try_lower_if_to_joinir] Select lowering used for {}",
func.signature.name
);
}