phase29ao(p22): dedup pattern1 coreloop construction

This commit is contained in:
2025-12-30 09:58:25 +09:00
parent 8756ad7a28
commit 066da59136
8 changed files with 255 additions and 320 deletions

View File

@ -12,6 +12,7 @@
//! Lowerer processes CorePlan without any pattern knowledge.
mod helpers;
mod pattern1_coreloop_builder;
mod pattern1_simple_while;
mod pattern2_break;
mod pattern3_if_phi;

View File

@ -0,0 +1,214 @@
use super::helpers::{create_phi_bindings, LoopBlocksStandard5};
use super::{CoreEffectPlan, CoreLoopPlan, CorePhiInfo};
use crate::ast::ASTNode;
use crate::mir::basic_block::EdgeArgs;
use crate::mir::builder::control_flow::edgecfg::api::{BranchStub, EdgeStub, ExitKind, Frag};
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
use crate::mir::builder::MirBuilder;
use crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout;
use crate::mir::MirType;
use std::collections::BTreeMap;
pub(super) fn build_pattern1_coreloop(
builder: &mut MirBuilder,
loop_var: &str,
condition: &ASTNode,
loop_increment: &ASTNode,
_ctx: &LoopPatternContext,
) -> Result<CoreLoopPlan, String> {
let loop_var_init = builder
.variable_ctx
.variable_map
.get(loop_var)
.copied()
.ok_or_else(|| format!("[normalizer] Loop variable {} not found", loop_var))?;
let blocks = LoopBlocksStandard5::allocate(builder)?;
let LoopBlocksStandard5 {
preheader_bb,
header_bb,
body_bb,
step_bb,
after_bb,
} = blocks;
let loop_var_current = builder.alloc_typed(MirType::Integer);
let cond_loop = builder.alloc_typed(MirType::Bool);
let loop_var_next = builder.alloc_typed(MirType::Integer);
let phi_bindings = create_phi_bindings(&[(loop_var, loop_var_current)]);
let (loop_cond_lhs, loop_cond_op, loop_cond_rhs, loop_cond_consts) =
super::PlanNormalizer::lower_compare_ast(condition, builder, &phi_bindings)?;
let (loop_inc_lhs, loop_inc_op, loop_inc_rhs, loop_inc_consts) =
super::PlanNormalizer::lower_binop_ast(loop_increment, builder, &phi_bindings)?;
let mut header_effects = loop_cond_consts;
header_effects.push(CoreEffectPlan::Compare {
dst: cond_loop,
lhs: loop_cond_lhs,
op: loop_cond_op,
rhs: loop_cond_rhs,
});
let mut step_effects = loop_inc_consts;
step_effects.push(CoreEffectPlan::BinOp {
dst: loop_var_next,
lhs: loop_inc_lhs,
op: loop_inc_op,
rhs: loop_inc_rhs,
});
let block_effects = vec![
(preheader_bb, vec![]),
(header_bb, header_effects),
(body_bb, vec![]),
(step_bb, step_effects),
];
let phis = vec![CorePhiInfo {
block: header_bb,
dst: loop_var_current,
inputs: vec![(preheader_bb, loop_var_init), (step_bb, loop_var_next)],
tag: format!("loop_var_{}", loop_var),
}];
let empty_args = EdgeArgs {
layout: JumpArgsLayout::CarriersOnly,
values: vec![],
};
let branches = vec![BranchStub {
from: header_bb,
cond: cond_loop,
then_target: body_bb,
then_args: empty_args.clone(),
else_target: after_bb,
else_args: empty_args.clone(),
}];
let wires = vec![
EdgeStub {
from: body_bb,
kind: ExitKind::Normal,
target: Some(step_bb),
args: empty_args.clone(),
},
EdgeStub {
from: step_bb,
kind: ExitKind::Normal,
target: Some(header_bb),
args: empty_args.clone(),
},
];
let frag = Frag {
entry: header_bb,
block_params: BTreeMap::new(),
exits: BTreeMap::new(),
wires,
branches,
};
let final_values = vec![(loop_var.to_string(), loop_var_current)];
Ok(CoreLoopPlan {
preheader_bb,
header_bb,
body_bb,
step_bb,
after_bb,
found_bb: after_bb,
body: vec![],
cond_loop,
cond_match: cond_loop,
block_effects,
phis,
frag,
final_values,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::{ASTNode, BinaryOperator, LiteralValue, Span};
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
use crate::mir::builder::MirBuilder;
fn make_condition(loop_var: &str, limit: i64) -> ASTNode {
ASTNode::BinaryOp {
operator: BinaryOperator::Less,
left: Box::new(ASTNode::Variable {
name: loop_var.to_string(),
span: Span::unknown(),
}),
right: Box::new(ASTNode::Literal {
value: LiteralValue::Integer(limit),
span: Span::unknown(),
}),
span: Span::unknown(),
}
}
fn make_increment(loop_var: &str) -> ASTNode {
ASTNode::BinaryOp {
operator: BinaryOperator::Add,
left: Box::new(ASTNode::Variable {
name: loop_var.to_string(),
span: Span::unknown(),
}),
right: Box::new(ASTNode::Literal {
value: LiteralValue::Integer(1),
span: Span::unknown(),
}),
span: Span::unknown(),
}
}
fn make_body(loop_var: &str) -> Vec<ASTNode> {
vec![ASTNode::Assignment {
target: Box::new(ASTNode::Variable {
name: loop_var.to_string(),
span: Span::unknown(),
}),
value: Box::new(make_increment(loop_var)),
span: Span::unknown(),
}]
}
#[test]
fn build_pattern1_coreloop_has_expected_frag_shape() {
let mut builder = MirBuilder::new();
builder.enter_function_for_test("pattern1_coreloop_test".to_string());
let loop_var = "i";
let loop_var_init = builder.alloc_typed(MirType::Integer);
builder
.variable_ctx
.variable_map
.insert(loop_var.to_string(), loop_var_init);
let condition = make_condition(loop_var, 3);
let loop_increment = make_increment(loop_var);
let body = make_body(loop_var);
let ctx = LoopPatternContext::new(&condition, &body, "pattern1_coreloop_test", false, false);
let loop_plan = build_pattern1_coreloop(
&mut builder,
loop_var,
&condition,
&loop_increment,
&ctx,
)
.expect("pattern1 coreloop build should succeed");
assert_eq!(loop_plan.phis.len(), 1);
assert_eq!(loop_plan.frag.branches.len(), 1);
assert_eq!(loop_plan.frag.wires.len(), 2);
assert!(loop_plan.frag.exits.is_empty());
builder.exit_function_for_test();
}
}

View File

@ -1,12 +1,7 @@
use super::helpers::{create_phi_bindings, LoopBlocksStandard5};
use super::{CoreEffectPlan, CoreLoopPlan, CorePhiInfo, CorePlan, Pattern1SimpleWhilePlan};
use crate::mir::basic_block::EdgeArgs;
use crate::mir::builder::control_flow::edgecfg::api::{BranchStub, EdgeStub, ExitKind, Frag};
use super::pattern1_coreloop_builder::build_pattern1_coreloop;
use super::{CorePlan, Pattern1SimpleWhilePlan};
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
use crate::mir::builder::MirBuilder;
use crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout;
use crate::mir::MirType;
use std::collections::BTreeMap;
impl super::PlanNormalizer {
/// Phase 286 P2.1: Pattern1SimpleWhile → CorePlan 変換
@ -35,140 +30,13 @@ impl super::PlanNormalizer {
);
}
// Step 1: Get host ValueId for loop variable
let loop_var_init = builder
.variable_ctx
.variable_map
.get(&parts.loop_var)
.copied()
.ok_or_else(|| format!("[normalizer] Loop variable {} not found", parts.loop_var))?;
// Step 2-3: Allocate BasicBlockIds
let blocks = LoopBlocksStandard5::allocate(builder)?;
let LoopBlocksStandard5 {
preheader_bb,
header_bb,
body_bb,
step_bb,
after_bb,
} = blocks;
if debug {
trace_logger.debug(
"normalizer/pattern1_simple_while",
&format!(
"Allocated: preheader={:?}, header={:?}, body={:?}, step={:?}, after={:?}",
preheader_bb, header_bb, body_bb, step_bb, after_bb
),
);
}
// Step 4: Allocate ValueIds for loop control
let loop_var_current = builder.alloc_typed(MirType::Integer);
let cond_loop = builder.alloc_typed(MirType::Bool);
let loop_var_next = builder.alloc_typed(MirType::Integer);
// Step 5: Build phi_bindings
let phi_bindings = create_phi_bindings(&[(&parts.loop_var, loop_var_current)]);
// Step 6: Lower AST expressions
let (loop_cond_lhs, loop_cond_op, loop_cond_rhs, loop_cond_consts) =
Self::lower_compare_ast(&parts.condition, builder, &phi_bindings)?;
let (loop_inc_lhs, loop_inc_op, loop_inc_rhs, loop_inc_consts) =
Self::lower_binop_ast(&parts.loop_increment, builder, &phi_bindings)?;
// Step 7: Build header_effects
let mut header_effects = loop_cond_consts;
header_effects.push(CoreEffectPlan::Compare {
dst: cond_loop,
lhs: loop_cond_lhs,
op: loop_cond_op,
rhs: loop_cond_rhs,
});
// Step 8: Build step_effects
let mut step_effects = loop_inc_consts;
step_effects.push(CoreEffectPlan::BinOp {
dst: loop_var_next,
lhs: loop_inc_lhs,
op: loop_inc_op,
rhs: loop_inc_rhs,
});
// Step 9: Build block_effects
let block_effects = vec![
(preheader_bb, vec![]),
(header_bb, header_effects),
(body_bb, vec![]),
(step_bb, step_effects),
];
// Step 10: Build PHI
let phis = vec![CorePhiInfo {
block: header_bb,
dst: loop_var_current,
inputs: vec![(preheader_bb, loop_var_init), (step_bb, loop_var_next)],
tag: format!("loop_var_{}", parts.loop_var),
}];
// Step 11: Build Frag
let empty_args = EdgeArgs {
layout: JumpArgsLayout::CarriersOnly,
values: vec![],
};
let branches = vec![BranchStub {
from: header_bb,
cond: cond_loop,
then_target: body_bb,
then_args: empty_args.clone(),
else_target: after_bb,
else_args: empty_args.clone(),
}];
let wires = vec![
EdgeStub {
from: body_bb,
kind: ExitKind::Normal,
target: Some(step_bb),
args: empty_args.clone(),
},
EdgeStub {
from: step_bb,
kind: ExitKind::Normal,
target: Some(header_bb),
args: empty_args.clone(),
},
];
let frag = Frag {
entry: header_bb,
block_params: BTreeMap::new(),
exits: BTreeMap::new(),
wires,
branches,
};
// Step 12: Build final_values
let final_values = vec![(parts.loop_var.clone(), loop_var_current)];
// Step 13: Build CoreLoopPlan
let loop_plan = CoreLoopPlan {
preheader_bb,
header_bb,
body_bb,
step_bb,
after_bb,
found_bb: after_bb,
body: vec![],
cond_loop,
cond_match: cond_loop,
block_effects,
phis,
frag,
final_values,
};
let loop_plan = build_pattern1_coreloop(
builder,
&parts.loop_var,
&parts.condition,
&parts.loop_increment,
ctx,
)?;
if debug {
trace_logger.debug(

View File

@ -1,24 +1,15 @@
use super::helpers::{create_phi_bindings, LoopBlocksStandard5};
use super::{CoreEffectPlan, CoreLoopPlan, CorePhiInfo, CorePlan};
use crate::mir::basic_block::{BasicBlockId, EdgeArgs};
use super::pattern1_coreloop_builder::build_pattern1_coreloop;
use super::CorePlan;
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
use crate::mir::builder::control_flow::plan::facts::feature_facts::{
CleanupKindFacts, ExitKindFacts,
};
use crate::mir::builder::control_flow::plan::facts::skeleton_facts::SkeletonKind;
use crate::mir::builder::control_flow::plan::normalize::CanonicalLoopFacts;
use crate::mir::builder::control_flow::edgecfg::api::{BranchStub, EdgeStub, ExitKind, Frag};
use crate::mir::builder::MirBuilder;
use crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout;
use crate::mir::control_form::LoopId;
use crate::mir::MirType;
use std::collections::{BTreeMap, BTreeSet};
impl super::PlanNormalizer {
pub(in crate::mir::builder) fn normalize_loop_skeleton_from_facts(
builder: &mut MirBuilder,
facts: &CanonicalLoopFacts,
_ctx: &LoopPatternContext,
ctx: &LoopPatternContext,
) -> Result<Option<CorePlan>, String> {
if facts.value_join_needed {
return Ok(None);
@ -30,165 +21,14 @@ impl super::PlanNormalizer {
return Ok(None);
};
let loop_var_init = builder
.variable_ctx
.variable_map
.get(&pattern1.loop_var)
.copied()
.ok_or_else(|| {
format!(
"[normalizer] Loop variable {} not found",
pattern1.loop_var
)
})?;
let blocks = LoopBlocksStandard5::allocate(builder)?;
let LoopBlocksStandard5 {
preheader_bb,
header_bb,
body_bb,
step_bb,
after_bb,
} = blocks;
let loop_var_current = builder.alloc_typed(MirType::Integer);
let cond_loop = builder.alloc_typed(MirType::Bool);
let loop_var_next = builder.alloc_typed(MirType::Integer);
let phi_bindings = create_phi_bindings(&[(&pattern1.loop_var, loop_var_current)]);
let (loop_cond_lhs, loop_cond_op, loop_cond_rhs, loop_cond_consts) =
Self::lower_compare_ast(&pattern1.condition, builder, &phi_bindings)?;
let (loop_inc_lhs, loop_inc_op, loop_inc_rhs, loop_inc_consts) =
Self::lower_binop_ast(&pattern1.loop_increment, builder, &phi_bindings)?;
let mut header_effects = loop_cond_consts;
header_effects.push(CoreEffectPlan::Compare {
dst: cond_loop,
lhs: loop_cond_lhs,
op: loop_cond_op,
rhs: loop_cond_rhs,
});
let mut step_effects = loop_inc_consts;
step_effects.push(CoreEffectPlan::BinOp {
dst: loop_var_next,
lhs: loop_inc_lhs,
op: loop_inc_op,
rhs: loop_inc_rhs,
});
let block_effects = vec![
(preheader_bb, vec![]),
(header_bb, header_effects),
(body_bb, vec![]),
(step_bb, step_effects),
];
let phis = vec![CorePhiInfo {
block: header_bb,
dst: loop_var_current,
inputs: vec![(preheader_bb, loop_var_init), (step_bb, loop_var_next)],
tag: format!("loop_var_{}", pattern1.loop_var),
}];
let empty_args = EdgeArgs {
layout: JumpArgsLayout::CarriersOnly,
values: vec![],
};
let branches = vec![BranchStub {
from: header_bb,
cond: cond_loop,
then_target: body_bb,
then_args: empty_args.clone(),
else_target: after_bb,
else_args: empty_args.clone(),
}];
let wires = vec![
EdgeStub {
from: body_bb,
kind: ExitKind::Normal,
target: Some(step_bb),
args: empty_args.clone(),
},
EdgeStub {
from: step_bb,
kind: ExitKind::Normal,
target: Some(header_bb),
args: empty_args.clone(),
},
];
let frag = Frag {
entry: header_bb,
block_params: BTreeMap::new(),
exits: build_exitmap_from_presence(
&facts.exit_kinds_present,
&facts.cleanup_kinds_present,
body_bb,
),
wires,
branches,
};
let final_values = vec![(pattern1.loop_var.clone(), loop_var_current)];
let loop_plan = CoreLoopPlan {
preheader_bb,
header_bb,
body_bb,
step_bb,
after_bb,
found_bb: after_bb,
body: vec![],
cond_loop,
cond_match: cond_loop,
block_effects,
phis,
frag,
final_values,
};
let loop_plan = build_pattern1_coreloop(
builder,
&pattern1.loop_var,
&pattern1.condition,
&pattern1.loop_increment,
ctx,
)?;
Ok(Some(CorePlan::Loop(loop_plan)))
}
}
fn build_exitmap_from_presence(
exit_kinds_present: &BTreeSet<ExitKindFacts>,
cleanup_kinds_present: &BTreeSet<CleanupKindFacts>,
from: BasicBlockId,
) -> BTreeMap<ExitKind, Vec<EdgeStub>> {
let present = union_exit_kinds(exit_kinds_present, cleanup_kinds_present);
let mut exits = BTreeMap::new();
for kind in present {
let exit_kind = match kind {
ExitKindFacts::Return => ExitKind::Return,
ExitKindFacts::Break => ExitKind::Break(LoopId(0)),
ExitKindFacts::Continue => ExitKind::Continue(LoopId(0)),
};
exits.insert(exit_kind, vec![EdgeStub::without_args(from, exit_kind)]);
}
exits
}
fn union_exit_kinds(
exit_kinds_present: &BTreeSet<ExitKindFacts>,
cleanup_kinds_present: &BTreeSet<CleanupKindFacts>,
) -> BTreeSet<ExitKindFacts> {
let mut combined = exit_kinds_present.clone();
combined.extend(
cleanup_kinds_present.iter().map(exit_kind_facts_from_cleanup_kind),
);
combined
}
fn exit_kind_facts_from_cleanup_kind(kind: &CleanupKindFacts) -> ExitKindFacts {
match kind {
CleanupKindFacts::Return => ExitKindFacts::Return,
CleanupKindFacts::Break => ExitKindFacts::Break,
CleanupKindFacts::Continue => ExitKindFacts::Continue,
}
}