2025-11-18 18:56:35 +09:00
|
|
|
|
/*!
|
|
|
|
|
|
* 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};
|
|
|
|
|
|
|
|
|
|
|
|
/// ループ構造の形だけを表す箱だよ。
|
|
|
|
|
|
///
|
2025-11-19 08:04:43 +09:00
|
|
|
|
/// - `preheader` : ループ直前のブロック(キャリア/ピン変数のコピー元)
|
|
|
|
|
|
/// - `header` : ループヘッダ(条件判定と header PHI が置かれる)
|
|
|
|
|
|
/// - `body` : 代表的なループ本体ブロック(最初の body など)
|
|
|
|
|
|
/// - `latch` : ヘッダへ戻るバックエッジを張るブロック
|
|
|
|
|
|
/// - `exit` : ループを抜けた先のブロック
|
|
|
|
|
|
/// - `continue_targets` : continue 文を含み、`header` へ遷移するブロック群(エッジの「出発点」)
|
|
|
|
|
|
/// - `break_targets` : break 文を含み、`exit` へ遷移するブロック群(エッジの「出発点」)
|
2025-11-18 18:56:35 +09:00
|
|
|
|
#[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 にバックエッジがあること
|
2025-11-19 08:04:43 +09:00
|
|
|
|
/// - continue_targets の各ブロックから header へのエッジがあること
|
|
|
|
|
|
/// - break_targets の各ブロックから exit へのエッジがあること
|
2025-11-18 18:56:35 +09:00
|
|
|
|
#[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
|
|
|
|
|
|
);
|
2025-11-19 08:04:43 +09:00
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2025-11-18 18:56:35 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|