feat(joinir): Phase 61-4-F ToplevelOps and production path integration

Phase 61-4-F: Loop-outside If JoinIR production path

Changes:
- F.1: Add ToplevelOps struct implementing PhiBuilderOps for MirBuilder
  - Enables emit_toplevel_phis() to emit PHI instructions via MirBuilder
  - Uses insert_phi_at_head_spanned for proper PHI placement
  - ~70 lines, thin wrapper following box theory

- F.2: Integrate production path with emit_toplevel_phis
  - Replace TODO with actual PHI emission call
  - Build IfShape from branch blocks
  - Log PHI count on dry-run

- Add IfToplevelTest.* to try_lower_if_to_joinir allowed list
  - Fixes function name guard that blocked testing

Note: Pattern matching currently only supports return patterns
(IfMerge/IfSelect). Local variable assignment patterns fall back
to existing PHI generation, which correctly produces valid MIR.

🤖 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-29 15:32:40 +09:00
parent 3439de0f65
commit 3194cc1e6c
2 changed files with 102 additions and 6 deletions

View File

@ -1,6 +1,79 @@
use super::{MirBuilder, ValueId}; use super::{MirBuilder, ValueId};
use crate::ast::ASTNode; use crate::ast::ASTNode;
use crate::mir::control_form::IfShape;
use crate::mir::loop_api::LoopBuilderApi; // for current_block() use crate::mir::loop_api::LoopBuilderApi; // for current_block()
use crate::mir::phi_core::phi_builder_box::PhiBuilderOps;
use crate::mir::{BasicBlockId, ConstValue, MirInstruction};
/// Phase 61-4-F: MirBuilder 用 PhiBuilderOps 実装
///
/// ループ外 if の JoinIR 経路で emit_toplevel_phis() を呼ぶためのラッパー。
struct ToplevelOps<'a>(&'a mut MirBuilder);
impl<'a> PhiBuilderOps for ToplevelOps<'a> {
fn new_value(&mut self) -> ValueId {
self.0.next_value_id()
}
fn emit_phi(
&mut self,
block: BasicBlockId,
dst: ValueId,
inputs: Vec<(BasicBlockId, ValueId)>,
) -> Result<(), String> {
// merge ブロックの先頭に PHI を挿入
if let (Some(func), Some(_cur_bb)) = (self.0.current_function.as_mut(), self.0.current_block)
{
crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
func,
block,
dst,
inputs,
self.0.current_span,
);
} else {
self.0.emit_instruction(MirInstruction::Phi { dst, inputs })?;
}
Ok(())
}
fn update_var(&mut self, name: String, value: ValueId) {
self.0.variable_map.insert(name, value);
}
fn get_block_predecessors(&self, block: BasicBlockId) -> Vec<BasicBlockId> {
if let Some(ref func) = self.0.current_function {
func.blocks
.get(&block)
.map(|bb| bb.predecessors.iter().copied().collect())
.unwrap_or_default()
} else {
Vec::new()
}
}
fn emit_void(&mut self) -> ValueId {
let void_id = self.0.next_value_id();
let _ = self.0.emit_instruction(MirInstruction::Const {
dst: void_id,
value: ConstValue::Void,
});
void_id
}
fn set_current_block(&mut self, block: BasicBlockId) -> Result<(), String> {
self.0.current_block = Some(block);
Ok(())
}
fn block_exists(&self, block: BasicBlockId) -> bool {
if let Some(ref func) = self.0.current_function {
func.blocks.contains_key(&block)
} else {
false
}
}
}
impl MirBuilder { impl MirBuilder {
/// Lower an if/else using a structured IfForm (header→then/else→merge). /// Lower an if/else using a structured IfForm (header→then/else→merge).
@ -178,15 +251,37 @@ impl MirBuilder {
); );
} }
// 本番経路が有効な場合のみ success = true // Phase 61-4-F: 本番経路 - emit_toplevel_phis でPHI生成
if joinir_toplevel { if joinir_toplevel {
joinir_success = true; // IfShape 構築
// TODO: Phase 61-4-E 本番経路のPHI生成 let if_shape = IfShape {
// 現時点では dry-run 的動作(フォールバック経路を使用) cond_block: pre_branch_bb,
then_block,
else_block: Some(else_block),
merge_block,
};
// PHI 生成
let phi_count = {
let mut ops = ToplevelOps(self);
crate::mir::loop_builder::IfInLoopPhiEmitter::emit_toplevel_phis(
&phi_spec,
&pre_if_var_map,
&then_var_map_end,
else_var_map_end_opt.as_ref(),
&mut ops,
&if_shape,
)?
};
if joinir_dryrun { if joinir_dryrun {
eprintln!("[Phase 61-4] ⚠️ Production path not yet implemented, using fallback"); eprintln!(
"[Phase 61-4] ✅ Production path: {} PHIs generated via JoinIR",
phi_count
);
} }
joinir_success = false; // フォールバック使用
joinir_success = true;
} }
} }
None => { None => {

View File

@ -157,6 +157,7 @@ pub fn try_lower_if_to_joinir(
func.signature.name.starts_with("IfSelectTest.") || func.signature.name.starts_with("IfSelectTest.") ||
func.signature.name.starts_with("IfSelectLocalTest.") || // Phase 33-10 test func.signature.name.starts_with("IfSelectLocalTest.") || // Phase 33-10 test
func.signature.name.starts_with("IfMergeTest.") || func.signature.name.starts_with("IfMergeTest.") ||
func.signature.name.starts_with("IfToplevelTest.") || // Phase 61-4: loop-outside if test
func.signature.name.starts_with("Stage1JsonScannerTestBox.") || // Phase 33-5 test func.signature.name.starts_with("Stage1JsonScannerTestBox.") || // Phase 33-5 test
// Stage-1 rollout (env-controlled) // Stage-1 rollout (env-controlled)