diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md index 9ec4d852..8037639a 100644 --- a/CURRENT_TASK.md +++ b/CURRENT_TASK.md @@ -131,7 +131,7 @@ - docs: - `docs/private/roadmap2/phases/phase-46-ifmerge-loop-reassign/README.md` -### 1-00i. Phase 49–56 — JoinIR Frontend 本線統合(print_tokens / filter)✅ 進行中 +### 1-00i. Phase 49–56 — JoinIR Frontend 本線統合(print_tokens / filter)✅ 完了 - Phase 49–52: `cf_loop` に JoinIR Frontend ルートを追加し(dev フラグ付き)、`LoopFrontendBinding` / JSON v0 / expr タイプ(Field/NewBox)を整備。 - `HAKO_JOINIR_PRINT_TOKENS_MAIN` / `HAKO_JOINIR_ARRAY_FILTER_MAIN` で対象ループだけを Frontend 経由にルーティング。 @@ -140,6 +140,17 @@ - Phase 56: `ArrayExtBox.filter/2` 向けに `LoopFrontendBinding::for_array_filter` を MethodCall ベース(`arr.size()`)に修正し、外部参照 `arr/pred` を Binding 経由で渡す構造に統一。 - JoinIR に `ConditionalMethodCall` / Unary / Call を追加し、filter の「pred が true のときだけ push する」パターンを 4 ブロック構造で表現。 - `HAKO_JOINIR_ARRAY_FILTER_MAIN=1` で Route B(JoinIR Frontend 経路)がフォールバックなし完走(テスト済み、既定は従来ルート)。 + +### 1-00j. Phase 57–58 — If 側 PHI Level 3 本体(conservative/if_phi 残り)✅ 仕上げ中 + +- Phase 57 では、新しい抽象を増やさずに既存の Conservative/IfPHI 本体のうち安全な部分を薄くした。 + - `phi.rs` での冗長な `ConservativeMerge::analyze` 呼び出しを 2 回→1 回に削減し、軽量化。 + - `PhiMergeOps` trait と loop_builder.rs 側の実装を完全削除し、Loop 側の PHI 生成責務を JoinIR+LoopScopeShape 側に寄せた。 + - `infer_type_from_phi` には「レガシー型推論箱であり、JoinIR 型情報導入後の削減候補」であることをコメントで明示し、将来の削除条件をドキュメント化。 +- Phase 58 では ConservativeMerge 本体を `phi_merge.rs::merge_all_vars` にインライン化し、`conservative.rs` の多くをドキュメントコメント+最小限の補助関数だけに縮退。 + - conservative.rs: 149 行 → 57 行(約 92 行削減、約 62% 削減)。 + - ConservativeMerge struct 定義と付随テストコードを削除し、挙動差分がないことを phi_core / JoinIR テストで確認。 +- JoinIR/phi_core 関連テストは全て PASS。既知の `local` キーワード問題を除き、新たな退行はなし。 - Phase P1: **If Handler 箱化モジュール化** ✅ 完了(2025-11-29) - ループ内 If 処理の 5 パターン(Empty/SingleVarThen/SingleVarBoth/ConditionalEffect/Unsupported)を `IfInLoopPattern` enum で分類。 - `if_in_loop/` モジュール(9 ファイル、~480 行)を新設し、`stmt_handlers.rs` から 154 行削減(40% 削減達成)。 @@ -166,6 +177,9 @@ - 他の関数(print_tokens 等)も JoinIR 経路に乗せてから本体削除 - **Classifier Trio** - LoopVarClassBox / LoopExitLivenessBox / LocalScopeInspectorBox を LoopScopeShape に吸収し、JoinIR lowering / LoopForm 側から直接 LoopScopeShape を見る構造に整理。 +- **Mir 決定性(小フェーズ予定)** + - 一部テスト(`loop_with_continue_and_break_edge_copy_merge` / `nested_loop_with_multi_continue_break_edge_copy_merge`)で、`MirFunction.blocks: HashMap` / `BasicBlock.predecessors: HashSet` に起因する非決定的な predecessor 順のフラッキーテストが残っている。 + - 将来の小フェーズで Phase 25.1 と同様のパターン(`BTreeMap` / `BTreeSet` など決定的な順序構造、もしくはテスト側で sort 比較)に寄せて解消する予定。 --- diff --git a/src/mir/join_ir/lowering/if_dry_runner.rs b/src/mir/join_ir/lowering/if_dry_runner.rs index 8bf1900d..97fcb677 100644 --- a/src/mir/join_ir/lowering/if_dry_runner.rs +++ b/src/mir/join_ir/lowering/if_dry_runner.rs @@ -65,6 +65,7 @@ impl IfLoweringDryRunner { func, *block_id, self.debug_level >= 3, + None, // Phase 61-1: Pure If(dry-runは常にPure If) ) { Some(join_inst) => { lowered_count += 1; diff --git a/src/mir/join_ir/lowering/if_merge.rs b/src/mir/join_ir/lowering/if_merge.rs index 531cb015..68fcd6a4 100644 --- a/src/mir/join_ir/lowering/if_merge.rs +++ b/src/mir/join_ir/lowering/if_merge.rs @@ -18,8 +18,13 @@ use crate::mir::join_ir::{JoinInst, MergePair}; use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId}; use std::collections::HashSet; +// Phase 61-1: If-in-loop context support +use super::if_phi_context::IfPhiContext; + pub struct IfMergeLowerer { debug_level: u8, + // Phase 61-1: If-in-loop context (None = Pure If) + context: Option, } /// 検出された IfMerge パターン情報 @@ -39,13 +44,37 @@ struct IfBranch { impl IfMergeLowerer { pub fn new(debug_level: u8) -> Self { - Self { debug_level } + Self { + debug_level, + context: None, // Phase 61-1: デフォルトは Pure If + } } /// Phase 33-8: debug-level backward compat wrapper 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 = IfMergeLowerer::with_context(debug_level, context); + /// ``` + pub fn with_context(debug_level: u8, context: IfPhiContext) -> Self { + Self { + debug_level, + context: Some(context), } } diff --git a/src/mir/join_ir/lowering/if_phi_context.rs b/src/mir/join_ir/lowering/if_phi_context.rs new file mode 100644 index 00000000..284dafed --- /dev/null +++ b/src/mir/join_ir/lowering/if_phi_context.rs @@ -0,0 +1,132 @@ +//! Phase 61-1: If-in-loop 用 PHI コンテキスト +//! +//! loop_builder.rs から carrier_names 情報を JoinIR に渡すための構造体 +//! +//! ## 背景 +//! +//! ループ内の if では、ループキャリア変数に対して「片腕 PHI」が必要になる。 +//! 従来は PhiBuilderBox::set_if_context() で carrier_names を渡していたが、 +//! Phase 61-1 で JoinIR 経路に統一するため、この情報を渡す手段が必要。 +//! +//! ## 設計 +//! +//! ``` +//! // loop_builder.rs +//! let carrier_names = ...; +//! let context = IfPhiContext::for_loop_body(carrier_names); +//! try_lower_if_to_joinir(func, block_id, debug, Some(&context)); +//! ``` +//! +//! ## 責務 +//! +//! - ループ内 if のコンテキスト情報を保持 +//! - carrier_names の判定ユーティリティ提供 + +use std::collections::BTreeSet; + +/// If-in-loop 用 PHI コンテキスト +#[derive(Debug, Clone)] +pub struct IfPhiContext { + /// ループ内の if かどうか + /// + /// true の場合、carrier_names に含まれる変数は「片腕 PHI」が必要 + pub in_loop_body: bool, + + /// ループキャリア変数名リスト + /// + /// ループ内 if で片腕 PHI が必要な変数を指定する。 + /// 例: `loop(i < 3) { if cond { x = 1 } }` の場合、 + /// x は carrier 変数として扱われ、else 側で pre_if 値を使用する PHI を生成する。 + pub carrier_names: BTreeSet, +} + +impl IfPhiContext { + /// ループ内 if 用コンテキスト作成 + /// + /// # Arguments + /// + /// * `carrier_names` - ループキャリア変数名リスト(BTreeSet で決定的イテレーション保証) + /// + /// # Example + /// + /// ```ignore + /// let carrier_names = pre_if_var_map + /// .keys() + /// .filter(|name| !name.starts_with("__pin$")) + /// .cloned() + /// .collect(); + /// + /// let context = IfPhiContext::for_loop_body(carrier_names); + /// ``` + pub fn for_loop_body(carrier_names: BTreeSet) -> Self { + Self { + in_loop_body: true, + carrier_names, + } + } + + /// 指定された変数がループキャリアかどうか判定 + /// + /// # Arguments + /// + /// * `var_name` - 判定対象の変数名 + /// + /// # Returns + /// + /// - `true`: ループキャリア変数(片腕 PHI が必要) + /// - `false`: 通常変数(純粋な if PHI) + /// + /// # Example + /// + /// ```ignore + /// if context.is_carrier("x") { + /// // 片腕 PHI 生成: phi [then_val, pre_if_val] + /// } else { + /// // 純粋 if PHI: phi [then_val, else_val] + /// } + /// ``` + pub fn is_carrier(&self, var_name: &str) -> bool { + self.carrier_names.contains(var_name) + } + + /// ループキャリア変数の数を取得 + pub fn carrier_count(&self) -> usize { + self.carrier_names.len() + } + + /// ループ内 if かどうか判定 + pub fn is_in_loop(&self) -> bool { + self.in_loop_body + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_for_loop_body() { + let mut carrier_names = BTreeSet::new(); + carrier_names.insert("x".to_string()); + carrier_names.insert("y".to_string()); + + let context = IfPhiContext::for_loop_body(carrier_names); + + assert!(context.is_in_loop()); + assert!(context.is_carrier("x")); + assert!(context.is_carrier("y")); + assert!(!context.is_carrier("z")); + assert_eq!(context.carrier_count(), 2); + } + + #[test] + fn test_is_carrier() { + let mut carrier_names = BTreeSet::new(); + carrier_names.insert("loop_var".to_string()); + + let context = IfPhiContext::for_loop_body(carrier_names); + + assert!(context.is_carrier("loop_var")); + assert!(!context.is_carrier("local_var")); + } +} diff --git a/src/mir/join_ir/lowering/if_select.rs b/src/mir/join_ir/lowering/if_select.rs index a693adde..1006c9c4 100644 --- a/src/mir/join_ir/lowering/if_select.rs +++ b/src/mir/join_ir/lowering/if_select.rs @@ -13,8 +13,13 @@ use crate::mir::join_ir::JoinInst; use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId}; +// 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) + context: Option, } /// If/Else パターンの分類 @@ -46,13 +51,37 @@ struct IfBranch { impl IfSelectLowerer { pub fn new(debug_level: u8) -> Self { - Self { debug_level } + Self { + debug_level, + context: None, // Phase 61-1: デフォルトは Pure If + } } /// Phase 33-8: debug-level backward compat wrapper 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), } } diff --git a/src/mir/join_ir/lowering/mod.rs b/src/mir/join_ir/lowering/mod.rs index ac8096db..fb6c24a4 100644 --- a/src/mir/join_ir/lowering/mod.rs +++ b/src/mir/join_ir/lowering/mod.rs @@ -22,6 +22,7 @@ pub mod funcscanner_trim; pub mod generic_case_a; pub mod if_dry_runner; // Phase 33-10.0 pub mod if_merge; // Phase 33-7 +pub mod if_phi_context; // Phase 61-1 pub mod if_select; // Phase 33 pub mod loop_form_intake; pub mod loop_scope_shape; @@ -92,11 +93,16 @@ pub(crate) fn is_loop_lowered_function(name: &str) -> bool { /// - 1 variable → Select /// - 2+ variables → IfMerge /// +/// Phase 61-1: If-in-loop support +/// - `context` parameter: If-in-loop context (carrier_names for loop variables) +/// - None = Pure If, Some(_) = If-in-loop +/// /// 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, + context: Option<&if_phi_context::IfPhiContext>, // Phase 61-1: If-in-loop context ) -> Option { // 1. dev トグルチェック if !crate::config::env::joinir_if_select_enabled() { @@ -151,7 +157,12 @@ pub fn try_lower_if_to_joinir( // 3. Phase 33-7: IfMerge を優先的に試行(複数変数パターン) // IfMerge が成功すればそれを返す、失敗したら Select を試行 - let if_merge_lowerer = if_merge::IfMergeLowerer::new(debug_level); + // Phase 61-1: context がある場合は with_context() を使用 + let if_merge_lowerer = if let Some(ctx) = context { + if_merge::IfMergeLowerer::with_context(debug_level, ctx.clone()) + } else { + 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) { @@ -166,7 +177,12 @@ pub fn try_lower_if_to_joinir( } // 4. IfMerge が失敗したら Select を試行(単一変数パターン) - let if_select_lowerer = if_select::IfSelectLowerer::new(debug_level); + // Phase 61-1: context がある場合は with_context() を使用 + let if_select_lowerer = if let Some(ctx) = context { + if_select::IfSelectLowerer::with_context(debug_level, ctx.clone()) + } else { + if_select::IfSelectLowerer::new(debug_level) + }; if !if_select_lowerer.can_lower_to_select(func, block_id) { if debug_level >= 1 { diff --git a/src/mir/loop_builder.rs b/src/mir/loop_builder.rs index 6aab8105..3f68123e 100644 --- a/src/mir/loop_builder.rs +++ b/src/mir/loop_builder.rs @@ -1151,38 +1151,76 @@ impl<'a> LoopBuilder<'a> { // Region 情報(entry/exit/slots)をログに出すよ。 crate::mir::region::observer::observe_control_form(self.parent_builder, &form); - let mut ops = Ops(self); - - // Phase 26-F-2: BodyLocalPhiBuilder削除 - // Phase 35-5: if_body_local_merge.rs削除、PhiBuilderBoxに吸収 - // 理由: 箱理論による責務分離(ループスコープ分析 vs if-merge専用処理) - - // Phase 26-E: PhiBuilderBox SSOT統合(If PHI生成) - // Legacy: merge_modified_with_control() → New: PhiBuilderBox::generate_phis() - let mut phi_builder = crate::mir::phi_core::phi_builder_box::PhiBuilderBox::new(); - - // Phase 26-F-3: ループ内if-mergeコンテキスト設定(ChatGPT設計) - // pre_if_var_mapにある全変数をcarrier候補として扱う(保守的だが安全) - // 理由: ループ内変数は全てキャリア候補の可能性があるため + // Phase 61-1: If-in-loop JoinIR化(開発フラグ制御) + // carrier_namesを作成(両経路で共通) let carrier_names: std::collections::BTreeSet = pre_if_var_map .keys() .filter(|name| !name.starts_with("__pin$")) // 一時変数除外 .cloned() .collect(); - phi_builder.set_if_context( - true, // in_loop_body = true - carrier_names, - ); + // Phase 61-1: JoinIR経路を試行(`Ops`作成前に実行) + let joinir_success = if crate::config::env::joinir_if_select_enabled() { + // IfPhiContext作成 + let if_phi_context = + crate::mir::join_ir::lowering::if_phi_context::IfPhiContext::for_loop_body( + carrier_names.clone(), + ); - // Phase 35-5: if_body_local_merge.rs削除、ロジックはPhiBuilderBox内に統合 + // JoinIR経路を試行 + if let Some(ref func) = self.parent_builder.current_function { + match crate::mir::join_ir::lowering::try_lower_if_to_joinir( + func, + pre_branch_bb, + false, // debug + Some(&if_phi_context), + ) { + Some(join_inst) => { + eprintln!("[Phase 61-1] ✅ If-in-loop lowered via JoinIR: {:?}", join_inst); - let post_snapshots = if let Some(ref else_map) = else_var_map_end_opt { - vec![then_var_map_end.clone(), else_map.clone()] + // TODO: join_inst を dry-run して PHI 生成 + // Phase 61-1では一旦スキップし、Phase 61-2で実装 + eprintln!("[Phase 61-1] ⚠️ JoinIR dry-run not yet implemented, using fallback"); + false // 一旦フォールバック + } + None => { + eprintln!("[Phase 61-1] ⏭️ JoinIR pattern not matched, using fallback"); + false + } + } + } else { + false + } } else { - vec![then_var_map_end.clone()] + false }; - phi_builder.generate_phis(&mut ops, &form, &pre_if_var_map, &post_snapshots)?; + + let mut ops = Ops(self); + + // Phase 26-F-2: BodyLocalPhiBuilder削除 + // Phase 35-5: if_body_local_merge.rs削除、PhiBuilderBoxに吸収 + // 理由: 箱理論による責務分離(ループスコープ分析 vs if-merge専用処理) + + // フォールバック: PhiBuilderBox経路(既存) + if !joinir_success { + // Phase 26-E: PhiBuilderBox SSOT統合(If PHI生成) + // Legacy: merge_modified_with_control() → New: PhiBuilderBox::generate_phis() + let mut phi_builder = crate::mir::phi_core::phi_builder_box::PhiBuilderBox::new(); + + // Phase 26-F-3: ループ内if-mergeコンテキスト設定(ChatGPT設計) + phi_builder.set_if_context( + true, // in_loop_body = true + carrier_names, + ); + + // Phase 35-5: if_body_local_merge.rs削除、ロジックはPhiBuilderBox内に統合 + let post_snapshots = if let Some(ref else_map) = else_var_map_end_opt { + vec![then_var_map_end.clone(), else_map.clone()] + } else { + vec![then_var_map_end.clone()] + }; + phi_builder.generate_phis(&mut ops, &form, &pre_if_var_map, &post_snapshots)?; + } // Phase 26-E-4: PHI生成後に variable_map をリセット(ChatGPT/Task先生指示) // 理由: else_var_map_end_opt が正しい snapshot を保持したまま PHI 生成に渡す必要がある diff --git a/src/tests/mir_joinir_if_select.rs b/src/tests/mir_joinir_if_select.rs index 080d1e34..f6760359 100644 --- a/src/tests/mir_joinir_if_select.rs +++ b/src/tests/mir_joinir_if_select.rs @@ -133,7 +133,7 @@ mod tests { let func = create_simple_pattern_mir(); let entry_block = func.entry_block; - let result = try_lower_if_to_joinir(&func, entry_block, true); + let result = try_lower_if_to_joinir(&func, entry_block, true, None); // Phase 61-1: Pure If assert!( result.is_some(), @@ -159,7 +159,7 @@ mod tests { // ==== 2. Local pattern (env ON) ==== let func = create_local_pattern_mir(); let entry_block = func.entry_block; - let result = try_lower_if_to_joinir(&func, entry_block, true); + let result = try_lower_if_to_joinir(&func, entry_block, true, None); // Phase 61-1: Pure If assert!( result.is_some(), @@ -187,7 +187,7 @@ mod tests { let func = create_simple_pattern_mir(); let entry_block = func.entry_block; - let result = try_lower_if_to_joinir(&func, entry_block, false); + let result = try_lower_if_to_joinir(&func, entry_block, false, None); // Phase 61-1: Pure If assert!( result.is_none(), @@ -202,7 +202,7 @@ mod tests { let mut func = create_simple_pattern_mir(); func.signature.name = "WrongName.test/1".to_string(); let entry_block = func.entry_block; - let result = try_lower_if_to_joinir(&func, entry_block, true); + let result = try_lower_if_to_joinir(&func, entry_block, true, None); // Phase 61-1: Pure If assert!( result.is_none(), @@ -519,7 +519,7 @@ mod tests { let func = create_if_merge_simple_pattern_mir(); let entry_block = func.entry_block; - let result = try_lower_if_to_joinir(&func, entry_block, true); + let result = try_lower_if_to_joinir(&func, entry_block, true, None); // Phase 61-1: Pure If assert!( result.is_some(), @@ -560,7 +560,7 @@ mod tests { let func = create_if_merge_multiple_pattern_mir(); let entry_block = func.entry_block; - let result = try_lower_if_to_joinir(&func, entry_block, true); + let result = try_lower_if_to_joinir(&func, entry_block, true, None); // Phase 61-1: Pure If assert!( result.is_some(),