2025-08-12 11:33:48 +00:00
/*!
* MIR Verification - SSA form and semantic verification
2025-09-17 07:43:07 +09:00
*
2025-08-12 11:33:48 +00:00
* Implements dominance checking , SSA verification , and semantic analysis
* /
2025-09-22 21:52:39 +09:00
use super ::{ MirFunction , MirModule } ;
2025-08-26 05:49:23 +09:00
use crate ::debug ::log as dlog ;
2025-09-17 07:43:07 +09:00
use crate ::mir ::verification_types ::VerificationError ;
2025-09-17 05:56:33 +09:00
mod awaits ;
2025-09-17 07:43:07 +09:00
mod barrier ;
2025-11-21 06:25:17 +09:00
mod cfg ;
mod dom ;
2025-09-17 07:43:07 +09:00
mod legacy ;
2025-09-22 07:54:25 +09:00
mod ssa ;
2025-11-21 06:25:17 +09:00
mod utils ;
2025-08-12 11:33:48 +00:00
/// MIR verifier for SSA form and semantic correctness
pub struct MirVerifier {
/// Current verification errors
errors : Vec < VerificationError > ,
}
impl MirVerifier {
/// Create a new MIR verifier
pub fn new ( ) -> Self {
2025-09-17 07:43:07 +09:00
Self { errors : Vec ::new ( ) }
2025-08-12 11:33:48 +00:00
}
2025-09-17 07:43:07 +09:00
2025-08-12 11:33:48 +00:00
/// Verify an entire MIR module
pub fn verify_module ( & mut self , module : & MirModule ) -> Result < ( ) , Vec < VerificationError > > {
self . errors . clear ( ) ;
2025-09-17 07:43:07 +09:00
2025-12-01 11:10:46 +09:00
// Stage‑ B/selfhost 専用: dev verify を一時緩和するためのトグル
if ! crate ::config ::env ::stageb_dev_verify_enabled ( ) {
return Ok ( ( ) ) ;
}
2025-08-16 17:39:04 +09:00
for ( _name , function ) in & module . functions {
2025-08-12 11:33:48 +00:00
if let Err ( mut func_errors ) = self . verify_function ( function ) {
2025-11-19 02:44:40 +09:00
// Dev-only trace: BreakFinderBox / LoopSSA 周辺のSSAバグを詳細に観測する。
//
// NYASH_BREAKFINDER_SSA_TRACE=1 のときだけ有効になり、
// compiler_stageb.hako など大規模モジュール内での
// BreakFinderBox.* に対する UndefinedValue を詳細に出力する。
//
// 併せて、同じトグルで「任意の関数」に対する UndefinedValue も
// 簡易ログとして出力し、どの関数で支配関係が崩れているかを
// 追いやすくしている(箱理論の観測レイヤー強化)。
2025-11-21 06:25:17 +09:00
if std ::env ::var ( " NYASH_BREAKFINDER_SSA_TRACE " ) . ok ( ) . as_deref ( ) = = Some ( " 1 " ) {
2025-12-03 13:59:06 +09:00
let log = crate ::runtime ::get_global_ring0 ( ) . log . clone ( ) ;
2025-11-19 02:44:40 +09:00
// 1) BreakFinderBox / LoopSSA 向けの詳細ログ
if function . signature . name . starts_with ( " BreakFinderBox. " )
| | function . signature . name . starts_with ( " LoopSSA. " )
{
for e in & func_errors {
if let VerificationError ::UndefinedValue {
value ,
block ,
instruction_index ,
} = e
{
if let Some ( bb ) = function . blocks . get ( block ) {
let inst = bb . instructions . get ( * instruction_index ) ;
2025-12-03 13:59:06 +09:00
log . debug ( & format! (
2025-11-19 02:44:40 +09:00
" [breakfinder/ssa] UndefinedValue {:?} in fn {} at bb={:?}, inst={} => {:?} " ,
value ,
function . signature . name ,
block ,
instruction_index ,
inst ,
2025-12-03 13:59:06 +09:00
) ) ;
2025-11-19 02:44:40 +09:00
} else {
2025-12-03 13:59:06 +09:00
log . debug ( & format! (
2025-11-19 02:44:40 +09:00
" [breakfinder/ssa] UndefinedValue {:?} in fn {} at bb={:?}, inst={} " ,
value ,
function . signature . name ,
block ,
instruction_index
2025-12-03 13:59:06 +09:00
) ) ;
2025-11-19 02:44:40 +09:00
}
}
}
}
// 2) 任意の関数向けの簡易 UndefinedValue ログ
for e in & func_errors {
match e {
VerificationError ::UndefinedValue {
value ,
block ,
instruction_index ,
} = > {
2025-12-03 13:59:06 +09:00
log . debug ( & format! (
2025-11-19 02:44:40 +09:00
" [mir-ssa-debug] UndefinedValue {:?} in fn {} at bb={:?}, inst={} " ,
value ,
function . signature . name ,
block ,
instruction_index
2025-12-03 13:59:06 +09:00
) ) ;
2025-11-20 06:38:43 +09:00
if let Some ( bb ) = function . blocks . get ( block ) {
2025-11-28 17:42:19 +09:00
let inst_opt = bb
. all_spanned_instructions_enumerated ( )
. nth ( * instruction_index ) ;
2025-11-24 15:02:51 +09:00
if let Some ( ( _idx , sp ) ) = inst_opt {
2025-12-03 13:59:06 +09:00
log . debug ( & format! (
" [mir-ssa-debug-inst] inst={:?} " ,
sp . inst
) ) ;
2025-11-20 06:38:43 +09:00
}
}
2025-11-19 02:44:40 +09:00
}
VerificationError ::DominatorViolation {
value ,
use_block ,
def_block ,
} = > {
2025-12-03 13:59:06 +09:00
log . debug ( & format! (
2025-11-19 02:44:40 +09:00
" [mir-ssa-debug] DominatorViolation {:?} in fn {}: use_block={:?}, def_block={:?} " ,
value ,
function . signature . name ,
use_block ,
def_block
2025-12-03 13:59:06 +09:00
) ) ;
2025-11-19 02:44:40 +09:00
}
_ = > { }
}
}
}
2025-08-12 11:33:48 +00:00
// Add function context to errors
2025-08-16 17:39:04 +09:00
for _error in & mut func_errors {
2025-08-12 11:33:48 +00:00
// Could add function name to error context here
}
self . errors . extend ( func_errors ) ;
}
}
2025-09-17 07:43:07 +09:00
2025-08-12 11:33:48 +00:00
if self . errors . is_empty ( ) {
Ok ( ( ) )
} else {
Err ( self . errors . clone ( ) )
}
}
2025-09-17 07:43:07 +09:00
2025-08-12 11:33:48 +00:00
/// Verify a single MIR function
2025-09-17 07:43:07 +09:00
pub fn verify_function (
& mut self ,
function : & MirFunction ,
) -> Result < ( ) , Vec < VerificationError > > {
2025-08-12 11:33:48 +00:00
let mut local_errors = Vec ::new ( ) ;
2025-09-17 07:43:07 +09:00
2025-08-12 11:33:48 +00:00
// 1. Check SSA form
if let Err ( mut ssa_errors ) = self . verify_ssa_form ( function ) {
local_errors . append ( & mut ssa_errors ) ;
}
2025-09-17 07:43:07 +09:00
2025-08-12 11:33:48 +00:00
// 2. Check dominance relations
if let Err ( mut dom_errors ) = self . verify_dominance ( function ) {
local_errors . append ( & mut dom_errors ) ;
}
2025-09-17 07:43:07 +09:00
2025-08-12 11:33:48 +00:00
// 3. Check control flow integrity
if let Err ( mut cfg_errors ) = self . verify_control_flow ( function ) {
local_errors . append ( & mut cfg_errors ) ;
}
2025-08-23 16:33:32 +09:00
// 4. Check merge-block value usage (ensure Phi is used)
if let Err ( mut merge_errors ) = self . verify_merge_uses ( function ) {
local_errors . append ( & mut merge_errors ) ;
}
2025-08-24 02:52:11 +09:00
// 5. Minimal checks for WeakRef/Barrier
if let Err ( mut weak_barrier_errors ) = self . verify_weakref_and_barrier ( function ) {
local_errors . append ( & mut weak_barrier_errors ) ;
}
2025-08-26 00:48:09 +09:00
// 6. Light barrier-context diagnostic (strict mode only)
if let Err ( mut barrier_ctx ) = self . verify_barrier_context ( function ) {
local_errors . append ( & mut barrier_ctx ) ;
}
2025-08-31 06:22:48 +09:00
// 7. Forbid legacy instructions (must be rewritten to Core-15)
if let Err ( mut legacy_errors ) = self . verify_no_legacy_ops ( function ) {
local_errors . append ( & mut legacy_errors ) ;
}
2025-09-02 03:41:51 +09:00
// 8. Async semantics: ensure checkpoints around await
if let Err ( mut await_cp ) = self . verify_await_checkpoints ( function ) {
local_errors . append ( & mut await_cp ) ;
}
2025-09-17 07:43:07 +09:00
2025-09-19 02:07:38 +09:00
// 9. PHI-off strict edge-copy policy (optional)
if crate ::config ::env ::mir_no_phi ( ) & & crate ::config ::env ::verify_edge_copy_strict ( ) {
if let Err ( mut ecs ) = self . verify_edge_copy_strict ( function ) {
local_errors . append ( & mut ecs ) ;
}
}
2025-11-13 16:40:58 +09:00
// 10. Ret-block purity (optional, dev-only)
if crate ::config ::env ::verify_ret_purity ( ) {
if let Err ( mut rpe ) = self . verify_ret_block_purity ( function ) {
local_errors . append ( & mut rpe ) ;
}
}
2025-08-12 11:33:48 +00:00
if local_errors . is_empty ( ) {
Ok ( ( ) )
} else {
2025-08-26 05:49:23 +09:00
if dlog ::on ( " NYASH_DEBUG_VERIFIER " ) {
2025-12-03 13:59:06 +09:00
let log = crate ::runtime ::get_global_ring0 ( ) . log . clone ( ) ;
log . debug ( & format! (
2025-09-17 07:43:07 +09:00
" [VERIFY] {} errors in function {} " ,
local_errors . len ( ) ,
function . signature . name
2025-12-03 13:59:06 +09:00
) ) ;
2025-08-26 06:30:01 +09:00
for e in & local_errors {
match e {
2025-09-17 07:43:07 +09:00
VerificationError ::MergeUsesPredecessorValue {
value ,
merge_block ,
pred_block ,
} = > {
2025-12-03 13:59:06 +09:00
log . debug ( & format! (
2025-08-26 06:30:01 +09:00
" • MergeUsesPredecessorValue: value=%{:?} merge_bb={:?} pred_bb={:?} -- hint: insert/use Phi in merge block for values from predecessors " ,
value , merge_block , pred_block
2025-12-03 13:59:06 +09:00
) ) ;
2025-08-26 06:30:01 +09:00
}
2025-09-17 07:43:07 +09:00
VerificationError ::DominatorViolation {
value ,
use_block ,
def_block ,
} = > {
2025-12-03 13:59:06 +09:00
log . debug ( & format! (
2025-08-26 06:30:01 +09:00
" • DominatorViolation: value=%{:?} use_bb={:?} def_bb={:?} -- hint: ensure definition dominates use, or route via Phi " ,
value , use_block , def_block
2025-12-03 13:59:06 +09:00
) ) ;
2025-08-26 06:30:01 +09:00
}
2025-09-17 07:43:07 +09:00
VerificationError ::InvalidPhi {
phi_value ,
block ,
reason ,
} = > {
2025-12-03 13:59:06 +09:00
log . debug ( & format! (
2025-08-26 06:30:01 +09:00
" • InvalidPhi: phi_dst=%{:?} in bb={:?} reason={} -- hint: check inputs cover all predecessors and placed at block start " ,
phi_value , block , reason
2025-12-03 13:59:06 +09:00
) ) ;
2025-08-26 06:30:01 +09:00
}
2025-09-17 07:43:07 +09:00
VerificationError ::InvalidWeakRefSource {
weak_ref ,
block ,
instruction_index ,
reason ,
} = > {
2025-12-03 13:59:06 +09:00
log . debug ( & format! (
2025-08-26 06:47:13 +09:00
" • InvalidWeakRefSource: weak=%{:?} at {}:{} reason='{}' -- hint: source must be WeakRef(new)/WeakNew; ensure creation precedes load and value flows correctly " ,
weak_ref , block , instruction_index , reason
2025-12-03 13:59:06 +09:00
) ) ;
2025-08-26 06:47:13 +09:00
}
2025-09-17 07:43:07 +09:00
VerificationError ::InvalidBarrierPointer {
ptr ,
block ,
instruction_index ,
reason ,
} = > {
2025-12-03 13:59:06 +09:00
log . debug ( & format! (
2025-08-26 06:47:13 +09:00
" • InvalidBarrierPointer: ptr=%{:?} at {}:{} reason='{}' -- hint: barrier pointer must be a valid ref (not void/null); ensure it is defined and non-void " ,
ptr , block , instruction_index , reason
2025-12-03 13:59:06 +09:00
) ) ;
2025-08-26 06:47:13 +09:00
}
2025-09-17 07:43:07 +09:00
VerificationError ::SuspiciousBarrierContext {
block ,
instruction_index ,
note ,
} = > {
2025-12-03 13:59:06 +09:00
log . debug ( & format! (
2025-08-26 06:47:13 +09:00
" • SuspiciousBarrierContext: at {}:{} note='{}' -- hint: place barrier within ±2 of load/store/ref ops in same block or disable strict check " ,
block , instruction_index , note
2025-12-03 13:59:06 +09:00
) ) ;
2025-08-26 06:47:13 +09:00
}
2025-08-26 06:30:01 +09:00
other = > {
2025-12-03 13:59:06 +09:00
log . debug ( & format! ( " • {:?} " , other ) ) ;
2025-08-26 06:30:01 +09:00
}
}
}
2025-08-26 05:49:23 +09:00
}
2025-08-12 11:33:48 +00:00
Err ( local_errors )
}
}
2025-08-24 02:52:11 +09:00
2025-09-19 02:07:38 +09:00
/// When PHI-off strict mode is enabled, enforce that merge blocks do not
/// introduce self-copies and that each predecessor provides a Copy into the
/// merged destination for values used in the merge block that do not dominate it.
fn verify_edge_copy_strict (
& self ,
function : & MirFunction ,
) -> Result < ( ) , Vec < VerificationError > > {
let mut errors = Vec ::new ( ) ;
let preds = utils ::compute_predecessors ( function ) ;
let def_block = utils ::compute_def_blocks ( function ) ;
let dominators = utils ::compute_dominators ( function ) ;
for ( merge_bid , merge_bb ) in & function . blocks {
let p = preds . get ( merge_bid ) . cloned ( ) . unwrap_or_default ( ) ;
if p . len ( ) < 2 {
continue ; // Only enforce on real merges (>=2 predecessors)
}
// Collect values used in merge block
let mut used_values = std ::collections ::HashSet ::new ( ) ;
2025-11-24 15:02:51 +09:00
for sp in merge_bb . all_spanned_instructions ( ) {
for v in sp . inst . used_values ( ) {
2025-09-19 02:07:38 +09:00
used_values . insert ( v ) ;
}
}
// For each used value that doesn't dominate the merge block, enforce edge-copy policy
for & u in & used_values {
if let Some ( & def_bb ) = def_block . get ( & u ) {
// If the def dominates the merge block, edge copies are not required
let dominated = dominators
. get ( merge_bid )
. map ( | set | set . contains ( & def_bb ) )
. unwrap_or ( false ) ;
if dominated & & def_bb ! = * merge_bid {
continue ;
}
}
// Merge block itself must not contain a Copy to the merged value
2025-11-21 06:25:17 +09:00
let has_self_copy_in_merge = merge_bb . instructions . iter ( ) . any (
| inst | matches! ( inst , super ::MirInstruction ::Copy { dst , .. } if * dst = = u ) ,
) ;
2025-09-19 02:07:38 +09:00
if has_self_copy_in_merge {
errors . push ( VerificationError ::EdgeCopyStrictViolation {
block : * merge_bid ,
value : u ,
pred_block : None ,
2025-11-21 06:25:17 +09:00
reason :
" merge block contains Copy to merged value; use predecessor copies only "
. to_string ( ) ,
2025-09-19 02:07:38 +09:00
} ) ;
}
// Each predecessor must provide a Copy into the merged destination
for pred in & p {
2025-11-21 06:25:17 +09:00
let Some ( pbb ) = function . blocks . get ( pred ) else {
continue ;
} ;
let has_copy = pbb . instructions . iter ( ) . any ( | inst | {
matches! (
inst ,
super ::MirInstruction ::Copy { dst , .. } if * dst = = u
)
} ) ;
2025-09-19 02:07:38 +09:00
if ! has_copy {
errors . push ( VerificationError ::MergeUsesPredecessorValue {
value : u ,
merge_block : * merge_bid ,
pred_block : * pred ,
} ) ;
}
}
}
}
2025-11-21 06:25:17 +09:00
if errors . is_empty ( ) {
Ok ( ( ) )
} else {
Err ( errors )
}
2025-09-19 02:07:38 +09:00
}
2025-11-13 16:40:58 +09:00
/// Verify that any block ending with Return contains no side-effecting instructions before it.
/// Allowed before Return: Const, Copy, Phi, Nop only. Others are considered side-effecting for this policy.
2025-11-21 06:25:17 +09:00
fn verify_ret_block_purity (
& self ,
function : & MirFunction ,
) -> Result < ( ) , Vec < VerificationError > > {
2025-11-13 16:40:58 +09:00
use super ::MirInstruction as I ;
let mut errors = Vec ::new ( ) ;
for ( bid , bb ) in & function . blocks {
if let Some ( I ::Return { .. } ) = bb . terminator {
for ( idx , inst ) in bb . instructions . iter ( ) . enumerate ( ) {
let allowed = matches! (
inst ,
I ::Const { .. } | I ::Copy { .. } | I ::Phi { .. } | I ::Nop
) ;
if ! allowed {
let name = format! ( " {:?} " , inst ) ;
errors . push ( VerificationError ::RetBlockSideEffect {
block : * bid ,
instruction_index : idx ,
name ,
} ) ;
}
}
}
}
2025-11-21 06:25:17 +09:00
if errors . is_empty ( ) {
Ok ( ( ) )
} else {
Err ( errors )
}
2025-11-13 16:40:58 +09:00
}
2025-08-31 06:22:48 +09:00
/// Reject legacy instructions that should be rewritten to Core-15 equivalents
/// Skips check when NYASH_VERIFY_ALLOW_LEGACY=1
fn verify_no_legacy_ops ( & self , function : & MirFunction ) -> Result < ( ) , Vec < VerificationError > > {
2025-09-17 05:56:33 +09:00
legacy ::check_no_legacy_ops ( function )
2025-08-31 06:22:48 +09:00
}
2025-09-02 09:26:09 +09:00
/// Ensure that each Await instruction (or ExternCall(env.future.await)) is immediately
/// preceded and followed by a checkpoint.
2025-09-02 03:41:51 +09:00
/// A checkpoint is either MirInstruction::Safepoint or ExternCall("env.runtime", "checkpoint").
2025-09-17 07:43:07 +09:00
fn verify_await_checkpoints (
& self ,
function : & MirFunction ,
) -> Result < ( ) , Vec < VerificationError > > {
2025-09-17 05:56:33 +09:00
awaits ::check_await_checkpoints ( function )
2025-09-02 03:41:51 +09:00
}
2025-08-24 02:52:11 +09:00
/// Verify WeakRef/Barrier minimal semantics
2025-09-17 07:43:07 +09:00
fn verify_weakref_and_barrier (
& self ,
function : & MirFunction ,
) -> Result < ( ) , Vec < VerificationError > > {
2025-09-17 05:56:33 +09:00
barrier ::check_weakref_and_barrier ( function )
2025-08-26 00:48:09 +09:00
}
/// Light diagnostic: Barrier should be near memory ops in the same block (best-effort)
/// Enabled only when NYASH_VERIFY_BARRIER_STRICT=1
fn verify_barrier_context ( & self , function : & MirFunction ) -> Result < ( ) , Vec < VerificationError > > {
2025-09-17 05:56:33 +09:00
barrier ::check_barrier_context ( function )
2025-08-24 02:52:11 +09:00
}
2025-09-17 07:43:07 +09:00
2025-08-12 11:33:48 +00:00
/// Verify SSA form properties
fn verify_ssa_form ( & self , function : & MirFunction ) -> Result < ( ) , Vec < VerificationError > > {
2025-09-22 07:54:25 +09:00
ssa ::check_ssa_form ( function )
2025-08-12 11:33:48 +00:00
}
2025-09-17 07:43:07 +09:00
2025-08-23 19:27:02 +09:00
/// Verify dominance relations (def must dominate use across blocks)
2025-08-12 11:33:48 +00:00
fn verify_dominance ( & self , function : & MirFunction ) -> Result < ( ) , Vec < VerificationError > > {
2025-09-22 07:54:25 +09:00
dom ::check_dominance ( function )
2025-08-12 11:33:48 +00:00
}
2025-09-17 07:43:07 +09:00
2025-08-12 11:33:48 +00:00
/// Verify control flow graph integrity
fn verify_control_flow ( & self , function : & MirFunction ) -> Result < ( ) , Vec < VerificationError > > {
2025-09-22 07:54:25 +09:00
cfg ::check_control_flow ( function )
2025-08-12 11:33:48 +00:00
}
2025-08-23 16:33:32 +09:00
/// Verify that blocks with multiple predecessors do not use predecessor-defined values directly.
/// In merge blocks, values coming from predecessors must be routed through Phi.
fn verify_merge_uses ( & self , function : & MirFunction ) -> Result < ( ) , Vec < VerificationError > > {
2025-09-22 07:54:25 +09:00
cfg ::check_merge_uses ( function )
2025-08-23 16:33:32 +09:00
}
2025-09-17 07:43:07 +09:00
2025-08-12 11:33:48 +00:00
/// Get all verification errors from the last run
pub fn get_errors ( & self ) -> & [ VerificationError ] {
& self . errors
}
2025-09-17 07:43:07 +09:00
2025-08-12 11:33:48 +00:00
/// Clear verification errors
pub fn clear_errors ( & mut self ) {
self . errors . clear ( ) ;
}
}
impl Default for MirVerifier {
fn default ( ) -> Self {
Self ::new ( )
}
}
#[ cfg(test) ]
2025-09-22 07:54:25 +09:00
mod tests { }