diff --git a/src/config/env.rs b/src/config/env.rs index 3bf1d98d..9b827096 100644 --- a/src/config/env.rs +++ b/src/config/env.rs @@ -218,9 +218,51 @@ pub fn joinir_llvm_experiment_enabled() -> bool { } /// Phase 33: JoinIR If Select 実験の有効化 -/// Set NYASH_JOINIR_IF_SELECT=1 to enable experimental If/Else → Select lowering. +/// Primary: HAKO_JOINIR_IF_SELECT (Phase 33-8+). Fallback: NYASH_JOINIR_IF_SELECT (deprecated). pub fn joinir_if_select_enabled() -> bool { - env_bool("NYASH_JOINIR_IF_SELECT") + // Primary: HAKO_JOINIR_IF_SELECT + if let Some(v) = env_flag("HAKO_JOINIR_IF_SELECT") { + return v; + } + // Fallback: NYASH_JOINIR_IF_SELECT (deprecated) + if env_bool("NYASH_JOINIR_IF_SELECT") { + warn_alias_once("NYASH_JOINIR_IF_SELECT", "HAKO_JOINIR_IF_SELECT"); + return true; + } + false +} + +/// Phase 33-8: JoinIR Stage-1 rollout toggle +/// Set HAKO_JOINIR_STAGE1=1 to enable JoinIR lowering for Stage-1 functions. +pub fn joinir_stage1_enabled() -> bool { + // Primary: HAKO_JOINIR_STAGE1 + if let Some(v) = env_flag("HAKO_JOINIR_STAGE1") { + return v; + } + // Fallback: NYASH_JOINIR_STAGE1 (deprecated) + if env_bool("NYASH_JOINIR_STAGE1") { + warn_alias_once("NYASH_JOINIR_STAGE1", "HAKO_JOINIR_STAGE1"); + return true; + } + false +} + +/// Phase 33-8: JoinIR debug log level (0-3) +/// - 0: No logs (default) +/// - 1: Basic logs (which functions were lowered) +/// - 2: Pattern matching details (CFG analysis) +/// - 3: Full dump (all variables, all instructions) +pub fn joinir_debug_level() -> u8 { + // Primary: HAKO_JOINIR_DEBUG + if let Ok(v) = std::env::var("HAKO_JOINIR_DEBUG") { + return v.parse().unwrap_or(0); + } + // Fallback: NYASH_JOINIR_DEBUG (deprecated) + if let Ok(v) = std::env::var("NYASH_JOINIR_DEBUG") { + warn_alias_once("NYASH_JOINIR_DEBUG", "HAKO_JOINIR_DEBUG"); + return v.parse().unwrap_or(0); + } + 0 } // VM legacy by-name call fallback was removed (Phase 2 complete). diff --git a/src/mir/join_ir/lowering/if_merge.rs b/src/mir/join_ir/lowering/if_merge.rs index 07b2d9f2..10eb1312 100644 --- a/src/mir/join_ir/lowering/if_merge.rs +++ b/src/mir/join_ir/lowering/if_merge.rs @@ -11,7 +11,7 @@ use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId}; use std::collections::HashSet; pub struct IfMergeLowerer { - debug: bool, + debug_level: u8, } /// 検出された IfMerge パターン情報 @@ -30,8 +30,13 @@ struct IfBranch { } impl IfMergeLowerer { - pub fn new(debug: bool) -> Self { - Self { debug } + pub fn new(debug_level: u8) -> Self { + Self { debug_level } + } + + /// Phase 33-8: debug-level backward compat wrapper + pub fn with_debug(debug: bool) -> Self { + Self { debug_level: if debug { 1 } else { 0 } } } /// if/else が IfMerge に lowering できるかチェック @@ -47,13 +52,25 @@ impl IfMergeLowerer { ) -> Option { let pattern = self.find_if_merge_pattern(func, if_block_id)?; - if self.debug { + // Phase 33-8: Level 1 - Basic lowering info + if self.debug_level >= 1 { eprintln!( - "[IfMergeLowerer] lowering to IfMerge with {} merge pairs", + "[IfMergeLowerer] ✅ lowering to IfMerge with {} merge pairs", pattern.merge_pairs.len() ); } + // Phase 33-8: Level 3 - Full merge details + if self.debug_level >= 3 { + eprintln!("[IfMergeLowerer] cond: {:?}", pattern.cond); + for (i, pair) in pattern.merge_pairs.iter().enumerate() { + eprintln!( + "[IfMergeLowerer] pair[{}]: dst={:?}, then={:?}, else={:?}", + i, pair.dst, pair.then_val, pair.else_val + ); + } + } + // IfMerge 命令を生成 Some(JoinInst::IfMerge { cond: pattern.cond, @@ -99,9 +116,10 @@ impl IfMergeLowerer { ); if !is_then_return || !is_else_return { - if self.debug { + // Phase 33-8: Level 2 - Pattern matching details + if self.debug_level >= 2 { eprintln!( - "[IfMergeLowerer] not return pattern (then={}, else={})", + "[IfMergeLowerer] ❌ not return pattern (then={}, else={})", is_then_return, is_else_return ); } @@ -112,7 +130,8 @@ impl IfMergeLowerer { let then_writes = self.extract_written_vars(&then_block.instructions); let else_writes = self.extract_written_vars(&else_block.instructions); - if self.debug { + // Phase 33-8: Level 3 - Full variable dump + if self.debug_level >= 3 { eprintln!( "[IfMergeLowerer] then writes: {:?}, else writes: {:?}", then_writes, else_writes @@ -123,8 +142,9 @@ impl IfMergeLowerer { let common_writes: HashSet<_> = then_writes.intersection(&else_writes).copied().collect(); if common_writes.is_empty() { - if self.debug { - eprintln!("[IfMergeLowerer] no common writes found"); + // Phase 33-8: Level 2 - Pattern matching details + if self.debug_level >= 2 { + eprintln!("[IfMergeLowerer] ❌ no common writes found"); } return None; } @@ -230,7 +250,10 @@ mod tests { #[test] fn test_if_merge_lowerer_creation() { - let lowerer = IfMergeLowerer::new(false); - assert!(!lowerer.debug); + let lowerer = IfMergeLowerer::new(0); + assert_eq!(lowerer.debug_level, 0); + + let lowerer_compat = IfMergeLowerer::with_debug(true); + assert_eq!(lowerer_compat.debug_level, 1); } } diff --git a/src/mir/join_ir/lowering/if_select.rs b/src/mir/join_ir/lowering/if_select.rs index 6fd477e6..ae1e6981 100644 --- a/src/mir/join_ir/lowering/if_select.rs +++ b/src/mir/join_ir/lowering/if_select.rs @@ -6,7 +6,7 @@ use crate::mir::join_ir::JoinInst; use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId}; pub struct IfSelectLowerer { - debug: bool, + debug_level: u8, } /// If/Else パターンの分類 @@ -37,8 +37,13 @@ struct IfBranch { } impl IfSelectLowerer { - pub fn new(debug: bool) -> Self { - Self { debug } + pub fn new(debug_level: u8) -> Self { + Self { debug_level } + } + + /// Phase 33-8: debug-level backward compat wrapper + pub fn with_debug(debug: bool) -> Self { + Self { debug_level: if debug { 1 } else { 0 } } } /// if/else が Select に lowering できるかチェック @@ -54,13 +59,22 @@ impl IfSelectLowerer { ) -> Option { let pattern = self.find_if_pattern(func, if_block_id)?; - if self.debug { + // Phase 33-8: Level 1 - Basic lowering info + if self.debug_level >= 1 { eprintln!( - "[IfSelectLowerer] lowering {:?} pattern to Select", + "[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); @@ -99,8 +113,9 @@ impl IfSelectLowerer { // 3. simple パターンのチェック if let Some(pattern) = self.try_match_simple_pattern(&branch, then_block, else_block) { - if self.debug { - eprintln!("[IfSelectLowerer] matched simple pattern"); + // Phase 33-8: Level 2 - Pattern matching details + if self.debug_level >= 2 { + eprintln!("[IfSelectLowerer] ✅ matched simple pattern"); } return Some(pattern); } @@ -108,14 +123,16 @@ impl IfSelectLowerer { // 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"); + // Phase 33-8: Level 2 - Pattern matching details + if self.debug_level >= 2 { + eprintln!("[IfSelectLowerer] ✅ matched local pattern"); } return Some(pattern); } - if self.debug { - eprintln!("[IfSelectLowerer] no pattern matched"); + // Phase 33-8: Level 2 - Pattern matching details + if self.debug_level >= 2 { + eprintln!("[IfSelectLowerer] ❌ no pattern matched"); } None } diff --git a/src/mir/join_ir/lowering/mod.rs b/src/mir/join_ir/lowering/mod.rs index 5e5b830e..17dd2f98 100644 --- a/src/mir/join_ir/lowering/mod.rs +++ b/src/mir/join_ir/lowering/mod.rs @@ -73,15 +73,29 @@ pub fn try_lower_if_to_joinir( return None; } - // 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"; + // Phase 33-8: デバッグログレベル取得(0-3) + let debug_level = crate::config::env::joinir_debug_level(); + let _debug = debug || debug_level >= 1; + + // 2. Phase 33-8: 関数名ガード拡張(テスト + Stage-1 rollout + 明示承認) + let is_allowed = + // Test functions (always enabled) + func.signature.name.starts_with("IfSelectTest.") || + func.signature.name.starts_with("IfMergeTest.") || + func.signature.name.starts_with("Stage1JsonScannerTestBox.") || // Phase 33-5 test + + // Stage-1 rollout (env-controlled) + (crate::config::env::joinir_stage1_enabled() && + func.signature.name.starts_with("Stage1")) || + + // Explicit approvals (Phase 33-4で検証済み, always on) + matches!(func.signature.name.as_str(), + "JsonShapeToMap._read_value_from_pair/1" | + "Stage1JsonScannerBox.value_start_after_key_pos/2" + ); if !is_allowed { - if debug { + if debug_level >= 2 { eprintln!( "[try_lower_if_to_joinir] skipping non-allowed function: {}", func.signature.name @@ -90,15 +104,19 @@ pub fn try_lower_if_to_joinir( return None; } + if debug_level >= 1 { + eprintln!("[try_lower_if_to_joinir] trying to lower {}", func.signature.name); + } + // 3. Phase 33-7: IfMerge を優先的に試行(複数変数パターン) // IfMerge が成功すればそれを返す、失敗したら Select を試行 - let if_merge_lowerer = if_merge::IfMergeLowerer::new(debug); + let if_merge_lowerer = if_merge::IfMergeLowerer::new(debug_level); 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 { + if debug_level >= 1 { eprintln!( - "[try_lower_if_to_joinir] IfMerge lowering used for {}", + "[try_lower_if_to_joinir] ✅ IfMerge lowering used for {}", func.signature.name ); } @@ -107,10 +125,10 @@ pub fn try_lower_if_to_joinir( } // 4. IfMerge が失敗したら Select を試行(単一変数パターン) - let if_select_lowerer = if_select::IfSelectLowerer::new(debug); + let if_select_lowerer = if_select::IfSelectLowerer::new(debug_level); if !if_select_lowerer.can_lower_to_select(func, block_id) { - if debug { + if debug_level >= 1 { eprintln!( "[try_lower_if_to_joinir] pattern not matched for {}", func.signature.name @@ -121,9 +139,9 @@ pub fn try_lower_if_to_joinir( let result = if_select_lowerer.lower_if_to_select(func, block_id); - if result.is_some() && debug { + if result.is_some() && debug_level >= 1 { eprintln!( - "[try_lower_if_to_joinir] Select lowering used for {}", + "[try_lower_if_to_joinir] ✅ Select lowering used for {}", func.signature.name ); } diff --git a/tools/joinir_ab_test.sh b/tools/joinir_ab_test.sh new file mode 100644 index 00000000..a708aef7 --- /dev/null +++ b/tools/joinir_ab_test.sh @@ -0,0 +1,72 @@ +#!/bin/bash +# Phase 33-8: A/B test automation for JoinIR lowering +# +# Usage: +# tools/joinir_ab_test.sh +# +# Example: +# tools/joinir_ab_test.sh apps/tests/joinir_if_merge_simple.hako + +set -euo pipefail + +test_case=$1 # e.g., "apps/tests/joinir_if_merge_simple.hako" + +if [ ! -f "$test_case" ]; then + echo "❌ Test file not found: $test_case" + exit 1 +fi + +echo "🧪 Testing: $test_case" +echo "" + +# Route A: Traditional if_phi +echo "=== Route A (if_phi) ===" +HAKO_JOINIR_IF_SELECT=0 \ +NYASH_PARSER_STAGE3=1 \ +HAKO_PARSER_STAGE3=1 \ + ./target/release/hakorune "$test_case" \ + > /tmp/route_a.out 2>&1 +route_a_rc=$? +echo "Route A RC: $route_a_rc" +echo "" + +# Route B: JoinIR Select/IfMerge +echo "=== Route B (JoinIR) ===" +HAKO_JOINIR_IF_SELECT=1 \ +HAKO_JOINIR_STAGE1=1 \ +HAKO_JOINIR_DEBUG=1 \ +NYASH_PARSER_STAGE3=1 \ +HAKO_PARSER_STAGE3=1 \ + ./target/release/hakorune "$test_case" \ + > /tmp/route_b.out 2>&1 +route_b_rc=$? +echo "Route B RC: $route_b_rc" +echo "" + +# Comparison +echo "=== 📊 Comparison ===" + +# RC check +if [ $route_a_rc -eq $route_b_rc ]; then + echo "✅ RC matched: $route_a_rc" +else + echo "❌ RC mismatch: A=$route_a_rc, B=$route_b_rc" + exit 1 +fi + +# Output check (ignore debug logs starting with '[') +if diff <(grep -v '^\[' /tmp/route_a.out) <(grep -v '^\[' /tmp/route_b.out); then + echo "✅ Output matched" +else + echo "❌ Output differs:" + diff <(grep -v '^\[' /tmp/route_a.out) <(grep -v '^\[' /tmp/route_b.out) || true + exit 1 +fi + +# Extract lowering info from Route B +echo "" +echo "=== 🔍 Lowering Info ===" +grep -E "IfMerge|IfSelect|if_phi" /tmp/route_b.out || echo "⚠️ No lowering info found" + +echo "" +echo "🎉 A/B test PASSED for $test_case"