phase29ao(p9): scaffold block params for valuejoin (strict/dev verify)
This commit is contained in:
@ -18,8 +18,8 @@ Scope: Repo root の旧リンク互換。現行の入口は `docs/development/cu
|
||||
**CorePlan migration 道筋 SSOT**
|
||||
`docs/development/current/main/design/coreplan-migration-roadmap-ssot.md` が移行タスクの Done 判定の入口。
|
||||
|
||||
**Next implementation (Phase 29ao P9)**
|
||||
`docs/development/current/main/phases/phase-29ao/P9-VALUEJOIN-MINIMAL-WIRE-INSTRUCTIONS.md`
|
||||
**Next implementation (Phase 29ao P10, TBD)**
|
||||
`docs/development/current/main/phases/phase-29ao/P9-VALUEJOIN-MINIMAL-WIRE-INSTRUCTIONS.md` の Next を参照
|
||||
|
||||
**2025-12-29: Phase 29am P0 COMPLETE (CorePlan If/Exit lowerer/verifier)**
|
||||
CorePlan の If/Exit を lowerer/verifier で扱えるようにして、CorePlan 移行の土台を作った。
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
|
||||
## Current Focus: Phase 29ao(CorePlan composition)
|
||||
|
||||
Next: Phase 29ao P9(ValueJoin minimal wire)
|
||||
指示書: `docs/development/current/main/phases/phase-29ao/P9-VALUEJOIN-MINIMAL-WIRE-INSTRUCTIONS.md`
|
||||
Next: Phase 29ao P10(ValueJoin minimal wiring, TBD)
|
||||
指示書: `docs/development/current/main/phases/phase-29ao/P9-VALUEJOIN-MINIMAL-WIRE-INSTRUCTIONS.md` の Next を参照
|
||||
運用ルール: integration filter で phase143_* は回さない(JoinIR 回帰は phase29ae pack のみ)
|
||||
運用ルール: phase286_pattern9_* は legacy pack (SKIP) を使う
|
||||
移行道筋 SSOT: `docs/development/current/main/design/coreplan-migration-roadmap-ssot.md`
|
||||
@ -53,6 +53,11 @@ Next: Phase 29ao P9(ValueJoin minimal wire)
|
||||
- 変更: `src/mir/builder/control_flow/edgecfg/api/compose/seq.rs` / `src/mir/builder/control_flow/edgecfg/api/compose/if_.rs` / `src/mir/builder/control_flow/edgecfg/api/compose/cleanup.rs` / `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`
|
||||
- 検証: `cargo test --release -p nyash-rust` / `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 P9 完了** ✅
|
||||
- 目的: EdgeCFG の block params 足場を追加し、strict/dev で join 受け口の整合を fail-fast で固定
|
||||
- 変更: `src/mir/builder/control_flow/edgecfg/api/block_params.rs` / `src/mir/builder/control_flow/edgecfg/api/frag.rs` / `src/mir/builder/control_flow/edgecfg/api/compose/seq.rs` / `src/mir/builder/control_flow/edgecfg/api/compose/if_.rs` / `src/mir/builder/control_flow/edgecfg/api/compose/cleanup.rs` / `src/mir/builder/control_flow/edgecfg/api/compose/loop_.rs` / `src/mir/builder/control_flow/edgecfg/api/verify.rs` / `src/mir/builder/control_flow/edgecfg/api/emit.rs` / `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`
|
||||
- 検証: `cargo test --release -p nyash-rust --lib` / `cargo build --release` / `./tools/smokes/v2/run.sh --profile quick` / `./tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh`
|
||||
|
||||
**2025-12-29: Phase 29an P15 完了** ✅
|
||||
- 目的: P0–P14 の成果を closeout 形式でまとめ、次フェーズ(Phase 29ao)入口を固定
|
||||
- 変更: `docs/development/current/main/phases/phase-29an/README.md` / `docs/development/current/main/10-Now.md` / `docs/development/current/main/30-Backlog.md` / `CURRENT_TASK.md`
|
||||
|
||||
@ -15,8 +15,8 @@ Related:
|
||||
|
||||
- **Phase 29ao(active): CorePlan composition from Skeleton/Feature**
|
||||
- 入口: `docs/development/current/main/phases/phase-29ao/README.md`
|
||||
- 状況: P0/P1/P2/P3/P4/P5/P6/P7/P8 ✅ 完了 / Next: P9
|
||||
- Next 指示書: `docs/development/current/main/phases/phase-29ao/P9-VALUEJOIN-MINIMAL-WIRE-INSTRUCTIONS.md`
|
||||
- 状況: P0/P1/P2/P3/P4/P5/P6/P7/P8/P9 ✅ 完了 / Next: P10(TBD)
|
||||
- Next 指示書: `docs/development/current/main/phases/phase-29ao/P9-VALUEJOIN-MINIMAL-WIRE-INSTRUCTIONS.md` の Next を参照
|
||||
|
||||
- **Phase 29af(✅ COMPLETE): Boundary hygiene / regression entrypoint / carrier layout SSOT**
|
||||
- 入口: `docs/development/current/main/phases/phase-29af/README.md`
|
||||
|
||||
@ -63,7 +63,12 @@ Gate(SSOT):
|
||||
- 指示書: `docs/development/current/main/phases/phase-29ao/P8-VALUEJOIN-EDGEARGS-COMPOSE-PRESERVE-INSTRUCTIONS.md`
|
||||
- ねらい: compose::seq/if_/cleanup が EdgeArgs(layout+values) を保持することをテストで固定
|
||||
|
||||
## P9: ValueJoin minimal wire(BlockParams 足場 + strict/dev Fail-Fast)✅
|
||||
|
||||
- 指示書: `docs/development/current/main/phases/phase-29ao/P9-VALUEJOIN-MINIMAL-WIRE-INSTRUCTIONS.md`
|
||||
- ねらい: EdgeCFG の block params 足場と strict/dev verify を追加し、join 受け口の整合を Fail-Fast で固定
|
||||
|
||||
## Next(planned)
|
||||
|
||||
- P9: ValueJoin の最小 wire(block params 足場 + strict/dev Fail-Fast)
|
||||
- 指示書: `docs/development/current/main/phases/phase-29ao/P9-VALUEJOIN-MINIMAL-WIRE-INSTRUCTIONS.md`
|
||||
- P10: ValueJoin 最小配線(join block/PHI の 1 ケースだけ接続、SSOT verify を維持)
|
||||
- 方向性: `docs/development/current/main/phases/phase-29ao/P9-VALUEJOIN-MINIMAL-WIRE-INSTRUCTIONS.md` の Next を参照
|
||||
|
||||
9
src/mir/builder/control_flow/edgecfg/api/block_params.rs
Normal file
9
src/mir/builder/control_flow/edgecfg/api/block_params.rs
Normal file
@ -0,0 +1,9 @@
|
||||
use crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout;
|
||||
use crate::mir::ValueId;
|
||||
|
||||
/// Join block params (same layout as EdgeArgs).
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct BlockParams {
|
||||
pub layout: JumpArgsLayout,
|
||||
pub params: Vec<ValueId>,
|
||||
}
|
||||
@ -59,18 +59,34 @@ pub(crate) fn cleanup(
|
||||
let mut exits = BTreeMap::new();
|
||||
let mut wires = Vec::new();
|
||||
let mut branches = Vec::new();
|
||||
let mut block_params = main.block_params;
|
||||
let Frag {
|
||||
block_params: cleanup_block_params,
|
||||
exits: cleanup_exits,
|
||||
wires: cleanup_wires,
|
||||
branches: cleanup_branches,
|
||||
..
|
||||
} = cleanup_frag;
|
||||
|
||||
if let Err(message) = super::merge_block_params(
|
||||
&mut block_params,
|
||||
cleanup_block_params,
|
||||
"compose::cleanup",
|
||||
) {
|
||||
return Err(message);
|
||||
}
|
||||
|
||||
// Validate cleanup_frag structure (only exits allowed, no wires/branches)
|
||||
if !cleanup_frag.wires.is_empty() || !cleanup_frag.branches.is_empty() {
|
||||
if !cleanup_wires.is_empty() || !cleanup_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()
|
||||
cleanup_wires.len(),
|
||||
cleanup_branches.len()
|
||||
));
|
||||
}
|
||||
|
||||
// Validate cleanup_frag exits (only Normal + Return allowed in P3)
|
||||
for (kind, _) in &cleanup_frag.exits {
|
||||
for (kind, _) in &cleanup_exits {
|
||||
match kind {
|
||||
ExitKind::Normal | ExitKind::Return => {} // OK
|
||||
_ => {
|
||||
@ -83,7 +99,7 @@ pub(crate) fn cleanup(
|
||||
}
|
||||
|
||||
// Process cleanup Normal exits
|
||||
if let Some(normal_stubs) = cleanup_frag.exits.get(&ExitKind::Normal) {
|
||||
if let Some(normal_stubs) = cleanup_exits.get(&ExitKind::Normal) {
|
||||
for mut stub in normal_stubs.clone() {
|
||||
match normal_target {
|
||||
Some(target_bb) => {
|
||||
@ -101,7 +117,7 @@ pub(crate) fn cleanup(
|
||||
}
|
||||
|
||||
// Process cleanup Return exits
|
||||
if let Some(return_stubs) = cleanup_frag.exits.get(&ExitKind::Return) {
|
||||
if let Some(return_stubs) = cleanup_exits.get(&ExitKind::Return) {
|
||||
for mut stub in return_stubs.clone() {
|
||||
match ret_target {
|
||||
Some(target_bb) => {
|
||||
@ -128,6 +144,7 @@ pub(crate) fn cleanup(
|
||||
|
||||
Ok(Frag {
|
||||
entry: main.entry, // Entry = main entry (header_bb)
|
||||
block_params,
|
||||
exits,
|
||||
wires,
|
||||
branches,
|
||||
@ -165,6 +182,7 @@ mod tests {
|
||||
);
|
||||
let cleanup_frag = Frag {
|
||||
entry: cleanup_entry,
|
||||
block_params: BTreeMap::new(),
|
||||
exits,
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
|
||||
@ -69,6 +69,23 @@ pub(crate) fn if_(
|
||||
|
||||
let mut exits = BTreeMap::new();
|
||||
let mut wires = Vec::new();
|
||||
let mut block_params = t.block_params;
|
||||
let join_entry = join_frag.entry;
|
||||
|
||||
if let Err(message) = super::merge_block_params(
|
||||
&mut block_params,
|
||||
e.block_params,
|
||||
"compose::if_/else",
|
||||
) {
|
||||
panic!("{}", message);
|
||||
}
|
||||
if let Err(message) = super::merge_block_params(
|
||||
&mut block_params,
|
||||
join_frag.block_params,
|
||||
"compose::if_/join",
|
||||
) {
|
||||
panic!("{}", message);
|
||||
}
|
||||
|
||||
// then の全 exit を処理
|
||||
for (kind, stubs) in t.exits {
|
||||
@ -78,7 +95,7 @@ pub(crate) fn if_(
|
||||
let wired_stubs: Vec<EdgeStub> = stubs
|
||||
.into_iter()
|
||||
.map(|mut stub| {
|
||||
stub.target = Some(join_frag.entry);
|
||||
stub.target = Some(join_entry);
|
||||
stub
|
||||
})
|
||||
.collect();
|
||||
@ -101,7 +118,7 @@ pub(crate) fn if_(
|
||||
let wired_stubs: Vec<EdgeStub> = stubs
|
||||
.into_iter()
|
||||
.map(|mut stub| {
|
||||
stub.target = Some(join_frag.entry);
|
||||
stub.target = Some(join_entry);
|
||||
stub
|
||||
})
|
||||
.collect();
|
||||
@ -132,6 +149,7 @@ pub(crate) fn if_(
|
||||
|
||||
Frag {
|
||||
entry: header, // if の入口は header
|
||||
block_params,
|
||||
exits, // t/e の非 Normal + join_frag.exits
|
||||
wires, // t/e.Normal → join_frag.entry + t/e/join の wires
|
||||
branches, // Phase 267 P0: header の BranchStub + t/e/join の branches
|
||||
@ -185,6 +203,7 @@ mod tests {
|
||||
);
|
||||
let then_frag = Frag {
|
||||
entry: then_entry,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: then_exits,
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
@ -202,6 +221,7 @@ mod tests {
|
||||
);
|
||||
let else_frag = Frag {
|
||||
entry: else_entry,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: else_exits,
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
|
||||
@ -53,6 +53,7 @@ pub(crate) fn loop_(
|
||||
// Phase 265 P2: exit 集合の配線処理(wires/exits 分離)
|
||||
let mut exits = BTreeMap::new();
|
||||
let mut wires = Vec::new(); // Phase 265 P2: 配線済み内部配線
|
||||
let block_params = body.block_params;
|
||||
|
||||
for (kind, stubs) in body.exits {
|
||||
match kind {
|
||||
@ -95,6 +96,7 @@ pub(crate) fn loop_(
|
||||
|
||||
Frag {
|
||||
entry: header, // ループの入口
|
||||
block_params,
|
||||
exits, // Normal, Return, Unwind のみ(未配線)
|
||||
wires, // Continue → header, Break → after(配線済み)
|
||||
branches, // Phase 267 P0: body の branches
|
||||
|
||||
@ -64,6 +64,12 @@
|
||||
* - Phase 280: SSOT positioning (composition as pattern absorption destination)
|
||||
*/
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::config::env;
|
||||
use crate::mir::basic_block::BasicBlockId;
|
||||
use crate::mir::builder::control_flow::edgecfg::api::block_params::BlockParams;
|
||||
|
||||
mod cleanup;
|
||||
mod if_;
|
||||
mod loop_;
|
||||
@ -78,6 +84,27 @@ pub(crate) use loop_::loop_;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use seq::seq;
|
||||
|
||||
pub(super) fn merge_block_params(
|
||||
target: &mut BTreeMap<BasicBlockId, BlockParams>,
|
||||
incoming: BTreeMap<BasicBlockId, BlockParams>,
|
||||
context: &str,
|
||||
) -> Result<(), String> {
|
||||
let strict = env::joinir_strict_enabled() || env::joinir_dev_enabled();
|
||||
for (block, params) in incoming {
|
||||
if target.contains_key(&block) {
|
||||
if strict {
|
||||
return Err(format!(
|
||||
"[{}] duplicate block_params for {:?}",
|
||||
context, block
|
||||
));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
target.insert(block, params);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{cleanup, if_, loop_, seq};
|
||||
@ -110,6 +137,7 @@ mod tests {
|
||||
|
||||
let body_frag = Frag {
|
||||
entry: body_entry,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: body_exits,
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
@ -145,6 +173,7 @@ mod tests {
|
||||
|
||||
let body_frag = Frag {
|
||||
entry: body_entry,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: body_exits,
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
@ -199,6 +228,7 @@ mod tests {
|
||||
);
|
||||
let body_frag = Frag {
|
||||
entry: body,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: body_exits,
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
@ -233,6 +263,7 @@ mod tests {
|
||||
);
|
||||
let body_frag = Frag {
|
||||
entry: body,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: body_exits,
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
@ -267,6 +298,7 @@ mod tests {
|
||||
);
|
||||
let body_frag = Frag {
|
||||
entry: body,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: body_exits,
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
@ -297,6 +329,7 @@ mod tests {
|
||||
);
|
||||
let a_frag = Frag {
|
||||
entry: a_entry,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: a_exits,
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
@ -309,6 +342,7 @@ mod tests {
|
||||
);
|
||||
let b_frag = Frag {
|
||||
entry: b_entry,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: b_exits,
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
@ -352,6 +386,7 @@ mod tests {
|
||||
);
|
||||
let a_frag = Frag {
|
||||
entry: a_entry,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: a_exits,
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
@ -364,6 +399,7 @@ mod tests {
|
||||
);
|
||||
let b_frag = Frag {
|
||||
entry: b_entry,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: b_exits,
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
@ -404,6 +440,7 @@ mod tests {
|
||||
);
|
||||
let then_frag = Frag {
|
||||
entry: then_entry,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: then_exits,
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
@ -416,6 +453,7 @@ mod tests {
|
||||
);
|
||||
let else_frag = Frag {
|
||||
entry: else_entry,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: else_exits,
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
@ -428,6 +466,7 @@ mod tests {
|
||||
);
|
||||
let join_frag = Frag {
|
||||
entry: join_entry,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: join_exits,
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
@ -487,6 +526,7 @@ mod tests {
|
||||
);
|
||||
let then_frag = Frag {
|
||||
entry: then_entry,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: then_exits,
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
@ -503,6 +543,7 @@ mod tests {
|
||||
);
|
||||
let else_frag = Frag {
|
||||
entry: else_entry,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: else_exits,
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
@ -510,6 +551,7 @@ mod tests {
|
||||
|
||||
let join_frag = Frag {
|
||||
entry: join_entry,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: BTreeMap::new(),
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
@ -551,6 +593,7 @@ mod tests {
|
||||
// Main Frag: empty (no exits)
|
||||
let main_frag = Frag {
|
||||
entry: main_entry,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: BTreeMap::new(),
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
@ -559,6 +602,7 @@ mod tests {
|
||||
// Cleanup Frag: Return exit
|
||||
let cleanup_frag = Frag {
|
||||
entry: cleanup_bb,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: BTreeMap::from([(
|
||||
ExitKind::Return,
|
||||
vec![EdgeStub {
|
||||
@ -600,6 +644,7 @@ mod tests {
|
||||
// Main Frag: empty
|
||||
let main_frag = Frag {
|
||||
entry: main_entry,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: BTreeMap::new(),
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
@ -608,6 +653,7 @@ mod tests {
|
||||
// Cleanup Frag: Return exit
|
||||
let cleanup_frag = Frag {
|
||||
entry: cleanup_bb,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: BTreeMap::from([(
|
||||
ExitKind::Return,
|
||||
vec![EdgeStub {
|
||||
@ -649,6 +695,7 @@ mod tests {
|
||||
// Main Frag: empty
|
||||
let main_frag = Frag {
|
||||
entry: main_entry,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: BTreeMap::new(),
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
@ -657,6 +704,7 @@ mod tests {
|
||||
// Cleanup Frag: Normal exit
|
||||
let cleanup_frag = Frag {
|
||||
entry: cleanup_bb,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: BTreeMap::from([(
|
||||
ExitKind::Normal,
|
||||
vec![EdgeStub {
|
||||
@ -698,6 +746,7 @@ mod tests {
|
||||
// Main Frag: empty
|
||||
let main_frag = Frag {
|
||||
entry: main_entry,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: BTreeMap::new(),
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
@ -706,6 +755,7 @@ mod tests {
|
||||
// Cleanup Frag: Normal exit
|
||||
let cleanup_frag = Frag {
|
||||
entry: cleanup_bb,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: BTreeMap::from([(
|
||||
ExitKind::Normal,
|
||||
vec![EdgeStub {
|
||||
|
||||
@ -41,6 +41,16 @@ use crate::mir::builder::control_flow::edgecfg::api::frag::Frag;
|
||||
pub(crate) fn seq(a: Frag, b: Frag) -> Frag {
|
||||
let mut exits = BTreeMap::new();
|
||||
let mut wires = Vec::new();
|
||||
let mut block_params = a.block_params;
|
||||
let b_entry = b.entry;
|
||||
|
||||
if let Err(message) = super::merge_block_params(
|
||||
&mut block_params,
|
||||
b.block_params,
|
||||
"compose::seq",
|
||||
) {
|
||||
panic!("{}", message);
|
||||
}
|
||||
|
||||
// a の全 exit を処理
|
||||
for (kind, stubs) in a.exits {
|
||||
@ -50,7 +60,7 @@ pub(crate) fn seq(a: Frag, b: Frag) -> Frag {
|
||||
let wired_stubs: Vec<EdgeStub> = stubs
|
||||
.into_iter()
|
||||
.map(|mut stub| {
|
||||
stub.target = Some(b.entry);
|
||||
stub.target = Some(b_entry);
|
||||
stub
|
||||
})
|
||||
.collect();
|
||||
@ -82,6 +92,7 @@ pub(crate) fn seq(a: Frag, b: Frag) -> Frag {
|
||||
|
||||
Frag {
|
||||
entry: a.entry, // seq の入口は a の入口
|
||||
block_params,
|
||||
exits, // a の非 Normal + b の全 exit
|
||||
wires, // a.Normal → b.entry + a.wires + b.wires
|
||||
branches, // Phase 267 P0: a.branches + b.branches
|
||||
@ -92,6 +103,7 @@ pub(crate) fn seq(a: Frag, b: Frag) -> Frag {
|
||||
mod tests {
|
||||
use super::seq;
|
||||
use crate::mir::basic_block::{BasicBlockId, EdgeArgs};
|
||||
use crate::mir::builder::control_flow::edgecfg::api::block_params::BlockParams;
|
||||
use crate::mir::builder::control_flow::edgecfg::api::edge_stub::EdgeStub;
|
||||
use crate::mir::builder::control_flow::edgecfg::api::exit_kind::ExitKind;
|
||||
use crate::mir::builder::control_flow::edgecfg::api::frag::Frag;
|
||||
@ -117,6 +129,7 @@ mod tests {
|
||||
exits.insert(ExitKind::Normal, vec![stub]);
|
||||
let a = Frag {
|
||||
entry: a_entry,
|
||||
block_params: BTreeMap::new(),
|
||||
exits,
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
@ -127,4 +140,23 @@ mod tests {
|
||||
assert_eq!(composed.wires[0].target, Some(b_entry));
|
||||
assert_eq!(composed.wires[0].args, args);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seq_preserves_block_params() {
|
||||
let a_entry = BasicBlockId::new(1);
|
||||
let b_entry = BasicBlockId::new(2);
|
||||
let join_bb = BasicBlockId::new(3);
|
||||
let a = Frag::new(a_entry);
|
||||
let mut b = Frag::new(b_entry);
|
||||
b.block_params.insert(
|
||||
join_bb,
|
||||
BlockParams {
|
||||
layout: JumpArgsLayout::ExprResultPlusCarriers,
|
||||
params: vec![ValueId(42)],
|
||||
},
|
||||
);
|
||||
|
||||
let composed = seq(a, b);
|
||||
assert!(composed.block_params.contains_key(&join_bb));
|
||||
}
|
||||
}
|
||||
|
||||
@ -405,6 +405,7 @@ mod tests {
|
||||
|
||||
let frag = Frag {
|
||||
entry: header,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: BTreeMap::new(),
|
||||
wires: vec![],
|
||||
branches: vec![branch],
|
||||
@ -489,6 +490,7 @@ mod tests {
|
||||
|
||||
let frag = Frag {
|
||||
entry: bb0,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: BTreeMap::new(),
|
||||
wires: vec![wire],
|
||||
branches: vec![branch],
|
||||
@ -516,6 +518,7 @@ mod tests {
|
||||
|
||||
let then_frag = Frag {
|
||||
entry: then_entry,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: {
|
||||
let mut exits = BTreeMap::new();
|
||||
exits.insert(
|
||||
@ -530,6 +533,7 @@ mod tests {
|
||||
|
||||
let else_frag = Frag {
|
||||
entry: else_entry,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: {
|
||||
let mut exits = BTreeMap::new();
|
||||
exits.insert(
|
||||
@ -544,6 +548,7 @@ mod tests {
|
||||
|
||||
let join_frag = Frag {
|
||||
entry: join_entry,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: BTreeMap::new(),
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use crate::mir::basic_block::BasicBlockId;
|
||||
use super::block_params::BlockParams;
|
||||
use super::exit_kind::ExitKind;
|
||||
use super::edge_stub::EdgeStub;
|
||||
use super::branch_stub::BranchStub;
|
||||
@ -39,6 +40,9 @@ pub struct Frag {
|
||||
/// 断片の入口ブロック
|
||||
pub entry: BasicBlockId,
|
||||
|
||||
/// 断片の block params(join 受け口)
|
||||
pub block_params: BTreeMap<BasicBlockId, BlockParams>,
|
||||
|
||||
/// 断片からの未配線脱出エッジ(ExitKind → EdgeStub のリスト)
|
||||
///
|
||||
/// Phase 265 P2: target = None のみ(外へ出る exit)
|
||||
@ -64,6 +68,7 @@ impl Frag {
|
||||
pub fn new(entry: BasicBlockId) -> Self {
|
||||
Self {
|
||||
entry,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: BTreeMap::new(),
|
||||
wires: vec![], // Phase 265 P2: 配線済み内部配線
|
||||
branches: vec![], // Phase 267 P0: 配線済み分岐
|
||||
@ -76,6 +81,7 @@ impl Frag {
|
||||
exits.insert(stub.kind, vec![stub]);
|
||||
Self {
|
||||
entry,
|
||||
block_params: BTreeMap::new(),
|
||||
exits,
|
||||
wires: vec![], // Phase 265 P2: 配線済み内部配線
|
||||
branches: vec![], // Phase 267 P0: 配線済み分岐
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
|
||||
pub mod exit_kind;
|
||||
pub mod edge_stub;
|
||||
pub mod block_params;
|
||||
pub mod frag;
|
||||
pub mod compose;
|
||||
pub mod verify;
|
||||
@ -26,6 +27,8 @@ pub mod branch_stub; // Phase 267 P0: 追加
|
||||
// 公開型(安定)
|
||||
pub use exit_kind::ExitKind;
|
||||
pub use edge_stub::EdgeStub;
|
||||
#[allow(unused_imports)]
|
||||
pub use block_params::BlockParams;
|
||||
pub use frag::Frag;
|
||||
pub use branch_stub::BranchStub; // Phase 267 P0: 追加
|
||||
|
||||
|
||||
@ -110,6 +110,8 @@ pub fn verify_frag_invariants(frag: &Frag) -> Result<(), String> {
|
||||
/// - 新規に `verify_frag_invariants_strict()` を追加し、P266 の PoC/emit 側だけ strict を使う
|
||||
pub fn verify_frag_invariants_strict(frag: &Frag) -> Result<(), String> {
|
||||
use super::exit_kind::ExitKind;
|
||||
use crate::mir::basic_block::{BasicBlockId, EdgeArgs};
|
||||
use crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout;
|
||||
|
||||
// 1. exits と wires の両方が空の場合は警告(非致命的)
|
||||
if frag.exits.is_empty() && frag.wires.is_empty() {
|
||||
@ -145,6 +147,53 @@ pub fn verify_frag_invariants_strict(frag: &Frag) -> Result<(), String> {
|
||||
}
|
||||
}
|
||||
|
||||
if crate::config::env::joinir_strict_enabled() || crate::config::env::joinir_dev_enabled() {
|
||||
let check_edge_args = |target: Option<BasicBlockId>,
|
||||
args: &EdgeArgs,
|
||||
context: &str|
|
||||
-> Result<(), String> {
|
||||
if args.layout != JumpArgsLayout::ExprResultPlusCarriers {
|
||||
return Ok(());
|
||||
}
|
||||
let target = target.ok_or_else(|| {
|
||||
format!(
|
||||
"[verify_frag_strict] ExprResultPlusCarriers requires target block (context={})",
|
||||
context
|
||||
)
|
||||
})?;
|
||||
let params = frag.block_params.get(&target).ok_or_else(|| {
|
||||
format!(
|
||||
"[verify_frag_strict] Missing block_params for target {:?} (context={})",
|
||||
target, context
|
||||
)
|
||||
})?;
|
||||
if params.layout != args.layout {
|
||||
return Err(format!(
|
||||
"[verify_frag_strict] BlockParams layout mismatch at {:?} (context={}, block={:?}, edge={:?})",
|
||||
target, context, params.layout, args.layout
|
||||
));
|
||||
}
|
||||
if params.params.len() != args.values.len() {
|
||||
return Err(format!(
|
||||
"[verify_frag_strict] BlockParams length mismatch at {:?} (context={}, params={}, args={})",
|
||||
target,
|
||||
context,
|
||||
params.params.len(),
|
||||
args.values.len()
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
|
||||
for stub in &frag.wires {
|
||||
check_edge_args(stub.target, &stub.args, "wire")?;
|
||||
}
|
||||
for branch in &frag.branches {
|
||||
check_edge_args(Some(branch.then_target), &branch.then_args, "branch/then")?;
|
||||
check_edge_args(Some(branch.else_target), &branch.else_args, "branch/else")?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -152,7 +201,13 @@ pub fn verify_frag_invariants_strict(frag: &Frag) -> Result<(), String> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::collections::BTreeMap;
|
||||
use crate::mir::basic_block::BasicBlockId;
|
||||
use crate::mir::basic_block::{BasicBlockId, EdgeArgs};
|
||||
use crate::mir::builder::control_flow::edgecfg::api::block_params::BlockParams;
|
||||
use crate::mir::builder::control_flow::edgecfg::api::edge_stub::EdgeStub;
|
||||
use crate::mir::builder::control_flow::edgecfg::api::exit_kind::ExitKind;
|
||||
use crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout;
|
||||
use crate::mir::ValueId;
|
||||
use std::env;
|
||||
|
||||
#[test]
|
||||
fn test_verify_frag_basic() {
|
||||
@ -162,6 +217,7 @@ mod tests {
|
||||
|
||||
let frag = Frag {
|
||||
entry: header,
|
||||
block_params: BTreeMap::new(),
|
||||
exits,
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
@ -170,4 +226,77 @@ mod tests {
|
||||
// P1: Always returns Ok(())
|
||||
assert!(verify_frag_invariants(&frag).is_ok());
|
||||
}
|
||||
|
||||
fn strict_env_guard() -> impl Drop {
|
||||
env::set_var("NYASH_JOINIR_STRICT", "1");
|
||||
struct Guard;
|
||||
impl Drop for Guard {
|
||||
fn drop(&mut self) {
|
||||
let _ = env::remove_var("NYASH_JOINIR_STRICT");
|
||||
}
|
||||
}
|
||||
Guard
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strict_requires_block_params_for_expr_result_layout() {
|
||||
let _guard = strict_env_guard();
|
||||
let entry = BasicBlockId(1);
|
||||
let target = BasicBlockId(2);
|
||||
let args = EdgeArgs {
|
||||
layout: JumpArgsLayout::ExprResultPlusCarriers,
|
||||
values: vec![ValueId(10), ValueId(11)],
|
||||
};
|
||||
|
||||
let frag = Frag {
|
||||
entry,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: BTreeMap::new(),
|
||||
wires: vec![EdgeStub {
|
||||
from: entry,
|
||||
kind: ExitKind::Normal,
|
||||
target: Some(target),
|
||||
args,
|
||||
}],
|
||||
branches: vec![],
|
||||
};
|
||||
|
||||
let err = verify_frag_invariants_strict(&frag).unwrap_err();
|
||||
assert!(err.contains("Missing block_params"), "unexpected err: {}", err);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strict_accepts_matching_block_params_layout_and_len() {
|
||||
let _guard = strict_env_guard();
|
||||
let entry = BasicBlockId(1);
|
||||
let target = BasicBlockId(2);
|
||||
let args = EdgeArgs {
|
||||
layout: JumpArgsLayout::ExprResultPlusCarriers,
|
||||
values: vec![ValueId(10), ValueId(11)],
|
||||
};
|
||||
|
||||
let mut block_params = BTreeMap::new();
|
||||
block_params.insert(
|
||||
target,
|
||||
BlockParams {
|
||||
layout: JumpArgsLayout::ExprResultPlusCarriers,
|
||||
params: vec![ValueId(100), ValueId(101)],
|
||||
},
|
||||
);
|
||||
|
||||
let frag = Frag {
|
||||
entry,
|
||||
block_params,
|
||||
exits: BTreeMap::new(),
|
||||
wires: vec![EdgeStub {
|
||||
from: entry,
|
||||
kind: ExitKind::Normal,
|
||||
target: Some(target),
|
||||
args,
|
||||
}],
|
||||
branches: vec![],
|
||||
};
|
||||
|
||||
assert!(verify_frag_invariants_strict(&frag).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@ pub(super) fn normal_exit_frag(
|
||||
);
|
||||
Frag {
|
||||
entry,
|
||||
block_params: BTreeMap::new(),
|
||||
exits,
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
@ -45,6 +46,7 @@ pub(super) fn build_body_if_frag(
|
||||
|
||||
let step_frag = Frag {
|
||||
entry: step_bb,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: BTreeMap::new(),
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
|
||||
@ -144,6 +144,7 @@ impl super::PlanNormalizer {
|
||||
|
||||
let frag = Frag {
|
||||
entry: header_bb,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: BTreeMap::new(),
|
||||
wires,
|
||||
branches,
|
||||
|
||||
@ -259,6 +259,7 @@ impl super::PlanNormalizer {
|
||||
|
||||
let frag = Frag {
|
||||
entry: header_bb,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: BTreeMap::new(),
|
||||
wires,
|
||||
branches,
|
||||
|
||||
@ -247,6 +247,7 @@ impl super::PlanNormalizer {
|
||||
|
||||
let frag = Frag {
|
||||
entry: header_bb,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: BTreeMap::new(),
|
||||
wires,
|
||||
branches,
|
||||
|
||||
@ -259,6 +259,7 @@ impl super::PlanNormalizer {
|
||||
|
||||
let frag = Frag {
|
||||
entry: header_bb,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: BTreeMap::new(),
|
||||
wires,
|
||||
branches,
|
||||
|
||||
@ -263,6 +263,7 @@ impl super::PlanNormalizer {
|
||||
|
||||
let frag = Frag {
|
||||
entry: header_bb,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: BTreeMap::new(),
|
||||
wires,
|
||||
branches,
|
||||
@ -502,6 +503,7 @@ impl super::PlanNormalizer {
|
||||
|
||||
let frag = Frag {
|
||||
entry: header_bb,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: BTreeMap::new(),
|
||||
wires,
|
||||
branches,
|
||||
|
||||
@ -239,6 +239,7 @@ impl super::PlanNormalizer {
|
||||
|
||||
let main_frag = Frag {
|
||||
entry: header_bb,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: BTreeMap::new(),
|
||||
wires: main_wires,
|
||||
branches: main_branches,
|
||||
@ -261,6 +262,7 @@ impl super::PlanNormalizer {
|
||||
|
||||
let cleanup_frag = Frag {
|
||||
entry: found_bb,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: BTreeMap::from([(ExitKind::Return, cleanup_exits)]),
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
|
||||
@ -174,6 +174,7 @@ impl super::PlanNormalizer {
|
||||
|
||||
let frag = Frag {
|
||||
entry: header_bb,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: BTreeMap::new(),
|
||||
wires,
|
||||
branches,
|
||||
|
||||
@ -330,6 +330,7 @@ impl super::PlanNormalizer {
|
||||
|
||||
let main_frag = Frag {
|
||||
entry: header_bb,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: BTreeMap::new(),
|
||||
wires: main_wires,
|
||||
branches: main_branches,
|
||||
@ -344,6 +345,7 @@ impl super::PlanNormalizer {
|
||||
|
||||
let cleanup_frag = Frag {
|
||||
entry: found_bb,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: BTreeMap::from([(ExitKind::Return, cleanup_exits)]),
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
|
||||
@ -328,10 +328,18 @@ impl super::PlanNormalizer {
|
||||
else_args: empty_args.clone(),
|
||||
}];
|
||||
|
||||
branches.extend(body_if_frag.branches);
|
||||
let Frag {
|
||||
block_params,
|
||||
branches: body_branches,
|
||||
wires: body_wires,
|
||||
exits: body_exits,
|
||||
..
|
||||
} = body_if_frag;
|
||||
|
||||
branches.extend(body_branches);
|
||||
|
||||
let mut wires = Vec::new();
|
||||
wires.extend(body_if_frag.wires);
|
||||
wires.extend(body_wires);
|
||||
|
||||
wires.push(EdgeStub {
|
||||
from: step_bb,
|
||||
@ -341,12 +349,13 @@ impl super::PlanNormalizer {
|
||||
});
|
||||
|
||||
let mut exits = BTreeMap::new();
|
||||
for (kind, stubs) in body_if_frag.exits {
|
||||
for (kind, stubs) in body_exits {
|
||||
exits.insert(kind, stubs);
|
||||
}
|
||||
|
||||
let frag = Frag {
|
||||
entry: header_bb,
|
||||
block_params,
|
||||
exits,
|
||||
wires,
|
||||
branches,
|
||||
|
||||
@ -124,6 +124,7 @@ impl super::PlanNormalizer {
|
||||
|
||||
let frag = Frag {
|
||||
entry: header_bb,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: build_exitmap_from_presence(
|
||||
&facts.exit_kinds_present,
|
||||
&facts.cleanup_kinds_present,
|
||||
|
||||
@ -700,6 +700,7 @@ mod tests {
|
||||
}],
|
||||
frag: Frag {
|
||||
entry: header_bb,
|
||||
block_params: BTreeMap::new(),
|
||||
exits: BTreeMap::new(),
|
||||
wires: vec![],
|
||||
branches: vec![],
|
||||
|
||||
Reference in New Issue
Block a user