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-17 07:43:07 +09:00
use super ::{ BasicBlockId , MirFunction , MirModule , ValueId } ;
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 11:45:57 +09:00
use std ::collections ::HashMap ;
2025-09-17 05:56:33 +09:00
mod awaits ;
2025-09-17 07:43:07 +09:00
mod barrier ;
mod legacy ;
2025-09-17 05:56:33 +09:00
mod utils ;
2025-08-12 11:33:48 +00:00
2025-09-17 05:56:33 +09:00
// VerificationError moved to crate::mir::verification_types
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-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 ) {
// 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-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-09-17 07:43:07 +09:00
eprintln! (
" [VERIFY] {} errors in function {} " ,
local_errors . len ( ) ,
function . signature . name
) ;
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-08-26 06:30:01 +09:00
eprintln! (
" • MergeUsesPredecessorValue: value=%{:?} merge_bb={:?} pred_bb={:?} -- hint: insert/use Phi in merge block for values from predecessors " ,
value , merge_block , pred_block
) ;
}
2025-09-17 07:43:07 +09:00
VerificationError ::DominatorViolation {
value ,
use_block ,
def_block ,
} = > {
2025-08-26 06:30:01 +09:00
eprintln! (
" • DominatorViolation: value=%{:?} use_bb={:?} def_bb={:?} -- hint: ensure definition dominates use, or route via Phi " ,
value , use_block , def_block
) ;
}
2025-09-17 07:43:07 +09:00
VerificationError ::InvalidPhi {
phi_value ,
block ,
reason ,
} = > {
2025-08-26 06:30:01 +09:00
eprintln! (
" • InvalidPhi: phi_dst=%{:?} in bb={:?} reason={} -- hint: check inputs cover all predecessors and placed at block start " ,
phi_value , block , reason
) ;
}
2025-09-17 07:43:07 +09:00
VerificationError ::InvalidWeakRefSource {
weak_ref ,
block ,
instruction_index ,
reason ,
} = > {
2025-08-26 06:47:13 +09:00
eprintln! (
" • 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-09-17 07:43:07 +09:00
VerificationError ::InvalidBarrierPointer {
ptr ,
block ,
instruction_index ,
reason ,
} = > {
2025-08-26 06:47:13 +09:00
eprintln! (
" • 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-09-17 07:43:07 +09:00
VerificationError ::SuspiciousBarrierContext {
block ,
instruction_index ,
note ,
} = > {
2025-08-26 06:47:13 +09:00
eprintln! (
" • 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-08-26 06:30:01 +09:00
other = > {
eprintln! ( " • {:?} " , other ) ;
}
}
}
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 ( ) ;
for inst in merge_bb . all_instructions ( ) {
for v in inst . used_values ( ) {
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
let has_self_copy_in_merge = merge_bb
. instructions
. iter ( )
. any ( | inst | matches! ( inst , super ::MirInstruction ::Copy { dst , .. } if * dst = = u ) ) ;
if has_self_copy_in_merge {
errors . push ( VerificationError ::EdgeCopyStrictViolation {
block : * merge_bid ,
value : u ,
pred_block : None ,
reason : " merge block contains Copy to merged value; use predecessor copies only " . to_string ( ) ,
} ) ;
}
// Each predecessor must provide a Copy into the merged destination
for pred in & p {
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
) ) ;
if ! has_copy {
errors . push ( VerificationError ::MergeUsesPredecessorValue {
value : u ,
merge_block : * merge_bid ,
pred_block : * pred ,
} ) ;
}
}
}
}
if errors . is_empty ( ) { Ok ( ( ) ) } else { Err ( errors ) }
}
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-16 23:49:36 +09:00
// Allow non-SSA (edge-copy) mode for PHI-less MIR when enabled via env
2025-09-17 07:43:07 +09:00
if crate ::config ::env ::verify_allow_no_phi ( ) {
2025-09-16 23:49:36 +09:00
return Ok ( ( ) ) ;
}
2025-08-12 11:33:48 +00:00
let mut errors = Vec ::new ( ) ;
let mut definitions = HashMap ::new ( ) ;
2025-09-17 07:43:07 +09:00
2025-08-12 11:33:48 +00:00
// Check that each value is defined exactly once
for ( block_id , block ) in & function . blocks {
for ( inst_idx , instruction ) in block . all_instructions ( ) . enumerate ( ) {
if let Some ( dst ) = instruction . dst_value ( ) {
if let Some ( ( first_block , _ ) ) = definitions . insert ( dst , ( * block_id , inst_idx ) ) {
errors . push ( VerificationError ::MultipleDefinition {
value : dst ,
first_block ,
second_block : * block_id ,
} ) ;
}
}
}
}
2025-09-17 07:43:07 +09:00
2025-08-12 11:33:48 +00:00
// Check that all used values are defined
for ( block_id , block ) in & function . blocks {
for ( inst_idx , instruction ) in block . all_instructions ( ) . enumerate ( ) {
for used_value in instruction . used_values ( ) {
if ! definitions . contains_key ( & used_value ) {
errors . push ( VerificationError ::UndefinedValue {
value : used_value ,
block : * block_id ,
instruction_index : inst_idx ,
} ) ;
}
}
}
}
2025-09-17 07:43:07 +09:00
2025-08-12 11:33:48 +00:00
if errors . is_empty ( ) {
Ok ( ( ) )
} else {
Err ( errors )
}
}
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-16 23:49:36 +09:00
// Allow non-SSA (edge-copy) mode for PHI-less MIR when enabled via env
2025-09-17 07:43:07 +09:00
if crate ::config ::env ::verify_allow_no_phi ( ) {
2025-09-16 23:49:36 +09:00
return Ok ( ( ) ) ;
}
2025-08-23 19:27:02 +09:00
let mut errors = Vec ::new ( ) ;
// Build def -> block map and dominators
2025-09-17 06:55:39 +09:00
let def_block = utils ::compute_def_blocks ( function ) ;
let dominators = utils ::compute_dominators ( function ) ;
2025-08-23 19:27:02 +09:00
for ( use_block_id , block ) in & function . blocks {
2025-08-12 11:33:48 +00:00
for instruction in block . all_instructions ( ) {
2025-08-26 06:30:01 +09:00
// Phi inputs are special: they are defined in predecessors; skip dominance check for them
2025-09-17 07:43:07 +09:00
if let super ::MirInstruction ::Phi { .. } = instruction {
continue ;
}
2025-08-12 11:33:48 +00:00
for used_value in instruction . used_values ( ) {
2025-08-23 19:27:02 +09:00
if let Some ( & def_bb ) = def_block . get ( & used_value ) {
if def_bb ! = * use_block_id {
let doms = dominators . get ( use_block_id ) . unwrap ( ) ;
if ! doms . contains ( & def_bb ) {
errors . push ( VerificationError ::DominatorViolation {
value : used_value ,
use_block : * use_block_id ,
def_block : def_bb ,
} ) ;
}
}
2025-08-12 11:33:48 +00:00
}
}
}
}
2025-08-23 19:27:02 +09:00
2025-09-17 07:43:07 +09:00
if errors . is_empty ( ) {
Ok ( ( ) )
} else {
Err ( errors )
}
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 > > {
let mut errors = Vec ::new ( ) ;
2025-09-17 07:43:07 +09:00
2025-08-12 11:33:48 +00:00
// Check that all referenced blocks exist
for ( block_id , block ) in & function . blocks {
for successor in & block . successors {
if ! function . blocks . contains_key ( successor ) {
errors . push ( VerificationError ::ControlFlowError {
block : * block_id ,
reason : format ! ( " References non-existent block {} " , successor ) ,
} ) ;
}
}
}
2025-09-17 07:43:07 +09:00
2025-08-12 11:33:48 +00:00
// Check that all blocks are reachable from entry
2025-09-17 06:55:39 +09:00
let reachable = utils ::compute_reachable_blocks ( function ) ;
2025-08-12 11:33:48 +00:00
for block_id in function . blocks . keys ( ) {
if ! reachable . contains ( block_id ) & & * block_id ! = function . entry_block {
2025-09-17 07:43:07 +09:00
errors . push ( VerificationError ::UnreachableBlock { block : * block_id } ) ;
2025-08-12 11:33:48 +00:00
}
}
2025-09-17 07:43:07 +09:00
2025-08-12 11:33:48 +00:00
if errors . is_empty ( ) {
Ok ( ( ) )
} else {
Err ( errors )
}
}
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-16 23:49:36 +09:00
// Allow non-SSA (edge-copy) mode for PHI-less MIR when enabled via env
2025-09-17 07:43:07 +09:00
if crate ::config ::env ::verify_allow_no_phi ( ) {
2025-09-16 23:49:36 +09:00
return Ok ( ( ) ) ;
}
2025-08-23 16:33:32 +09:00
let mut errors = Vec ::new ( ) ;
2025-09-17 06:55:39 +09:00
let preds = utils ::compute_predecessors ( function ) ;
let def_block = utils ::compute_def_blocks ( function ) ;
let dominators = utils ::compute_dominators ( function ) ;
2025-08-23 16:33:32 +09:00
// Helper: collect phi dsts in a block
2025-09-17 07:43:07 +09:00
let mut phi_dsts_in_block : std ::collections ::HashMap <
BasicBlockId ,
std ::collections ::HashSet < ValueId > ,
> = std ::collections ::HashMap ::new ( ) ;
2025-08-23 16:33:32 +09:00
for ( bid , block ) in & function . blocks {
let set = phi_dsts_in_block . entry ( * bid ) . or_default ( ) ;
for inst in block . all_instructions ( ) {
2025-09-17 07:43:07 +09:00
if let super ::MirInstruction ::Phi { dst , .. } = inst {
set . insert ( * dst ) ;
}
2025-08-23 16:33:32 +09:00
}
}
for ( bid , block ) in & function . blocks {
2025-09-17 07:43:07 +09:00
let Some ( pred_list ) = preds . get ( bid ) else {
continue ;
} ;
if pred_list . len ( ) < 2 {
continue ;
}
2025-08-23 16:33:32 +09:00
let phi_dsts = phi_dsts_in_block . get ( bid ) ;
2025-08-23 19:27:02 +09:00
let doms_of_block = dominators . get ( bid ) . unwrap ( ) ;
2025-08-23 16:33:32 +09:00
// check instructions including terminator
for inst in block . all_instructions ( ) {
2025-08-26 06:30:01 +09:00
// Skip Phi: its inputs are allowed to come from predecessors by SSA definition
2025-09-17 07:43:07 +09:00
if let super ::MirInstruction ::Phi { .. } = inst {
continue ;
}
2025-08-23 16:33:32 +09:00
for used in inst . used_values ( ) {
if let Some ( & db ) = def_block . get ( & used ) {
2025-08-23 19:27:02 +09:00
// If def doesn't dominate merge block, it must be routed via phi
if ! doms_of_block . contains ( & db ) {
2025-08-23 16:33:32 +09:00
let is_phi_dst = phi_dsts . map ( | s | s . contains ( & used ) ) . unwrap_or ( false ) ;
if ! is_phi_dst {
errors . push ( VerificationError ::MergeUsesPredecessorValue {
value : used ,
merge_block : * bid ,
pred_block : db ,
} ) ;
}
}
}
}
}
}
2025-09-17 07:43:07 +09:00
if errors . is_empty ( ) {
Ok ( ( ) )
} else {
Err ( errors )
}
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 ( ) ;
}
2025-08-23 19:27:02 +09:00
2025-09-17 06:55:39 +09:00
// Wrapper helpers removed; use verification::utils directly at call sites
2025-08-12 11:33:48 +00:00
}
impl Default for MirVerifier {
fn default ( ) -> Self {
Self ::new ( )
}
}
impl std ::fmt ::Display for VerificationError {
fn fmt ( & self , f : & mut std ::fmt ::Formatter < '_ > ) -> std ::fmt ::Result {
match self {
2025-09-17 07:43:07 +09:00
VerificationError ::UndefinedValue {
value ,
block ,
instruction_index ,
} = > {
write! (
f ,
" Undefined value {} used in block {} at instruction {} " ,
value , block , instruction_index
)
}
VerificationError ::MultipleDefinition {
value ,
first_block ,
second_block ,
} = > {
write! (
f ,
" Value {} defined multiple times: first in block {}, again in block {} " ,
value , first_block , second_block
)
}
VerificationError ::InvalidPhi {
phi_value ,
block ,
reason ,
} = > {
write! (
f ,
" Invalid phi function {} in block {}: {} " ,
phi_value , block , reason
)
}
2025-08-12 11:33:48 +00:00
VerificationError ::UnreachableBlock { block } = > {
write! ( f , " Unreachable block {} " , block )
2025-09-17 07:43:07 +09:00
}
2025-08-12 11:33:48 +00:00
VerificationError ::ControlFlowError { block , reason } = > {
write! ( f , " Control flow error in block {}: {} " , block , reason )
2025-09-17 07:43:07 +09:00
}
VerificationError ::DominatorViolation {
value ,
use_block ,
def_block ,
} = > {
write! (
f ,
" Value {} used in block {} but defined in non-dominating block {} " ,
value , use_block , def_block
)
}
VerificationError ::MergeUsesPredecessorValue {
value ,
merge_block ,
pred_block ,
} = > {
write! (
f ,
" Merge block {} uses predecessor-defined value {} from block {} without Phi " ,
merge_block , value , pred_block
)
}
VerificationError ::InvalidWeakRefSource {
weak_ref ,
block ,
instruction_index ,
reason ,
} = > {
write! (
f ,
" Invalid WeakRef source {} in block {} at {}: {} " ,
weak_ref , block , instruction_index , reason
)
}
VerificationError ::InvalidBarrierPointer {
ptr ,
block ,
instruction_index ,
reason ,
} = > {
write! (
f ,
" Invalid Barrier pointer {} in block {} at {}: {} " ,
ptr , block , instruction_index , reason
)
}
VerificationError ::SuspiciousBarrierContext {
block ,
instruction_index ,
note ,
} = > {
write! (
f ,
" Suspicious Barrier context in block {} at {}: {} " ,
block , instruction_index , note
)
}
VerificationError ::UnsupportedLegacyInstruction {
block ,
instruction_index ,
name ,
} = > {
write! (
f ,
" Unsupported legacy instruction '{}' in block {} at {} (enable rewrite passes) " ,
name , block , instruction_index
)
}
VerificationError ::MissingCheckpointAroundAwait {
block ,
instruction_index ,
position ,
} = > {
write! (
f ,
" Missing {} checkpoint around await in block {} at instruction {} " ,
position , block , instruction_index
)
}
2025-09-19 02:07:38 +09:00
VerificationError ::EdgeCopyStrictViolation { block , value , pred_block , reason } = > {
if let Some ( pb ) = pred_block {
write! (
f ,
" EdgeCopyStrictViolation for value {} at merge block {} from pred {}: {} " ,
value , block , pb , reason
)
} else {
write! (
f ,
" EdgeCopyStrictViolation for value {} at merge block {}: {} " ,
value , block , reason
)
}
}
2025-08-12 11:33:48 +00:00
}
}
}
#[ cfg(test) ]
mod tests {
use super ::* ;
2025-09-17 07:43:07 +09:00
use crate ::ast ::{ ASTNode , LiteralValue , Span } ;
use crate ::mir ::{
BasicBlock , EffectMask , FunctionSignature , MirBuilder , MirFunction , MirPrinter , MirType ,
} ;
2025-08-12 11:33:48 +00:00
#[ test ]
fn test_valid_function_verification ( ) {
let signature = FunctionSignature {
name : " test " . to_string ( ) ,
params : vec ! [ ] ,
return_type : MirType ::Void ,
effects : EffectMask ::PURE ,
} ;
2025-09-17 07:43:07 +09:00
2025-08-12 11:33:48 +00:00
let entry_block = BasicBlockId ::new ( 0 ) ;
let function = MirFunction ::new ( signature , entry_block ) ;
2025-09-17 07:43:07 +09:00
2025-08-12 11:33:48 +00:00
let mut verifier = MirVerifier ::new ( ) ;
let result = verifier . verify_function ( & function ) ;
2025-09-17 07:43:07 +09:00
2025-08-12 11:33:48 +00:00
assert! ( result . is_ok ( ) , " Valid function should pass verification " ) ;
}
2025-09-17 07:43:07 +09:00
2025-08-12 11:33:48 +00:00
#[ test ]
fn test_undefined_value_detection ( ) {
// This test would create a function with undefined value usage
// and verify that the verifier catches it
// Implementation details would depend on the specific test case
}
2025-08-23 16:33:32 +09:00
#[ test ]
fn test_if_merge_uses_phi_not_predecessor ( ) {
// Program:
// if true { result = "A" } else { result = "B" }
// result
let ast = ASTNode ::Program {
statements : vec ! [
ASTNode ::If {
2025-09-17 07:43:07 +09:00
condition : Box ::new ( ASTNode ::Literal {
value : LiteralValue ::Bool ( true ) ,
span : Span ::unknown ( ) ,
} ) ,
then_body : vec ! [ ASTNode ::Assignment {
target : Box ::new ( ASTNode ::Variable {
name : " result " . to_string ( ) ,
span : Span ::unknown ( ) ,
} ) ,
value : Box ::new ( ASTNode ::Literal {
value : LiteralValue ::String ( " A " . to_string ( ) ) ,
span : Span ::unknown ( ) ,
} ) ,
2025-08-23 16:33:32 +09:00
span : Span ::unknown ( ) ,
} ] ,
2025-09-17 07:43:07 +09:00
else_body : Some ( vec! [ ASTNode ::Assignment {
target : Box ::new ( ASTNode ::Variable {
name : " result " . to_string ( ) ,
span : Span ::unknown ( ) ,
} ) ,
value : Box ::new ( ASTNode ::Literal {
value : LiteralValue ::String ( " B " . to_string ( ) ) ,
span : Span ::unknown ( ) ,
} ) ,
2025-08-23 16:33:32 +09:00
span : Span ::unknown ( ) ,
} ] ) ,
span : Span ::unknown ( ) ,
} ,
2025-09-17 07:43:07 +09:00
ASTNode ::Variable {
name : " result " . to_string ( ) ,
span : Span ::unknown ( ) ,
} ,
2025-08-23 16:33:32 +09:00
] ,
span : Span ::unknown ( ) ,
} ;
let mut builder = MirBuilder ::new ( ) ;
let module = builder . build_module ( ast ) . expect ( " build mir " ) ;
// Verify: should be OK (no MergeUsesPredecessorValue)
let mut verifier = MirVerifier ::new ( ) ;
let res = verifier . verify_module ( & module ) ;
2025-09-17 07:43:07 +09:00
if let Err ( errs ) = & res {
eprintln! ( " Verifier errors: {:?} " , errs ) ;
}
2025-08-23 16:33:32 +09:00
assert! ( res . is_ok ( ) , " MIR should pass merge-phi verification " ) ;
// Optional: ensure printer shows a phi in merge and ret returns a defined value
let mut printer = MirPrinter ::verbose ( ) ;
let mir_text = printer . print_module ( & module ) ;
2025-09-17 07:43:07 +09:00
assert! (
mir_text . contains ( " phi " ) ,
" Printed MIR should contain a phi in merge block \n {} " ,
mir_text
) ;
2025-08-23 16:33:32 +09:00
}
2025-08-23 19:27:02 +09:00
#[ test ]
fn test_merge_use_before_phi_detected ( ) {
2025-09-17 07:43:07 +09:00
use crate ::mir ::{ ConstValue , MirInstruction } ;
2025-08-23 19:27:02 +09:00
// Construct a function with a bad merge use (no phi)
let signature = FunctionSignature {
name : " merge_bad " . to_string ( ) ,
params : vec ! [ ] ,
return_type : MirType ::String ,
effects : EffectMask ::PURE ,
} ;
let entry = BasicBlockId ::new ( 0 ) ;
let mut f = MirFunction ::new ( signature , entry ) ;
let then_bb = BasicBlockId ::new ( 1 ) ;
let else_bb = BasicBlockId ::new ( 2 ) ;
let merge_bb = BasicBlockId ::new ( 3 ) ;
let cond = f . next_value_id ( ) ; // %0
{
let b0 = f . get_block_mut ( entry ) . unwrap ( ) ;
2025-09-17 07:43:07 +09:00
b0 . add_instruction ( MirInstruction ::Const {
dst : cond ,
value : ConstValue ::Bool ( true ) ,
} ) ;
b0 . add_instruction ( MirInstruction ::Branch {
condition : cond ,
then_bb ,
else_bb ,
} ) ;
2025-08-23 19:27:02 +09:00
}
let v1 = f . next_value_id ( ) ; // %1
let mut b1 = BasicBlock ::new ( then_bb ) ;
2025-09-17 07:43:07 +09:00
b1 . add_instruction ( MirInstruction ::Const {
dst : v1 ,
value : ConstValue ::String ( " A " . to_string ( ) ) ,
} ) ;
2025-08-23 19:27:02 +09:00
b1 . add_instruction ( MirInstruction ::Jump { target : merge_bb } ) ;
f . add_block ( b1 ) ;
let v2 = f . next_value_id ( ) ; // %2
let mut b2 = BasicBlock ::new ( else_bb ) ;
2025-09-17 07:43:07 +09:00
b2 . add_instruction ( MirInstruction ::Const {
dst : v2 ,
value : ConstValue ::String ( " B " . to_string ( ) ) ,
} ) ;
2025-08-23 19:27:02 +09:00
b2 . add_instruction ( MirInstruction ::Jump { target : merge_bb } ) ;
f . add_block ( b2 ) ;
let mut b3 = BasicBlock ::new ( merge_bb ) ;
// Illegal: directly use v1 from predecessor
b3 . add_instruction ( MirInstruction ::Return { value : Some ( v1 ) } ) ;
f . add_block ( b3 ) ;
f . update_cfg ( ) ;
let mut verifier = MirVerifier ::new ( ) ;
let res = verifier . verify_function ( & f ) ;
2025-09-17 07:43:07 +09:00
assert! (
res . is_err ( ) ,
" Verifier should error on merge use without phi "
) ;
2025-08-23 19:27:02 +09:00
let errs = res . err ( ) . unwrap ( ) ;
2025-09-17 07:43:07 +09:00
assert! (
errs . iter ( ) . any ( | e | matches! (
e ,
VerificationError ::MergeUsesPredecessorValue { .. }
| VerificationError ::DominatorViolation { .. }
) ) ,
" Expected merge/dominator error, got: {:?} " ,
errs
) ;
2025-08-23 19:27:02 +09:00
}
2025-08-26 06:35:39 +09:00
#[ test ]
fn test_loop_phi_normalization ( ) {
// Program:
// local i = 0
// loop(false) { i = 1 }
// i
let ast = ASTNode ::Program {
statements : vec ! [
ASTNode ::Local {
variables : vec ! [ " i " . to_string ( ) ] ,
2025-09-17 07:43:07 +09:00
initial_values : vec ! [ Some ( Box ::new ( ASTNode ::Literal {
value : LiteralValue ::Integer ( 0 ) ,
span : Span ::unknown ( ) ,
} ) ) ] ,
2025-08-26 06:35:39 +09:00
span : Span ::unknown ( ) ,
} ,
ASTNode ::Loop {
2025-09-17 07:43:07 +09:00
condition : Box ::new ( ASTNode ::Literal {
value : LiteralValue ::Bool ( false ) ,
span : Span ::unknown ( ) ,
} ) ,
body : vec ! [ ASTNode ::Assignment {
target : Box ::new ( ASTNode ::Variable {
name : " i " . to_string ( ) ,
span : Span ::unknown ( ) ,
} ) ,
value : Box ::new ( ASTNode ::Literal {
value : LiteralValue ::Integer ( 1 ) ,
span : Span ::unknown ( ) ,
} ) ,
2025-08-26 06:35:39 +09:00
span : Span ::unknown ( ) ,
} ] ,
span : Span ::unknown ( ) ,
} ,
2025-09-17 07:43:07 +09:00
ASTNode ::Variable {
name : " i " . to_string ( ) ,
span : Span ::unknown ( ) ,
} ,
2025-08-26 06:35:39 +09:00
] ,
span : Span ::unknown ( ) ,
} ;
let mut builder = MirBuilder ::new ( ) ;
let module = builder . build_module ( ast ) . expect ( " build mir " ) ;
// Verify SSA/dominance: should pass
let mut verifier = MirVerifier ::new ( ) ;
let res = verifier . verify_module ( & module ) ;
2025-09-17 07:43:07 +09:00
if let Err ( errs ) = & res {
eprintln! ( " Verifier errors: {:?} " , errs ) ;
}
assert! (
res . is_ok ( ) ,
" MIR loop with phi normalization should pass verification "
) ;
2025-08-26 06:35:39 +09:00
// Ensure phi is printed (header phi for variable i)
let printer = MirPrinter ::verbose ( ) ;
let mir_text = printer . print_module ( & module ) ;
2025-09-17 07:43:07 +09:00
assert! (
mir_text . contains ( " phi " ) ,
" Printed MIR should contain a phi for loop header \n {} " ,
mir_text
) ;
2025-08-26 06:35:39 +09:00
}
#[ test ]
fn test_loop_nested_if_phi ( ) {
// Program:
// local x = 0
// loop(false) { if true { x = 1 } else { x = 2 } }
// x
let ast = ASTNode ::Program {
statements : vec ! [
ASTNode ::Local {
variables : vec ! [ " x " . to_string ( ) ] ,
2025-09-17 07:43:07 +09:00
initial_values : vec ! [ Some ( Box ::new ( ASTNode ::Literal {
value : LiteralValue ::Integer ( 0 ) ,
span : Span ::unknown ( ) ,
} ) ) ] ,
2025-08-26 06:35:39 +09:00
span : Span ::unknown ( ) ,
} ,
ASTNode ::Loop {
2025-09-17 07:43:07 +09:00
condition : Box ::new ( ASTNode ::Literal {
value : LiteralValue ::Bool ( false ) ,
span : Span ::unknown ( ) ,
} ) ,
body : vec ! [ ASTNode ::If {
condition : Box ::new ( ASTNode ::Literal {
value : LiteralValue ::Bool ( true ) ,
span : Span ::unknown ( ) ,
} ) ,
then_body : vec ! [ ASTNode ::Assignment {
target : Box ::new ( ASTNode ::Variable {
name : " x " . to_string ( ) ,
span : Span ::unknown ( ) ,
} ) ,
value : Box ::new ( ASTNode ::Literal {
value : LiteralValue ::Integer ( 1 ) ,
span : Span ::unknown ( ) ,
} ) ,
2025-08-26 06:35:39 +09:00
span : Span ::unknown ( ) ,
} ] ,
2025-09-17 07:43:07 +09:00
else_body : Some ( vec! [ ASTNode ::Assignment {
target : Box ::new ( ASTNode ::Variable {
name : " x " . to_string ( ) ,
span : Span ::unknown ( ) ,
} ) ,
value : Box ::new ( ASTNode ::Literal {
value : LiteralValue ::Integer ( 2 ) ,
span : Span ::unknown ( ) ,
} ) ,
2025-08-26 06:35:39 +09:00
span : Span ::unknown ( ) ,
} ] ) ,
span : Span ::unknown ( ) ,
} ] ,
span : Span ::unknown ( ) ,
} ,
2025-09-17 07:43:07 +09:00
ASTNode ::Variable {
name : " x " . to_string ( ) ,
span : Span ::unknown ( ) ,
} ,
2025-08-26 06:35:39 +09:00
] ,
span : Span ::unknown ( ) ,
} ;
let mut builder = MirBuilder ::new ( ) ;
let module = builder . build_module ( ast ) . expect ( " build mir " ) ;
let mut verifier = MirVerifier ::new ( ) ;
let res = verifier . verify_module ( & module ) ;
2025-09-17 07:43:07 +09:00
if let Err ( errs ) = & res {
eprintln! ( " Verifier errors: {:?} " , errs ) ;
}
assert! (
res . is_ok ( ) ,
" Nested if in loop should pass verification with proper phis "
) ;
2025-08-26 06:35:39 +09:00
let printer = MirPrinter ::verbose ( ) ;
let mir_text = printer . print_module ( & module ) ;
2025-09-17 07:43:07 +09:00
assert! (
mir_text . contains ( " phi " ) ,
" Printed MIR should contain phi nodes for nested if/loop \n {} " ,
mir_text
) ;
2025-08-26 06:35:39 +09:00
}
2025-08-23 16:33:32 +09:00
}