feat(edgecfg): Phase 268-270 savepoint (if_form adoption + Pattern9 minimal loop SSOT)
This commit is contained in:
@ -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));
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -568,3 +568,94 @@ impl MirBuilder {
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 269 P0: Pattern8 Frag 版 lowerer(test-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 terminator(Branch/Jump/Return)
|
||||
/// ```
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `builder`: MirBuilder
|
||||
/// - `join_module`: Pattern8 の JoinModule(lower_scan_bool_predicate_minimal から生成)
|
||||
/// - `boundary`: JoinInlineBoundary(host <-> join 境界情報)
|
||||
/// - `debug`: デバッグフラグ
|
||||
///
|
||||
/// # Returns
|
||||
/// - `Ok(Some(ValueId))`: expr_result(ret_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"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 { .. }
|
||||
)
|
||||
})
|
||||
}
|
||||
@ -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,
|
||||
|
||||
@ -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(())
|
||||
}
|
||||
|
||||
@ -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 ベース emit(emission 層経由)
|
||||
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();
|
||||
|
||||
Reference in New Issue
Block a user