**Phase 25.1m: Continue PHI修正** - seal_phis に continue_snapshots 入力を追加 (loopform_builder.rs) - LoopShape::debug_validate に continue/break エッジ検証追加 (control_form.rs) - test_seal_phis_includes_continue_snapshots テスト追加 - 実証テスト成功: balanced scan loop で 228回イテレーション確認 **Bug A修正: main(args) でループ未実行問題** - LoopBuilder::build_loop で entry → preheader への jump 追加 - decls.rs でデュアル関数作成時のブロック接続修正 - mir_static_main_args_loop.rs テスト追加 **パーサー改善**: - parser_box.hako に HAKO_PARSER_PROG_MAX ガード追加(無限ループ対策) 🎉 成果: - Continue 文の PHI predecessor mismatch エラー完全解消 - main(args) パラメータ有りループが正常動作 - Stage-B balanced scan で continue 正常動作確認 (228回イテレーション) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
243 lines
8.8 KiB
Rust
243 lines
8.8 KiB
Rust
/*!
|
||
* ControlForm – 共通制御構造ビュー(Loop / If の箱化レイヤ)
|
||
*
|
||
* 目的:
|
||
* - LoopForm v2(ループ)と If 降下を、1段上の「制御構造の形」として統一的に眺めるための薄いレイヤだよ。
|
||
* - Conservative PHI Box や将来の可視化/検証ロジックが、Loop 専用 / If 専用に分かれず、
|
||
* ControlForm という SSOT から情報を取れるようにするのがねらいだよ。
|
||
*
|
||
* このモジュール自体は構造定義とデバッグ用のユーティリティのみを提供し、
|
||
* 既存の LoopBuilder / If 降下の挙動は変えないよ(Phase 25.1f では観測レイヤ専用)。
|
||
*/
|
||
|
||
use crate::mir::{BasicBlock, BasicBlockId, MirFunction};
|
||
|
||
/// ループ構造の形だけを表す箱だよ。
|
||
///
|
||
/// - `preheader` : ループ直前のブロック(キャリア/ピン変数のコピー元)
|
||
/// - `header` : ループヘッダ(条件判定と header PHI が置かれる)
|
||
/// - `body` : 代表的なループ本体ブロック(最初の body など)
|
||
/// - `latch` : ヘッダへ戻るバックエッジを張るブロック
|
||
/// - `exit` : ループを抜けた先のブロック
|
||
/// - `continue_targets` : continue 文を含み、`header` へ遷移するブロック群(エッジの「出発点」)
|
||
/// - `break_targets` : break 文を含み、`exit` へ遷移するブロック群(エッジの「出発点」)
|
||
#[derive(Debug, Clone)]
|
||
pub struct LoopShape {
|
||
pub preheader: BasicBlockId,
|
||
pub header: BasicBlockId,
|
||
pub body: BasicBlockId,
|
||
pub latch: BasicBlockId,
|
||
pub exit: BasicBlockId,
|
||
pub continue_targets: Vec<BasicBlockId>,
|
||
pub break_targets: Vec<BasicBlockId>,
|
||
}
|
||
|
||
/// if/else 構造の形だけを表す箱だよ。
|
||
///
|
||
/// - `cond_block` : 条件式を評価するブロック
|
||
/// - `then_block` : then ブランチの先頭ブロック
|
||
/// - `else_block` : else ブランチの先頭ブロック(無ければ None)
|
||
/// - `merge_block`: then/else の合流ブロック
|
||
#[derive(Debug, Clone)]
|
||
pub struct IfShape {
|
||
pub cond_block: BasicBlockId,
|
||
pub then_block: BasicBlockId,
|
||
pub else_block: Option<BasicBlockId>,
|
||
pub merge_block: BasicBlockId,
|
||
}
|
||
|
||
/// 制御構造の種別だよ。
|
||
#[derive(Debug, Clone)]
|
||
pub enum ControlKind {
|
||
Loop(LoopShape),
|
||
If(IfShape),
|
||
}
|
||
|
||
/// ループ / if / 将来の switch などを、共通のビューとして扱う箱だよ。
|
||
///
|
||
/// - `entry` : 構造に入る入口ブロック
|
||
/// - `exits` : 構造を抜けたあとのブロック群
|
||
/// - `kind` : Loop / If などの種別ごとの Shape
|
||
#[derive(Debug, Clone)]
|
||
pub struct ControlForm {
|
||
pub entry: BasicBlockId,
|
||
pub exits: Vec<BasicBlockId>,
|
||
pub kind: ControlKind,
|
||
}
|
||
|
||
impl ControlForm {
|
||
/// ループ用 Shape から ControlForm を生成するよ。
|
||
///
|
||
/// ループの entry は preheader、exit は exit ブロック 1 つとみなす。
|
||
pub fn from_loop(shape: LoopShape) -> Self {
|
||
ControlForm {
|
||
entry: shape.preheader,
|
||
exits: vec![shape.exit],
|
||
kind: ControlKind::Loop(shape),
|
||
}
|
||
}
|
||
|
||
/// If 用 Shape から ControlForm を生成するよ。
|
||
///
|
||
/// If の entry は cond_block、exit は merge_block 1 つとみなす。
|
||
pub fn from_if(shape: IfShape) -> Self {
|
||
ControlForm {
|
||
entry: shape.cond_block,
|
||
exits: vec![shape.merge_block],
|
||
kind: ControlKind::If(shape),
|
||
}
|
||
}
|
||
|
||
/// これはループかな?という軽い判定だよ。
|
||
pub fn is_loop(&self) -> bool {
|
||
matches!(self.kind, ControlKind::Loop(_))
|
||
}
|
||
|
||
/// これは if 構造かな?という軽い判定だよ。
|
||
pub fn is_if(&self) -> bool {
|
||
matches!(self.kind, ControlKind::If(_))
|
||
}
|
||
|
||
/// デバッグ用に構造をダンプするよ。
|
||
///
|
||
/// 呼び出し側で `NYASH_CONTROL_FORM_TRACE=1` を見る想定なので、
|
||
/// ここでは単純に eprintln! するだけにしておく。
|
||
pub fn debug_dump(&self) {
|
||
match &self.kind {
|
||
ControlKind::Loop(shape) => {
|
||
eprintln!(
|
||
"[ControlForm::Loop] entry={:?} preheader={:?} header={:?} body={:?} latch={:?} exit={:?} continue={:?} break={:?}",
|
||
self.entry,
|
||
shape.preheader,
|
||
shape.header,
|
||
shape.body,
|
||
shape.latch,
|
||
shape.exit,
|
||
shape.continue_targets,
|
||
shape.break_targets,
|
||
);
|
||
}
|
||
ControlKind::If(shape) => {
|
||
eprintln!(
|
||
"[ControlForm::If] entry={:?} cond={:?} then={:?} else={:?} merge={:?} exits={:?}",
|
||
self.entry,
|
||
shape.cond_block,
|
||
shape.then_block,
|
||
shape.else_block,
|
||
shape.merge_block,
|
||
self.exits,
|
||
);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// ControlForm の invariant を軽く検査するための CFG 抽象だよ。
|
||
///
|
||
/// 実装は MirFunction などに持たせて、`debug_validate` から使う想定。
|
||
pub trait CfgLike {
|
||
fn has_edge(&self, from: BasicBlockId, to: BasicBlockId) -> bool;
|
||
fn predecessors_len(&self, block: BasicBlockId) -> usize;
|
||
}
|
||
|
||
impl CfgLike for MirFunction {
|
||
fn has_edge(&self, from: BasicBlockId, to: BasicBlockId) -> bool {
|
||
self.blocks
|
||
.get(&from)
|
||
.map(|bb: &BasicBlock| bb.successors.contains(&to))
|
||
.unwrap_or(false)
|
||
}
|
||
|
||
fn predecessors_len(&self, block: BasicBlockId) -> usize {
|
||
self.blocks
|
||
.get(&block)
|
||
.map(|bb: &BasicBlock| bb.predecessors.len())
|
||
.unwrap_or(0)
|
||
}
|
||
}
|
||
|
||
/// ControlForm トレース用の環境フラグを判定するヘルパーだよ。
|
||
///
|
||
/// - 未設定 → 既定で ON
|
||
/// - "0" / "false" → OFF
|
||
/// - それ以外 → ON
|
||
pub fn is_control_form_trace_on() -> bool {
|
||
std::env::var("NYASH_CONTROL_FORM_TRACE")
|
||
.map(|v| v != "0" && v.to_lowercase() != "false")
|
||
.unwrap_or(true)
|
||
}
|
||
|
||
impl LoopShape {
|
||
/// Debug ビルドでだけ呼ぶ用の簡易 invariant チェックだよ。
|
||
///
|
||
/// - preheader → header にエッジがあること
|
||
/// - latch → header にバックエッジがあること
|
||
/// - continue_targets の各ブロックから header へのエッジがあること
|
||
/// - break_targets の各ブロックから exit へのエッジがあること
|
||
#[cfg(debug_assertions)]
|
||
pub fn debug_validate<C: CfgLike>(&self, cfg: &C) {
|
||
debug_assert!(
|
||
cfg.has_edge(self.preheader, self.header),
|
||
"LoopShape invalid: preheader -> header edge missing: {:?} -> {:?}",
|
||
self.preheader,
|
||
self.header
|
||
);
|
||
debug_assert!(
|
||
cfg.has_edge(self.latch, self.header),
|
||
"LoopShape invalid: latch -> header backedge missing: {:?} -> {:?}",
|
||
self.latch,
|
||
self.header
|
||
);
|
||
|
||
for ct in &self.continue_targets {
|
||
debug_assert!(
|
||
cfg.has_edge(*ct, self.header),
|
||
"LoopShape invalid: continue source block {:?} does not branch to header {:?}",
|
||
ct,
|
||
self.header
|
||
);
|
||
}
|
||
|
||
for bt in &self.break_targets {
|
||
debug_assert!(
|
||
cfg.has_edge(*bt, self.exit),
|
||
"LoopShape invalid: break source block {:?} does not branch to exit {:?}",
|
||
bt,
|
||
self.exit
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
impl IfShape {
|
||
/// Debug ビルドでだけ呼ぶ用の簡易 invariant チェックだよ。
|
||
///
|
||
/// - cond → then / else にエッジがあること
|
||
/// - merge については、predecessor 情報がまだ配線途中のケースもあるので
|
||
/// ここでは「0 ならログだけ出す(panic しない)」ことにするよ。
|
||
#[cfg(debug_assertions)]
|
||
pub fn debug_validate<C: CfgLike>(&self, cfg: &C) {
|
||
debug_assert!(
|
||
cfg.has_edge(self.cond_block, self.then_block),
|
||
"IfShape invalid: cond -> then edge missing: {:?} -> {:?}",
|
||
self.cond_block,
|
||
self.then_block
|
||
);
|
||
if let Some(else_blk) = self.else_block {
|
||
debug_assert!(
|
||
cfg.has_edge(self.cond_block, else_blk),
|
||
"IfShape invalid: cond -> else edge missing: {:?} -> {:?}",
|
||
self.cond_block,
|
||
else_blk
|
||
);
|
||
}
|
||
let preds = cfg.predecessors_len(self.merge_block);
|
||
if preds == 0 && std::env::var("NYASH_CONTROL_FORM_TRACE").ok().as_deref() == Some("1") {
|
||
eprintln!(
|
||
"[ControlForm::IfShape] WARN: merge block {:?} has no predecessors yet",
|
||
self.merge_block
|
||
);
|
||
}
|
||
}
|
||
}
|