use crate::canon::{AggKind, IrHeap, IrVal};
use crate::func::Func;
use crate::value::gc_ref::{AnyRef, ExnRef, ExternRef, Rooted};
use crate::value::types::{HeapType, ValType};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct V128(u128);
impl V128 {
pub fn as_u128(self) -> u128 {
self.0
}
}
impl From<u128> for V128 {
fn from(value: u128) -> Self {
V128(value)
}
}
impl From<V128> for u128 {
fn from(value: V128) -> Self {
value.0
}
}
#[derive(Copy, Clone, Debug)]
pub enum Val {
I32(i32),
I64(i64),
F32(u32),
F64(u64),
V128(V128),
FuncRef(Option<Func>),
ExternRef(Option<Rooted<ExternRef>>),
AnyRef(Option<Rooted<AnyRef>>),
ExnRef(Option<Rooted<ExnRef>>),
}
impl Val {
pub fn null_func_ref() -> Val {
Val::FuncRef(None)
}
pub fn null_extern_ref() -> Val {
Val::ExternRef(None)
}
pub fn null_any_ref() -> Val {
Val::AnyRef(None)
}
pub fn null_exn_ref() -> Val {
Val::ExnRef(None)
}
pub fn i32(&self) -> Option<i32> {
match self {
Val::I32(x) => Some(*x),
_ => None,
}
}
pub fn unwrap_i32(&self) -> i32 {
self.i32().expect("expected i32")
}
pub fn i64(&self) -> Option<i64> {
match self {
Val::I64(x) => Some(*x),
_ => None,
}
}
pub fn unwrap_i64(&self) -> i64 {
self.i64().expect("expected i64")
}
pub fn f32(&self) -> Option<f32> {
match self {
Val::F32(bits) => Some(f32::from_bits(*bits)),
_ => None,
}
}
pub fn unwrap_f32(&self) -> f32 {
self.f32().expect("expected f32")
}
pub fn f64(&self) -> Option<f64> {
match self {
Val::F64(bits) => Some(f64::from_bits(*bits)),
_ => None,
}
}
pub fn unwrap_f64(&self) -> f64 {
self.f64().expect("expected f64")
}
pub fn v128(&self) -> Option<V128> {
match self {
Val::V128(x) => Some(*x),
_ => None,
}
}
pub fn unwrap_v128(&self) -> V128 {
self.v128().expect("expected v128")
}
pub(crate) fn is_null_ref(&self) -> bool {
matches!(
self,
Val::FuncRef(None) | Val::ExternRef(None) | Val::AnyRef(None) | Val::ExnRef(None)
)
}
pub(crate) fn from_ref(r: Ref) -> Val {
match r {
Ref::Func(f) => Val::FuncRef(f),
Ref::Extern(e) => Val::ExternRef(e),
Ref::Any(a) => Val::AnyRef(a),
Ref::Exn(x) => Val::ExnRef(x),
}
}
pub(crate) fn to_ref(self) -> Ref {
match self {
Val::FuncRef(f) => Ref::Func(f),
Val::ExternRef(e) => Ref::Extern(e),
Val::AnyRef(a) => Ref::Any(a),
Val::ExnRef(x) => Ref::Exn(x),
_ => unreachable!("operand validated as a reference"),
}
}
pub(crate) fn default_for(ty: &IrVal) -> Val {
match ty {
IrVal::I32 => Val::I32(0),
IrVal::I64 => Val::I64(0),
IrVal::F32 => Val::F32(0),
IrVal::F64 => Val::F64(0),
IrVal::V128 => Val::V128(V128::from(0)),
IrVal::Ref { heap, .. } => Val::null_for_heap(heap),
}
}
pub(crate) fn null_for_heap(heap: &IrHeap) -> Val {
match heap {
IrHeap::Func | IrHeap::NoFunc | IrHeap::Concrete(_, AggKind::Func) => {
Val::FuncRef(None)
}
IrHeap::Extern | IrHeap::NoExtern => Val::ExternRef(None),
IrHeap::Exn | IrHeap::NoExn => Val::ExnRef(None),
_ => Val::AnyRef(None),
}
}
pub(crate) fn default_for_valtype(ty: &ValType) -> Val {
match ty {
ValType::I32 => Val::I32(0),
ValType::I64 => Val::I64(0),
ValType::F32 => Val::F32(0),
ValType::F64 => Val::F64(0),
ValType::V128 => Val::V128(V128::from(0)),
ValType::Ref(rt) => match rt.heap_type() {
HeapType::Func | HeapType::NoFunc | HeapType::ConcreteFunc(_) => Val::FuncRef(None),
HeapType::Extern | HeapType::NoExtern => Val::ExternRef(None),
HeapType::Exn | HeapType::NoExn => Val::ExnRef(None),
_ => Val::AnyRef(None),
},
}
}
}
#[derive(Clone, Debug)]
pub enum Ref {
Func(Option<Func>),
Extern(Option<Rooted<ExternRef>>),
Any(Option<Rooted<AnyRef>>),
Exn(Option<Rooted<ExnRef>>),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn float_bits_round_trip_preserves_nan() {
let nan_bits = 0x7fc0_1234_u32;
let v = Val::F32(nan_bits);
assert!(v.unwrap_f32().is_nan());
assert_eq!(v.unwrap_f32().to_bits(), nan_bits);
}
#[test]
fn float_accessors_decode_bits() {
assert_eq!(
Val::F32(1.5_f32.to_bits()).unwrap_f32().to_bits(),
1.5_f32.to_bits()
);
assert_eq!(
Val::F64(2.5_f64.to_bits()).unwrap_f64().to_bits(),
2.5_f64.to_bits()
);
assert_eq!(Val::I32(-7).unwrap_i32(), -7);
assert_eq!(Val::I64(9).unwrap_i64(), 9);
assert!(Val::I32(0).f64().is_none());
}
#[test]
fn default_for_numeric_types() {
use crate::canon::IrVal;
assert_eq!(Val::default_for(&IrVal::I32).unwrap_i32(), 0);
assert_eq!(Val::default_for(&IrVal::I64).unwrap_i64(), 0);
assert_eq!(Val::default_for(&IrVal::F32).unwrap_f32().to_bits(), 0);
assert_eq!(Val::default_for(&IrVal::F64).unwrap_f64().to_bits(), 0);
}
}