2025-09-17 05:56:33 +09:00
|
|
|
use crate::mir::verification_types::VerificationError;
|
2025-09-17 07:43:07 +09:00
|
|
|
use crate::mir::{function::MirFunction, MirInstruction};
|
2025-09-17 05:56:33 +09:00
|
|
|
|
|
|
|
|
/// Verify WeakRef/Barrier minimal semantics
|
|
|
|
|
pub fn check_weakref_and_barrier(function: &MirFunction) -> Result<(), Vec<VerificationError>> {
|
|
|
|
|
use crate::mir::{BasicBlockId, ValueId};
|
|
|
|
|
let mut errors = Vec::new();
|
|
|
|
|
// Build def map value -> (block, idx, &inst)
|
2025-09-17 07:43:07 +09:00
|
|
|
let mut def_map: std::collections::HashMap<ValueId, (BasicBlockId, usize, &MirInstruction)> =
|
|
|
|
|
std::collections::HashMap::new();
|
2025-09-17 05:56:33 +09:00
|
|
|
for (bid, block) in &function.blocks {
|
|
|
|
|
for (idx, inst) in block.all_instructions().enumerate() {
|
2025-09-17 07:43:07 +09:00
|
|
|
if let Some(dst) = inst.dst_value() {
|
|
|
|
|
def_map.insert(dst, (*bid, idx, inst));
|
|
|
|
|
}
|
2025-09-17 05:56:33 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (bid, block) in &function.blocks {
|
|
|
|
|
for (idx, inst) in block.all_instructions().enumerate() {
|
|
|
|
|
match inst {
|
2025-09-17 07:43:07 +09:00
|
|
|
MirInstruction::WeakRef {
|
|
|
|
|
op: crate::mir::WeakRefOp::Load,
|
|
|
|
|
value,
|
|
|
|
|
..
|
|
|
|
|
} => match def_map.get(value) {
|
|
|
|
|
Some((_db, _di, def_inst)) => match def_inst {
|
|
|
|
|
MirInstruction::WeakRef {
|
|
|
|
|
op: crate::mir::WeakRefOp::New,
|
|
|
|
|
..
|
|
|
|
|
}
|
|
|
|
|
| MirInstruction::WeakNew { .. } => {}
|
|
|
|
|
_ => errors.push(VerificationError::InvalidWeakRefSource {
|
|
|
|
|
weak_ref: *value,
|
|
|
|
|
block: *bid,
|
|
|
|
|
instruction_index: idx,
|
|
|
|
|
reason: "weakref.load source is not a weakref.new/weak_new".to_string(),
|
2025-09-17 05:56:33 +09:00
|
|
|
}),
|
2025-09-17 07:43:07 +09:00
|
|
|
},
|
|
|
|
|
None => errors.push(VerificationError::InvalidWeakRefSource {
|
|
|
|
|
weak_ref: *value,
|
|
|
|
|
block: *bid,
|
|
|
|
|
instruction_index: idx,
|
|
|
|
|
reason: "weakref.load source is undefined".to_string(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
MirInstruction::WeakLoad { weak_ref, .. } => match def_map.get(weak_ref) {
|
|
|
|
|
Some((_db, _di, def_inst)) => match def_inst {
|
|
|
|
|
MirInstruction::WeakNew { .. }
|
|
|
|
|
| MirInstruction::WeakRef {
|
|
|
|
|
op: crate::mir::WeakRefOp::New,
|
|
|
|
|
..
|
|
|
|
|
} => {}
|
|
|
|
|
_ => errors.push(VerificationError::InvalidWeakRefSource {
|
|
|
|
|
weak_ref: *weak_ref,
|
|
|
|
|
block: *bid,
|
|
|
|
|
instruction_index: idx,
|
|
|
|
|
reason: "weak_load source is not a weak_new/weakref.new".to_string(),
|
2025-09-17 05:56:33 +09:00
|
|
|
}),
|
2025-09-17 07:43:07 +09:00
|
|
|
},
|
|
|
|
|
None => errors.push(VerificationError::InvalidWeakRefSource {
|
|
|
|
|
weak_ref: *weak_ref,
|
|
|
|
|
block: *bid,
|
|
|
|
|
instruction_index: idx,
|
|
|
|
|
reason: "weak_load source is undefined".to_string(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
MirInstruction::Barrier { ptr, .. }
|
|
|
|
|
| MirInstruction::BarrierRead { ptr }
|
|
|
|
|
| MirInstruction::BarrierWrite { ptr } => {
|
2025-09-17 05:56:33 +09:00
|
|
|
if let Some((_db, _di, def_inst)) = def_map.get(ptr) {
|
2025-09-17 07:43:07 +09:00
|
|
|
if let MirInstruction::Const {
|
2025-11-21 06:25:17 +09:00
|
|
|
value: crate::mir::ConstValue::Void,
|
2025-09-17 07:43:07 +09:00
|
|
|
..
|
|
|
|
|
} = def_inst
|
|
|
|
|
{
|
2025-09-17 05:56:33 +09:00
|
|
|
errors.push(VerificationError::InvalidBarrierPointer {
|
2025-09-17 07:43:07 +09:00
|
|
|
ptr: *ptr,
|
|
|
|
|
block: *bid,
|
|
|
|
|
instruction_index: idx,
|
2025-09-17 05:56:33 +09:00
|
|
|
reason: "barrier pointer is void".to_string(),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
if errors.is_empty() {
|
|
|
|
|
Ok(())
|
|
|
|
|
} else {
|
|
|
|
|
Err(errors)
|
|
|
|
|
}
|
2025-09-17 05:56:33 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Light diagnostic: Barrier should be near memory ops in the same block (best-effort)
|
|
|
|
|
/// Enabled only when NYASH_VERIFY_BARRIER_STRICT=1
|
|
|
|
|
pub fn check_barrier_context(function: &MirFunction) -> Result<(), Vec<VerificationError>> {
|
|
|
|
|
let strict = std::env::var("NYASH_VERIFY_BARRIER_STRICT").ok().as_deref() == Some("1");
|
2025-09-17 07:43:07 +09:00
|
|
|
if !strict {
|
|
|
|
|
return Ok(());
|
|
|
|
|
}
|
2025-09-17 05:56:33 +09:00
|
|
|
|
|
|
|
|
let mut errors = Vec::new();
|
|
|
|
|
for (bid, block) in &function.blocks {
|
2025-09-17 07:43:07 +09:00
|
|
|
let mut insts: Vec<(usize, &MirInstruction)> =
|
|
|
|
|
block.instructions.iter().enumerate().collect();
|
|
|
|
|
if let Some(term) = &block.terminator {
|
|
|
|
|
insts.push((usize::MAX, term));
|
|
|
|
|
}
|
2025-09-17 05:56:33 +09:00
|
|
|
for (idx, inst) in &insts {
|
2025-09-17 07:43:07 +09:00
|
|
|
let is_barrier = matches!(
|
|
|
|
|
inst,
|
|
|
|
|
MirInstruction::Barrier { .. }
|
|
|
|
|
| MirInstruction::BarrierRead { .. }
|
|
|
|
|
| MirInstruction::BarrierWrite { .. }
|
2025-09-17 05:56:33 +09:00
|
|
|
);
|
2025-09-17 07:43:07 +09:00
|
|
|
if !is_barrier {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2025-09-17 05:56:33 +09:00
|
|
|
|
|
|
|
|
// Look around +-2 instructions for a memory op hint
|
|
|
|
|
let mut has_mem_neighbor = false;
|
|
|
|
|
for (j, other) in &insts {
|
2025-09-17 07:43:07 +09:00
|
|
|
if *j == *idx {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
let dist = if *idx == usize::MAX || *j == usize::MAX {
|
|
|
|
|
99
|
|
|
|
|
} else {
|
|
|
|
|
idx.max(j) - idx.min(j)
|
|
|
|
|
};
|
|
|
|
|
if dist > 2 {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if matches!(
|
|
|
|
|
other,
|
|
|
|
|
MirInstruction::Load { .. }
|
|
|
|
|
| MirInstruction::Store { .. }
|
|
|
|
|
| MirInstruction::ArrayGet { .. }
|
|
|
|
|
| MirInstruction::ArraySet { .. }
|
|
|
|
|
| MirInstruction::RefGet { .. }
|
|
|
|
|
| MirInstruction::RefSet { .. }
|
|
|
|
|
) {
|
|
|
|
|
has_mem_neighbor = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-09-17 05:56:33 +09:00
|
|
|
}
|
|
|
|
|
if !has_mem_neighbor {
|
|
|
|
|
errors.push(VerificationError::SuspiciousBarrierContext {
|
|
|
|
|
block: *bid,
|
|
|
|
|
instruction_index: *idx,
|
|
|
|
|
note: "barrier without nearby memory op (±2 inst)".to_string(),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
if errors.is_empty() {
|
|
|
|
|
Ok(())
|
|
|
|
|
} else {
|
|
|
|
|
Err(errors)
|
|
|
|
|
}
|
2025-09-17 05:56:33 +09:00
|
|
|
}
|