feat(joinir): Phase 286 P2.6.1 - Pattern3 Plan 完走(normalizer 実装 + Fail-Fast 統一)
## 変更内容 ### Router Fail-Fast 統一 - Pattern3 stub fallback 特例を撤去 - extract 成功 → normalize/lower 失敗は即 Err(他パターンと統一) ### normalize_pattern3_if_phi() 実装 - CFG 構造: 8 blocks (preheader, header, body, then, else, merge, step, after) - PHI 構成: 3本(header×2 + merge×1) - Header: loop_var_current, carrier_current - Merge: carrier_next (if-else 合流) - Frag: BranchStub×2 + EdgeStub×4(Pattern1 流儀の直接構築) ### 発見・修正 - lowerer は body_bb の block_effects を無視して loop_plan.body を emit - body_bb effects は CorePlan::Effect(...) として loop_plan.body に配置 ## テスト結果 - Phase 118 smoke: PASS (出力 12) - quick: 154/154 PASS - Plan line 完走確認: route=plan ... Pattern3_IfPhi MATCHED 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -281,25 +281,8 @@ fn try_plan_extractors(
|
||||
let log_msg = format!("route=plan strategy=extract pattern={}", entry.name);
|
||||
trace::trace().pattern("route", &log_msg, true);
|
||||
|
||||
// Phase 286 P2.6: Pattern3 PoC exception - allow fallback to legacy
|
||||
// Pattern3 extractor validates structure, but normalizer is stub
|
||||
if entry.name.contains("Pattern3") {
|
||||
match lower_via_plan(builder, domain_plan, ctx) {
|
||||
Ok(value_id) => return Ok(value_id),
|
||||
Err(_) => {
|
||||
if ctx.debug {
|
||||
trace::trace().debug(
|
||||
"route/plan",
|
||||
"Pattern3 Plan normalization stub - fallback to legacy Pattern3",
|
||||
);
|
||||
}
|
||||
continue; // Try next extractor (will eventually reach legacy Pattern3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 286 P2.4.1: Fail-Fast - extract 成功 → normalize/lower 失敗は即 Err
|
||||
// 構造的 Fail-Fast: 文字列判定なし、extract が Some なら fallback 禁止
|
||||
// Phase 286 P2.6.1: Fail-Fast 統一 - extract 成功 → normalize/lower 失敗は即 Err
|
||||
// Pattern3 stub fallback 撤去(normalizer 実装済み)
|
||||
return lower_via_plan(builder, domain_plan, ctx);
|
||||
} else {
|
||||
// Extraction returned None - try next extractor
|
||||
|
||||
@ -2066,14 +2066,301 @@ impl PlanNormalizer {
|
||||
/// ↓
|
||||
/// back-edge to header
|
||||
/// ```
|
||||
/// Phase 286 P2.6.1: Pattern3IfPhi → CorePlan 変換
|
||||
///
|
||||
/// Expands Pattern3 (Loop with If-Phi) semantics into generic CorePlan:
|
||||
/// - CFG structure: preheader → header → body → then/else → merge → step → header
|
||||
/// - 3 PHIs: 2 in header (loop_var, carrier), 1 in merge (carrier_next)
|
||||
/// - If-else branching with PHI merge (no Select instruction)
|
||||
fn normalize_pattern3_if_phi(
|
||||
_builder: &mut MirBuilder,
|
||||
_parts: Pattern3IfPhiPlan,
|
||||
_ctx: &LoopPatternContext,
|
||||
builder: &mut MirBuilder,
|
||||
parts: Pattern3IfPhiPlan,
|
||||
ctx: &LoopPatternContext,
|
||||
) -> Result<CorePlan, String> {
|
||||
// Phase 286 P2.6 PoC: Stub implementation
|
||||
// Extractor validates Pattern3 structure, but normalization is deferred
|
||||
// Fallback to legacy Pattern3 implementation via Ok(None) in router
|
||||
Err("Pattern3 Plan normalization not yet implemented (PoC stub - fallback to legacy)".to_string())
|
||||
use crate::mir::builder::control_flow::joinir::trace;
|
||||
|
||||
let trace_logger = trace::trace();
|
||||
let debug = ctx.debug;
|
||||
|
||||
if debug {
|
||||
trace_logger.debug(
|
||||
"normalizer/pattern3_if_phi",
|
||||
&format!(
|
||||
"Phase 286 P2.6.1: Normalizing Pattern3IfPhi for {} (loop_var: {}, carrier_var: {})",
|
||||
ctx.func_name, parts.loop_var, parts.carrier_var
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Step 1: Get host ValueIds for variables
|
||||
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))?;
|
||||
|
||||
let carrier_init = builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.get(&parts.carrier_var)
|
||||
.copied()
|
||||
.ok_or_else(|| format!("[normalizer] Carrier variable {} not found", parts.carrier_var))?;
|
||||
|
||||
// Step 2: Capture preheader block
|
||||
let preheader_bb = builder
|
||||
.current_block
|
||||
.ok_or_else(|| "[normalizer] No current block for loop entry".to_string())?;
|
||||
|
||||
// Step 3: Allocate BasicBlockIds for 8 blocks
|
||||
let header_bb = builder.next_block_id();
|
||||
let body_bb = builder.next_block_id();
|
||||
let then_bb = builder.next_block_id();
|
||||
let else_bb = builder.next_block_id();
|
||||
let merge_bb = builder.next_block_id();
|
||||
let step_bb = builder.next_block_id();
|
||||
let after_bb = builder.next_block_id();
|
||||
|
||||
if debug {
|
||||
trace_logger.debug(
|
||||
"normalizer/pattern3_if_phi",
|
||||
&format!(
|
||||
"Allocated: preheader={:?}, header={:?}, body={:?}, then={:?}, else={:?}, merge={:?}, step={:?}, after={:?}",
|
||||
preheader_bb, header_bb, body_bb, then_bb, else_bb, merge_bb, step_bb, after_bb
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Step 4: Allocate ValueIds for loop control
|
||||
let loop_var_current = builder.alloc_typed(MirType::Integer); // header PHI dst
|
||||
let carrier_current = builder.alloc_typed(MirType::Integer); // header PHI dst
|
||||
let cond_loop = builder.alloc_typed(MirType::Bool); // header condition
|
||||
let cond_if = builder.alloc_typed(MirType::Bool); // body condition
|
||||
let carrier_then = builder.alloc_typed(MirType::Integer); // then update result
|
||||
let carrier_else = builder.alloc_typed(MirType::Integer); // else update result
|
||||
let carrier_next = builder.alloc_typed(MirType::Integer); // merge PHI dst
|
||||
let loop_var_next = builder.alloc_typed(MirType::Integer); // step update result
|
||||
|
||||
// Step 5: Build phi_bindings - PHI dst takes precedence over variable_map
|
||||
let mut phi_bindings: BTreeMap<String, crate::mir::ValueId> = BTreeMap::new();
|
||||
phi_bindings.insert(parts.loop_var.clone(), loop_var_current);
|
||||
phi_bindings.insert(parts.carrier_var.clone(), carrier_current);
|
||||
|
||||
// Step 6: Lower AST expressions
|
||||
// Lower loop condition (e.g., `i < 3`)
|
||||
let (loop_cond_lhs, loop_cond_op, loop_cond_rhs, loop_cond_consts) =
|
||||
Self::lower_compare_ast(&parts.condition, builder, &phi_bindings)?;
|
||||
|
||||
// Lower if condition (e.g., `i > 0`)
|
||||
let (if_cond_lhs, if_cond_op, if_cond_rhs, if_cond_consts) =
|
||||
Self::lower_compare_ast(&parts.if_condition, builder, &phi_bindings)?;
|
||||
|
||||
// Lower then update (e.g., `sum + 1`)
|
||||
let (then_lhs, then_op, then_rhs, then_consts) =
|
||||
Self::lower_binop_ast(&parts.then_update, builder, &phi_bindings)?;
|
||||
|
||||
// Lower else update (e.g., `sum + 0`)
|
||||
let (else_lhs, else_op, else_rhs, else_consts) =
|
||||
Self::lower_binop_ast(&parts.else_update, builder, &phi_bindings)?;
|
||||
|
||||
// Lower loop increment (e.g., `i + 1`)
|
||||
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 (loop condition)
|
||||
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 body plans (if condition) - goes into loop_plan.body, not block_effects
|
||||
// Note: lowerer emits loop_plan.body for body_bb, ignoring block_effects
|
||||
let mut body_plans: Vec<CorePlan> = Vec::new();
|
||||
for const_effect in if_cond_consts {
|
||||
body_plans.push(CorePlan::Effect(const_effect));
|
||||
}
|
||||
body_plans.push(CorePlan::Effect(CoreEffectPlan::Compare {
|
||||
dst: cond_if,
|
||||
lhs: if_cond_lhs,
|
||||
op: if_cond_op,
|
||||
rhs: if_cond_rhs,
|
||||
}));
|
||||
|
||||
// Step 9: Build then_effects (carrier_then = carrier_current + 1)
|
||||
let mut then_effects = then_consts;
|
||||
then_effects.push(CoreEffectPlan::BinOp {
|
||||
dst: carrier_then,
|
||||
lhs: then_lhs,
|
||||
op: then_op,
|
||||
rhs: then_rhs,
|
||||
});
|
||||
|
||||
// Step 10: Build else_effects (carrier_else = carrier_current + 0)
|
||||
let mut else_effects = else_consts;
|
||||
else_effects.push(CoreEffectPlan::BinOp {
|
||||
dst: carrier_else,
|
||||
lhs: else_lhs,
|
||||
op: else_op,
|
||||
rhs: else_rhs,
|
||||
});
|
||||
|
||||
// Step 11: Build step_effects (loop_var_next = loop_var_current + 1)
|
||||
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 12: Build block_effects (7 blocks)
|
||||
// Note: body_bb effects are in loop_plan.body, not here
|
||||
let block_effects = vec![
|
||||
(preheader_bb, vec![]),
|
||||
(header_bb, header_effects),
|
||||
(body_bb, vec![]), // Effects in loop_plan.body
|
||||
(then_bb, then_effects),
|
||||
(else_bb, else_effects),
|
||||
(merge_bb, vec![]), // PHI only, no effects
|
||||
(step_bb, step_effects),
|
||||
];
|
||||
|
||||
// Step 13: Build PHIs (3 PHIs: 2 in header, 1 in merge)
|
||||
let phis = vec![
|
||||
// Header PHI 1: loop variable
|
||||
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),
|
||||
},
|
||||
// Header PHI 2: carrier (receives carrier_next from merge via step)
|
||||
CorePhiInfo {
|
||||
block: header_bb,
|
||||
dst: carrier_current,
|
||||
inputs: vec![
|
||||
(preheader_bb, carrier_init),
|
||||
(step_bb, carrier_next),
|
||||
],
|
||||
tag: format!("carrier_{}", parts.carrier_var),
|
||||
},
|
||||
// Merge PHI: if-else merge (carrier_next = phi(then:carrier_then, else:carrier_else))
|
||||
CorePhiInfo {
|
||||
block: merge_bb,
|
||||
dst: carrier_next,
|
||||
inputs: vec![
|
||||
(then_bb, carrier_then),
|
||||
(else_bb, carrier_else),
|
||||
],
|
||||
tag: format!("merge_{}", parts.carrier_var),
|
||||
},
|
||||
];
|
||||
|
||||
// Step 14: Build Frag (branches + wires)
|
||||
let empty_args = EdgeArgs {
|
||||
layout: JumpArgsLayout::CarriersOnly,
|
||||
values: vec![],
|
||||
};
|
||||
|
||||
// 2 branches: header (loop cond), body (if cond)
|
||||
let branches = vec![
|
||||
// header: cond_loop → body/after
|
||||
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(),
|
||||
},
|
||||
// body: cond_if → then/else
|
||||
BranchStub {
|
||||
from: body_bb,
|
||||
cond: cond_if,
|
||||
then_target: then_bb,
|
||||
then_args: empty_args.clone(),
|
||||
else_target: else_bb,
|
||||
else_args: empty_args.clone(),
|
||||
},
|
||||
];
|
||||
|
||||
// 4 wires: then→merge, else→merge, merge→step, step→header
|
||||
let wires = vec![
|
||||
// then → merge
|
||||
EdgeStub {
|
||||
from: then_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: Some(merge_bb),
|
||||
args: empty_args.clone(),
|
||||
},
|
||||
// else → merge
|
||||
EdgeStub {
|
||||
from: else_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: Some(merge_bb),
|
||||
args: empty_args.clone(),
|
||||
},
|
||||
// merge → step
|
||||
EdgeStub {
|
||||
from: merge_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: Some(step_bb),
|
||||
args: empty_args.clone(),
|
||||
},
|
||||
// step → header (back-edge)
|
||||
EdgeStub {
|
||||
from: step_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: Some(header_bb),
|
||||
args: empty_args.clone(),
|
||||
},
|
||||
];
|
||||
|
||||
let frag = Frag {
|
||||
entry: header_bb,
|
||||
exits: BTreeMap::new(),
|
||||
wires,
|
||||
branches,
|
||||
};
|
||||
|
||||
// Step 15: Build final_values
|
||||
let final_values = vec![
|
||||
(parts.loop_var.clone(), loop_var_current),
|
||||
(parts.carrier_var.clone(), carrier_current),
|
||||
];
|
||||
|
||||
// Step 16: Build CoreLoopPlan
|
||||
// found_bb = after_bb (no early exit)
|
||||
// cond_match = cond_if (if condition for body branching)
|
||||
let loop_plan = CoreLoopPlan {
|
||||
preheader_bb,
|
||||
header_bb,
|
||||
body_bb,
|
||||
step_bb,
|
||||
after_bb,
|
||||
found_bb: after_bb, // No early exit
|
||||
body: body_plans, // Body effects (if condition)
|
||||
cond_loop,
|
||||
cond_match: cond_if, // If condition
|
||||
block_effects,
|
||||
phis,
|
||||
frag,
|
||||
final_values,
|
||||
};
|
||||
|
||||
if debug {
|
||||
trace_logger.debug(
|
||||
"normalizer/pattern3_if_phi",
|
||||
"CorePlan construction complete (7 blocks, 3 PHIs)",
|
||||
);
|
||||
}
|
||||
|
||||
Ok(CorePlan::Loop(loop_plan))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user