Skip to main content

wasmi_core/
value.rs

1use crate::{hint::unlikely, TrapCode};
2
3/// Type of a value.
4///
5/// See [`Val`] for details.
6///
7/// [`Val`]: enum.Value.html
8#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
9pub enum ValType {
10    /// 32-bit signed or unsigned integer.
11    I32,
12    /// 64-bit signed or unsigned integer.
13    I64,
14    /// 32-bit IEEE 754-2008 floating point number.
15    F32,
16    /// 64-bit IEEE 754-2008 floating point number.
17    F64,
18    /// A 128-bit Wasm `simd` proposal vector.
19    V128,
20    /// A nullable function reference.
21    FuncRef,
22    /// A nullable external reference.
23    ExternRef,
24}
25
26impl ValType {
27    /// Returns `true` if [`ValType`] is a Wasm numeric type.
28    ///
29    /// This is `true` for [`ValType::I32`], [`ValType::I64`],
30    /// [`ValType::F32`] and [`ValType::F64`].
31    pub fn is_num(&self) -> bool {
32        matches!(self, Self::I32 | Self::I64 | Self::F32 | Self::F64)
33    }
34
35    /// Returns `true` if [`ValType`] is a Wasm reference type.
36    ///
37    /// This is `true` for [`ValType::FuncRef`] and [`ValType::ExternRef`].
38    pub fn is_ref(&self) -> bool {
39        matches!(self, Self::ExternRef | Self::FuncRef)
40    }
41}
42
43/// Convert one type to another by rounding to the nearest integer towards zero.
44///
45/// # Errors
46///
47/// Traps when the input float cannot be represented by the target integer or
48/// when the input float is NaN.
49pub trait TryTruncateInto<T, E> {
50    /// Convert one type to another by rounding to the nearest integer towards zero.
51    ///
52    /// # Errors
53    ///
54    /// - If the input float value is NaN (not a number).
55    /// - If the input float value cannot be represented using the truncated
56    ///   integer type.
57    fn try_truncate_into(self) -> Result<T, E>;
58}
59
60/// Convert one type to another by rounding to the nearest integer towards zero.
61///
62/// # Note
63///
64/// This has saturating semantics for when the integer cannot represent the float.
65///
66/// Returns
67///
68/// - `0` when the input is NaN.
69/// - `int::MIN` when the input is -INF.
70/// - `int::MAX` when the input is +INF.
71pub trait TruncateSaturateInto<T> {
72    /// Convert one type to another by rounding to the nearest integer towards zero.
73    fn truncate_saturate_into(self) -> T;
74}
75
76/// Sign-extends `Self` integer type from `T` integer type.
77pub trait SignExtendFrom<T> {
78    /// Convert one type to another by extending with leading zeroes.
79    fn sign_extend_from(self) -> Self;
80}
81
82/// Integer value.
83pub trait Integer: Sized + Unsigned {
84    /// Returns `true` if `self` is zero.
85    #[allow(clippy::wrong_self_convention)]
86    fn is_zero(self) -> bool;
87    /// Counts leading zeros in the bitwise representation of the value.
88    fn leading_zeros(self) -> Self;
89    /// Counts trailing zeros in the bitwise representation of the value.
90    fn trailing_zeros(self) -> Self;
91    /// Counts 1-bits in the bitwise representation of the value.
92    fn count_ones(self) -> Self;
93    /// Shift-left `self` by `other`.
94    fn shl(lhs: Self, rhs: Self) -> Self;
95    /// Signed shift-right `self` by `other`.
96    fn shr_s(lhs: Self, rhs: Self) -> Self;
97    /// Unsigned shift-right `self` by `other`.
98    fn shr_u(lhs: Self, rhs: Self) -> Self;
99    /// Get left bit rotation result.
100    fn rotl(lhs: Self, rhs: Self) -> Self;
101    /// Get right bit rotation result.
102    fn rotr(lhs: Self, rhs: Self) -> Self;
103    /// Signed integer division.
104    ///
105    /// # Errors
106    ///
107    /// If `other` is equal to zero.
108    fn div_s(lhs: Self, rhs: Self) -> Result<Self, TrapCode>;
109    /// Unsigned integer division.
110    ///
111    /// # Errors
112    ///
113    /// If `other` is equal to zero.
114    fn div_u(lhs: Self::Uint, rhs: Self::Uint) -> Result<Self::Uint, TrapCode>;
115    /// Signed integer remainder.
116    ///
117    /// # Errors
118    ///
119    /// If `other` is equal to zero.
120    fn rem_s(lhs: Self, rhs: Self) -> Result<Self, TrapCode>;
121    /// Unsigned integer remainder.
122    ///
123    /// # Errors
124    ///
125    /// If `other` is equal to zero.
126    fn rem_u(lhs: Self::Uint, rhs: Self::Uint) -> Result<Self::Uint, TrapCode>;
127}
128
129/// Integer types that have an unsigned mirroring type.
130pub trait Unsigned {
131    /// The unsigned type.
132    type Uint;
133
134    /// Converts `self` losslessly to the unsigned type.
135    fn to_unsigned(self) -> Self::Uint;
136}
137
138impl Unsigned for i32 {
139    type Uint = u32;
140    #[inline]
141    fn to_unsigned(self) -> Self::Uint {
142        self as _
143    }
144}
145
146impl Unsigned for i64 {
147    type Uint = u64;
148    #[inline]
149    fn to_unsigned(self) -> Self::Uint {
150        self as _
151    }
152}
153
154/// Float-point value.
155pub trait Float: Sized {
156    /// Get absolute value.
157    fn abs(self) -> Self;
158    /// Returns the largest integer less than or equal to a number.
159    fn floor(self) -> Self;
160    /// Returns the smallest integer greater than or equal to a number.
161    fn ceil(self) -> Self;
162    /// Returns the integer part of a number.
163    fn trunc(self) -> Self;
164    /// Returns the nearest integer to a number. Ties are round to even number.
165    fn nearest(self) -> Self;
166    /// Takes the square root of a number.
167    fn sqrt(self) -> Self;
168    /// Returns the minimum of the two numbers.
169    fn min(lhs: Self, rhs: Self) -> Self;
170    /// Returns the maximum of the two numbers.
171    fn max(lhs: Self, rhs: Self) -> Self;
172    /// Sets sign of this value to the sign of other value.
173    fn copysign(lhs: Self, rhs: Self) -> Self;
174    /// Fused multiply-add with a single rounding error.
175    #[cfg(feature = "simd")]
176    fn mul_add(a: Self, b: Self, c: Self) -> Self;
177}
178
179macro_rules! impl_try_truncate_into {
180    (@primitive $from: ident, $into: ident, $rmin:literal, $rmax:literal) => {
181        impl TryTruncateInto<$into, TrapCode> for $from {
182            #[inline]
183            fn try_truncate_into(self) -> Result<$into, TrapCode> {
184                if self.is_nan() {
185                    return Err(TrapCode::BadConversionToInteger);
186                }
187                if self <= $rmin || self >= $rmax {
188                    return Err(TrapCode::IntegerOverflow);
189                }
190                Ok(self as _)
191            }
192        }
193
194        impl TruncateSaturateInto<$into> for $from {
195            #[inline]
196            fn truncate_saturate_into(self) -> $into {
197                if self.is_nan() {
198                    return <$into as Default>::default();
199                }
200                if self.is_infinite() && self.is_sign_positive() {
201                    return <$into>::MAX;
202                }
203                if self.is_infinite() && self.is_sign_negative() {
204                    return <$into>::MIN;
205                }
206                self as _
207            }
208        }
209    };
210}
211
212impl_try_truncate_into!(@primitive f32, i32, -2147483904.0_f32, 2147483648.0_f32);
213impl_try_truncate_into!(@primitive f32, u32,          -1.0_f32, 4294967296.0_f32);
214impl_try_truncate_into!(@primitive f64, i32, -2147483649.0_f64, 2147483648.0_f64);
215impl_try_truncate_into!(@primitive f64, u32,          -1.0_f64, 4294967296.0_f64);
216impl_try_truncate_into!(@primitive f32, i64, -9223373136366403584.0_f32,  9223372036854775808.0_f32);
217impl_try_truncate_into!(@primitive f32, u64,                   -1.0_f32, 18446744073709551616.0_f32);
218impl_try_truncate_into!(@primitive f64, i64, -9223372036854777856.0_f64,  9223372036854775808.0_f64);
219impl_try_truncate_into!(@primitive f64, u64,                   -1.0_f64, 18446744073709551616.0_f64);
220
221macro_rules! impl_sign_extend_from {
222    ( $( impl SignExtendFrom<$from_type:ty> for $for_type:ty; )* ) => {
223        $(
224            impl SignExtendFrom<$from_type> for $for_type {
225                #[inline]
226                #[allow(clippy::cast_lossless)]
227                fn sign_extend_from(self) -> Self {
228                    (self as $from_type) as Self
229                }
230            }
231        )*
232    };
233}
234impl_sign_extend_from! {
235    impl SignExtendFrom<i8> for i32;
236    impl SignExtendFrom<i16> for i32;
237    impl SignExtendFrom<i8> for i64;
238    impl SignExtendFrom<i16> for i64;
239    impl SignExtendFrom<i32> for i64;
240}
241
242macro_rules! impl_integer {
243    ($ty:ty) => {
244        impl Integer for $ty {
245            #[inline]
246            fn is_zero(self) -> bool {
247                self == 0
248            }
249            #[inline]
250            #[allow(clippy::cast_lossless)]
251            fn leading_zeros(self) -> Self {
252                self.leading_zeros() as _
253            }
254            #[inline]
255            #[allow(clippy::cast_lossless)]
256            fn trailing_zeros(self) -> Self {
257                self.trailing_zeros() as _
258            }
259            #[inline]
260            #[allow(clippy::cast_lossless)]
261            fn count_ones(self) -> Self {
262                self.count_ones() as _
263            }
264            #[inline]
265            fn shl(lhs: Self, rhs: Self) -> Self {
266                lhs.wrapping_shl(rhs as u32)
267            }
268            #[inline]
269            fn shr_s(lhs: Self, rhs: Self) -> Self {
270                lhs.wrapping_shr(rhs as u32)
271            }
272            #[inline]
273            fn shr_u(lhs: Self, rhs: Self) -> Self {
274                lhs.to_unsigned().wrapping_shr(rhs as u32) as _
275            }
276            #[inline]
277            fn rotl(lhs: Self, rhs: Self) -> Self {
278                lhs.rotate_left(rhs as u32)
279            }
280            #[inline]
281            fn rotr(lhs: Self, rhs: Self) -> Self {
282                lhs.rotate_right(rhs as u32)
283            }
284            #[inline]
285            fn div_s(lhs: Self, rhs: Self) -> Result<Self, TrapCode> {
286                if unlikely(rhs == 0) {
287                    return Err(TrapCode::IntegerDivisionByZero);
288                }
289                let (result, overflow) = lhs.overflowing_div(rhs);
290                if unlikely(overflow) {
291                    return Err(TrapCode::IntegerOverflow);
292                }
293                Ok(result)
294            }
295            #[inline]
296            fn div_u(lhs: Self::Uint, rhs: Self::Uint) -> Result<Self::Uint, TrapCode> {
297                if unlikely(rhs == 0) {
298                    return Err(TrapCode::IntegerDivisionByZero);
299                }
300                let (result, overflow) = lhs.overflowing_div(rhs);
301                if unlikely(overflow) {
302                    return Err(TrapCode::IntegerOverflow);
303                }
304                Ok(result)
305            }
306            #[inline]
307            fn rem_s(lhs: Self, rhs: Self) -> Result<Self, TrapCode> {
308                if unlikely(rhs == 0) {
309                    return Err(TrapCode::IntegerDivisionByZero);
310                }
311                Ok(lhs.wrapping_rem(rhs))
312            }
313            #[inline]
314            fn rem_u(lhs: Self::Uint, rhs: Self::Uint) -> Result<Self::Uint, TrapCode> {
315                if unlikely(rhs == 0) {
316                    return Err(TrapCode::IntegerDivisionByZero);
317                }
318                Ok(lhs.wrapping_rem(rhs))
319            }
320        }
321    };
322}
323impl_integer!(i32);
324impl_integer!(i64);
325
326// We cannot call the math functions directly, because they are not all available in `core`.
327// In no-std cases we instead rely on `libm`.
328// These wrappers handle that delegation.
329macro_rules! impl_float {
330    ($ty:ty) => {
331        impl Float for $ty {
332            #[inline]
333            fn abs(self) -> Self {
334                WasmFloatExt::abs(self)
335            }
336            #[inline]
337            fn floor(self) -> Self {
338                WasmFloatExt::floor(self)
339            }
340            #[inline]
341            fn ceil(self) -> Self {
342                WasmFloatExt::ceil(self)
343            }
344            #[inline]
345            fn trunc(self) -> Self {
346                WasmFloatExt::trunc(self)
347            }
348            #[inline]
349            fn nearest(self) -> Self {
350                WasmFloatExt::nearest(self)
351            }
352            #[inline]
353            fn sqrt(self) -> Self {
354                WasmFloatExt::sqrt(self)
355            }
356            #[inline]
357            fn min(lhs: Self, rhs: Self) -> Self {
358                // Note: equal to the unstable `f32::minimum` method.
359                //
360                // Once `f32::minimum` is stable we can simply use it here.
361                if lhs < rhs {
362                    lhs
363                } else if rhs < lhs {
364                    rhs
365                } else if lhs == rhs {
366                    if lhs.is_sign_negative() && rhs.is_sign_positive() {
367                        lhs
368                    } else {
369                        rhs
370                    }
371                } else {
372                    // At least one input is NaN. Use `+` to perform NaN propagation and quieting.
373                    lhs + rhs
374                }
375            }
376            #[inline]
377            fn max(lhs: Self, rhs: Self) -> Self {
378                // Note: equal to the unstable `f32::maximum` method.
379                //
380                // Once `f32::maximum` is stable we can simply use it here.
381                if lhs > rhs {
382                    lhs
383                } else if rhs > lhs {
384                    rhs
385                } else if lhs == rhs {
386                    if lhs.is_sign_positive() && rhs.is_sign_negative() {
387                        lhs
388                    } else {
389                        rhs
390                    }
391                } else {
392                    // At least one input is NaN. Use `+` to perform NaN propagation and quieting.
393                    lhs + rhs
394                }
395            }
396            #[inline]
397            fn copysign(lhs: Self, rhs: Self) -> Self {
398                WasmFloatExt::copysign(lhs, rhs)
399            }
400            #[inline]
401            #[cfg(feature = "simd")]
402            fn mul_add(a: Self, b: Self, c: Self) -> Self {
403                WasmFloatExt::mul_add(a, b, c)
404            }
405        }
406    };
407}
408impl_float!(f32);
409impl_float!(f64);
410
411/// Low-level Wasm float interface to support `no_std` environments.
412///
413/// # Dev. Note
414///
415/// The problem is that in `no_std` builds the Rust standard library
416/// does not specify all of the below methods for `f32` and `f64`.
417/// Thus this trait serves as an adapter to import this functionality
418/// via `libm`.
419trait WasmFloatExt {
420    /// Equivalent to the Wasm `{f32,f64}.abs` instructions.
421    fn abs(self) -> Self;
422    /// Equivalent to the Wasm `{f32,f64}.ceil` instructions.
423    fn ceil(self) -> Self;
424    /// Equivalent to the Wasm `{f32,f64}.floor` instructions.
425    fn floor(self) -> Self;
426    /// Equivalent to the Wasm `{f32,f64}.trunc` instructions.
427    fn trunc(self) -> Self;
428    /// Equivalent to the Wasm `{f32,f64}.sqrt` instructions.
429    fn sqrt(self) -> Self;
430    /// Equivalent to the Wasm `{f32,f64}.nearest` instructions.
431    fn nearest(self) -> Self;
432    /// Equivalent to the Wasm `{f32,f64}.copysign` instructions.
433    fn copysign(self, other: Self) -> Self;
434    /// Fused multiply-add with just 1 rounding error.
435    #[cfg(feature = "simd")]
436    fn mul_add(self, a: Self, b: Self) -> Self;
437}
438
439#[cfg(not(feature = "std"))]
440macro_rules! impl_wasm_float {
441    ($ty:ty) => {
442        impl WasmFloatExt for $ty {
443            #[inline]
444            fn abs(self) -> Self {
445                <libm::Libm<Self>>::fabs(self)
446            }
447
448            #[inline]
449            fn ceil(self) -> Self {
450                <libm::Libm<Self>>::ceil(self)
451            }
452
453            #[inline]
454            fn floor(self) -> Self {
455                <libm::Libm<Self>>::floor(self)
456            }
457
458            #[inline]
459            fn trunc(self) -> Self {
460                <libm::Libm<Self>>::trunc(self)
461            }
462
463            #[inline]
464            fn nearest(self) -> Self {
465                let round = <libm::Libm<Self>>::round(self);
466                if <Self as WasmFloatExt>::abs(self - <Self as WasmFloatExt>::trunc(self)) != 0.5 {
467                    return round;
468                }
469                let rem = round % 2.0;
470                if rem == 1.0 {
471                    <Self as WasmFloatExt>::floor(self)
472                } else if rem == -1.0 {
473                    <Self as WasmFloatExt>::ceil(self)
474                } else {
475                    round
476                }
477            }
478
479            #[inline]
480            fn sqrt(self) -> Self {
481                <libm::Libm<Self>>::sqrt(self)
482            }
483
484            #[inline]
485            fn copysign(self, other: Self) -> Self {
486                <libm::Libm<Self>>::copysign(self, other)
487            }
488
489            #[inline]
490            #[cfg(feature = "simd")]
491            fn mul_add(self, a: Self, b: Self) -> Self {
492                <libm::Libm<Self>>::fma(self, a, b)
493            }
494        }
495    };
496}
497
498/// The Wasm `simd` proposal's `v128` type.
499#[derive(Debug, Copy, Clone, PartialEq, Eq)]
500#[repr(transparent)]
501pub struct V128([u8; 16]);
502
503impl From<u128> for V128 {
504    fn from(value: u128) -> Self {
505        Self(value.to_le_bytes())
506    }
507}
508
509impl V128 {
510    /// Returns the `self` as a 128-bit Rust integer.
511    pub fn as_u128(&self) -> u128 {
512        u128::from_ne_bytes(self.0)
513    }
514}
515
516/// Extension trait for `f32` and `f64` to turn a NaN value into a quiet-NaN value.
517#[cfg(feature = "std")]
518trait IntoQuietNan: Sized {
519    /// Converts `self` into a quiet-NaN if `self` is a NaN, otherwise returns `None`.
520    fn into_quiet_nan(self) -> Option<Self>;
521}
522
523#[cfg(feature = "std")]
524macro_rules! impl_into_quiet_nan {
525    ( $( ($float:ty, $bits:ty, $mask:literal) );* $(;)? ) => {
526        $(
527            impl IntoQuietNan for $float {
528                #[inline]
529                fn into_quiet_nan(self) -> Option<Self> {
530                    const QUIET_BIT: $bits = $mask;
531                    if !self.is_nan() {
532                        return None;
533                    }
534                    Some(Self::from_bits(self.to_bits() | QUIET_BIT))
535                }
536            }
537        )*
538    };
539}
540#[cfg(feature = "std")]
541impl_into_quiet_nan! {
542    (f32, u32, 0x0040_0000);
543    (f64, u64, 0x0008_0000_0000_0000);
544}
545
546#[cfg(feature = "std")]
547macro_rules! impl_wasm_float {
548    ($ty:ty) => {
549        impl WasmFloatExt for $ty {
550            #[inline]
551            fn abs(self) -> Self {
552                self.abs()
553            }
554
555            #[inline]
556            fn ceil(self) -> Self {
557                if let Some(qnan) = self.into_quiet_nan() {
558                    return qnan;
559                }
560                self.ceil()
561            }
562
563            #[inline]
564            fn floor(self) -> Self {
565                if let Some(qnan) = self.into_quiet_nan() {
566                    return qnan;
567                }
568                self.floor()
569            }
570
571            #[inline]
572            fn trunc(self) -> Self {
573                if let Some(qnan) = self.into_quiet_nan() {
574                    return qnan;
575                }
576                self.trunc()
577            }
578
579            #[inline]
580            fn nearest(self) -> Self {
581                if let Some(qnan) = self.into_quiet_nan() {
582                    return qnan;
583                }
584                self.round_ties_even()
585            }
586
587            #[inline]
588            fn sqrt(self) -> Self {
589                if let Some(qnan) = self.into_quiet_nan() {
590                    return qnan;
591                }
592                self.sqrt()
593            }
594
595            #[inline]
596            fn copysign(self, other: Self) -> Self {
597                self.copysign(other)
598            }
599
600            #[inline]
601            #[cfg(feature = "simd")]
602            fn mul_add(self, a: Self, b: Self) -> Self {
603                self.mul_add(a, b)
604            }
605        }
606    };
607}
608impl_wasm_float!(f32);
609impl_wasm_float!(f64);
610
611#[cfg(test)]
612mod tests {
613    use super::*;
614
615    #[test]
616    fn wasm_float_min_regression_works() {
617        assert_eq!(Float::min(-0.0_f32, 0.0_f32).to_bits(), 0x8000_0000);
618        assert_eq!(Float::min(0.0_f32, -0.0_f32).to_bits(), 0x8000_0000);
619    }
620
621    #[test]
622    fn wasm_float_max_regression_works() {
623        assert_eq!(Float::max(-0.0_f32, 0.0_f32).to_bits(), 0x0000_0000);
624        assert_eq!(Float::max(0.0_f32, -0.0_f32).to_bits(), 0x0000_0000);
625    }
626
627    #[test]
628    fn copysign_regression_works() {
629        // This test has been directly extracted from a WebAssembly Specification assertion.
630        assert!(f32::from_bits(0xFFC00000).is_nan());
631        assert_eq!(
632            Float::copysign(f32::from_bits(0xFFC00000), f32::from_bits(0x0000_0000)).to_bits(),
633            0x7FC00000,
634        )
635    }
636}