#![allow(
clippy::cast_possible_wrap,
clippy::cast_sign_loss,
clippy::cast_possible_truncation,
// Operand-stack / local indexing is bounds-guaranteed by validation (stack height — #33).
clippy::indexing_slicing
)]
use crate::canon::{AggKind, IrHeap, RefKind, ScalarKind, Slot};
use crate::store::{read_slot, write_slot, NULL_REF};
#[cfg(feature = "simd")]
use crate::value::V128;
use crate::value::{HeapType, Val, ValType};
#[cfg(feature = "simd")]
pub(super) const SLOT_BYTES: usize = 16;
#[cfg(not(feature = "simd"))]
pub(super) const SLOT_BYTES: usize = 8;
#[derive(Copy, Clone, Debug)]
pub(super) struct Cell([u8; SLOT_BYTES]);
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub(super) struct RefTag(u8);
impl RefTag {
pub(super) const NONE: RefTag = RefTag(0);
const FUNC: RefTag = RefTag(1);
const EXTERN: RefTag = RefTag(2);
const ANY: RefTag = RefTag(3);
const EXN: RefTag = RefTag(4);
#[inline]
pub(super) fn of_val(v: &Val) -> RefTag {
match v {
Val::FuncRef(_) => RefTag::FUNC,
Val::ExternRef(_) => RefTag::EXTERN,
Val::AnyRef(_) => RefTag::ANY,
Val::ExnRef(_) => RefTag::EXN,
_ => RefTag::NONE,
}
}
#[inline]
pub(super) fn of_refkind(kind: RefKind) -> RefTag {
match kind {
RefKind::Func => RefTag::FUNC,
RefKind::Extern => RefTag::EXTERN,
RefKind::Any => RefTag::ANY,
RefKind::Exn => RefTag::EXN,
}
}
#[inline]
pub(super) fn refkind(self) -> Option<RefKind> {
match self {
RefTag::FUNC => Some(RefKind::Func),
RefTag::EXTERN => Some(RefKind::Extern),
RefTag::ANY => Some(RefKind::Any),
RefTag::EXN => Some(RefKind::Exn),
_ => None,
}
}
}
impl Cell {
#[inline]
fn lo<const N: usize>(self) -> [u8; N] {
let mut out = [0u8; N];
out.copy_from_slice(&self.0[..N]);
out
}
#[inline]
pub(super) fn unwrap_i32(self) -> i32 {
i32::from_le_bytes(self.lo())
}
#[inline]
pub(super) fn unwrap_i64(self) -> i64 {
i64::from_le_bytes(self.lo())
}
#[inline]
pub(super) fn unwrap_f32(self) -> f32 {
f32::from_bits(u32::from_le_bytes(self.lo()))
}
#[inline]
pub(super) fn unwrap_f64(self) -> f64 {
f64::from_bits(u64::from_le_bytes(self.lo()))
}
#[cfg(feature = "simd")]
#[inline]
pub(super) fn unwrap_v128(self) -> V128 {
V128::from(u128::from_le_bytes(self.lo()))
}
#[inline]
pub(super) fn bytes(&self) -> &[u8] {
&self.0
}
#[inline]
pub(super) fn from_i32(v: i32) -> Cell {
Cell::of_bytes(v.to_le_bytes())
}
#[inline]
pub(super) fn from_i64(v: i64) -> Cell {
Cell::of_bytes(v.to_le_bytes())
}
#[inline]
pub(super) fn from_f32(v: f32) -> Cell {
Cell::of_bytes(v.to_bits().to_le_bytes())
}
#[inline]
pub(super) fn from_f64(v: f64) -> Cell {
Cell::of_bytes(v.to_bits().to_le_bytes())
}
#[inline]
pub(super) fn handle(self) -> u32 {
u32::from_le_bytes(self.lo())
}
#[inline]
pub(super) fn is_null(self) -> bool {
self.handle() == NULL_REF
}
}
#[inline]
pub(super) fn encode(v: Val) -> Cell {
let mut cell = Cell([0u8; SLOT_BYTES]);
write_slot(slot_for_val(&v), &mut cell.0, v);
cell
}
#[inline]
pub(super) fn decode(cell: Cell, ty: &ValType) -> Val {
read_slot(slot_for_valtype(ty), &cell.0)
}
#[inline]
fn slot_for_val(v: &Val) -> Slot {
match v {
Val::I32(_) => Slot::Scalar {
offset: 0,
kind: ScalarKind::I32,
},
Val::I64(_) => Slot::Scalar {
offset: 0,
kind: ScalarKind::I64,
},
Val::F32(_) => Slot::Scalar {
offset: 0,
kind: ScalarKind::F32,
},
Val::F64(_) => Slot::Scalar {
offset: 0,
kind: ScalarKind::F64,
},
#[cfg(feature = "simd")]
Val::V128(_) => Slot::Scalar {
offset: 0,
kind: ScalarKind::V128,
},
#[cfg(not(feature = "simd"))]
Val::V128(_) => unreachable!("v128 requires the simd feature"),
Val::FuncRef(_) => Slot::Ref {
offset: 0,
kind: RefKind::Func,
},
Val::ExternRef(_) => Slot::Ref {
offset: 0,
kind: RefKind::Extern,
},
Val::AnyRef(_) => Slot::Ref {
offset: 0,
kind: RefKind::Any,
},
Val::ExnRef(_) => Slot::Ref {
offset: 0,
kind: RefKind::Exn,
},
}
}
#[inline]
fn slot_for_valtype(ty: &ValType) -> Slot {
match ty {
ValType::I32 => Slot::Scalar {
offset: 0,
kind: ScalarKind::I32,
},
ValType::I64 => Slot::Scalar {
offset: 0,
kind: ScalarKind::I64,
},
ValType::F32 => Slot::Scalar {
offset: 0,
kind: ScalarKind::F32,
},
ValType::F64 => Slot::Scalar {
offset: 0,
kind: ScalarKind::F64,
},
#[cfg(feature = "simd")]
ValType::V128 => Slot::Scalar {
offset: 0,
kind: ScalarKind::V128,
},
#[cfg(not(feature = "simd"))]
ValType::V128 => unreachable!("v128 requires the simd feature"),
ValType::Ref(rt) => Slot::Ref {
offset: 0,
kind: refkind_of_heap(rt.heap_type()),
},
}
}
#[inline]
pub(super) fn stack_slot_for_field(field: Slot) -> Slot {
match field {
Slot::Scalar { kind, .. } => {
let kind = match kind {
ScalarKind::I8 | ScalarKind::I16 => ScalarKind::I32,
k => k,
};
Slot::Scalar { offset: 0, kind }
}
Slot::Ref { kind, .. } => Slot::Ref { offset: 0, kind },
}
}
#[inline]
pub(super) fn refkind_of_heap(heap: &HeapType) -> RefKind {
match heap {
HeapType::Func | HeapType::NoFunc | HeapType::ConcreteFunc(_) => RefKind::Func,
HeapType::Extern | HeapType::NoExtern => RefKind::Extern,
HeapType::Exn | HeapType::NoExn => RefKind::Exn,
_ => RefKind::Any,
}
}
#[inline]
pub(super) fn refkind_of_irheap(heap: &IrHeap) -> RefKind {
match heap {
IrHeap::Func | IrHeap::NoFunc | IrHeap::Concrete(_, AggKind::Func) => RefKind::Func,
IrHeap::Extern | IrHeap::NoExtern => RefKind::Extern,
IrHeap::Exn | IrHeap::NoExn => RefKind::Exn,
_ => RefKind::Any,
}
}
impl Cell {
#[inline]
pub(super) fn of_bytes<const N: usize>(bytes: [u8; N]) -> Cell {
let mut b = [0u8; SLOT_BYTES];
b[..N].copy_from_slice(&bytes);
Cell(b)
}
}
#[cfg(test)]
#[path = "cell_tests.rs"]
mod tests;