use crate::{
asm_generation::fuel::abstract_instruction_set::AbstractInstructionSet,
asm_lang::{ConstantRegister, ControlFlowOp, VirtualRegister},
};
use either::Either;
use std::collections::HashSet;
use sway_error::error::CompileError;
use sway_types::Span;
impl AbstractInstructionSet {
pub(crate) fn verify(self) -> Result<AbstractInstructionSet, CompileError> {
for op in self.ops.iter() {
if let Either::Right(ControlFlowOp::ReturnFromCall { zero, reta }) = &op.opcode {
if !matches!(zero, VirtualRegister::Constant(ConstantRegister::Zero)) {
return Err(CompileError::Internal(
"ReturnFromCall incorrectly not using $zero",
op.owning_span.as_ref().unwrap_or(&Span::dummy()).clone(),
));
}
if !matches!(
reta,
VirtualRegister::Constant(ConstantRegister::CallReturnAddress)
) {
return Err(CompileError::Internal(
"ReturnFromCall incorrectly not using $reta",
op.owning_span.as_ref().unwrap_or(&Span::dummy()).clone(),
));
}
}
}
macro_rules! add_virt_regs {
($regs: expr, $set: expr) => {
let mut regs = $regs;
regs.retain(|®| matches!(reg, VirtualRegister::Virtual(_)));
$set.extend(regs.into_iter());
};
}
let mut use_regs = HashSet::new();
let mut def_regs = HashSet::new();
for op in &self.ops {
add_virt_regs!(op.use_registers(), use_regs);
add_virt_regs!(op.def_registers(), def_regs);
}
if def_regs.is_superset(&use_regs) {
Ok(self)
} else {
let bad_regs = use_regs
.difference(&def_regs)
.map(|reg| match reg {
VirtualRegister::Virtual(name) => format!("$r{name}"),
VirtualRegister::Constant(creg) => creg.to_string(),
})
.collect::<Vec<_>>()
.join(", ");
Err(CompileError::InternalOwned(
format!("Program erroneously uses uninitialized virtual registers: {bad_regs}"),
Span::dummy(),
))
}
}
}