feat(edgecfg): Phase 281 P3 - cleanup Normal wiring + docs
This commit is contained in:
@ -381,35 +381,134 @@ pub(crate) fn loop_(
|
||||
}
|
||||
}
|
||||
|
||||
/// cleanup 合成: finally の後継(すべての exit を正規化)
|
||||
/// Phase 281 P3: cleanup() Normal + Return exit wiring implementation
|
||||
///
|
||||
/// # Phase 280: Composition SSOT
|
||||
/// Wires cleanup Normal/Return exits to specified targets or propagates them upward.
|
||||
///
|
||||
/// ## Planned Composition Law (Future: Phase 280+)
|
||||
/// # Contract (P3 Implementation)
|
||||
///
|
||||
/// - All exits (Normal/Break/Continue/Return/Unwind) → `cleanup` (`EdgeStub` → `wires`)
|
||||
/// - Cleanup re-dispatches original exit (`ExitTag` + payload via block params)
|
||||
/// - `Invoke.err` also routed through cleanup
|
||||
/// **Input**:
|
||||
/// - `main`: Main control flow (loop structure Frag)
|
||||
/// - `cleanup_frag`: Exit handler (Normal/Return exits only, no wires/branches)
|
||||
/// - `normal_target`: Where to wire Normal exits
|
||||
/// - `Some(bb)`: Wire Normal → bb (internal closure, target = Some)
|
||||
/// - `None`: Propagate Normal → wires (upward propagation, target = None)
|
||||
/// - `ret_target`: Where to wire Return exits
|
||||
/// - `Some(bb)`: Wire Return → bb (internal closure, target = Some)
|
||||
/// - `None`: Propagate Return → wires (upward propagation, target = None)
|
||||
///
|
||||
/// ## Status
|
||||
/// **Output**:
|
||||
/// - Frag with main's structure + cleanup's exits wired/propagated
|
||||
///
|
||||
/// - **Signature fixed**: Phase 264
|
||||
/// - **Implementation**: TODO (Phase 280+)
|
||||
/// - **Usage**: Not yet used (planned for exception/async cleanup)
|
||||
/// **Invariants**:
|
||||
/// - 1 block = 1 terminator (no duplicate BranchStubs)
|
||||
/// - cleanup_frag must have empty wires/branches (Fail-Fast if not)
|
||||
/// - cleanup_frag.exits must contain only Normal/Return (Fail-Fast for other kinds)
|
||||
/// - normal_target=Some: Normal exits → wires (internal)
|
||||
/// - normal_target=None: Normal exits → wires (target=None, propagate upward)
|
||||
/// - ret_target=Some: Return exits → wires (internal)
|
||||
/// - ret_target=None: Return exits → wires (target=None, propagate upward)
|
||||
///
|
||||
/// # 配線ルール(TODO実装)
|
||||
/// - body の全 exit を cleanup 経由へリライト
|
||||
/// - cleanup 後に元の exit を再発射(ExitTag + payload を block params で運ぶ)
|
||||
/// - Invoke.err も cleanup に寄せる
|
||||
/// # Implementation Status
|
||||
///
|
||||
/// # 引数
|
||||
/// - `body`: 本体の断片
|
||||
/// - `cleanup_block`: cleanup 処理を行うブロック
|
||||
/// P3: Normal + Return wiring logic implemented
|
||||
/// Future: Break/Continue/Unwind support (P4+)
|
||||
///
|
||||
/// # Migration Notes (Phase 264 → Phase 281)
|
||||
///
|
||||
/// Old signature (Phase 264): `cleanup(body: Frag, cleanup_block: BasicBlockId) -> Frag`
|
||||
/// Phase 281 P1: `cleanup(main: Frag, cleanup: Frag) -> Result<Frag, String>`
|
||||
/// Phase 281 P2: `cleanup(main: Frag, cleanup_frag: Frag, ret_target: Option<BasicBlockId>) -> Result<Frag, String>`
|
||||
/// Phase 281 P3: `cleanup(main: Frag, cleanup_frag: Frag, normal_target: Option<BasicBlockId>, ret_target: Option<BasicBlockId>) -> Result<Frag, String>`
|
||||
///
|
||||
/// Rationale: Pattern6/7 require flexible exit wiring for Normal/Return exits.
|
||||
/// cleanup_frag must be "exit-only" to prevent terminator confusion.
|
||||
pub(crate) fn cleanup(
|
||||
_body: Frag,
|
||||
_cleanup_block: BasicBlockId,
|
||||
) -> Frag {
|
||||
todo!("Phase 280+: cleanup 合成は将来実装予定(exception/async cleanup 用)")
|
||||
main: Frag,
|
||||
cleanup_frag: Frag,
|
||||
normal_target: Option<BasicBlockId>,
|
||||
ret_target: Option<BasicBlockId>,
|
||||
) -> Result<Frag, String> {
|
||||
// Phase 281 P3: Normal + Return exit wiring implementation
|
||||
// - Supported: Normal, Return exits
|
||||
// - Unsupported: Break, Continue, Unwind (Fail-Fast)
|
||||
|
||||
let mut exits = BTreeMap::new();
|
||||
let mut wires = Vec::new();
|
||||
let mut branches = Vec::new();
|
||||
|
||||
// Validate cleanup_frag structure (only exits allowed, no wires/branches)
|
||||
if !cleanup_frag.wires.is_empty() || !cleanup_frag.branches.is_empty() {
|
||||
return Err(format!(
|
||||
"compose::cleanup() Phase 281 P3: cleanup_frag must have empty wires/branches (only exits allowed), found {} wires, {} branches",
|
||||
cleanup_frag.wires.len(),
|
||||
cleanup_frag.branches.len()
|
||||
));
|
||||
}
|
||||
|
||||
// Validate cleanup_frag exits (only Normal + Return allowed in P3)
|
||||
for (kind, _) in &cleanup_frag.exits {
|
||||
match kind {
|
||||
ExitKind::Normal | ExitKind::Return => {}, // OK
|
||||
_ => {
|
||||
return Err(format!(
|
||||
"compose::cleanup() Phase 281 P3: unsupported exit kind {:?} in cleanup_frag (only Normal/Return allowed)",
|
||||
kind
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process cleanup Normal exits
|
||||
if let Some(normal_stubs) = cleanup_frag.exits.get(&ExitKind::Normal) {
|
||||
for mut stub in normal_stubs.clone() {
|
||||
match normal_target {
|
||||
Some(target_bb) => {
|
||||
// Wire: Normal → target_bb (internal closure)
|
||||
stub.target = Some(target_bb);
|
||||
wires.push(stub);
|
||||
}
|
||||
None => {
|
||||
// Propagate: Normal → wires (target=None, upward propagation)
|
||||
stub.target = None;
|
||||
wires.push(stub);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process cleanup Return exits
|
||||
if let Some(return_stubs) = cleanup_frag.exits.get(&ExitKind::Return) {
|
||||
for mut stub in return_stubs.clone() {
|
||||
match ret_target {
|
||||
Some(target_bb) => {
|
||||
// Wire: Return → target_bb (internal closure)
|
||||
stub.target = Some(target_bb);
|
||||
wires.push(stub);
|
||||
}
|
||||
None => {
|
||||
// Propagate: Return → wires (target=None, will be emitted as Return terminator)
|
||||
// Note: Return exits can have target=None in wires (Phase 267 special case)
|
||||
stub.target = None;
|
||||
wires.push(stub);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Preserve main's exits/wires/branches
|
||||
for (kind, stubs) in main.exits {
|
||||
exits.entry(kind).or_insert_with(Vec::new).extend(stubs);
|
||||
}
|
||||
wires.extend(main.wires);
|
||||
branches.extend(main.branches);
|
||||
|
||||
Ok(Frag {
|
||||
entry: main.entry, // Entry = main entry (header_bb)
|
||||
exits,
|
||||
wires,
|
||||
branches,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -418,6 +517,7 @@ mod tests {
|
||||
use crate::mir::basic_block::BasicBlockId;
|
||||
use super::super::exit_kind::ExitKind;
|
||||
use super::super::edge_stub::EdgeStub;
|
||||
use crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout;
|
||||
|
||||
#[test]
|
||||
fn test_loop_preserves_exits() {
|
||||
@ -866,4 +966,200 @@ mod tests {
|
||||
// then/else Normal are in wires
|
||||
assert_eq!(if_frag.wires.len(), 2);
|
||||
}
|
||||
|
||||
// Phase 281 P2: cleanup() test - Return propagation
|
||||
#[test]
|
||||
fn test_cleanup_return_propagation() {
|
||||
let main_entry = BasicBlockId(100);
|
||||
let cleanup_bb = BasicBlockId(200);
|
||||
|
||||
// Main Frag: empty (no exits)
|
||||
let main_frag = Frag {
|
||||
entry: main_entry,
|
||||
exits: BTreeMap::new(),
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
};
|
||||
|
||||
// Cleanup Frag: Return exit
|
||||
let cleanup_frag = Frag {
|
||||
entry: cleanup_bb,
|
||||
exits: BTreeMap::from([(
|
||||
ExitKind::Return,
|
||||
vec![EdgeStub {
|
||||
from: cleanup_bb,
|
||||
kind: ExitKind::Return,
|
||||
target: None, // Unresolved
|
||||
args: EdgeArgs {
|
||||
layout: JumpArgsLayout::CarriersOnly,
|
||||
values: vec![],
|
||||
},
|
||||
}],
|
||||
)]),
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
};
|
||||
|
||||
// Execute: normal_target=None, ret_target=None → propagate Return
|
||||
let result = cleanup(main_frag, cleanup_frag, None, None);
|
||||
|
||||
// Verify: Return in wires (target=None, to be emitted as terminator)
|
||||
assert!(result.is_ok());
|
||||
let composed = result.unwrap();
|
||||
assert_eq!(composed.entry, main_entry);
|
||||
assert_eq!(composed.wires.len(), 1); // Return in wires
|
||||
|
||||
let return_wire = &composed.wires[0];
|
||||
assert_eq!(return_wire.from, cleanup_bb);
|
||||
assert_eq!(return_wire.kind, ExitKind::Return);
|
||||
assert_eq!(return_wire.target, None); // Unresolved (upward propagation)
|
||||
}
|
||||
|
||||
// Phase 281 P2: cleanup() test - Return wiring
|
||||
#[test]
|
||||
fn test_cleanup_return_wiring() {
|
||||
let main_entry = BasicBlockId(100);
|
||||
let cleanup_bb = BasicBlockId(200);
|
||||
let target_bb = BasicBlockId(300); // Wire destination
|
||||
|
||||
// Main Frag: empty
|
||||
let main_frag = Frag {
|
||||
entry: main_entry,
|
||||
exits: BTreeMap::new(),
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
};
|
||||
|
||||
// Cleanup Frag: Return exit
|
||||
let cleanup_frag = Frag {
|
||||
entry: cleanup_bb,
|
||||
exits: BTreeMap::from([(
|
||||
ExitKind::Return,
|
||||
vec![EdgeStub {
|
||||
from: cleanup_bb,
|
||||
kind: ExitKind::Return,
|
||||
target: None, // Unresolved
|
||||
args: EdgeArgs {
|
||||
layout: JumpArgsLayout::CarriersOnly,
|
||||
values: vec![],
|
||||
},
|
||||
}],
|
||||
)]),
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
};
|
||||
|
||||
// Execute: normal_target=None, ret_target=Some(target_bb) → wire Return
|
||||
let result = cleanup(main_frag, cleanup_frag, None, Some(target_bb));
|
||||
|
||||
// Verify: Return in wires (not exits), wired to target_bb
|
||||
assert!(result.is_ok());
|
||||
let composed = result.unwrap();
|
||||
assert_eq!(composed.entry, main_entry);
|
||||
assert_eq!(composed.exits.len(), 0); // No exits (closed)
|
||||
assert_eq!(composed.wires.len(), 1); // Return wired
|
||||
|
||||
let wired_stub = &composed.wires[0];
|
||||
assert_eq!(wired_stub.from, cleanup_bb);
|
||||
assert_eq!(wired_stub.kind, ExitKind::Return);
|
||||
assert_eq!(wired_stub.target, Some(target_bb)); // Wired!
|
||||
}
|
||||
|
||||
// Phase 281 P3: cleanup() test - Normal propagation
|
||||
#[test]
|
||||
fn test_cleanup_normal_propagation() {
|
||||
let main_entry = BasicBlockId(100);
|
||||
let cleanup_bb = BasicBlockId(200);
|
||||
|
||||
// Main Frag: empty
|
||||
let main_frag = Frag {
|
||||
entry: main_entry,
|
||||
exits: BTreeMap::new(),
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
};
|
||||
|
||||
// Cleanup Frag: Normal exit
|
||||
let cleanup_frag = Frag {
|
||||
entry: cleanup_bb,
|
||||
exits: BTreeMap::from([(
|
||||
ExitKind::Normal,
|
||||
vec![EdgeStub {
|
||||
from: cleanup_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: None, // Unresolved
|
||||
args: EdgeArgs {
|
||||
layout: JumpArgsLayout::CarriersOnly,
|
||||
values: vec![],
|
||||
},
|
||||
}],
|
||||
)]),
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
};
|
||||
|
||||
// Execute: normal_target=None, ret_target=None → propagate Normal
|
||||
let result = cleanup(main_frag, cleanup_frag, None, None);
|
||||
|
||||
// Verify: Normal in wires (target=None, upward propagation)
|
||||
assert!(result.is_ok());
|
||||
let composed = result.unwrap();
|
||||
assert_eq!(composed.entry, main_entry);
|
||||
assert_eq!(composed.wires.len(), 1); // Normal in wires
|
||||
|
||||
let normal_wire = &composed.wires[0];
|
||||
assert_eq!(normal_wire.from, cleanup_bb);
|
||||
assert_eq!(normal_wire.kind, ExitKind::Normal);
|
||||
assert_eq!(normal_wire.target, None); // Unresolved (upward propagation)
|
||||
}
|
||||
|
||||
// Phase 281 P3: cleanup() test - Normal wiring
|
||||
#[test]
|
||||
fn test_cleanup_normal_wiring() {
|
||||
let main_entry = BasicBlockId(100);
|
||||
let cleanup_bb = BasicBlockId(200);
|
||||
let target_bb = BasicBlockId(300); // Wire destination
|
||||
|
||||
// Main Frag: empty
|
||||
let main_frag = Frag {
|
||||
entry: main_entry,
|
||||
exits: BTreeMap::new(),
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
};
|
||||
|
||||
// Cleanup Frag: Normal exit
|
||||
let cleanup_frag = Frag {
|
||||
entry: cleanup_bb,
|
||||
exits: BTreeMap::from([(
|
||||
ExitKind::Normal,
|
||||
vec![EdgeStub {
|
||||
from: cleanup_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: None, // Unresolved
|
||||
args: EdgeArgs {
|
||||
layout: JumpArgsLayout::CarriersOnly,
|
||||
values: vec![],
|
||||
},
|
||||
}],
|
||||
)]),
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
};
|
||||
|
||||
// Execute: normal_target=Some(target_bb), ret_target=None → wire Normal
|
||||
let result = cleanup(main_frag, cleanup_frag, Some(target_bb), None);
|
||||
|
||||
// Verify: Normal in wires (not exits), wired to target_bb
|
||||
assert!(result.is_ok());
|
||||
let composed = result.unwrap();
|
||||
assert_eq!(composed.entry, main_entry);
|
||||
assert_eq!(composed.exits.len(), 0); // No exits (closed)
|
||||
assert_eq!(composed.wires.len(), 1); // Normal wired
|
||||
|
||||
let wired_stub = &composed.wires[0];
|
||||
assert_eq!(wired_stub.from, cleanup_bb);
|
||||
assert_eq!(wired_stub.kind, ExitKind::Normal);
|
||||
assert_eq!(wired_stub.target, Some(target_bb)); // Wired!
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@ use crate::mir::{BinaryOp, CompareOp, ConstValue, Effect, EffectMask, MirType};
|
||||
use crate::mir::builder::control_flow::edgecfg::api::{BranchStub, EdgeStub, ExitKind, Frag};
|
||||
use crate::mir::basic_block::EdgeArgs;
|
||||
use crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
/// Phase 273 P1: PlanNormalizer - DomainPlan → CorePlan 変換 (SSOT)
|
||||
pub(in crate::mir::builder) struct PlanNormalizer;
|
||||
@ -295,11 +296,13 @@ impl PlanNormalizer {
|
||||
values: vec![i_current],
|
||||
};
|
||||
|
||||
// Phase 280 TODO: Hand-rolled Frag construction for early exit pattern
|
||||
// Reason: `found_bb` is early Return, doesn't fit compose::if_() model
|
||||
// Future: Consider compose::cleanup() for early exit normalization (Phase 281+)
|
||||
// Current structure: 2 BranchStub (header→body/after, body→found/step) + 2 EdgeStub (step→header, found→Return)
|
||||
let branches = vec![
|
||||
// Phase 281 P2: compose::cleanup() for early exit pattern
|
||||
// Reason: `found_bb` is early Return, handled by cleanup() wiring
|
||||
// Structure: main Frag (header, body, step) + cleanup Frag (found Return)
|
||||
use crate::mir::builder::control_flow::edgecfg::api::compose;
|
||||
|
||||
// Build main Frag (loop structure: header, body, step)
|
||||
let main_branches = vec![
|
||||
BranchStub {
|
||||
from: header_bb,
|
||||
cond: cond_loop,
|
||||
@ -311,31 +314,49 @@ impl PlanNormalizer {
|
||||
BranchStub {
|
||||
from: body_bb,
|
||||
cond: cond_match,
|
||||
then_target: found_bb,
|
||||
then_target: found_bb, // Early exit (handled by cleanup)
|
||||
then_args: empty_args.clone(),
|
||||
else_target: step_bb,
|
||||
else_args: empty_args.clone(),
|
||||
},
|
||||
];
|
||||
|
||||
let wires = vec![
|
||||
let main_wires = vec![
|
||||
EdgeStub {
|
||||
from: step_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: Some(header_bb),
|
||||
args: empty_args,
|
||||
args: empty_args.clone(),
|
||||
},
|
||||
];
|
||||
|
||||
let main_frag = Frag {
|
||||
entry: header_bb,
|
||||
exits: BTreeMap::new(), // No exits from main (only wires)
|
||||
wires: main_wires,
|
||||
branches: main_branches,
|
||||
};
|
||||
|
||||
// Build cleanup Frag (found_bb Return)
|
||||
let cleanup_exits = vec![
|
||||
EdgeStub {
|
||||
from: found_bb,
|
||||
kind: ExitKind::Return,
|
||||
target: None,
|
||||
target: None, // Will be wired by cleanup()
|
||||
args: ret_found_args,
|
||||
},
|
||||
];
|
||||
|
||||
let mut frag = Frag::new(header_bb);
|
||||
frag.branches = branches;
|
||||
frag.wires = wires;
|
||||
let cleanup_frag = Frag {
|
||||
entry: found_bb, // cleanup entry (not used, but required)
|
||||
exits: BTreeMap::from([(ExitKind::Return, cleanup_exits)]),
|
||||
wires: vec![], // Return is in exits, not wires
|
||||
branches: vec![],
|
||||
};
|
||||
|
||||
// Compose! normal_target=None, ret_target=None → Return を上へ伝播
|
||||
let frag = compose::cleanup(main_frag, cleanup_frag, None, None)
|
||||
.expect("compose::cleanup() failed in normalize_scan_with_init");
|
||||
|
||||
// Step 11: Build final_values (Phase 273 P2)
|
||||
let final_values = vec![(parts.loop_var.clone(), i_current)];
|
||||
@ -381,6 +402,8 @@ impl PlanNormalizer {
|
||||
ctx: &LoopPatternContext,
|
||||
) -> Result<CorePlan, String> {
|
||||
use crate::mir::builder::control_flow::joinir::trace;
|
||||
use crate::mir::builder::control_flow::edgecfg::api::compose;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
let trace_logger = trace::trace();
|
||||
let debug = ctx.debug;
|
||||
@ -637,6 +660,72 @@ impl PlanNormalizer {
|
||||
},
|
||||
];
|
||||
|
||||
// Step 9.5: Build Frags for compose::if_() (Phase 281 P0)
|
||||
// Create empty_args for EdgeStub construction
|
||||
let empty_args = EdgeArgs {
|
||||
layout: JumpArgsLayout::CarriersOnly,
|
||||
values: vec![],
|
||||
};
|
||||
|
||||
// Build then_frag: entry=then_bb, Normal exit to step
|
||||
// IMPORTANT: Use explicit EdgeStub construction with empty_args (not without_args)
|
||||
let mut then_exits = BTreeMap::new();
|
||||
then_exits.insert(
|
||||
ExitKind::Normal,
|
||||
vec![EdgeStub {
|
||||
from: then_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: None, // Unresolved exit (compose will wire to join)
|
||||
args: empty_args.clone(), // CarriersOnly layout
|
||||
}],
|
||||
);
|
||||
let then_frag = Frag {
|
||||
entry: then_bb,
|
||||
exits: then_exits,
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
};
|
||||
|
||||
// Build else_frag: entry=else_bb, Normal exit to step
|
||||
// IMPORTANT: Use explicit EdgeStub construction with empty_args (not without_args)
|
||||
let mut else_exits = BTreeMap::new();
|
||||
else_exits.insert(
|
||||
ExitKind::Normal,
|
||||
vec![EdgeStub {
|
||||
from: else_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: None, // Unresolved exit (compose will wire to join)
|
||||
args: empty_args.clone(), // CarriersOnly layout
|
||||
}],
|
||||
);
|
||||
let else_frag = Frag {
|
||||
entry: else_bb,
|
||||
exits: else_exits,
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
};
|
||||
|
||||
// Build step_frag: entry=step_bb, EMPTY (no back-edge here)
|
||||
// Reason: step→header back-edge is loop-level responsibility, not body_if_frag's
|
||||
// The back-edge will be added manually in Step 12 (final merge)
|
||||
let step_frag = Frag {
|
||||
entry: step_bb,
|
||||
exits: BTreeMap::new(),
|
||||
wires: vec![], // Empty - no back-edge here
|
||||
branches: vec![],
|
||||
};
|
||||
|
||||
// Phase 281 P0: Use compose::if_() for body branch (cond_match: then/else → step)
|
||||
let body_if_frag = compose::if_(
|
||||
body_bb, // header: where cond_match is evaluated
|
||||
cond_match, // condition ValueId
|
||||
then_frag, // then branch
|
||||
empty_args.clone(), // then_entry_args
|
||||
else_frag, // else branch
|
||||
empty_args.clone(), // else_entry_args
|
||||
step_frag, // join target (step_bb)
|
||||
);
|
||||
|
||||
// Step 10: Build block_effects (SSOT ordering: preheader, header, body, then, else, step)
|
||||
let block_effects = vec![
|
||||
(preheader_bb, vec![]), // No effects in preheader
|
||||
@ -691,20 +780,12 @@ impl PlanNormalizer {
|
||||
},
|
||||
];
|
||||
|
||||
// Step 12: Build Frag (2 branches + 3 wires)
|
||||
//
|
||||
// Phase 280 TODO: Hand-rolled Frag construction for split scan pattern
|
||||
// Target (Phase 281): compose::if_(body_bb, cond_match, then_frag, else_frag, step_frag)
|
||||
// Reason deferred: 挙動不変保証が難しい、Phase 280 は SSOT positioning 優先
|
||||
// Migration: Phase 281+ で compose::if_() への移行を検討
|
||||
// Current structure: 2 BranchStub (header→body/after, body→then/else) + 3 EdgeStub (then→step, else→step, step→header)
|
||||
let empty_args = EdgeArgs {
|
||||
layout: JumpArgsLayout::CarriersOnly,
|
||||
values: vec![],
|
||||
};
|
||||
|
||||
let branches = vec![
|
||||
// header -> body/after
|
||||
// Step 12: Build Frag - Phase 281 P0 Complete
|
||||
// - Header branch (hand-rolled): header → body/after
|
||||
// - Body branch (composed): body → then/else → step (from compose::if_())
|
||||
// - Step back-edge (hand-rolled): step → header
|
||||
let mut branches = vec![
|
||||
// Header branch (cond_loop) - hand-rolled
|
||||
BranchStub {
|
||||
from: header_bb,
|
||||
cond: cond_loop,
|
||||
@ -713,44 +794,34 @@ impl PlanNormalizer {
|
||||
else_target: after_bb,
|
||||
else_args: empty_args.clone(),
|
||||
},
|
||||
// body -> then/else
|
||||
BranchStub {
|
||||
from: body_bb,
|
||||
cond: cond_match,
|
||||
then_target: then_bb,
|
||||
then_args: empty_args.clone(),
|
||||
else_target: else_bb,
|
||||
else_args: empty_args.clone(),
|
||||
},
|
||||
];
|
||||
|
||||
let wires = vec![
|
||||
// then -> step
|
||||
EdgeStub {
|
||||
from: then_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: Some(step_bb),
|
||||
args: empty_args.clone(),
|
||||
},
|
||||
// else -> step
|
||||
EdgeStub {
|
||||
from: else_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,
|
||||
},
|
||||
];
|
||||
// Merge body_if_frag branches (body → then/else)
|
||||
branches.extend(body_if_frag.branches);
|
||||
|
||||
let mut frag = Frag::new(header_bb);
|
||||
frag.branches = branches;
|
||||
frag.wires = wires;
|
||||
// Merge body_if_frag wires (then/else → step)
|
||||
let mut wires = Vec::new();
|
||||
wires.extend(body_if_frag.wires);
|
||||
|
||||
// Add step back-edge (hand-rolled) - loop-level responsibility
|
||||
wires.push(EdgeStub {
|
||||
from: step_bb,
|
||||
kind: ExitKind::Normal,
|
||||
target: Some(header_bb),
|
||||
args: empty_args.clone(),
|
||||
});
|
||||
|
||||
let mut exits = BTreeMap::new();
|
||||
for (kind, stubs) in body_if_frag.exits {
|
||||
exits.insert(kind, stubs);
|
||||
}
|
||||
|
||||
let frag = Frag {
|
||||
entry: header_bb,
|
||||
exits,
|
||||
wires,
|
||||
branches,
|
||||
};
|
||||
|
||||
// Step 13: Build final_values (i, start for post-loop)
|
||||
let final_values = vec![
|
||||
|
||||
@ -92,6 +92,8 @@
|
||||
use crate::mir::join_ir::{JoinContId, JoinFuncId};
|
||||
use crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout;
|
||||
use crate::mir::{BasicBlock, BasicBlockId, MirFunction, MirInstruction, ValueId};
|
||||
use crate::mir::effect::EffectMask;
|
||||
use crate::ast::Span;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use super::super::{join_func_name, JoinIrVmBridgeError};
|
||||
|
||||
Reference in New Issue
Block a user