diff --git a/docs/development/current/main/10-Now.md b/docs/development/current/main/10-Now.md index f069eb2a..0aab8d2c 100644 --- a/docs/development/current/main/10-Now.md +++ b/docs/development/current/main/10-Now.md @@ -2,11 +2,17 @@ ## Current Focus: Phase 29af(Boundary Hygiene SSOT 固定) +**2025-12-29: Phase 29af P3 完了** ✅ +- 目的: carrier の順序(loop_var + carriers)を merge 側 SSOT に統合(仕様不変) +- 入口: `src/mir/builder/control_flow/joinir/merge/boundary_carrier_layout.rs` +- 検証: `cargo build --release` / `./tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh` / `./tools/smokes/v2/run.sh --profile quick` PASS + **2025-12-29: Phase 29af P1 完了** ✅ - 目的: boundary hygiene を merge 入口(`contract_checks`)へ集約して再発検知を SSOT 化(仕様不変) - 実装: `src/mir/builder/control_flow/joinir/merge/contract_checks/boundary_hygiene.rs`(strict/dev のみ) - 配線: `src/mir/builder/control_flow/joinir/merge/contract_checks/boundary_creation.rs` - 検証: `cargo build --release` / `./tools/smokes/v2/run.sh --profile quick` / `./tools/smokes/v2/run.sh --profile integration --filter "phase29ab_pattern2_"` / `./tools/smokes/v2/run.sh --profile integration --filter "phase1883_"` PASS +- JoinIR 回帰確認: `./tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh` **2025-12-29: Phase 29af P0 完了** ✅ - 目的: Pattern2 の boundary 情報の歪みを SSOT 化し、exit/header/latch の責務境界を固定(仕様不変) diff --git a/docs/development/current/main/30-Backlog.md b/docs/development/current/main/30-Backlog.md index de254f5c..9eb6114d 100644 --- a/docs/development/current/main/30-Backlog.md +++ b/docs/development/current/main/30-Backlog.md @@ -8,7 +8,7 @@ Related: ## 直近(JoinIR/selfhost) -- **Phase 29af P1(✅ COMPLETE): Pattern2 Boundary Hygiene(contract_checks 集約)** +- **Phase 29af P3(✅ COMPLETE): BoundaryCarrierLayout SSOT(order 統合)** - 入口: `docs/development/current/main/phases/phase-29af/README.md` - **Phase 29ae P1(✅ COMPLETE): JoinIR Regression Pack (SSOT固定)** diff --git a/docs/development/current/main/phases/phase-29ae/README.md b/docs/development/current/main/phases/phase-29ae/README.md index ac50edf2..7647c2bf 100644 --- a/docs/development/current/main/phases/phase-29ae/README.md +++ b/docs/development/current/main/phases/phase-29ae/README.md @@ -19,10 +19,7 @@ Goal: JoinIR の最小回帰セットを SSOT として固定する。 ## Commands -- `./tools/smokes/v2/run.sh --profile integration --filter "phase29ab_pattern2_"` -- `./tools/smokes/v2/run.sh --profile integration --filter "phase29ab_pattern6_"` -- `./tools/smokes/v2/run.sh --profile integration --filter "phase29ab_pattern7_"` -- `./tools/smokes/v2/run.sh --profile integration --filter "phase1883_"`(RC=9 を PASS 扱い) +- `./tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh` ## Status diff --git a/docs/development/current/main/phases/phase-29af/P2-JOINIR-REGRESSION-PACK-ENTRYPOINT-INSTRUCTIONS.md b/docs/development/current/main/phases/phase-29af/P2-JOINIR-REGRESSION-PACK-ENTRYPOINT-INSTRUCTIONS.md new file mode 100644 index 00000000..10788182 --- /dev/null +++ b/docs/development/current/main/phases/phase-29af/P2-JOINIR-REGRESSION-PACK-ENTRYPOINT-INSTRUCTIONS.md @@ -0,0 +1,36 @@ +# Phase 29af P2: JoinIR Regression Pack Entrypoint — Instructions + +Status: Ready for execution +Scope: 回帰パックの実行導線を 1 コマンドに収束(仕様不変) + +## Goal + +JoinIR の回帰確認を “1本のスクリプト” に固定し、phase29ab / phase1883 の再実行を迷わず回せる状態にする。 + +## Non-goals + +- 挙動変更(release 既定挙動の変更) +- env var の追加 +- fixture/smoke の増加(新規ケースは不要) + +## Implementation Steps + +1) 回帰パックの entrypoint script を追加 + - `tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh` + - 内容: `phase29ab_pattern2_` / `phase29ab_pattern6_` / `phase29ab_pattern7_` / `phase1883_` を順に呼ぶだけ + +2) docs を一本化 + - `docs/development/current/main/phases/phase-29ae/README.md` + - Commands を上記 script 1 本に収束 + - `docs/development/current/main/10-Now.md` + - “JoinIR 回帰確認はこの 1 本” を追記 + +## Verification + +- `./tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh` +- `./tools/smokes/v2/run.sh --profile quick` + +## Acceptance Criteria + +- 回帰パックが 1 コマンドで PASS +- quick 154/154 PASS(不変) diff --git a/docs/development/current/main/phases/phase-29af/README.md b/docs/development/current/main/phases/phase-29af/README.md index f451747d..438e4764 100644 --- a/docs/development/current/main/phases/phase-29af/README.md +++ b/docs/development/current/main/phases/phase-29af/README.md @@ -6,6 +6,8 @@ Goal: Pattern2 の boundary 情報の歪みを SSOT で整理し、将来の回 - P0: ✅ COMPLETE(commit: `19f2c6b7f`) - P1: ✅ COMPLETE(merge `contract_checks` への集約) +- P2: ✅ COMPLETE(JoinIR 回帰パックを 1 コマンドに収束) +- P3: ✅ COMPLETE(BoundaryCarrierLayout SSOT) ## Boundary Contract (SSOT) @@ -34,6 +36,22 @@ P0 で確定した boundary hygiene を、merge 入口の `contract_checks` に - 配線: `src/mir/builder/control_flow/joinir/merge/contract_checks/boundary_creation.rs` - 実行条件: `joinir_strict` または `joinir_dev` のみ Fail-Fast +## P2: JoinIR Regression Pack Entrypoint + +JoinIR 回帰確認の導線を 1 コマンドに収束する(仕様不変)。 + +- Script: `tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh` +- SSOT: `docs/development/current/main/phases/phase-29ae/README.md`(Commands) +- 指示書: `docs/development/current/main/phases/phase-29af/P2-JOINIR-REGRESSION-PACK-ENTRYPOINT-INSTRUCTIONS.md` + +## P3: BoundaryCarrierLayout SSOT + +carrier の順序(loop_var + carriers)を merge 側の SSOT に統合する(仕様不変)。 + +- SSOT: `src/mir/builder/control_flow/joinir/merge/boundary_carrier_layout.rs` +- 適用: tail_call_policy / latch_incoming_recorder の order 統一 +- contract_checks: `phase29af/boundary_hygiene/layout_len`(strict/dev のみ) + ## Verification - `cargo build --release` diff --git a/src/mir/builder/control_flow/joinir/merge/boundary_carrier_layout.rs b/src/mir/builder/control_flow/joinir/merge/boundary_carrier_layout.rs new file mode 100644 index 00000000..921f95cb --- /dev/null +++ b/src/mir/builder/control_flow/joinir/merge/boundary_carrier_layout.rs @@ -0,0 +1,48 @@ +use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary; + +/// Phase 29af P3: Boundary carrier layout SSOT (order only). +/// +/// Order rule: +/// - loop_var (if any) first +/// - then carriers (from carrier_info if present, otherwise exit_bindings order) +#[derive(Debug, Clone)] +pub struct BoundaryCarrierLayout { + ordered_names: Vec, +} + +impl BoundaryCarrierLayout { + pub fn from_boundary(boundary: &JoinInlineBoundary) -> Self { + let mut ordered_names = Vec::new(); + + if let Some(loop_var) = boundary.loop_var_name.as_deref() { + ordered_names.push(loop_var.to_string()); + } + + if let Some(ref carrier_info) = boundary.carrier_info { + for carrier in carrier_info.carriers.iter() { + ordered_names.push(carrier.name.clone()); + } + } else { + for binding in boundary.exit_bindings.iter() { + if boundary.loop_var_name.as_deref() == Some(binding.carrier_name.as_str()) { + continue; + } + ordered_names.push(binding.carrier_name.clone()); + } + } + + Self { ordered_names } + } + + pub fn ordered_names(&self) -> Vec<&str> { + self.ordered_names.iter().map(|name| name.as_str()).collect() + } + + pub fn ordered_arg_index(&self, name: &str) -> Option { + self.ordered_names.iter().position(|n| n == name) + } + + pub fn len(&self) -> usize { + self.ordered_names.len() + } +} diff --git a/src/mir/builder/control_flow/joinir/merge/contract_checks/boundary_hygiene.rs b/src/mir/builder/control_flow/joinir/merge/contract_checks/boundary_hygiene.rs index 1b4969ba..8e9f51a8 100644 --- a/src/mir/builder/control_flow/joinir/merge/contract_checks/boundary_hygiene.rs +++ b/src/mir/builder/control_flow/joinir/merge/contract_checks/boundary_hygiene.rs @@ -1,4 +1,5 @@ use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary; +use crate::mir::builder::control_flow::joinir::merge::boundary_carrier_layout::BoundaryCarrierLayout; use crate::mir::ValueId; use std::collections::BTreeSet; @@ -37,6 +38,20 @@ pub(in crate::mir::builder::control_flow::joinir::merge) fn verify_boundary_hygi } if let Some(carrier_info) = &boundary.carrier_info { + let expected_len = + carrier_info.carriers.len() + if boundary.loop_var_name.is_some() { 1 } else { 0 }; + let layout_len = BoundaryCarrierLayout::from_boundary(boundary).len(); + if expected_len != layout_len { + return Err(error_tags::freeze_with_hint( + "phase29af/boundary_hygiene/layout_len", + &format!( + "boundary carrier layout len {} does not match carrier_info len {}", + layout_len, expected_len + ), + "ensure loop_var + carriers ordering is consistent with carrier_info", + )); + } + let mut carrier_names = BTreeSet::new(); carrier_names.insert(carrier_info.loop_var_name.clone()); diff --git a/src/mir/builder/control_flow/joinir/merge/mod.rs b/src/mir/builder/control_flow/joinir/merge/mod.rs index 5372ff9b..d935196b 100644 --- a/src/mir/builder/control_flow/joinir/merge/mod.rs +++ b/src/mir/builder/control_flow/joinir/merge/mod.rs @@ -14,6 +14,7 @@ mod block_allocator; mod block_remapper; // Phase 284 P1: Block ID remap SSOT +mod boundary_carrier_layout; // Phase 29af P3: Carrier order SSOT mod boundary_logging; // Phase 287 P0.5: Boundary logging consolidation mod carrier_init_builder; mod config; diff --git a/src/mir/builder/control_flow/joinir/merge/rewriter/latch_incoming_recorder.rs b/src/mir/builder/control_flow/joinir/merge/rewriter/latch_incoming_recorder.rs index 77967018..ceeabba5 100644 --- a/src/mir/builder/control_flow/joinir/merge/rewriter/latch_incoming_recorder.rs +++ b/src/mir/builder/control_flow/joinir/merge/rewriter/latch_incoming_recorder.rs @@ -8,6 +8,7 @@ use crate::mir::builder::control_flow::joinir::merge::loop_header_phi_info::LoopHeaderPhiInfo; use crate::mir::builder::control_flow::joinir::merge::tail_call_classifier::TailCallKind; +use crate::mir::builder::control_flow::joinir::merge::boundary_carrier_layout::BoundaryCarrierLayout; use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary; use crate::mir::{BasicBlockId, ValueId}; use crate::mir::builder::control_flow::joinir::merge::dev_log; @@ -34,6 +35,9 @@ pub(in crate::mir::builder::control_flow::joinir::merge) fn record_if_backedge( return; } + let layout = BoundaryCarrierLayout::from_boundary(boundary); + let ordered_carriers = layout.ordered_names(); + if let Some(loop_var_name) = &boundary.loop_var_name { debug_assert!( !args.is_empty(), @@ -55,35 +59,20 @@ pub(in crate::mir::builder::control_flow::joinir::merge) fn record_if_backedge( } } } - if let Some(&latch_value) = args.first() { - loop_header_phi_info.set_latch_incoming(loop_var_name, new_block_id, latch_value); + if let Some(idx) = layout.ordered_arg_index(loop_var_name) { + if let Some(&latch_value) = args.get(idx) { + loop_header_phi_info.set_latch_incoming(loop_var_name, new_block_id, latch_value); + } } } // Other carriers (excluding loop_var) - let mut carrier_arg_idx = if boundary.loop_var_name.is_some() { 1 } else { 0 }; - if let Some(ref carrier_info) = boundary.carrier_info { - for carrier in carrier_info.carriers.iter() { - if boundary.loop_var_name.as_deref() == Some(carrier.name.as_str()) { - continue; - } - if let Some(&latch_value) = args.get(carrier_arg_idx) { - loop_header_phi_info.set_latch_incoming(&carrier.name, new_block_id, latch_value); - carrier_arg_idx += 1; - } + for (idx, carrier_name) in ordered_carriers.iter().enumerate() { + if boundary.loop_var_name.as_deref() == Some(*carrier_name) { + continue; } - } else { - for binding in boundary.exit_bindings.iter() { - if let Some(ref loop_var) = boundary.loop_var_name { - if &binding.carrier_name == loop_var { - continue; - } - } - - if let Some(&latch_value) = args.get(carrier_arg_idx) { - loop_header_phi_info.set_latch_incoming(&binding.carrier_name, new_block_id, latch_value); - carrier_arg_idx += 1; - } + if let Some(&latch_value) = args.get(idx) { + loop_header_phi_info.set_latch_incoming(carrier_name, new_block_id, latch_value); } } diff --git a/src/mir/builder/control_flow/joinir/merge/rewriter/tail_call_policy.rs b/src/mir/builder/control_flow/joinir/merge/rewriter/tail_call_policy.rs index b6183c5e..65cc5695 100644 --- a/src/mir/builder/control_flow/joinir/merge/rewriter/tail_call_policy.rs +++ b/src/mir/builder/control_flow/joinir/merge/rewriter/tail_call_policy.rs @@ -5,6 +5,7 @@ //! - Record latch incoming in one place for BackEdge use crate::mir::builder::control_flow::joinir::merge::contract_checks::is_entry_like_source; +use crate::mir::builder::control_flow::joinir::merge::boundary_carrier_layout::BoundaryCarrierLayout; use crate::mir::builder::control_flow::joinir::merge::loop_header_phi_info::LoopHeaderPhiInfo; use crate::mir::builder::control_flow::joinir::merge::rewriter::latch_incoming_recorder; use crate::mir::builder::control_flow::joinir::merge::tail_call_classifier::TailCallKind; @@ -38,29 +39,17 @@ pub(super) fn record_latch_incoming_if_backedge( let mut latch_args: Vec = Vec::new(); let mut loop_var_updated = false; - let mut ordered_carriers: Vec<&str> = Vec::new(); let mut other_phi_dsts: std::collections::BTreeSet = std::collections::BTreeSet::new(); if let Some(loop_var) = boundary.loop_var_name.as_deref() { - ordered_carriers.push(loop_var); for (name, entry) in loop_header_phi_info.carrier_phis.iter() { if name.as_str() != loop_var { other_phi_dsts.insert(entry.phi_dst); } } } - if let Some(ref carrier_info) = boundary.carrier_info { - for carrier in carrier_info.carriers.iter() { - ordered_carriers.push(carrier.name.as_str()); - } - } else { - for binding in boundary.exit_bindings.iter() { - if boundary.loop_var_name.as_deref() == Some(binding.carrier_name.as_str()) { - continue; - } - ordered_carriers.push(binding.carrier_name.as_str()); - } - } + let layout = BoundaryCarrierLayout::from_boundary(boundary); + let ordered_carriers = layout.ordered_names(); for (idx, carrier_name) in ordered_carriers.iter().enumerate() { let phi_dst = match loop_header_phi_info.get_carrier_phi(carrier_name) { diff --git a/tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh b/tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh new file mode 100644 index 00000000..ee2282da --- /dev/null +++ b/tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# phase29ae_regression_pack_vm.sh - JoinIR regression pack entrypoint (VM) + +source "$(dirname "$0")/../../../lib/test_runner.sh" +require_env || exit 2 + +run_filter() { + local label="$1" + local filter="$2" + + log_info "phase29ae_regression_pack_vm: ${label} (${filter})" + if ! "$NYASH_ROOT/tools/smokes/v2/run.sh" --profile integration --filter "$filter"; then + log_error "phase29ae_regression_pack_vm: ${label} failed" + return 1 + fi + + return 0 +} + +run_filter "pattern2" "phase29ab_pattern2_" || exit 1 +run_filter "pattern6" "phase29ab_pattern6_" || exit 1 +run_filter "pattern7" "phase29ab_pattern7_" || exit 1 +run_filter "phase1883" "phase1883_" || exit 1 + +log_success "phase29ae_regression_pack_vm: all JoinIR regression filters passed" +exit 0