Files
hakorune/src/mir/control_form.rs
nyash-codex a95fedf26a fix(mir): Phase 25.1m - Continue PHI修正 & Bug A main(args)ループ修正
**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>
2025-11-19 08:04:43 +09:00

243 lines
8.8 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*!
* 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
);
}
}
}