phase29am(p0): implement coreplan if/exit lowering
This commit is contained in:
@ -17,7 +17,7 @@
|
||||
//! - Legacy fallback has been removed (Fail-Fast on missing fields)
|
||||
//! - Pattern-specific emission functions (emit_scan_with_init_edgecfg) no longer used
|
||||
|
||||
use super::{CoreEffectPlan, CoreLoopPlan, CorePlan};
|
||||
use super::{CoreEffectPlan, CoreExitPlan, CoreIfPlan, CoreLoopPlan, CorePlan};
|
||||
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::{MirInstruction, ValueId};
|
||||
@ -41,18 +41,12 @@ impl PlanLowerer {
|
||||
match plan {
|
||||
CorePlan::Seq(plans) => Self::lower_seq(builder, plans, ctx),
|
||||
CorePlan::Loop(loop_plan) => Self::lower_loop(builder, loop_plan, ctx),
|
||||
CorePlan::If(_if_plan) => {
|
||||
// P1: If is handled inline in Loop body
|
||||
Err("[lowerer] Standalone CorePlan::If not yet supported".to_string())
|
||||
}
|
||||
CorePlan::If(if_plan) => Self::lower_if(builder, if_plan, ctx),
|
||||
CorePlan::Effect(effect) => {
|
||||
Self::emit_effect(builder, &effect)?;
|
||||
Ok(None)
|
||||
}
|
||||
CorePlan::Exit(_exit) => {
|
||||
// P1: Exit is handled by edge CFG in Loop
|
||||
Err("[lowerer] Standalone CorePlan::Exit not yet supported".to_string())
|
||||
}
|
||||
CorePlan::Exit(exit) => Self::lower_exit(builder, exit),
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,6 +97,74 @@ impl PlanLowerer {
|
||||
Self::lower_loop_generalized(builder, loop_plan, ctx)
|
||||
}
|
||||
|
||||
/// If: emit Branch and lower then/else plans (standalone)
|
||||
fn lower_if(
|
||||
builder: &mut MirBuilder,
|
||||
if_plan: CoreIfPlan,
|
||||
ctx: &LoopPatternContext,
|
||||
) -> Result<Option<ValueId>, String> {
|
||||
use crate::mir::builder::emission::branch::{emit_conditional, emit_jump};
|
||||
|
||||
let _pre_branch_bb = builder
|
||||
.current_block
|
||||
.ok_or_else(|| "[lowerer] No current block for CorePlan::If".to_string())?;
|
||||
|
||||
let then_bb = builder.next_block_id();
|
||||
let else_bb = builder.next_block_id();
|
||||
let merge_bb = builder.next_block_id();
|
||||
|
||||
builder.ensure_block_exists(then_bb)?;
|
||||
builder.ensure_block_exists(else_bb)?;
|
||||
builder.ensure_block_exists(merge_bb)?;
|
||||
|
||||
let mut condition_val = if_plan.condition;
|
||||
crate::mir::builder::ssa::local::finalize_branch_cond(builder, &mut condition_val);
|
||||
emit_conditional(builder, condition_val, then_bb, else_bb)?;
|
||||
|
||||
// then
|
||||
builder.start_new_block(then_bb)?;
|
||||
for plan in if_plan.then_plans {
|
||||
Self::lower(builder, plan, ctx)?;
|
||||
}
|
||||
let then_reaches_merge = !builder.is_current_block_terminated();
|
||||
if then_reaches_merge {
|
||||
emit_jump(builder, merge_bb)?;
|
||||
}
|
||||
|
||||
// else
|
||||
builder.start_new_block(else_bb)?;
|
||||
if let Some(else_plans) = if_plan.else_plans {
|
||||
for plan in else_plans {
|
||||
Self::lower(builder, plan, ctx)?;
|
||||
}
|
||||
}
|
||||
let else_reaches_merge = !builder.is_current_block_terminated();
|
||||
if else_reaches_merge {
|
||||
emit_jump(builder, merge_bb)?;
|
||||
}
|
||||
|
||||
// merge (may be unreachable if both branches terminate)
|
||||
builder.start_new_block(merge_bb)?;
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Exit: emit Return (standalone); Break/Continue require loop context
|
||||
fn lower_exit(
|
||||
builder: &mut MirBuilder,
|
||||
exit: CoreExitPlan,
|
||||
) -> Result<Option<ValueId>, String> {
|
||||
match exit {
|
||||
CoreExitPlan::Return(opt_val) => {
|
||||
builder.emit_instruction(MirInstruction::Return { value: opt_val })?;
|
||||
Ok(opt_val)
|
||||
}
|
||||
CoreExitPlan::Break => Err("[lowerer] CorePlan::Exit::Break requires loop context".to_string()),
|
||||
CoreExitPlan::Continue => {
|
||||
Err("[lowerer] CorePlan::Exit::Continue requires loop context".to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 273 P3: Generalized loop lowering (SSOT)
|
||||
fn lower_loop_generalized(
|
||||
builder: &mut MirBuilder,
|
||||
@ -265,3 +327,89 @@ impl PlanLowerer {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::ast::{ASTNode, LiteralValue, Span};
|
||||
use crate::mir::{ConstValue, MirInstruction};
|
||||
|
||||
fn make_ctx<'a>(condition: &'a ASTNode, body: &'a [ASTNode]) -> LoopPatternContext<'a> {
|
||||
LoopPatternContext::new(condition, body, "test_coreplan", false, false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lower_exit_return_sets_terminator() {
|
||||
let mut builder = MirBuilder::new();
|
||||
builder.enter_function_for_test("test_exit".to_string());
|
||||
|
||||
let ret_val = builder.alloc_value_for_test();
|
||||
builder
|
||||
.emit_for_test(MirInstruction::Const {
|
||||
dst: ret_val,
|
||||
value: ConstValue::Integer(1),
|
||||
})
|
||||
.expect("emit const");
|
||||
|
||||
let cond = ASTNode::Literal {
|
||||
value: LiteralValue::Bool(true),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let body: Vec<ASTNode> = vec![];
|
||||
let ctx = make_ctx(&cond, &body);
|
||||
|
||||
let plan = CorePlan::Exit(CoreExitPlan::Return(Some(ret_val)));
|
||||
let result = PlanLowerer::lower(&mut builder, plan, &ctx);
|
||||
assert!(result.is_ok());
|
||||
|
||||
let entry = builder.current_block_for_test().expect("entry block");
|
||||
let func = builder.scope_ctx.current_function.as_ref().expect("function");
|
||||
let block = func.get_block(entry).expect("block");
|
||||
assert!(
|
||||
matches!(block.terminator, Some(MirInstruction::Return { value: Some(v) }) if v == ret_val),
|
||||
"expected Return terminator"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lower_if_emits_branch() {
|
||||
let mut builder = MirBuilder::new();
|
||||
builder.enter_function_for_test("test_if".to_string());
|
||||
|
||||
let entry = builder.current_block_for_test().expect("entry block");
|
||||
let cond_val = builder.alloc_value_for_test();
|
||||
builder
|
||||
.emit_for_test(MirInstruction::Const {
|
||||
dst: cond_val,
|
||||
value: ConstValue::Bool(true),
|
||||
})
|
||||
.expect("emit const");
|
||||
|
||||
let then_val = builder.alloc_value_for_test();
|
||||
let if_plan = CoreIfPlan {
|
||||
condition: cond_val,
|
||||
then_plans: vec![CorePlan::Effect(CoreEffectPlan::Const {
|
||||
dst: then_val,
|
||||
value: ConstValue::Integer(2),
|
||||
})],
|
||||
else_plans: None,
|
||||
};
|
||||
|
||||
let cond = ASTNode::Literal {
|
||||
value: LiteralValue::Bool(true),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let body: Vec<ASTNode> = vec![];
|
||||
let ctx = make_ctx(&cond, &body);
|
||||
|
||||
let result = PlanLowerer::lower(&mut builder, CorePlan::If(if_plan), &ctx);
|
||||
assert!(result.is_ok());
|
||||
|
||||
let func = builder.scope_ctx.current_function.as_ref().expect("function");
|
||||
let block = func.get_block(entry).expect("entry block");
|
||||
assert!(
|
||||
matches!(block.terminator, Some(MirInstruction::Branch { .. })),
|
||||
"expected Branch terminator"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user