use crate::term::{BV, Bool};
use ordeal::CheckResult;
use ordeal::trap as ot;
use synth_core::WasmOp;
pub use ordeal::trap::{DivOp, FpFmt, IntTarget};
#[derive(Clone, Debug)]
pub struct DefineOrTrap {
pub value: BV,
pub may_trap: Bool,
}
impl DefineOrTrap {
fn to_ordeal(&self) -> ot::DefineOrTrap {
ot::DefineOrTrap {
value: self.value.term().clone(),
may_trap: self.may_trap.term().clone(),
}
}
}
pub enum TypeTrap<'a> {
Runtime {
actual_type_id: &'a BV,
expected_id: &'a BV,
},
StaticallyDischarged,
}
pub struct CallIndirect<'a> {
pub index: &'a BV,
pub table_size: &'a BV,
pub slot_ptr: &'a BV,
pub type_trap: TypeTrap<'a>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TrapVerdict {
Preserved,
Dropped(Vec<(String, u128)>),
Unknown,
}
pub fn div_op(op: &WasmOp) -> Option<DivOp> {
Some(match op {
WasmOp::I32DivU | WasmOp::I64DivU => DivOp::DivU,
WasmOp::I32DivS | WasmOp::I64DivS => DivOp::DivS,
WasmOp::I32RemU | WasmOp::I64RemU => DivOp::RemU,
WasmOp::I32RemS | WasmOp::I64RemS => DivOp::RemS,
_ => return None,
})
}
pub fn trap_div(op: DivOp, dividend: &BV, divisor: &BV) -> Bool {
Bool::from_ordeal(ot::trap_div(
op,
dividend.term(),
divisor.term(),
dividend.get_size(),
))
}
pub fn trunc_op(op: &WasmOp) -> Option<(FpFmt, IntTarget, bool)> {
Some(match op {
WasmOp::I32TruncF32S => (FpFmt::F32, IntTarget::I32, true),
WasmOp::I32TruncF32U => (FpFmt::F32, IntTarget::I32, false),
WasmOp::I32TruncF64S => (FpFmt::F64, IntTarget::I32, true),
WasmOp::I32TruncF64U => (FpFmt::F64, IntTarget::I32, false),
WasmOp::I64TruncF64S => (FpFmt::F64, IntTarget::I64, true),
WasmOp::I64TruncF64U => (FpFmt::F64, IntTarget::I64, false),
_ => return None,
})
}
pub fn trap_trunc(bits: &BV, fmt: FpFmt, target: IntTarget, signed: bool) -> Bool {
assert_eq!(
bits.get_size(),
fmt.total_bits(),
"trap_trunc: float operand term must be {} bits wide for {:?}",
fmt.total_bits(),
fmt
);
Bool::from_ordeal(ot::trap_trunc(bits.term(), fmt, target, signed))
}
pub fn trap_always() -> Bool {
Bool::from_ordeal(ot::trap_always())
}
pub fn trap_mem_oob(addr: &BV, size: &BV, mem_bound: &BV) -> Bool {
Bool::from_ordeal(ot::trap_mem_oob(addr.term(), size.term(), mem_bound.term()))
}
pub fn trap_call_indirect(ci: &CallIndirect) -> Bool {
let type_trap = match &ci.type_trap {
TypeTrap::Runtime {
actual_type_id,
expected_id,
} => ot::TypeTrap::Runtime {
actual_type_id: actual_type_id.term(),
expected_id: expected_id.term(),
},
TypeTrap::StaticallyDischarged => ot::TypeTrap::StaticallyDischarged,
};
let oci = ot::CallIndirect {
index: ci.index.term(),
table_size: ci.table_size.term(),
slot_ptr: ci.slot_ptr.term(),
type_trap,
};
Bool::from_ordeal(ot::trap_call_indirect(&oci))
}
fn verdict(result: CheckResult) -> TrapVerdict {
match result {
CheckResult::Unsat(cert) => match cert.recheck() {
Ok(()) => TrapVerdict::Preserved,
Err(_) => TrapVerdict::Unknown,
},
CheckResult::Sat(model) => TrapVerdict::Dropped(model.assignments),
CheckResult::Unknown => TrapVerdict::Unknown,
}
}
pub fn prove_trap_equivalence(orig: &DefineOrTrap, opt: &DefineOrTrap) -> TrapVerdict {
verdict(ot::prove_trap_equivalence(
&orig.to_ordeal(),
&opt.to_ordeal(),
))
}
pub fn prove_trap_condition_equivalence(orig_may_trap: &Bool, opt_may_trap: &Bool) -> TrapVerdict {
verdict(ot::prove_trap_condition_equivalence(
orig_may_trap.term(),
opt_may_trap.term(),
))
}