use super::{Const16, Const32};
use crate::{
core::UntypedVal,
engine::{Instr, TranslationError},
Error,
};
use num_derive::FromPrimitive;
#[cfg(doc)]
use super::Instruction;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Register(i16);
impl From<i16> for Register {
fn from(index: i16) -> Self {
Self::from_i16(index)
}
}
impl TryFrom<u32> for Register {
type Error = Error;
fn try_from(local_index: u32) -> Result<Self, Self::Error> {
let index = i16::try_from(local_index)
.map_err(|_| Error::from(TranslationError::RegisterOutOfBounds))?;
Ok(Self::from_i16(index))
}
}
impl Register {
pub fn from_i16(index: i16) -> Self {
Self(index)
}
pub fn to_i16(self) -> i16 {
self.0
}
pub fn is_const(self) -> bool {
self.0.is_negative()
}
pub fn next(self) -> Register {
Self(self.0.wrapping_add(1))
}
pub fn prev(self) -> Register {
Self(self.0.wrapping_sub(1))
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct RegisterSpan(Register);
impl RegisterSpan {
pub fn new(start: Register) -> Self {
Self(start)
}
pub fn iter(self, len: usize) -> RegisterSpanIter {
RegisterSpanIter::new(self.0, len)
}
pub fn iter_u16(self, len: u16) -> RegisterSpanIter {
RegisterSpanIter::new_u16(self.0, len)
}
pub fn head(self) -> Register {
self.0
}
pub fn head_mut(&mut self) -> &mut Register {
&mut self.0
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct RegisterSpanIter {
next: Register,
last: Register,
}
impl RegisterSpanIter {
pub fn from_raw_parts(start: Register, end: Register) -> Self {
debug_assert!(start.to_i16() <= end.to_i16());
Self {
next: start,
last: end,
}
}
fn new(start: Register, len: usize) -> Self {
let len = u16::try_from(len)
.unwrap_or_else(|_| panic!("out of bounds length for register span: {len}"));
Self::new_u16(start, len)
}
fn new_u16(start: Register, len: u16) -> Self {
let next = start;
let last = start
.0
.checked_add_unsigned(len)
.map(Register)
.expect("overflowing register index for register span");
Self::from_raw_parts(next, last)
}
pub fn span(self) -> RegisterSpan {
RegisterSpan(self.next)
}
pub fn len_as_u16(self) -> u16 {
self.last.0.abs_diff(self.next.0)
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
fn min_register(&self) -> Register {
self.span().head()
}
fn max_register(&self) -> Register {
self.clone()
.next_back()
.unwrap_or_else(|| self.min_register())
}
pub fn contains(&self, register: Register) -> bool {
if self.is_empty() {
return false;
}
let min = self.min_register();
let max = self.max_register();
min <= register && register <= max
}
pub fn has_overlapping_copies(results: Self, values: Self) -> bool {
assert_eq!(
results.len_as_u16(),
values.len_as_u16(),
"cannot copy between different sized register spans"
);
let len = results.len_as_u16();
if len <= 1 {
return false;
}
let first_value = values.span().head();
let first_result = results.span().head();
if first_value >= first_result {
return false;
}
let mut values = values;
let last_value = values
.next_back()
.expect("span is non empty and thus must return");
last_value >= first_result
}
}
impl Iterator for RegisterSpanIter {
type Item = Register;
fn next(&mut self) -> Option<Self::Item> {
if self.next == self.last {
return None;
}
let reg = self.next;
self.next = self.next.next();
Some(reg)
}
}
impl DoubleEndedIterator for RegisterSpanIter {
fn next_back(&mut self) -> Option<Self::Item> {
if self.next == self.last {
return None;
}
self.last = self.last.prev();
Some(self.last)
}
}
impl ExactSizeIterator for RegisterSpanIter {
fn len(&self) -> usize {
usize::from(self.len_as_u16())
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct BinInstr {
pub result: Register,
pub lhs: Register,
pub rhs: Register,
}
impl BinInstr {
pub fn new(result: Register, lhs: Register, rhs: Register) -> Self {
Self { result, lhs, rhs }
}
}
pub type BinInstrImm16<T> = BinInstrImm<Const16<T>>;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct BinInstrImm<T> {
pub result: Register,
pub reg_in: Register,
pub imm_in: T,
}
impl<T> BinInstrImm<T> {
pub fn new(result: Register, reg_in: Register, imm_in: T) -> Self {
Self {
result,
reg_in,
imm_in,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct UnaryInstr {
pub result: Register,
pub input: Register,
}
impl UnaryInstr {
pub fn new(result: Register, input: Register) -> Self {
Self { result, input }
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct LoadInstr {
pub result: Register,
pub ptr: Register,
}
impl LoadInstr {
pub fn new(result: Register, ptr: Register) -> Self {
Self { result, ptr }
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct LoadAtInstr {
pub result: Register,
pub address: Const32<u32>,
}
impl LoadAtInstr {
pub fn new(result: Register, address: Const32<u32>) -> Self {
Self { result, address }
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct LoadOffset16Instr {
pub result: Register,
pub ptr: Register,
pub offset: Const16<u32>,
}
impl LoadOffset16Instr {
pub fn new(result: Register, ptr: Register, offset: Const16<u32>) -> Self {
Self {
result,
ptr,
offset,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct StoreInstr {
pub ptr: Register,
pub offset: Const32<u32>,
}
impl StoreInstr {
pub fn new(ptr: Register, offset: Const32<u32>) -> Self {
Self { ptr, offset }
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct StoreOffset16Instr<T> {
pub ptr: Register,
pub offset: Const16<u32>,
pub value: T,
}
impl<T> StoreOffset16Instr<T> {
pub fn new(ptr: Register, offset: Const16<u32>, value: T) -> Self {
Self { ptr, offset, value }
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct StoreAtInstr<T> {
pub address: Const32<u32>,
pub value: T,
}
impl<T> StoreAtInstr<T> {
pub fn new(address: Const32<u32>, value: T) -> Self {
Self { address, value }
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Sign {
Pos,
Neg,
}
impl Sign {
pub fn to_f32(self) -> f32 {
match self {
Self::Pos => 1.0_f32,
Self::Neg => -1.0_f32,
}
}
pub fn to_f64(self) -> f64 {
match self {
Self::Pos => 1.0_f64,
Self::Neg => -1.0_f64,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct CallIndirectParams<T> {
pub table: TableIdx,
pub index: T,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct BranchOffset16(i16);
#[cfg(test)]
impl From<i16> for BranchOffset16 {
fn from(offset: i16) -> Self {
Self(offset)
}
}
impl TryFrom<BranchOffset> for BranchOffset16 {
type Error = Error;
fn try_from(offset: BranchOffset) -> Result<Self, Self::Error> {
let Ok(offset16) = i16::try_from(offset.to_i32()) else {
return Err(Error::from(TranslationError::BranchOffsetOutOfBounds));
};
Ok(Self(offset16))
}
}
impl From<BranchOffset16> for BranchOffset {
fn from(offset: BranchOffset16) -> Self {
Self::from(i32::from(offset.to_i16()))
}
}
impl BranchOffset16 {
pub fn is_init(self) -> bool {
self.to_i16() != 0
}
pub fn init(&mut self, valid_offset: BranchOffset) -> Result<(), Error> {
assert!(valid_offset.is_init());
assert!(!self.is_init());
let valid_offset16 = Self::try_from(valid_offset)?;
*self = valid_offset16;
Ok(())
}
pub fn to_i16(self) -> i16 {
self.0
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct BranchBinOpInstr {
pub lhs: Register,
pub rhs: Register,
pub offset: BranchOffset16,
}
impl BranchBinOpInstr {
pub fn new(lhs: Register, rhs: Register, offset: BranchOffset16) -> Self {
Self { lhs, rhs, offset }
}
}
pub type BranchBinOpInstrImm16<T> = BranchBinOpInstrImm<Const16<T>>;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct BranchBinOpInstrImm<T> {
pub lhs: Register,
pub rhs: T,
pub offset: BranchOffset16,
}
impl<T> BranchBinOpInstrImm<T> {
pub fn new(lhs: Register, rhs: T, offset: BranchOffset16) -> Self {
Self { lhs, rhs, offset }
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(transparent)]
pub struct FuncIdx(u32);
impl From<u32> for FuncIdx {
fn from(index: u32) -> Self {
Self(index)
}
}
impl From<FuncIdx> for u32 {
fn from(index: FuncIdx) -> Self {
index.0
}
}
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
#[repr(transparent)]
pub struct TableIdx([u8; 4]);
impl From<u32> for TableIdx {
fn from(index: u32) -> Self {
Self(index.to_ne_bytes())
}
}
impl From<TableIdx> for u32 {
fn from(index: TableIdx) -> Self {
u32::from_ne_bytes(index.0)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(transparent)]
pub struct SignatureIdx(u32);
impl From<u32> for SignatureIdx {
fn from(index: u32) -> Self {
Self(index)
}
}
impl From<SignatureIdx> for u32 {
fn from(index: SignatureIdx) -> Self {
index.0
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(transparent)]
pub struct GlobalIdx(u32);
impl From<u32> for GlobalIdx {
fn from(index: u32) -> Self {
Self(index)
}
}
impl From<GlobalIdx> for u32 {
fn from(index: GlobalIdx) -> Self {
index.0
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(transparent)]
pub struct DataSegmentIdx(u32);
impl From<u32> for DataSegmentIdx {
fn from(index: u32) -> Self {
Self(index)
}
}
impl From<DataSegmentIdx> for u32 {
fn from(index: DataSegmentIdx) -> Self {
index.0
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(transparent)]
pub struct ElementSegmentIdx(u32);
impl From<u32> for ElementSegmentIdx {
fn from(index: u32) -> Self {
Self(index)
}
}
impl From<ElementSegmentIdx> for u32 {
fn from(index: ElementSegmentIdx) -> Self {
index.0
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct BranchOffset(i32);
impl From<i32> for BranchOffset {
fn from(index: i32) -> Self {
Self(index)
}
}
impl BranchOffset {
pub fn uninit() -> Self {
Self(0)
}
pub fn from_src_to_dst(src: Instr, dst: Instr) -> Result<Self, Error> {
let src = i64::from(src.into_u32());
let dst = i64::from(dst.into_u32());
let Some(offset) = dst.checked_sub(src) else {
unreachable!(
"offset for forward branches must have `src` be smaller than or equal to `dst`"
);
};
let Ok(offset) = i32::try_from(offset) else {
return Err(Error::from(TranslationError::BranchOffsetOutOfBounds));
};
Ok(Self(offset))
}
pub fn is_init(self) -> bool {
self.to_i32() != 0
}
pub fn init(&mut self, valid_offset: BranchOffset) {
assert!(valid_offset.is_init());
assert!(!self.is_init());
*self = valid_offset;
}
pub fn to_i32(self) -> i32 {
self.0
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(transparent)]
pub struct BlockFuel(u32);
impl TryFrom<u64> for BlockFuel {
type Error = Error;
fn try_from(index: u64) -> Result<Self, Self::Error> {
match u32::try_from(index) {
Ok(index) => Ok(Self(index)),
Err(_) => Err(Error::from(TranslationError::BlockFuelOutOfBounds)),
}
}
}
impl BlockFuel {
pub fn bump_by(&mut self, amount: u64) -> Result<(), Error> {
let new_amount = self
.to_u64()
.checked_add(amount)
.ok_or(TranslationError::BlockFuelOutOfBounds)?;
self.0 = u32::try_from(new_amount).map_err(|_| TranslationError::BlockFuelOutOfBounds)?;
Ok(())
}
pub fn to_u64(self) -> u64 {
u64::from(self.0)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, FromPrimitive)]
#[repr(u32)]
pub enum BranchComparator {
I32Eq = 0,
I32Ne = 1,
I32LtS = 2,
I32LtU = 3,
I32LeS = 4,
I32LeU = 5,
I32GtS = 6,
I32GtU = 7,
I32GeS = 8,
I32GeU = 9,
I32And = 10,
I32Or = 11,
I32Xor = 12,
I32AndEqz = 13,
I32OrEqz = 14,
I32XorEqz = 15,
I64Eq = 16,
I64Ne = 17,
I64LtS = 18,
I64LtU = 19,
I64LeS = 20,
I64LeU = 21,
I64GtS = 22,
I64GtU = 23,
I64GeS = 24,
I64GeU = 25,
F32Eq = 26,
F32Ne = 27,
F32Lt = 28,
F32Le = 29,
F32Gt = 30,
F32Ge = 31,
F64Eq = 32,
F64Ne = 33,
F64Lt = 34,
F64Le = 35,
F64Gt = 36,
F64Ge = 37,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct ComparatorOffsetParam {
pub cmp: BranchComparator,
pub offset: BranchOffset,
}
impl ComparatorOffsetParam {
pub fn new(cmp: BranchComparator, offset: BranchOffset) -> Self {
Self { cmp, offset }
}
pub fn from_u64(value: u64) -> Option<Self> {
use num_traits::FromPrimitive as _;
let hi = (value >> 32) as u32;
let lo = (value & 0xFFFF_FFFF) as u32;
let cmp = BranchComparator::from_u32(hi)?;
let offset = BranchOffset::from(lo as i32);
Some(Self { cmp, offset })
}
pub fn from_untyped(value: UntypedVal) -> Option<Self> {
Self::from_u64(u64::from(value))
}
pub fn as_u64(&self) -> u64 {
let hi = self.cmp as u64;
let lo = self.offset.to_i32() as u64;
hi << 32 & lo
}
}
impl From<ComparatorOffsetParam> for UntypedVal {
fn from(params: ComparatorOffsetParam) -> Self {
Self::from(params.as_u64())
}
}