use crate::{RefType, TrapCode, hint::unlikely};
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ValType {
I32,
I64,
F32,
F64,
V128,
FuncRef,
ExternRef,
}
impl ValType {
pub fn is_num(&self) -> bool {
matches!(self, Self::I32 | Self::I64 | Self::F32 | Self::F64)
}
pub fn is_ref(&self) -> bool {
matches!(self, Self::ExternRef | Self::FuncRef)
}
pub fn as_ref(&self) -> Option<RefType> {
let ty = match self {
ValType::FuncRef => RefType::Func,
ValType::ExternRef => RefType::Extern,
_ => return None,
};
Some(ty)
}
}
impl From<RefType> for ValType {
fn from(ty: RefType) -> Self {
match ty {
RefType::Func => Self::FuncRef,
RefType::Extern => Self::ExternRef,
}
}
}
pub trait TryTruncateInto<T, E> {
fn try_truncate_into(self) -> Result<T, E>;
}
pub trait TruncateSaturateInto<T> {
fn truncate_saturate_into(self) -> T;
}
pub trait SignExtendFrom<T> {
fn sign_extend_from(self) -> Self;
}
pub trait Integer: Sized + Unsigned {
#[allow(clippy::wrong_self_convention)]
fn is_zero(self) -> bool;
fn leading_zeros(self) -> Self;
fn trailing_zeros(self) -> Self;
fn count_ones(self) -> Self;
fn shl(lhs: Self, rhs: Self) -> Self;
fn shr_s(lhs: Self, rhs: Self) -> Self;
fn shr_u(lhs: Self::Uint, rhs: Self::Uint) -> Self::Uint;
fn rotl(lhs: Self, rhs: Self) -> Self;
fn rotr(lhs: Self, rhs: Self) -> Self;
fn div_s(lhs: Self, rhs: Self) -> Result<Self, TrapCode>;
fn div_u(lhs: Self::Uint, rhs: Self::Uint) -> Result<Self::Uint, TrapCode>;
fn rem_s(lhs: Self, rhs: Self) -> Result<Self, TrapCode>;
fn rem_u(lhs: Self::Uint, rhs: Self::Uint) -> Result<Self::Uint, TrapCode>;
}
pub trait Unsigned {
type Uint;
}
impl Unsigned for i32 {
type Uint = u32;
}
impl Unsigned for i64 {
type Uint = u64;
}
pub trait Float: Sized {
fn abs(self) -> Self;
fn floor(self) -> Self;
fn ceil(self) -> Self;
fn trunc(self) -> Self;
fn nearest(self) -> Self;
fn sqrt(self) -> Self;
fn min(lhs: Self, rhs: Self) -> Self;
fn max(lhs: Self, rhs: Self) -> Self;
fn copysign(lhs: Self, rhs: Self) -> Self;
#[cfg(feature = "simd")]
fn mul_add(a: Self, b: Self, c: Self) -> Self;
}
macro_rules! impl_try_truncate_into {
(@primitive $from: ident, $into: ident, $rmin:literal, $rmax:literal) => {
impl TryTruncateInto<$into, TrapCode> for $from {
#[inline]
fn try_truncate_into(self) -> Result<$into, TrapCode> {
if self.is_nan() {
return Err(TrapCode::BadConversionToInteger);
}
if self <= $rmin || self >= $rmax {
return Err(TrapCode::IntegerOverflow);
}
Ok(self as _)
}
}
impl TruncateSaturateInto<$into> for $from {
#[inline]
fn truncate_saturate_into(self) -> $into {
if self.is_nan() {
return <$into as Default>::default();
}
if self.is_infinite() && self.is_sign_positive() {
return <$into>::MAX;
}
if self.is_infinite() && self.is_sign_negative() {
return <$into>::MIN;
}
self as _
}
}
};
}
impl_try_truncate_into!(@primitive f32, i32, -2147483904.0_f32, 2147483648.0_f32);
impl_try_truncate_into!(@primitive f32, u32, -1.0_f32, 4294967296.0_f32);
impl_try_truncate_into!(@primitive f64, i32, -2147483649.0_f64, 2147483648.0_f64);
impl_try_truncate_into!(@primitive f64, u32, -1.0_f64, 4294967296.0_f64);
impl_try_truncate_into!(@primitive f32, i64, -9223373136366403584.0_f32, 9223372036854775808.0_f32);
impl_try_truncate_into!(@primitive f32, u64, -1.0_f32, 18446744073709551616.0_f32);
impl_try_truncate_into!(@primitive f64, i64, -9223372036854777856.0_f64, 9223372036854775808.0_f64);
impl_try_truncate_into!(@primitive f64, u64, -1.0_f64, 18446744073709551616.0_f64);
macro_rules! impl_sign_extend_from {
( $( impl SignExtendFrom<$from_type:ty> for $for_type:ty; )* ) => {
$(
impl SignExtendFrom<$from_type> for $for_type {
#[inline]
#[allow(clippy::cast_lossless)]
fn sign_extend_from(self) -> Self {
(self as $from_type) as Self
}
}
)*
};
}
impl_sign_extend_from! {
impl SignExtendFrom<i8> for i32;
impl SignExtendFrom<i16> for i32;
impl SignExtendFrom<i8> for i64;
impl SignExtendFrom<i16> for i64;
impl SignExtendFrom<i32> for i64;
}
macro_rules! impl_integer {
($ty:ty) => {
impl Integer for $ty {
#[inline]
fn is_zero(self) -> bool {
self == 0
}
#[inline]
#[allow(clippy::cast_lossless)]
fn leading_zeros(self) -> Self {
self.leading_zeros() as _
}
#[inline]
#[allow(clippy::cast_lossless)]
fn trailing_zeros(self) -> Self {
self.trailing_zeros() as _
}
#[inline]
#[allow(clippy::cast_lossless)]
fn count_ones(self) -> Self {
self.count_ones() as _
}
#[inline]
fn shl(lhs: Self, rhs: Self) -> Self {
lhs.wrapping_shl(rhs as u32)
}
#[inline]
fn shr_s(lhs: Self, rhs: Self) -> Self {
lhs.wrapping_shr(rhs as u32)
}
#[inline]
fn shr_u(lhs: Self::Uint, rhs: Self::Uint) -> Self::Uint {
lhs.wrapping_shr(rhs as u32) as _
}
#[inline]
fn rotl(lhs: Self, rhs: Self) -> Self {
lhs.rotate_left(rhs as u32)
}
#[inline]
fn rotr(lhs: Self, rhs: Self) -> Self {
lhs.rotate_right(rhs as u32)
}
#[inline]
fn div_s(lhs: Self, rhs: Self) -> Result<Self, TrapCode> {
if unlikely(rhs == 0) {
return Err(TrapCode::IntegerDivisionByZero);
}
let (result, overflow) = lhs.overflowing_div(rhs);
if unlikely(overflow) {
return Err(TrapCode::IntegerOverflow);
}
Ok(result)
}
#[inline]
fn div_u(lhs: Self::Uint, rhs: Self::Uint) -> Result<Self::Uint, TrapCode> {
if unlikely(rhs == 0) {
return Err(TrapCode::IntegerDivisionByZero);
}
let (result, overflow) = lhs.overflowing_div(rhs);
if unlikely(overflow) {
return Err(TrapCode::IntegerOverflow);
}
Ok(result)
}
#[inline]
fn rem_s(lhs: Self, rhs: Self) -> Result<Self, TrapCode> {
if unlikely(rhs == 0) {
return Err(TrapCode::IntegerDivisionByZero);
}
Ok(lhs.wrapping_rem(rhs))
}
#[inline]
fn rem_u(lhs: Self::Uint, rhs: Self::Uint) -> Result<Self::Uint, TrapCode> {
if unlikely(rhs == 0) {
return Err(TrapCode::IntegerDivisionByZero);
}
Ok(lhs.wrapping_rem(rhs))
}
}
};
}
impl_integer!(i32);
impl_integer!(i64);
macro_rules! impl_float {
($ty:ty) => {
impl Float for $ty {
#[inline]
fn abs(self) -> Self {
WasmFloatExt::abs(self)
}
#[inline]
fn floor(self) -> Self {
WasmFloatExt::floor(self)
}
#[inline]
fn ceil(self) -> Self {
WasmFloatExt::ceil(self)
}
#[inline]
fn trunc(self) -> Self {
WasmFloatExt::trunc(self)
}
#[inline]
fn nearest(self) -> Self {
WasmFloatExt::nearest(self)
}
#[inline]
fn sqrt(self) -> Self {
WasmFloatExt::sqrt(self)
}
#[inline]
fn min(lhs: Self, rhs: Self) -> Self {
if lhs < rhs {
lhs
} else if rhs < lhs {
rhs
} else if lhs == rhs {
if lhs.is_sign_negative() && rhs.is_sign_positive() {
lhs
} else {
rhs
}
} else {
lhs + rhs
}
}
#[inline]
fn max(lhs: Self, rhs: Self) -> Self {
if lhs > rhs {
lhs
} else if rhs > lhs {
rhs
} else if lhs == rhs {
if lhs.is_sign_positive() && rhs.is_sign_negative() {
lhs
} else {
rhs
}
} else {
lhs + rhs
}
}
#[inline]
fn copysign(lhs: Self, rhs: Self) -> Self {
WasmFloatExt::copysign(lhs, rhs)
}
#[inline]
#[cfg(feature = "simd")]
fn mul_add(a: Self, b: Self, c: Self) -> Self {
WasmFloatExt::mul_add(a, b, c)
}
}
};
}
impl_float!(f32);
impl_float!(f64);
trait WasmFloatExt {
fn abs(self) -> Self;
fn ceil(self) -> Self;
fn floor(self) -> Self;
fn trunc(self) -> Self;
fn sqrt(self) -> Self;
fn nearest(self) -> Self;
fn copysign(self, other: Self) -> Self;
#[cfg(feature = "simd")]
fn mul_add(self, a: Self, b: Self) -> Self;
}
#[cfg(not(feature = "std"))]
macro_rules! impl_wasm_float {
($ty:ty) => {
impl WasmFloatExt for $ty {
#[inline]
fn abs(self) -> Self {
<libm::Libm<Self>>::fabs(self)
}
#[inline]
fn ceil(self) -> Self {
<libm::Libm<Self>>::ceil(self)
}
#[inline]
fn floor(self) -> Self {
<libm::Libm<Self>>::floor(self)
}
#[inline]
fn trunc(self) -> Self {
<libm::Libm<Self>>::trunc(self)
}
#[inline]
fn nearest(self) -> Self {
let round = <libm::Libm<Self>>::round(self);
if <Self as WasmFloatExt>::abs(self - <Self as WasmFloatExt>::trunc(self)) != 0.5 {
return round;
}
let rem = round % 2.0;
if rem == 1.0 {
<Self as WasmFloatExt>::floor(self)
} else if rem == -1.0 {
<Self as WasmFloatExt>::ceil(self)
} else {
round
}
}
#[inline]
fn sqrt(self) -> Self {
<libm::Libm<Self>>::sqrt(self)
}
#[inline]
fn copysign(self, other: Self) -> Self {
<libm::Libm<Self>>::copysign(self, other)
}
#[inline]
#[cfg(feature = "simd")]
fn mul_add(self, a: Self, b: Self) -> Self {
<libm::Libm<Self>>::fma(self, a, b)
}
}
};
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(transparent)]
pub struct V128([u8; 16]);
impl From<u128> for V128 {
fn from(value: u128) -> Self {
Self(value.to_le_bytes())
}
}
impl V128 {
pub fn as_u128(&self) -> u128 {
u128::from_ne_bytes(self.0)
}
}
#[cfg(feature = "std")]
trait IntoQuietNan: Sized {
fn into_quiet_nan(self) -> Option<Self>;
}
#[cfg(feature = "std")]
macro_rules! impl_into_quiet_nan {
( $( ($float:ty, $bits:ty, $mask:literal) );* $(;)? ) => {
$(
impl IntoQuietNan for $float {
#[inline]
fn into_quiet_nan(self) -> Option<Self> {
const QUIET_BIT: $bits = $mask;
if !self.is_nan() {
return None;
}
Some(Self::from_bits(self.to_bits() | QUIET_BIT))
}
}
)*
};
}
#[cfg(feature = "std")]
impl_into_quiet_nan! {
(f32, u32, 0x0040_0000);
(f64, u64, 0x0008_0000_0000_0000);
}
#[cfg(feature = "std")]
macro_rules! impl_wasm_float {
($ty:ty) => {
impl WasmFloatExt for $ty {
#[inline]
fn abs(self) -> Self {
self.abs()
}
#[inline]
fn ceil(self) -> Self {
if let Some(qnan) = self.into_quiet_nan() {
return qnan;
}
self.ceil()
}
#[inline]
fn floor(self) -> Self {
if let Some(qnan) = self.into_quiet_nan() {
return qnan;
}
self.floor()
}
#[inline]
fn trunc(self) -> Self {
if let Some(qnan) = self.into_quiet_nan() {
return qnan;
}
self.trunc()
}
#[inline]
fn nearest(self) -> Self {
if let Some(qnan) = self.into_quiet_nan() {
return qnan;
}
self.round_ties_even()
}
#[inline]
fn sqrt(self) -> Self {
if let Some(qnan) = self.into_quiet_nan() {
return qnan;
}
self.sqrt()
}
#[inline]
fn copysign(self, other: Self) -> Self {
self.copysign(other)
}
#[inline]
#[cfg(feature = "simd")]
fn mul_add(self, a: Self, b: Self) -> Self {
self.mul_add(a, b)
}
}
};
}
impl_wasm_float!(f32);
impl_wasm_float!(f64);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn wasm_float_min_regression_works() {
assert_eq!(Float::min(-0.0_f32, 0.0_f32).to_bits(), 0x8000_0000);
assert_eq!(Float::min(0.0_f32, -0.0_f32).to_bits(), 0x8000_0000);
}
#[test]
fn wasm_float_max_regression_works() {
assert_eq!(Float::max(-0.0_f32, 0.0_f32).to_bits(), 0x0000_0000);
assert_eq!(Float::max(0.0_f32, -0.0_f32).to_bits(), 0x0000_0000);
}
#[test]
fn copysign_regression_works() {
assert!(f32::from_bits(0xFFC00000).is_nan());
assert_eq!(
Float::copysign(f32::from_bits(0xFFC00000), f32::from_bits(0x0000_0000)).to_bits(),
0x7FC00000,
)
}
}