phase29ao(p33): planner-derive pattern2 loopbodylocal smokes
This commit is contained in:
@ -3,8 +3,7 @@
|
||||
## Current Focus
|
||||
|
||||
- Phase: `docs/development/current/main/phases/phase-29ao/README.md`
|
||||
- Next: Phase 29ao P33(Pattern2 LoopBodyLocal planner-derive + tag gate)
|
||||
- 指示書: `docs/development/current/main/phases/phase-29ao/P33-PLANNER-DERIVE-PATTERN2-LOOPBODYLOCAL-SMOKES-INSTRUCTIONS.md`
|
||||
- Next: Phase 29ao P34(TBD)
|
||||
|
||||
## Gate (SSOT)
|
||||
|
||||
|
||||
@ -5,8 +5,7 @@ Scope: 「次にやる候補」を短く列挙するメモ。入口は `docs/dev
|
||||
|
||||
## Active
|
||||
|
||||
- CorePlan migration: `docs/development/current/main/phases/phase-29ao/README.md`(Next: P33)
|
||||
- 指示書: `docs/development/current/main/phases/phase-29ao/P33-PLANNER-DERIVE-PATTERN2-LOOPBODYLOCAL-SMOKES-INSTRUCTIONS.md`
|
||||
- CorePlan migration: `docs/development/current/main/phases/phase-29ao/README.md`(Next: P34 TBD)
|
||||
|
||||
## Near-Term Candidates
|
||||
|
||||
|
||||
@ -34,8 +34,8 @@ Related:
|
||||
## 1.1 Current (active)
|
||||
|
||||
- Active phase: `docs/development/current/main/phases/phase-29ao/README.md`
|
||||
- Next step: `docs/development/current/main/phases/phase-29ao/P33-PLANNER-DERIVE-PATTERN2-LOOPBODYLOCAL-SMOKES-INSTRUCTIONS.md`
|
||||
- After P33: TBD
|
||||
- Next step: Phase 29ao P34 (TBD)
|
||||
- After P34: TBD
|
||||
|
||||
## 2. すでに固めた SSOT(再発防止の土台)
|
||||
|
||||
|
||||
@ -2,12 +2,17 @@
|
||||
|
||||
## Current Focus: Phase 29ao(CorePlan composition)
|
||||
|
||||
Next: Phase 29ao P33(TBD)
|
||||
Next: Phase 29ao P34(TBD)
|
||||
指示書: TBD
|
||||
運用ルール: integration filter で phase143_* は回さない(JoinIR 回帰は phase29ae pack のみ)
|
||||
運用ルール: phase286_pattern9_* は legacy pack (SKIP) を使う
|
||||
移行道筋 SSOT: `docs/development/current/main/design/coreplan-migration-roadmap-ssot.md`
|
||||
|
||||
**2025-12-30: Phase 29ao P33 完了** ✅
|
||||
- 目的: Pattern2 LoopBodyLocal を planner 由来 Pattern2Break に引き上げ、strict/dev の shadow adopt タグを回帰で固定(仕様不変)
|
||||
- 変更: `src/mir/builder/control_flow/plan/facts/pattern2_break_facts.rs` / `src/mir/builder/control_flow/plan/normalizer/pattern2_break.rs` / `tools/smokes/v2/profiles/integration/apps/phase29ab_pattern2_loopbodylocal_min_vm.sh` / `tools/smokes/v2/profiles/integration/apps/phase29ab_pattern2_loopbodylocal_seg_min_vm.sh` / `docs/development/current/main/phases/phase-29ao/README.md` / `docs/development/current/main/10-Now.md` / `docs/development/current/main/30-Backlog.md` / `docs/development/current/main/design/coreplan-migration-roadmap-ssot.md`
|
||||
- 検証: `cargo build --release` / `./tools/smokes/v2/run.sh --profile quick` / `./tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh`
|
||||
|
||||
**2025-12-30: Phase 29ao P32 完了** ✅
|
||||
- 目的: Pattern2 real-world を planner subset に引き上げ、strict/dev で Facts→CorePlan shadow adopt を踏ませる(仕様不変)
|
||||
- 変更: `src/mir/builder/control_flow/plan/facts/pattern2_break_facts.rs` / `tools/smokes/v2/profiles/integration/apps/phase263_pattern2_seg_realworld_min_vm.sh` / `docs/development/current/main/phases/phase-29ao/README.md` / `docs/development/current/main/10-Now.md` / `docs/development/current/main/30-Backlog.md` / `CURRENT_TASK.md` / `docs/development/current/main/design/coreplan-migration-roadmap-ssot.md`
|
||||
|
||||
@ -15,7 +15,7 @@ Related:
|
||||
|
||||
- **Phase 29ao(active): CorePlan composition from Skeleton/Feature**
|
||||
- 入口: `docs/development/current/main/phases/phase-29ao/README.md`
|
||||
- 状況: P0–P32 ✅ 完了 / Next: P33(TBD)
|
||||
- 状況: P0–P33 ✅ 完了 / Next: P34(TBD)
|
||||
- Next 指示書: TBD
|
||||
|
||||
- **Phase 29af(✅ COMPLETE): Boundary hygiene / regression entrypoint / carrier layout SSOT**
|
||||
|
||||
@ -18,10 +18,13 @@ Scope: Repo root の旧リンク互換。現行の入口は `docs/development/cu
|
||||
**CorePlan migration 道筋 SSOT**
|
||||
`docs/development/current/main/design/coreplan-migration-roadmap-ssot.md` が移行タスクの Done 判定の入口。
|
||||
|
||||
**Next implementation (Phase 29ao P33)**
|
||||
**Next implementation (Phase 29ao P34)**
|
||||
- 目的: TBD
|
||||
- 指示書: TBD
|
||||
- After P33: TBD
|
||||
- After P34: TBD
|
||||
|
||||
**2025-12-30: Phase 29ao P33 COMPLETE (Pattern2 LoopBodyLocal planner-derive + tag gate)**
|
||||
Pattern2 LoopBodyLocal を planner 由来 Pattern2Break に引き上げ、strict/dev の shadow adopt タグを回帰で固定した(仕様不変)。
|
||||
|
||||
**2025-12-30: Phase 29ao P32 COMPLETE (Pattern2 real-world strict/dev shadow adopt)**
|
||||
Pattern2 real-world を planner subset に引き上げ、strict/dev で Facts→CorePlan shadow adopt を踏ませた(仕様不変)。
|
||||
|
||||
@ -186,8 +186,12 @@ Gate(SSOT):
|
||||
- 指示書: `docs/development/current/main/phases/phase-29ao/P32-STRICT-ADOPT-PATTERN2-REALWORLD-FROM-FACTS-INSTRUCTIONS.md`
|
||||
- ねらい: `phase263_pattern2_*` が strict/dev で Facts→CorePlan shadow adopt を踏むことを “タグ必須” で固定し、CorePlan 完全移行の回帰穴を塞ぐ(仕様不変)
|
||||
|
||||
## P33: Pattern2 LoopBodyLocal planner-derive + tag gate ✅
|
||||
|
||||
- 指示書: `docs/development/current/main/phases/phase-29ao/P33-PLANNER-DERIVE-PATTERN2-LOOPBODYLOCAL-SMOKES-INSTRUCTIONS.md`
|
||||
- ねらい: `phase29ab_pattern2_loopbodylocal_{min,seg_min}` を planner 由来 Pattern2Break に引き上げ、shadow adopt タグを strict/dev 回帰で必須化(仕様不変)
|
||||
|
||||
## Next(planned)
|
||||
|
||||
- Next: P33(Pattern2 LoopBodyLocal planner-derive + tag gate)
|
||||
- 指示書: `docs/development/current/main/phases/phase-29ao/P33-PLANNER-DERIVE-PATTERN2-LOOPBODYLOCAL-SMOKES-INSTRUCTIONS.md`
|
||||
- After P33: TBD
|
||||
- Next: P34(TBD)
|
||||
- After P34: TBD
|
||||
|
||||
@ -7,6 +7,9 @@ use crate::mir::builder::control_flow::plan::extractors::common_helpers::{
|
||||
has_return_statement as common_has_return, ControlFlowDetector, extract_loop_increment_plan,
|
||||
is_true_literal,
|
||||
};
|
||||
use crate::mir::builder::control_flow::plan::facts::pattern2_loopbodylocal_facts::{
|
||||
try_extract_pattern2_loopbodylocal_facts, LoopBodyLocalShape,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(in crate::mir::builder) struct Pattern2BreakFacts {
|
||||
@ -27,6 +30,10 @@ pub(in crate::mir::builder) fn try_extract_pattern2_break_facts(
|
||||
return Ok(Some(realworld));
|
||||
}
|
||||
|
||||
if let Some(loopbodylocal) = try_extract_pattern2_break_loopbodylocal_subset(condition, body)? {
|
||||
return Ok(Some(loopbodylocal));
|
||||
}
|
||||
|
||||
let Some(loop_var) = extract_loop_var_for_plan_subset(condition) else {
|
||||
return Ok(None);
|
||||
};
|
||||
@ -152,6 +159,207 @@ fn try_extract_pattern2_break_realworld_subset(
|
||||
})
|
||||
}
|
||||
|
||||
fn try_extract_pattern2_break_loopbodylocal_subset(
|
||||
condition: &ASTNode,
|
||||
body: &[ASTNode],
|
||||
) -> Result<Option<Pattern2BreakFacts>, Freeze> {
|
||||
let Some(loop_var) = extract_loop_var_for_len_condition(condition) else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let counts = count_control_flow(body, ControlFlowDetector::default());
|
||||
if counts.break_count != 1 || counts.continue_count > 0 || counts.return_count > 0 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
if body.len() != 3 && body.len() != 4 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let loopbodylocal = match try_extract_pattern2_loopbodylocal_facts(condition, body)? {
|
||||
Some(facts) => facts,
|
||||
None => return Ok(None),
|
||||
};
|
||||
if loopbodylocal.loop_var != loop_var {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let (break_idx, _, carrier_update_in_break) = match find_break_if_parts(body) {
|
||||
Some(parts) => parts,
|
||||
None => return Ok(None),
|
||||
};
|
||||
if carrier_update_in_break.is_some() {
|
||||
return Ok(None);
|
||||
}
|
||||
if has_assignment_after(body, break_idx, &loopbodylocal.loopbodylocal_var) {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let break_condition = match loopbodylocal.shape {
|
||||
LoopBodyLocalShape::TrimSeg { s_var, i_var } => {
|
||||
if i_var != loop_var {
|
||||
return Ok(None);
|
||||
}
|
||||
let seg_expr = substring_call(
|
||||
&s_var,
|
||||
var(&loop_var),
|
||||
add(var(&loop_var), lit_int(1)),
|
||||
);
|
||||
let is_space = ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Equal,
|
||||
left: Box::new(seg_expr.clone()),
|
||||
right: Box::new(lit_str(" ")),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let is_tab = ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Equal,
|
||||
left: Box::new(seg_expr),
|
||||
right: Box::new(lit_str("\t")),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Or,
|
||||
left: Box::new(is_space),
|
||||
right: Box::new(is_tab),
|
||||
span: Span::unknown(),
|
||||
}
|
||||
}
|
||||
LoopBodyLocalShape::DigitPos { digits_var, ch_var } => {
|
||||
let ch_expr = match find_local_init_expr(body, &ch_var) {
|
||||
Some(expr) => expr,
|
||||
None => return Ok(None),
|
||||
};
|
||||
let index_expr = index_of_call_expr(&digits_var, ch_expr);
|
||||
ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Less,
|
||||
left: Box::new(index_expr),
|
||||
right: Box::new(lit_int(0)),
|
||||
span: Span::unknown(),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let loop_increment = match extract_loop_increment_at_end(body, &loop_var) {
|
||||
Some(inc) => inc,
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
Ok(Some(Pattern2BreakFacts {
|
||||
loop_var: loop_var.clone(),
|
||||
carrier_var: loop_var,
|
||||
loop_condition: condition.clone(),
|
||||
break_condition,
|
||||
carrier_update_in_break: None,
|
||||
carrier_update_in_body: loop_increment.clone(),
|
||||
loop_increment,
|
||||
}))
|
||||
}
|
||||
|
||||
fn extract_loop_var_for_len_condition(condition: &ASTNode) -> Option<String> {
|
||||
let ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Less | BinaryOperator::LessEqual,
|
||||
left,
|
||||
right,
|
||||
..
|
||||
} = condition
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
let ASTNode::Variable { name, .. } = left.as_ref() else {
|
||||
return None;
|
||||
};
|
||||
if !matches!(
|
||||
right.as_ref(),
|
||||
ASTNode::MethodCall { object, method, arguments, .. }
|
||||
if method == "length"
|
||||
&& arguments.is_empty()
|
||||
&& matches!(object.as_ref(), ASTNode::Variable { .. })
|
||||
) {
|
||||
return None;
|
||||
}
|
||||
Some(name.clone())
|
||||
}
|
||||
|
||||
fn find_break_if_parts(body: &[ASTNode]) -> Option<(usize, ASTNode, Option<ASTNode>)> {
|
||||
for (idx, stmt) in body.iter().enumerate() {
|
||||
if let Some(parts) = extract_break_if_parts(stmt) {
|
||||
return Some((idx, parts.0, parts.1));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn has_assignment_after(body: &[ASTNode], start_idx: usize, var_name: &str) -> bool {
|
||||
for stmt in body.iter().skip(start_idx + 1) {
|
||||
let ASTNode::Assignment { target, .. } = stmt else {
|
||||
continue;
|
||||
};
|
||||
if matches!(target.as_ref(), ASTNode::Variable { name, .. } if name == var_name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn find_local_init_expr(body: &[ASTNode], name: &str) -> Option<ASTNode> {
|
||||
for stmt in body {
|
||||
let ASTNode::Local {
|
||||
variables,
|
||||
initial_values,
|
||||
..
|
||||
} = stmt
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
if variables.len() != 1 || initial_values.len() != 1 {
|
||||
continue;
|
||||
}
|
||||
if variables[0] != name {
|
||||
continue;
|
||||
}
|
||||
let Some(expr) = initial_values[0].as_ref() else {
|
||||
return None;
|
||||
};
|
||||
return Some((*expr.clone()).clone());
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn extract_loop_increment_at_end(body: &[ASTNode], loop_var: &str) -> Option<ASTNode> {
|
||||
let last = body.last()?;
|
||||
let ASTNode::Assignment { target, value, .. } = last else {
|
||||
return None;
|
||||
};
|
||||
let ASTNode::Variable { name, .. } = target.as_ref() else {
|
||||
return None;
|
||||
};
|
||||
if name != loop_var {
|
||||
return None;
|
||||
}
|
||||
let ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Add,
|
||||
left,
|
||||
right,
|
||||
..
|
||||
} = value.as_ref()
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
if !matches!(left.as_ref(), ASTNode::Variable { name, .. } if name == loop_var) {
|
||||
return None;
|
||||
}
|
||||
if !matches!(
|
||||
right.as_ref(),
|
||||
ASTNode::Literal {
|
||||
value: LiteralValue::Integer(_),
|
||||
..
|
||||
}
|
||||
) {
|
||||
return None;
|
||||
}
|
||||
Some(value.as_ref().clone())
|
||||
}
|
||||
|
||||
fn extract_loop_var_for_plan_subset(condition: &ASTNode) -> Option<String> {
|
||||
let ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Less,
|
||||
@ -505,6 +713,15 @@ fn var(name: &str) -> ASTNode {
|
||||
}
|
||||
}
|
||||
|
||||
fn add(left: ASTNode, right: ASTNode) -> ASTNode {
|
||||
ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Add,
|
||||
left: Box::new(left),
|
||||
right: Box::new(right),
|
||||
span: Span::unknown(),
|
||||
}
|
||||
}
|
||||
|
||||
fn lit_int(value: i64) -> ASTNode {
|
||||
ASTNode::Literal {
|
||||
value: LiteralValue::Integer(value),
|
||||
@ -537,6 +754,15 @@ fn index_of_call(haystack: &str, sep: &str, loop_var: &str) -> ASTNode {
|
||||
}
|
||||
}
|
||||
|
||||
fn index_of_call_expr(haystack: &str, needle: ASTNode) -> ASTNode {
|
||||
ASTNode::MethodCall {
|
||||
object: Box::new(var(haystack)),
|
||||
method: "indexOf".to_string(),
|
||||
arguments: vec![needle],
|
||||
span: Span::unknown(),
|
||||
}
|
||||
}
|
||||
|
||||
fn substring_call(haystack: &str, start: ASTNode, end: ASTNode) -> ASTNode {
|
||||
ASTNode::MethodCall {
|
||||
object: Box::new(var(haystack)),
|
||||
@ -700,4 +926,76 @@ mod tests {
|
||||
other => panic!("unexpected loop_increment: {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_pattern2_break_loopbodylocal_trim_seg_subset() {
|
||||
let condition = binop(
|
||||
BinaryOperator::Less,
|
||||
v("i"),
|
||||
method_call("s", "length", vec![]),
|
||||
);
|
||||
let body = vec![
|
||||
local(
|
||||
"seg",
|
||||
method_call(
|
||||
"s",
|
||||
"substring",
|
||||
vec![v("i"), binop(BinaryOperator::Add, v("i"), lit_int(1))],
|
||||
),
|
||||
),
|
||||
ASTNode::If {
|
||||
condition: Box::new(binop(
|
||||
BinaryOperator::Or,
|
||||
binop(BinaryOperator::Equal, v("seg"), lit_str(" ")),
|
||||
binop(BinaryOperator::Equal, v("seg"), lit_str("\t")),
|
||||
)),
|
||||
then_body: vec![ASTNode::Break { span: Span::unknown() }],
|
||||
else_body: None,
|
||||
span: Span::unknown(),
|
||||
},
|
||||
assign("i", binop(BinaryOperator::Add, v("i"), lit_int(1))),
|
||||
];
|
||||
|
||||
let facts = try_extract_pattern2_break_facts(&condition, &body)
|
||||
.expect("Ok")
|
||||
.expect("Some facts");
|
||||
assert_eq!(facts.loop_var, "i");
|
||||
assert_eq!(facts.carrier_var, "i");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_pattern2_break_loopbodylocal_digit_pos_subset() {
|
||||
let condition = binop(
|
||||
BinaryOperator::Less,
|
||||
v("p"),
|
||||
method_call("s", "length", vec![]),
|
||||
);
|
||||
let body = vec![
|
||||
local(
|
||||
"ch",
|
||||
method_call(
|
||||
"s",
|
||||
"substring",
|
||||
vec![v("p"), binop(BinaryOperator::Add, v("p"), lit_int(1))],
|
||||
),
|
||||
),
|
||||
local(
|
||||
"digit_pos",
|
||||
method_call("digits", "indexOf", vec![v("ch")]),
|
||||
),
|
||||
ASTNode::If {
|
||||
condition: Box::new(binop(BinaryOperator::Less, v("digit_pos"), lit_int(0))),
|
||||
then_body: vec![ASTNode::Break { span: Span::unknown() }],
|
||||
else_body: None,
|
||||
span: Span::unknown(),
|
||||
},
|
||||
assign("p", binop(BinaryOperator::Add, v("p"), lit_int(1))),
|
||||
];
|
||||
|
||||
let facts = try_extract_pattern2_break_facts(&condition, &body)
|
||||
.expect("Ok")
|
||||
.expect("Some facts");
|
||||
assert_eq!(facts.loop_var, "p");
|
||||
assert_eq!(facts.carrier_var, "p");
|
||||
}
|
||||
}
|
||||
|
||||
@ -257,6 +257,18 @@ impl super::PlanNormalizer {
|
||||
|
||||
Ok((result_id, arg_effects))
|
||||
}
|
||||
ASTNode::BinaryOp { .. } => {
|
||||
let (lhs, op, rhs, mut consts) =
|
||||
Self::lower_binop_ast(ast, builder, phi_bindings)?;
|
||||
let result_id = builder.alloc_typed(MirType::Integer);
|
||||
consts.push(CoreEffectPlan::BinOp {
|
||||
dst: result_id,
|
||||
lhs,
|
||||
op,
|
||||
rhs,
|
||||
});
|
||||
Ok((result_id, consts))
|
||||
}
|
||||
_ => Err(format!("[normalizer] Unsupported value AST: {:?}", ast)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ use crate::mir::builder::control_flow::edgecfg::api::{
|
||||
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::{BinaryOp, ConstValue, MirType};
|
||||
use crate::mir::{BinaryOp, ConstValue, MirType, ValueId};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
impl super::PlanNormalizer {
|
||||
@ -115,9 +115,6 @@ impl super::PlanNormalizer {
|
||||
let (loop_cond_lhs, loop_cond_op, loop_cond_rhs, loop_cond_consts) =
|
||||
Self::lower_compare_ast(&parts.loop_condition, builder, &phi_bindings)?;
|
||||
|
||||
let (break_cond_lhs, break_cond_op, break_cond_rhs, break_cond_consts) =
|
||||
Self::lower_compare_ast(&parts.break_condition, builder, &phi_bindings)?;
|
||||
|
||||
let break_then_effects = if let Some(ref break_update_ast) = parts.carrier_update_in_break {
|
||||
let (lhs, op, rhs, consts) =
|
||||
Self::lower_binop_ast(break_update_ast, builder, &phi_bindings)?;
|
||||
@ -162,15 +159,15 @@ impl super::PlanNormalizer {
|
||||
|
||||
// Step 7: Build body plans (break condition check)
|
||||
let mut body_plans: Vec<CorePlan> = Vec::new();
|
||||
for const_effect in break_cond_consts {
|
||||
body_plans.push(CorePlan::Effect(const_effect));
|
||||
let break_cond_effects = Self::lower_break_condition_effects(
|
||||
&parts.break_condition,
|
||||
builder,
|
||||
&phi_bindings,
|
||||
cond_break,
|
||||
)?;
|
||||
for effect in break_cond_effects {
|
||||
body_plans.push(CorePlan::Effect(effect));
|
||||
}
|
||||
body_plans.push(CorePlan::Effect(CoreEffectPlan::Compare {
|
||||
dst: cond_break,
|
||||
lhs: break_cond_lhs,
|
||||
op: break_cond_op,
|
||||
rhs: break_cond_rhs,
|
||||
}));
|
||||
|
||||
// Step 8: Build step_effects
|
||||
let mut step_effects = carrier_consts;
|
||||
@ -310,4 +307,59 @@ impl super::PlanNormalizer {
|
||||
|
||||
Ok(CorePlan::Loop(loop_plan))
|
||||
}
|
||||
|
||||
fn lower_break_condition_effects(
|
||||
ast: &crate::ast::ASTNode,
|
||||
builder: &mut MirBuilder,
|
||||
phi_bindings: &BTreeMap<String, ValueId>,
|
||||
dst: ValueId,
|
||||
) -> Result<Vec<CoreEffectPlan>, String> {
|
||||
use crate::ast::{ASTNode, BinaryOperator};
|
||||
|
||||
match ast {
|
||||
ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Or,
|
||||
left,
|
||||
right,
|
||||
..
|
||||
} => {
|
||||
let (lhs_l, op_l, rhs_l, consts_l) =
|
||||
Self::lower_compare_ast(left, builder, phi_bindings)?;
|
||||
let (lhs_r, op_r, rhs_r, consts_r) =
|
||||
Self::lower_compare_ast(right, builder, phi_bindings)?;
|
||||
|
||||
let left_dst = builder.alloc_typed(MirType::Bool);
|
||||
let right_dst = builder.alloc_typed(MirType::Bool);
|
||||
|
||||
let mut effects = Vec::new();
|
||||
effects.extend(consts_l);
|
||||
effects.push(CoreEffectPlan::Compare {
|
||||
dst: left_dst,
|
||||
lhs: lhs_l,
|
||||
op: op_l,
|
||||
rhs: rhs_l,
|
||||
});
|
||||
effects.extend(consts_r);
|
||||
effects.push(CoreEffectPlan::Compare {
|
||||
dst: right_dst,
|
||||
lhs: lhs_r,
|
||||
op: op_r,
|
||||
rhs: rhs_r,
|
||||
});
|
||||
effects.push(CoreEffectPlan::BinOp {
|
||||
dst,
|
||||
lhs: left_dst,
|
||||
op: BinaryOp::Or,
|
||||
rhs: right_dst,
|
||||
});
|
||||
Ok(effects)
|
||||
}
|
||||
_ => {
|
||||
let (lhs, op, rhs, consts) = Self::lower_compare_ast(ast, builder, phi_bindings)?;
|
||||
let mut effects = consts;
|
||||
effects.push(CoreEffectPlan::Compare { dst, lhs, op, rhs });
|
||||
Ok(effects)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,6 +22,11 @@ fi
|
||||
OUTPUT_CLEAN=$(echo "$OUTPUT" | filter_noise)
|
||||
EXPECTED_TAG="[plan/pattern2/promotion_hint:DigitPos]"
|
||||
|
||||
if ! echo "$OUTPUT" | grep -qF "[coreplan/shadow_adopt:pattern2_break_subset]"; then
|
||||
test_fail "phase29ab_pattern2_loopbodylocal_min_vm: missing shadow adopt tag"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if echo "$OUTPUT_CLEAN" | grep -q "^2$" || echo "$OUTPUT" | grep -q "^RC: 2$"; then
|
||||
if echo "$OUTPUT_CLEAN" | grep -qF "$EXPECTED_TAG"; then
|
||||
test_pass "phase29ab_pattern2_loopbodylocal_min_vm: Pattern2 LoopBodyLocal promotion succeeded (output: 2)"
|
||||
|
||||
@ -22,6 +22,11 @@ fi
|
||||
OUTPUT_CLEAN=$(echo "$OUTPUT" | filter_noise)
|
||||
EXPECTED_TAG="[plan/pattern2/promotion_hint:TrimSeg]"
|
||||
|
||||
if ! echo "$OUTPUT" | grep -qF "[coreplan/shadow_adopt:pattern2_break_subset]"; then
|
||||
test_fail "phase29ab_pattern2_loopbodylocal_seg_min_vm: missing shadow adopt tag"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if echo "$OUTPUT_CLEAN" | grep -q "^2$" || echo "$OUTPUT" | grep -q "^RC: 2$"; then
|
||||
if echo "$OUTPUT_CLEAN" | grep -qF "$EXPECTED_TAG"; then
|
||||
test_pass "phase29ab_pattern2_loopbodylocal_seg_min_vm: Pattern2 Trim promotion succeeded (output: 2)"
|
||||
|
||||
Reference in New Issue
Block a user