feat(mir): Phase 25.1f完了 - Conservative PHI + ControlForm観測レイヤー

🎉 Conservative PHI Box理論による完全SSA構築

**Phase 7-B: Conservative PHI実装**
- 片方branchのみ定義変数に対応(emit_void使用)
- 全変数にPHI生成(Conservative Box理論)
- Stage-1 resolver全テスト緑化(3/3 PASS)

**Phase 25.1f: ControlForm観測レイヤー**
- LoopShape/IfShape/ControlForm構造定義
- Loop/If統一インターフェース実装
- debug_dump/debug_validate機能追加
- NYASH_CONTROL_FORM_TRACE環境変数対応

**主な変更**:
- src/mir/builder/phi.rs: Conservative PHI実装
- src/mir/control_form.rs: ControlForm構造(NEW)
- src/mir/loop_builder.rs: LoopForm v2デフォルト化

**テスト結果**:
 mir_stage1_using_resolver_min_fragment_verifies
 mir_stage1_using_resolver_full_collect_entries_verifies
 mir_parserbox_parse_program2_harness_parses_minimal_source

🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: ChatGPT <chatgpt@openai.com>
This commit is contained in:
nyash-codex
2025-11-18 18:56:35 +09:00
parent 8b37e9711d
commit d3cbc71c9b
81 changed files with 907 additions and 147 deletions

222
src/mir/control_form.rs Normal file
View File

@ -0,0 +1,222 @@
/*!
* 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 がジャンプするブロック群(通常は latch か 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 にバックエッジがあること
#[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
);
}
}
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
);
}
}
}