use crate::Register;
use crate::bytecode::Instruction;
use super::error::VerifierError;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum RegType {
#[default]
Unset,
Int,
VecInt,
VecXqmx,
Model,
Sample,
Any,
}
impl RegType {
pub fn type_name(self) -> &'static str {
match self {
Self::Unset => "unset",
Self::Int => "int",
Self::VecInt => "vec<int>",
Self::VecXqmx => "vec<xqmx>",
Self::Model => "model",
Self::Sample => "sample",
Self::Any => "any",
}
}
pub(crate) fn satisfies(self, req: RegTypeReq) -> bool {
match self {
Self::Unset => false,
Self::Any => true,
Self::Int => matches!(req, RegTypeReq::NonUnset | RegTypeReq::Int),
Self::VecInt => {
matches!(
req,
RegTypeReq::NonUnset | RegTypeReq::VecInt | RegTypeReq::AnyVec
)
}
Self::VecXqmx => matches!(req, RegTypeReq::NonUnset | RegTypeReq::AnyVec),
Self::Model => {
matches!(
req,
RegTypeReq::NonUnset | RegTypeReq::Model | RegTypeReq::Grid
)
}
Self::Sample => {
matches!(
req,
RegTypeReq::NonUnset | RegTypeReq::Sample | RegTypeReq::Grid
)
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum RegTypeReq {
NonUnset,
Int,
Model,
Sample,
VecInt,
AnyVec,
Grid,
}
impl RegTypeReq {
pub(crate) fn type_name(self) -> &'static str {
match self {
Self::NonUnset => "any",
Self::Int => "int",
Self::Model => "model",
Self::Sample => "sample",
Self::VecInt => "vec<int>",
Self::AnyVec => "vec",
Self::Grid => "model or sample",
}
}
}
pub(crate) fn check_reg(
offset: usize,
slot: u8,
req: RegTypeReq,
regs: &[RegType; 256],
) -> Result<(), VerifierError> {
debug_assert!(
usize::from(slot) < regs.len(),
"slot u8 always fits in [RegType; 256]"
);
let current = regs.get(usize::from(slot)).copied().unwrap_or_default();
if current == RegType::Unset {
return Err(VerifierError::ReadUnsetRegister { offset, reg: slot });
}
if !current.satisfies(req) {
return Err(VerifierError::RegisterTypeMismatch {
offset,
reg: slot,
expected: req.type_name(),
got: current.type_name(),
});
}
Ok(())
}
pub(crate) fn check_reads(
pos: usize,
instr: &Instruction,
regs: &[RegType; 256],
) -> Result<(), VerifierError> {
use RegTypeReq as R;
match instr {
Instruction::Load { reg } => check_reg(pos, reg.slot(), R::Int, regs)?,
Instruction::Output { reg } => check_reg(pos, reg.slot(), R::NonUnset, regs)?,
Instruction::Iter { reg } | Instruction::VecLen { reg } => {
check_reg(pos, reg.slot(), R::AnyVec, regs)?;
}
Instruction::VecGet { reg }
| Instruction::VecSet { reg }
| Instruction::VecPush { reg } => check_reg(pos, reg.slot(), R::VecInt, regs)?,
Instruction::Slack { indices, coeffs } => {
check_reg(pos, indices.slot(), R::VecInt, regs)?;
check_reg(pos, coeffs.slot(), R::VecInt, regs)?;
}
Instruction::GetLine { reg }
| Instruction::SetLine { reg }
| Instruction::AddLine { reg }
| Instruction::GetQuad { reg }
| Instruction::SetQuad { reg }
| Instruction::AddQuad { reg }
| Instruction::OneHotR { reg }
| Instruction::OneHotC { reg }
| Instruction::Exclude { reg }
| Instruction::Implies { reg } => check_reg(pos, reg.slot(), R::Model, regs)?,
Instruction::Resize { reg }
| Instruction::RowFind { reg }
| Instruction::ColFind { reg }
| Instruction::RowSum { reg }
| Instruction::ColSum { reg } => check_reg(pos, reg.slot(), R::Grid, regs)?,
Instruction::Equality {
model,
indices,
coeffs,
}
| Instruction::AtLeastW {
model,
indices,
coeffs,
} => {
check_reg(pos, model.slot(), R::Model, regs)?;
check_reg(pos, indices.slot(), R::VecInt, regs)?;
check_reg(pos, coeffs.slot(), R::VecInt, regs)?;
}
Instruction::AtLeast { model, indices } => {
check_reg(pos, model.slot(), R::Model, regs)?;
check_reg(pos, indices.slot(), R::VecInt, regs)?;
}
Instruction::Reduce { model } => check_reg(pos, model.slot(), R::Model, regs)?,
Instruction::Energy { model, sample } => {
check_reg(pos, model.slot(), R::Model, regs)?;
check_reg(pos, sample.slot(), R::Sample, regs)?;
}
_ => {}
}
Ok(())
}
pub(crate) fn write_effect(instr: &Instruction) -> Option<(Register, RegType)> {
Some(match instr {
Instruction::Stow { reg } | Instruction::Lidx { reg } => (*reg, RegType::Int),
Instruction::Drop { reg } => (*reg, RegType::Unset),
Instruction::Input { reg } | Instruction::LVal { reg } => (*reg, RegType::Any),
Instruction::Bqmx { reg } | Instruction::Sqmx { reg } | Instruction::Xqmx { reg } => {
(*reg, RegType::Model)
}
Instruction::Bsmx { reg } | Instruction::Ssmx { reg } | Instruction::Xsmx { reg } => {
(*reg, RegType::Sample)
}
Instruction::Vec { reg } | Instruction::VecI { reg } => (*reg, RegType::VecInt),
Instruction::VecX { reg } => (*reg, RegType::VecXqmx),
_ => return None,
})
}
pub(crate) fn apply_writes(instr: &Instruction, regs: &mut [RegType; 256]) {
if let Some((reg, reg_type)) = write_effect(instr)
&& let Some(slot) = regs.get_mut(usize::from(reg.slot()))
{
*slot = reg_type;
}
}