Files
hakorune/src/mir/control_form.rs

243 lines
8.8 KiB
Rust
Raw Normal View History

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