feat(edgecfg): Phase 268-270 savepoint (if_form adoption + Pattern9 minimal loop SSOT)

This commit is contained in:
2025-12-21 23:12:52 +09:00
parent 86a51cad2b
commit df715e909e
18 changed files with 1661 additions and 21 deletions

View File

@ -107,7 +107,9 @@ pub(crate) fn if_(
header: BasicBlockId,
cond: ValueId, // Phase 267 P0 で使用開始
t: Frag, // then 分岐
then_entry_args: EdgeArgs, // Phase 268 P1: then entry edge-args (SSOT)
e: Frag, // else 分岐
else_entry_args: EdgeArgs, // Phase 268 P1: else entry edge-args (SSOT)
join_frag: Frag, // join 以降の断片
) -> Frag {
// Phase 267 P0: header → then/else の BranchStub を作成
@ -115,15 +117,9 @@ pub(crate) fn if_(
from: header,
cond,
then_target: t.entry,
then_args: EdgeArgs {
layout: JumpArgsLayout::CarriersOnly,
values: vec![], // TODO: Phase 267 P2+ で then の入口引数計算
},
then_args: then_entry_args, // Phase 268 P1: caller provides
else_target: e.entry,
else_args: EdgeArgs {
layout: JumpArgsLayout::CarriersOnly,
values: vec![], // TODO: Phase 267 P2+ で else の入口引数計算
},
else_args: else_entry_args, // Phase 268 P1: caller provides
};
let mut exits = BTreeMap::new();
@ -639,7 +635,21 @@ mod tests {
};
// Execute: compose::if_()
let if_frag = if_(header, ValueId(0), then_frag, else_frag, join_frag);
let if_frag = if_(
header,
ValueId(0),
then_frag,
EdgeArgs {
layout: JumpArgsLayout::CarriersOnly,
values: vec![],
},
else_frag,
EdgeArgs {
layout: JumpArgsLayout::CarriersOnly,
values: vec![],
},
join_frag,
);
// Verify: entry = header
assert_eq!(if_frag.entry, header);
@ -707,7 +717,21 @@ mod tests {
};
// Execute
let if_frag = if_(header, ValueId(0), then_frag, else_frag, join_frag);
let if_frag = if_(
header,
ValueId(0),
then_frag,
EdgeArgs {
layout: JumpArgsLayout::CarriersOnly,
values: vec![],
},
else_frag,
EdgeArgs {
layout: JumpArgsLayout::CarriersOnly,
values: vec![],
},
join_frag,
);
// Verify: Return and Unwind are in exits (unwired)
assert!(if_frag.exits.contains_key(&ExitKind::Return));

View File

@ -552,7 +552,21 @@ mod tests {
let cond = ValueId(100);
// Execute
let result = if_(header, cond, then_frag, else_frag, join_frag);
let result = if_(
header,
cond,
then_frag,
EdgeArgs {
layout: JumpArgsLayout::CarriersOnly,
values: vec![],
},
else_frag,
EdgeArgs {
layout: JumpArgsLayout::CarriersOnly,
values: vec![],
},
join_frag,
);
// Verify: 1本の BranchStub が生成された
assert_eq!(result.branches.len(), 1);

View File

@ -82,6 +82,7 @@ pub(in crate::mir::builder) mod pattern5_infinite_early_exit; // Phase 131-11
pub(in crate::mir::builder) mod pattern6_scan_with_init; // Phase 254 P0: index_of/find/contains pattern
pub(in crate::mir::builder) mod pattern7_split_scan; // Phase 256 P0: split/tokenization with variable step
pub(in crate::mir::builder) mod pattern8_scan_bool_predicate; // Phase 259 P0: boolean predicate scan (is_integer/is_valid)
pub(in crate::mir::builder) mod pattern9_accum_const_loop; // Phase 270 P1: accumulator const loop (橋渡しパターン)
pub(in crate::mir::builder) mod pattern_pipeline;
pub(in crate::mir::builder) mod router;
pub(in crate::mir::builder) mod trim_loop_lowering; // Phase 180: Dedicated Trim/P5 lowering module

View File

@ -568,3 +568,94 @@ impl MirBuilder {
Ok(result)
}
}
/// Phase 269 P0: Pattern8 Frag 版 lowerertest-only
///
/// # Purpose
/// - JoinModule → MIR terminator 生成を EdgeCFG Fragment API で実装
/// - 既存実装cf_loop_pattern8_bool_predicate_implと並走
/// - P1 以降で既存実装を置換する準備
///
/// # Architecture
/// ```
/// JoinModule (loop_step + k_exit)
/// ↓ 変換
/// Frag 構築loop body + early exits
/// ↓ compose::loop_()
/// Loop Frag
/// ↓ emit_frag()
/// MIR terminatorBranch/Jump/Return
/// ```
///
/// # Arguments
/// - `builder`: MirBuilder
/// - `join_module`: Pattern8 の JoinModulelower_scan_bool_predicate_minimal から生成)
/// - `boundary`: JoinInlineBoundaryhost <-> join 境界情報)
/// - `debug`: デバッグフラグ
///
/// # Returns
/// - `Ok(Some(ValueId))`: expr_resultret_bool の host ValueId
/// - `Err(String)`: Frag 構築失敗
#[cfg(test)]
pub(crate) fn lower_pattern8_frag(
_builder: &mut MirBuilder,
_join_module: crate::mir::join_ir::JoinModule,
_boundary: &crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary,
_debug: bool,
) -> Result<Option<ValueId>, String> {
// Phase 269 P0: Stub implementation
// TODO: Implement Frag-based lowering
//
// Implementation Plan:
// 1. Extract loop_step function from join_module
// 2. Build loop body Frag from loop_step instructions
// 3. Handle early exits (return false) as Return exit
// 4. Handle tail recursion as Continue exit
// 5. Handle loop exit (return true) as Break exit
// 6. Use compose::loop_() to create loop Frag
// 7. Use emit_frag() to generate MIR terminators
// 8. Return boundary.expr_result
Err("[pattern8_frag] Phase 269 P0 stub - not yet implemented".to_string())
}
#[cfg(test)]
mod tests {
use super::*;
/// Phase 269 P0: Test Pattern8 Frag lowering (minimal test)
///
/// Verifies that lower_pattern8_frag() can:
/// - Accept JoinModule from lower_scan_bool_predicate_minimal()
/// - Return appropriate error (stub implementation)
#[test]
fn test_pattern8_frag_lowering_stub() {
use crate::mir::builder::MirBuilder;
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
use crate::mir::join_ir::lowering::scan_bool_predicate_minimal::lower_scan_bool_predicate_minimal;
use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder;
// 1. Create minimal JoinModule
let mut join_value_space = JoinValueSpace::new();
let join_module = lower_scan_bool_predicate_minimal(
&mut join_value_space,
"me",
"is_digit",
);
// 2. Create minimal boundary (stub)
let boundary = JoinInlineBoundaryBuilder::new()
.with_inputs(vec![], vec![])
.build();
// 3. Create MirBuilder
let mut builder = MirBuilder::new();
// 4. Call lower_pattern8_frag (should return stub error)
let result = lower_pattern8_frag(&mut builder, join_module, &boundary, false);
// 5. Verify stub error
assert!(result.is_err());
assert!(result.unwrap_err().contains("stub"));
}
}

View File

@ -0,0 +1,482 @@
//! Phase 270 P1: Pattern 9 - Accumulator Const Loop (固定橋渡しパターン)
//!
//! **目的**: Phase 270 fixture (`phase270_p0_loop_min_const.hako`) を JoinIR 経路で通す
//!
//! **設計方針**:
//! - Pattern1 は test-only stub として保存(触らない)
//! - Pattern9 は Phase270 専用の最小固定パターン(汎用実装ではない)
//! - 将来 ExitKind+Frag に吸収される前提の橋渡しパターン
//!
//! ## 受理条件Fail-Fast 固定)
//!
//! 1. **ループ条件**: `i < <int literal>` のみ
//! 2. **ループ本体**: 代入2本のみ順序固定
//! - `sum = sum + i`
//! - `i = i + 1`
//! 3. **制御構文**: break/continue/return があれば `Ok(None)` でフォールバック
//! 4. **loop後**: `return sum`
//!
//! ## JoinIR 構造
//!
//! ```text
//! main(i_init, sum_init):
//! result = loop_step(i_init, sum_init)
//! return result
//!
//! loop_step(i, sum):
//! cond = (i < limit)
//! exit_cond = !cond
//! Jump(k_exit, [sum], cond=exit_cond)
//! sum_next = sum + i
//! i_next = i + 1
//! Call(loop_step, [i_next, sum_next]) // tail recursion
//!
//! k_exit(sum):
//! return sum
//! ```
use crate::ast::{ASTNode, BinaryOperator, LiteralValue};
use crate::mir::builder::MirBuilder;
use crate::mir::ValueId;
/// Phase 270 P1: Pattern 9 detection
///
/// **Fail-Fast 判定**: Phase270 fixture の形に厳密一致する場合のみ true
pub(crate) fn can_lower(builder: &MirBuilder, ctx: &super::router::LoopPatternContext) -> bool {
// Step 1: ループ条件チェック: i < <int literal>
let (loop_var, _limit) = match extract_loop_condition(ctx.condition) {
Some(result) => result,
None => return false,
};
// Step 2: ループ変数が variable_map に存在するか確認
if !builder.variable_ctx.variable_map.contains_key(&loop_var) {
return false;
}
// Step 3: ループ本体チェック: sum = sum + i; i = i + 1
let (_sum_var, _i_var) = match extract_loop_body_assignments(ctx.body, &loop_var) {
Some(result) => result,
None => return false,
};
// Step 4: break/continue/return があれば reject
if has_control_flow(ctx.body) {
return false;
}
// Pattern9 受理
true
}
/// Phase 270 P1: Pattern 9 lowering
///
/// **void stub 禁止**: JoinIR を生成して JoinIRConversionPipeline::execute へ渡す
pub(crate) fn lower(
builder: &mut MirBuilder,
ctx: &super::router::LoopPatternContext,
) -> Result<Option<ValueId>, String> {
// Step 1: 最終検証can_lower で漏れた条件確認)
if !can_lower(builder, ctx) {
return Ok(None); // Phase 263 PromoteDecision 思想: 他パターンへフォールバック
}
// Step 2: ループ条件とキャリア変数を抽出
let (loop_var, limit) = extract_loop_condition(ctx.condition)
.ok_or_else(|| "[pattern9] Failed to extract loop condition".to_string())?;
let (sum_var, _i_var) = extract_loop_body_assignments(ctx.body, &loop_var)
.ok_or_else(|| "[pattern9] Failed to extract body assignments".to_string())?;
// Step 3: variable_map から HOST ValueId を取得
let i_host_id = builder
.variable_ctx
.variable_map
.get(&loop_var)
.copied()
.ok_or_else(|| format!("[pattern9] Loop variable '{}' not found", loop_var))?;
let sum_host_id = builder
.variable_ctx
.variable_map
.get(&sum_var)
.copied()
.ok_or_else(|| format!("[pattern9] Accumulator variable '{}' not found", sum_var))?;
// Step 4: JoinIR lowerer を呼び出し
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
let mut join_value_space = JoinValueSpace::new();
let join_module = lower_accum_const_loop_joinir(limit, &mut join_value_space)?;
// Step 5: JoinInlineBoundary を構築
use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding;
use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder;
// k_exit のパラメータsum_exitを取得
let k_exit_func = join_module.require_function("k_exit", "Pattern 9");
let sum_exit_value = k_exit_func
.params
.first()
.copied()
.expect("[pattern9] k_exit must have parameter for sum");
// exit_bindings: sum のみi は捨てる)
let exit_binding = LoopExitBinding {
carrier_name: sum_var.clone(),
join_exit_value: sum_exit_value,
host_slot: sum_host_id,
role: CarrierRole::LoopState,
};
// main のパラメータi_init, sum_initを取得
let main_func = join_module.require_function("main", "Pattern 9");
let i_init_param = main_func
.params
.get(0)
.copied()
.expect("[pattern9] main must have i parameter");
let sum_init_param = main_func
.params
.get(1)
.copied()
.expect("[pattern9] main must have sum parameter");
let boundary = JoinInlineBoundaryBuilder::new()
.with_inputs(
vec![i_init_param, sum_init_param],
vec![i_host_id, sum_host_id],
)
.with_exit_bindings(vec![exit_binding])
.with_loop_var_name(Some(loop_var.clone()))
.build();
// Step 6: JoinIRConversionPipeline で MIR に変換
use super::conversion_pipeline::JoinIRConversionPipeline;
eprintln!("[pattern9] Lowering Phase270 fixture with Pattern9 (AccumConstLoop)");
let _ = JoinIRConversionPipeline::execute(
builder,
join_module,
Some(&boundary),
"pattern9",
ctx.debug,
)?;
// Phase 270 P1: Return Void (loop doesn't produce value)
let void_val = crate::mir::builder::emission::constant::emit_void(builder);
Ok(Some(void_val))
}
// ================================================================
// JoinIR Lowerer (Phase 270 P1 - 固定2キャリア: i, sum)
// ================================================================
/// Phase 270 P1: JoinIR lowerer for accumulator const loop
///
/// **構造**:
/// - main(i_init, sum_init) → loop_step(i, sum) → k_exit(sum)
/// - loop_step: 条件チェック → sum_next = sum + i → i_next = i + 1 → tail recursion
fn lower_accum_const_loop_joinir(
limit: i64,
join_value_space: &mut crate::mir::join_ir::lowering::join_value_space::JoinValueSpace,
) -> Result<crate::mir::join_ir::JoinModule, String> {
use crate::mir::join_ir::lowering::canonical_names as cn;
use crate::mir::join_ir::{
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule,
MirLikeInst, UnaryOp,
};
let mut join_module = JoinModule::new();
// Function IDs
let main_id = JoinFuncId::new(0);
let loop_step_id = JoinFuncId::new(1);
let k_exit_id = JoinFuncId::new(2);
// ValueId allocation
// main() params/locals
let i_main_param = join_value_space.alloc_param(); // i_init
let sum_main_param = join_value_space.alloc_param(); // sum_init
let loop_result = join_value_space.alloc_local(); // result from loop_step
// loop_step params/locals
let i_step_param = join_value_space.alloc_param(); // i
let sum_step_param = join_value_space.alloc_param(); // sum
let const_limit = join_value_space.alloc_local(); // limit constant
let cmp_lt = join_value_space.alloc_local(); // i < limit
let exit_cond = join_value_space.alloc_local(); // !(i < limit)
let sum_next = join_value_space.alloc_local(); // sum + i
let const_1 = join_value_space.alloc_local(); // increment constant
let i_next = join_value_space.alloc_local(); // i + 1
// k_exit params
let sum_exit_param = join_value_space.alloc_param(); // sum
// ================================================================
// main(i_init, sum_init) function
// ================================================================
let mut main_func = JoinFunction::new(
main_id,
"main".to_string(),
vec![i_main_param, sum_main_param],
);
// result = loop_step(i_main_param, sum_main_param)
main_func.body.push(JoinInst::Call {
func: loop_step_id,
args: vec![i_main_param, sum_main_param],
k_next: None,
dst: Some(loop_result),
});
// return result
main_func.body.push(JoinInst::Ret {
value: Some(loop_result),
});
join_module.add_function(main_func);
// ================================================================
// loop_step(i, sum) function
// ================================================================
let mut loop_step_func = JoinFunction::new(
loop_step_id,
cn::LOOP_STEP.to_string(),
vec![i_step_param, sum_step_param],
);
// cond = (i < limit)
loop_step_func
.body
.push(JoinInst::Compute(MirLikeInst::Const {
dst: const_limit,
value: ConstValue::Integer(limit),
}));
loop_step_func
.body
.push(JoinInst::Compute(MirLikeInst::Compare {
dst: cmp_lt,
op: CompareOp::Lt,
lhs: i_step_param,
rhs: const_limit,
}));
// exit_cond = !cond
loop_step_func
.body
.push(JoinInst::Compute(MirLikeInst::UnaryOp {
dst: exit_cond,
op: UnaryOp::Not,
operand: cmp_lt,
}));
// Jump(k_exit, [sum], cond=exit_cond)
loop_step_func.body.push(JoinInst::Jump {
cont: k_exit_id.as_cont(),
args: vec![sum_step_param],
cond: Some(exit_cond),
});
// sum_next = sum + i
loop_step_func
.body
.push(JoinInst::Compute(MirLikeInst::BinOp {
dst: sum_next,
op: BinOpKind::Add,
lhs: sum_step_param,
rhs: i_step_param,
}));
// i_next = i + 1
loop_step_func
.body
.push(JoinInst::Compute(MirLikeInst::Const {
dst: const_1,
value: ConstValue::Integer(1),
}));
loop_step_func
.body
.push(JoinInst::Compute(MirLikeInst::BinOp {
dst: i_next,
op: BinOpKind::Add,
lhs: i_step_param,
rhs: const_1,
}));
// Call(loop_step, [i_next, sum_next]) - tail recursion
loop_step_func.body.push(JoinInst::Call {
func: loop_step_id,
args: vec![i_next, sum_next],
k_next: None,
dst: None,
});
join_module.add_function(loop_step_func);
// ================================================================
// k_exit(sum) function
// ================================================================
let mut k_exit_func = JoinFunction::new(k_exit_id, cn::K_EXIT.to_string(), vec![sum_exit_param]);
// return sum
k_exit_func.body.push(JoinInst::Ret {
value: Some(sum_exit_param),
});
join_module.add_function(k_exit_func);
// Set entry point
join_module.entry = Some(main_id);
eprintln!("[joinir/pattern9] Generated JoinIR for AccumConstLoop Pattern");
eprintln!("[joinir/pattern9] Functions: main, loop_step, k_exit");
Ok(join_module)
}
// ================================================================
// Helper Functions (Fail-Fast 検証)
// ================================================================
/// ループ条件を抽出: `i < <int literal>` のみ受理
///
/// Returns: Some((loop_var, limit)) or None
fn extract_loop_condition(condition: &ASTNode) -> Option<(String, i64)> {
match condition {
ASTNode::BinaryOp {
operator: BinaryOperator::Less,
left,
right,
..
} => {
// left: Variable
let loop_var = match &**left {
ASTNode::Variable { name, .. } => name.clone(),
_ => return None,
};
// right: Literal::Integer
let limit = match &**right {
ASTNode::Literal {
value: LiteralValue::Integer(val),
..
} => *val,
_ => return None,
};
Some((loop_var, limit))
}
_ => None,
}
}
/// ループ本体の代入を抽出: `sum = sum + i; i = i + 1` のみ受理
///
/// Returns: Some((sum_var, i_var)) or None
fn extract_loop_body_assignments(body: &[ASTNode], expected_i: &str) -> Option<(String, String)> {
// 厳密に2つの代入のみ
if body.len() != 2 {
return None;
}
// 1つ目: sum = sum + i
let sum_var = match &body[0] {
ASTNode::Assignment { target, value, .. } => {
let sum_name = match target.as_ref() {
ASTNode::Variable { name, .. } => name.clone(),
_ => return None,
};
// value: sum + i
match value.as_ref() {
ASTNode::BinaryOp {
operator: BinaryOperator::Add,
left,
right,
..
} => {
// left: sum
let left_var = match left.as_ref() {
ASTNode::Variable { name, .. } => name,
_ => return None,
};
if left_var != &sum_name {
return None;
}
// right: i
let right_var = match right.as_ref() {
ASTNode::Variable { name, .. } => name,
_ => return None,
};
if right_var != expected_i {
return None;
}
sum_name
}
_ => return None,
}
}
_ => return None,
};
// 2つ目: i = i + 1
match &body[1] {
ASTNode::Assignment { target, value, .. } => {
let i_name = match target.as_ref() {
ASTNode::Variable { name, .. } => name,
_ => return None,
};
if i_name != expected_i {
return None;
}
// value: i + 1
match value.as_ref() {
ASTNode::BinaryOp {
operator: BinaryOperator::Add,
left,
right,
..
} => {
// left: i
let left_var = match left.as_ref() {
ASTNode::Variable { name, .. } => name,
_ => return None,
};
if left_var != expected_i {
return None;
}
// right: 1
match right.as_ref() {
ASTNode::Literal {
value: LiteralValue::Integer(1),
..
} => {}
_ => return None,
}
}
_ => return None,
}
}
_ => return None,
}
Some((sum_var, expected_i.to_string()))
}
/// break/continue/return の有無をチェック
fn has_control_flow(body: &[ASTNode]) -> bool {
body.iter().any(|stmt| {
matches!(
stmt,
ASTNode::Break { .. } | ASTNode::Continue { .. } | ASTNode::Return { .. }
)
})
}

View File

@ -208,6 +208,11 @@ pub(crate) static LOOP_PATTERNS: &[LoopPatternEntry] = &[
detect: super::pattern8_scan_bool_predicate::can_lower,
lower: super::pattern8_scan_bool_predicate::lower,
},
LoopPatternEntry {
name: "Pattern9_AccumConstLoop", // Phase 270 P1: accumulator const loop (橋渡しパターン, before P1)
detect: super::pattern9_accum_const_loop::can_lower,
lower: super::pattern9_accum_const_loop::lower,
},
LoopPatternEntry {
name: "Pattern3_WithIfPhi",
detect: super::pattern3_with_if_phi::can_lower,

View File

@ -36,3 +36,100 @@ pub fn emit_jump(b: &mut MirBuilder, target: BasicBlockId) -> Result<(), String>
})
}
}
/// Phase 268 P0: EdgeCFG Fragment ベースの if 条件分岐 emit
///
/// # 責務
/// - Frag 構築then/else/join の3断片
/// - compose::if_() で合成
/// - emit_frag() で MIR terminator に変換
///
/// # 引数
/// - `b`: MirBuilder への可変参照
/// - `pre_branch_bb`: 分岐前のヘッダーブロック
/// - `condition_val`: 分岐条件ValueId
/// - `then_block`: then 側の entry ブロック
/// - `then_exit_block`: then 側の exit ブロックmerge への飛び元)
/// - `then_reaches_merge`: then が merge に到達するか
/// - `else_block`: else 側の entry ブロック
/// - `else_exit_block`: else 側の exit ブロックmerge への飛び元)
/// - `else_reaches_merge`: else が merge に到達するか
/// - `merge_block`: merge ブロック
///
/// # 注意
/// - then_exit_block/else_exit_block は「実際に merge へ飛ぶブロック」と一致必須
/// - P0 では edge-args は空CarriersOnly, values=[]
///
/// # Phase 268 アーキテクチャ
/// ```text
/// if_form.rs (MirBuilder 層)
/// ↓ 呼び出し
/// emission/branch.rs::emit_conditional_edgecfg() (emission 層: 薄ラッパー)
/// ↓ 内部で使用
/// Frag 構築 + compose::if_() + emit_frag() (EdgeCFG Fragment API)
/// ↓ 最終的に呼び出し
/// set_branch_with_edge_args() / set_jump_with_edge_args() (Phase 260 SSOT)
/// ```
pub fn emit_conditional_edgecfg(
b: &mut MirBuilder,
pre_branch_bb: BasicBlockId,
condition_val: crate::mir::ValueId,
then_block: BasicBlockId,
then_exit_block: BasicBlockId,
then_reaches_merge: bool,
else_block: BasicBlockId,
else_exit_block: BasicBlockId,
else_reaches_merge: bool,
merge_block: BasicBlockId,
) -> Result<(), String> {
use crate::mir::builder::control_flow::edgecfg::api::{
compose, emit_frag, EdgeStub, ExitKind, Frag,
};
// Then Frag 構築from = then_exit_block: 実際の merge 飛び元)
let then_frag = if then_reaches_merge {
let stub = EdgeStub::without_args(then_exit_block, ExitKind::Normal);
Frag::with_single_exit(then_block, stub)
} else {
// Early return: no Normal exit
Frag::new(then_block)
};
// Else Frag 構築from = else_exit_block: 実際の merge 飛び元)
let else_frag = if else_reaches_merge {
let stub = EdgeStub::without_args(else_exit_block, ExitKind::Normal);
Frag::with_single_exit(else_block, stub)
} else {
// Early return: no Normal exit
Frag::new(else_block)
};
// Join Frag 構築
let join_frag = Frag::new(merge_block);
// Compose if_ (Phase 268 P1: entry edge-args from caller)
let if_frag = compose::if_(
pre_branch_bb,
condition_val,
then_frag,
crate::mir::basic_block::EdgeArgs {
layout: crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout::CarriersOnly,
values: vec![],
},
else_frag,
crate::mir::basic_block::EdgeArgs {
layout: crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout::CarriersOnly,
values: vec![],
},
join_frag,
);
// Emit to MIR
if let Some(ref mut func) = b.scope_ctx.current_function {
emit_frag(func, &if_frag)?;
} else {
return Err("[emit_conditional_edgecfg] current_function is None".to_string());
}
Ok(())
}

View File

@ -106,12 +106,7 @@ impl MirBuilder {
let pre_branch_bb = self.current_block()?;
let mut condition_val = condition_val;
crate::mir::builder::ssa::local::finalize_branch_cond(self, &mut condition_val);
crate::mir::builder::emission::branch::emit_conditional(
self,
condition_val,
then_block,
else_block,
)?;
// Phase 268 P0: emit_conditional() deleted (replaced by emit_conditional_edgecfg() at line 206)
// Snapshot variables before entering branches
let pre_if_var_map = self.variable_ctx.variable_map.clone();
@ -144,7 +139,7 @@ impl MirBuilder {
if then_reaches_merge {
// Scope leave for then-branch
self.hint_scope_leave(0);
crate::mir::builder::emission::branch::emit_jump(self, merge_block)?;
// Phase 268 P0: emit_jump() deleted (handled by emit_conditional_edgecfg())
}
// Pop then-branch debug region
self.debug_pop_region();
@ -199,11 +194,25 @@ impl MirBuilder {
if else_reaches_merge {
// Scope leave for else-branch
self.hint_scope_leave(0);
crate::mir::builder::emission::branch::emit_jump(self, merge_block)?;
// Phase 268 P0: emit_jump() deleted (handled by emit_conditional_edgecfg())
}
// Pop else-branch debug region
self.debug_pop_region();
// Phase 268 P0: EdgeCFG Fragment ベース emitemission 層経由)
crate::mir::builder::emission::branch::emit_conditional_edgecfg(
self,
pre_branch_bb,
condition_val,
then_block,
then_exit_block,
then_reaches_merge,
else_block,
else_exit_block,
else_reaches_merge,
merge_block,
)?;
// merge: primary result via helper, then delta-based variable merges
// Ensure PHIs are first in the block by suppressing entry pin copies here
self.suppress_next_entry_pin_copy();