wasmi_core/
value.rs

1use crate::{
2    hint::unlikely,
3    nan_preserving_float::{F32, F64},
4    TrapCode,
5};
6
7/// Type of a value.
8///
9/// See [`Val`] for details.
10///
11/// [`Val`]: enum.Value.html
12#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
13pub enum ValType {
14    /// 32-bit signed or unsigned integer.
15    I32,
16    /// 64-bit signed or unsigned integer.
17    I64,
18    /// 32-bit IEEE 754-2008 floating point number.
19    F32,
20    /// 64-bit IEEE 754-2008 floating point number.
21    F64,
22    /// A nullable function reference.
23    FuncRef,
24    /// A nullable external reference.
25    ExternRef,
26}
27
28impl ValType {
29    /// Returns `true` if [`ValType`] is a Wasm numeric type.
30    ///
31    /// This is `true` for [`ValType::I32`], [`ValType::I64`],
32    /// [`ValType::F32`] and [`ValType::F64`].
33    pub fn is_num(&self) -> bool {
34        matches!(self, Self::I32 | Self::I64 | Self::F32 | Self::F64)
35    }
36
37    /// Returns `true` if [`ValType`] is a Wasm reference type.
38    ///
39    /// This is `true` for [`ValType::FuncRef`] and [`ValType::ExternRef`].
40    pub fn is_ref(&self) -> bool {
41        matches!(self, Self::ExternRef | Self::FuncRef)
42    }
43}
44
45/// Convert one type to another by wrapping.
46pub trait WrapInto<T> {
47    /// Convert one type to another by wrapping.
48    fn wrap_into(self) -> T;
49}
50
51/// Convert one type to another by rounding to the nearest integer towards zero.
52///
53/// # Errors
54///
55/// Traps when the input float cannot be represented by the target integer or
56/// when the input float is NaN.
57pub trait TryTruncateInto<T, E> {
58    /// Convert one type to another by rounding to the nearest integer towards zero.
59    ///
60    /// # Errors
61    ///
62    /// - If the input float value is NaN (not a number).
63    /// - If the input float value cannot be represented using the truncated
64    ///   integer type.
65    fn try_truncate_into(self) -> Result<T, E>;
66}
67
68/// Convert one type to another by rounding to the nearest integer towards zero.
69///
70/// # Note
71///
72/// This has saturating semantics for when the integer cannot represent the float.
73///
74/// Returns
75///
76/// - `0` when the input is NaN.
77/// - `int::MIN` when the input is -INF.
78/// - `int::MAX` when the input is +INF.
79pub trait TruncateSaturateInto<T> {
80    /// Convert one type to another by rounding to the nearest integer towards zero.
81    fn truncate_saturate_into(self) -> T;
82}
83
84/// Convert one type to another by extending with leading zeroes.
85pub trait ExtendInto<T> {
86    /// Convert one type to another by extending with leading zeroes.
87    fn extend_into(self) -> T;
88}
89
90/// Sign-extends `Self` integer type from `T` integer type.
91pub trait SignExtendFrom<T> {
92    /// Convert one type to another by extending with leading zeroes.
93    fn sign_extend_from(self) -> Self;
94}
95
96/// Allows to efficiently load bytes from `memory` into a buffer.
97pub trait LoadInto {
98    /// Loads bytes from `memory` into `self`.
99    ///
100    /// # Errors
101    ///
102    /// Traps if the `memory` access is out of bounds.
103    fn load_into(&mut self, memory: &[u8], address: usize) -> Result<(), TrapCode>;
104}
105
106impl<const N: usize> LoadInto for [u8; N] {
107    #[inline]
108    fn load_into(&mut self, memory: &[u8], address: usize) -> Result<(), TrapCode> {
109        let slice: &Self = memory
110            .get(address..)
111            .and_then(|slice| slice.get(..N))
112            .and_then(|slice| slice.try_into().ok())
113            .ok_or(TrapCode::MemoryOutOfBounds)?;
114        *self = *slice;
115        Ok(())
116    }
117}
118
119/// Allows to efficiently write bytes from a buffer into `memory`.
120pub trait StoreFrom {
121    /// Writes bytes from `self` to `memory`.
122    ///
123    /// # Errors
124    ///
125    /// Traps if the `memory` access is out of bounds.
126    fn store_from(&self, memory: &mut [u8], address: usize) -> Result<(), TrapCode>;
127}
128
129impl<const N: usize> StoreFrom for [u8; N] {
130    #[inline]
131    fn store_from(&self, memory: &mut [u8], address: usize) -> Result<(), TrapCode> {
132        let slice: &mut Self = memory
133            .get_mut(address..)
134            .and_then(|slice| slice.get_mut(..N))
135            .and_then(|slice| slice.try_into().ok())
136            .ok_or(TrapCode::MemoryOutOfBounds)?;
137        *slice = *self;
138        Ok(())
139    }
140}
141
142/// Types that can be converted from and to little endian bytes.
143pub trait LittleEndianConvert {
144    /// The little endian bytes representation.
145    type Bytes: Default + LoadInto + StoreFrom;
146
147    /// Converts `self` into little endian bytes.
148    fn into_le_bytes(self) -> Self::Bytes;
149
150    /// Converts little endian bytes into `Self`.
151    fn from_le_bytes(bytes: Self::Bytes) -> Self;
152}
153
154macro_rules! impl_little_endian_convert_primitive {
155    ( $($primitive:ty),* $(,)? ) => {
156        $(
157            impl LittleEndianConvert for $primitive {
158                type Bytes = [::core::primitive::u8; ::core::mem::size_of::<$primitive>()];
159
160                #[inline]
161                fn into_le_bytes(self) -> Self::Bytes {
162                    <$primitive>::to_le_bytes(self)
163                }
164
165                #[inline]
166                fn from_le_bytes(bytes: Self::Bytes) -> Self {
167                    <$primitive>::from_le_bytes(bytes)
168                }
169            }
170        )*
171    };
172}
173impl_little_endian_convert_primitive!(u8, u16, u32, u64, i8, i16, i32, i64, f32, f64);
174
175macro_rules! impl_little_endian_convert_float {
176    ( $( struct $float_ty:ident($uint_ty:ty); )* $(,)? ) => {
177        $(
178            impl LittleEndianConvert for $float_ty {
179                type Bytes = <$uint_ty as LittleEndianConvert>::Bytes;
180
181                #[inline]
182                fn into_le_bytes(self) -> Self::Bytes {
183                    <$uint_ty>::into_le_bytes(self.to_bits())
184                }
185
186                #[inline]
187                fn from_le_bytes(bytes: Self::Bytes) -> Self {
188                    Self::from_bits(<$uint_ty>::from_le_bytes(bytes))
189                }
190            }
191        )*
192    };
193}
194impl_little_endian_convert_float!(
195    struct F32(u32);
196    struct F64(u64);
197);
198
199/// Arithmetic operations.
200pub trait ArithmeticOps<T>: Copy {
201    /// Add two values.
202    fn add(self, other: T) -> T;
203    /// Subtract two values.
204    fn sub(self, other: T) -> T;
205    /// Multiply two values.
206    fn mul(self, other: T) -> T;
207}
208
209/// Integer value.
210pub trait Integer<T>: ArithmeticOps<T> {
211    /// Counts leading zeros in the bitwise representation of the value.
212    fn leading_zeros(self) -> T;
213    /// Counts trailing zeros in the bitwise representation of the value.
214    fn trailing_zeros(self) -> T;
215    /// Counts 1-bits in the bitwise representation of the value.
216    fn count_ones(self) -> T;
217    /// Get left bit rotation result.
218    fn rotl(self, other: T) -> T;
219    /// Get right bit rotation result.
220    fn rotr(self, other: T) -> T;
221    /// Divide two values.
222    ///
223    /// # Errors
224    ///
225    /// If `other` is equal to zero.
226    fn div(self, other: T) -> Result<T, TrapCode>;
227    /// Get division remainder.
228    ///
229    /// # Errors
230    ///
231    /// If `other` is equal to zero.
232    fn rem(self, other: T) -> Result<T, TrapCode>;
233}
234
235/// Float-point value.
236pub trait Float<T>: ArithmeticOps<T> {
237    /// Get absolute value.
238    fn abs(self) -> T;
239    /// Returns the largest integer less than or equal to a number.
240    fn floor(self) -> T;
241    /// Returns the smallest integer greater than or equal to a number.
242    fn ceil(self) -> T;
243    /// Returns the integer part of a number.
244    fn trunc(self) -> T;
245    /// Returns the nearest integer to a number. Ties are round to even number.
246    fn nearest(self) -> T;
247    /// Takes the square root of a number.
248    fn sqrt(self) -> T;
249    /// Returns the division of the two numbers.
250    fn div(self, other: T) -> T;
251    /// Returns the minimum of the two numbers.
252    fn min(self, other: T) -> T;
253    /// Returns the maximum of the two numbers.
254    fn max(self, other: T) -> T;
255    /// Sets sign of this value to the sign of other value.
256    fn copysign(self, other: T) -> T;
257}
258
259macro_rules! impl_wrap_into {
260    ($from:ident, $into:ident) => {
261        impl WrapInto<$into> for $from {
262            #[inline]
263            fn wrap_into(self) -> $into {
264                self as $into
265            }
266        }
267    };
268    ($from:ident, $intermediate:ident, $into:ident) => {
269        impl WrapInto<$into> for $from {
270            #[inline]
271            fn wrap_into(self) -> $into {
272                $into::from(self as $intermediate)
273            }
274        }
275    };
276}
277
278impl_wrap_into!(i32, i8);
279impl_wrap_into!(i32, i16);
280impl_wrap_into!(i64, i8);
281impl_wrap_into!(i64, i16);
282impl_wrap_into!(i64, i32);
283impl_wrap_into!(i64, f32, F32);
284impl_wrap_into!(u64, f32, F32);
285
286// Casting to self
287impl_wrap_into!(i32, i32);
288impl_wrap_into!(i64, i64);
289impl_wrap_into!(F32, F32);
290impl_wrap_into!(F64, F64);
291
292impl WrapInto<F32> for F64 {
293    #[inline]
294    fn wrap_into(self) -> F32 {
295        (f64::from(self) as f32).into()
296    }
297}
298
299macro_rules! impl_try_truncate_into {
300    (@primitive $from: ident, $into: ident, $to_primitive:path, $rmin:literal, $rmax:literal) => {
301        impl TryTruncateInto<$into, TrapCode> for $from {
302            #[inline]
303            fn try_truncate_into(self) -> Result<$into, TrapCode> {
304                if self.is_nan() {
305                    return Err(TrapCode::BadConversionToInteger);
306                }
307                if self <= $rmin || self >= $rmax {
308                    return Err(TrapCode::IntegerOverflow);
309                }
310                Ok(self as _)
311            }
312        }
313
314        impl TruncateSaturateInto<$into> for $from {
315            #[inline]
316            fn truncate_saturate_into(self) -> $into {
317                if self.is_nan() {
318                    return <$into as Default>::default();
319                }
320                if self.is_infinite() && self.is_sign_positive() {
321                    return <$into>::MAX;
322                }
323                if self.is_infinite() && self.is_sign_negative() {
324                    return <$into>::MIN;
325                }
326                self as _
327            }
328        }
329    };
330    (@wrapped $from:ident, $intermediate:ident, $into:ident) => {
331        impl TryTruncateInto<$into, TrapCode> for $from {
332            #[inline]
333            fn try_truncate_into(self) -> Result<$into, TrapCode> {
334                $intermediate::from(self).try_truncate_into()
335            }
336        }
337
338        impl TruncateSaturateInto<$into> for $from {
339            #[inline]
340            fn truncate_saturate_into(self) -> $into {
341                $intermediate::from(self).truncate_saturate_into()
342            }
343        }
344    };
345}
346
347impl_try_truncate_into!(@primitive f32, i32, num_traits::cast::ToPrimitive::to_i32, -2147483904.0_f32, 2147483648.0_f32);
348impl_try_truncate_into!(@primitive f32, u32, num_traits::cast::ToPrimitive::to_u32,          -1.0_f32, 4294967296.0_f32);
349impl_try_truncate_into!(@primitive f64, i32, num_traits::cast::ToPrimitive::to_i32, -2147483649.0_f64, 2147483648.0_f64);
350impl_try_truncate_into!(@primitive f64, u32, num_traits::cast::ToPrimitive::to_u32,          -1.0_f64, 4294967296.0_f64);
351impl_try_truncate_into!(@primitive f32, i64, num_traits::cast::ToPrimitive::to_i64, -9223373136366403584.0_f32,  9223372036854775808.0_f32);
352impl_try_truncate_into!(@primitive f32, u64, num_traits::cast::ToPrimitive::to_u64,                   -1.0_f32, 18446744073709551616.0_f32);
353impl_try_truncate_into!(@primitive f64, i64, num_traits::cast::ToPrimitive::to_i64, -9223372036854777856.0_f64,  9223372036854775808.0_f64);
354impl_try_truncate_into!(@primitive f64, u64, num_traits::cast::ToPrimitive::to_u64,                   -1.0_f64, 18446744073709551616.0_f64);
355impl_try_truncate_into!(@wrapped F32, f32, i32);
356impl_try_truncate_into!(@wrapped F32, f32, i64);
357impl_try_truncate_into!(@wrapped F64, f64, i32);
358impl_try_truncate_into!(@wrapped F64, f64, i64);
359impl_try_truncate_into!(@wrapped F32, f32, u32);
360impl_try_truncate_into!(@wrapped F32, f32, u64);
361impl_try_truncate_into!(@wrapped F64, f64, u32);
362impl_try_truncate_into!(@wrapped F64, f64, u64);
363
364macro_rules! impl_extend_into {
365    ($from:ident, $into:ident) => {
366        impl ExtendInto<$into> for $from {
367            #[inline]
368            #[allow(clippy::cast_lossless)]
369            fn extend_into(self) -> $into {
370                self as $into
371            }
372        }
373    };
374    ($from:ident, $intermediate:ident, $into:ident) => {
375        impl ExtendInto<$into> for $from {
376            #[inline]
377            #[allow(clippy::cast_lossless)]
378            fn extend_into(self) -> $into {
379                $into::from(self as $intermediate)
380            }
381        }
382    };
383}
384
385impl_extend_into!(i8, i32);
386impl_extend_into!(u8, i32);
387impl_extend_into!(i16, i32);
388impl_extend_into!(u16, i32);
389impl_extend_into!(i8, i64);
390impl_extend_into!(u8, i64);
391impl_extend_into!(i16, i64);
392impl_extend_into!(u16, i64);
393impl_extend_into!(i32, i64);
394impl_extend_into!(u32, i64);
395impl_extend_into!(u32, u64);
396
397impl_extend_into!(i32, f32, F32);
398impl_extend_into!(i32, f64, F64);
399impl_extend_into!(u32, f32, F32);
400impl_extend_into!(u32, f64, F64);
401impl_extend_into!(i64, f64, F64);
402impl_extend_into!(u64, f64, F64);
403impl_extend_into!(f32, f64, F64);
404
405// Casting to self
406impl_extend_into!(i32, i32);
407impl_extend_into!(i64, i64);
408impl_extend_into!(F32, F32);
409impl_extend_into!(F64, F64);
410
411impl ExtendInto<F64> for F32 {
412    #[inline]
413    fn extend_into(self) -> F64 {
414        F64::from(f64::from(f32::from(self)))
415    }
416}
417
418macro_rules! impl_sign_extend_from {
419    ( $( impl SignExtendFrom<$from_type:ty> for $for_type:ty; )* ) => {
420        $(
421            impl SignExtendFrom<$from_type> for $for_type {
422                #[inline]
423                #[allow(clippy::cast_lossless)]
424                fn sign_extend_from(self) -> Self {
425                    (self as $from_type) as Self
426                }
427            }
428        )*
429    };
430}
431impl_sign_extend_from! {
432    impl SignExtendFrom<i8> for i32;
433    impl SignExtendFrom<i16> for i32;
434    impl SignExtendFrom<i8> for i64;
435    impl SignExtendFrom<i16> for i64;
436    impl SignExtendFrom<i32> for i64;
437}
438
439macro_rules! impl_integer_arithmetic_ops {
440    ($type: ident) => {
441        impl ArithmeticOps<$type> for $type {
442            #[inline]
443            fn add(self, other: $type) -> $type {
444                self.wrapping_add(other)
445            }
446            #[inline]
447            fn sub(self, other: $type) -> $type {
448                self.wrapping_sub(other)
449            }
450            #[inline]
451            fn mul(self, other: $type) -> $type {
452                self.wrapping_mul(other)
453            }
454        }
455    };
456}
457
458impl_integer_arithmetic_ops!(i32);
459impl_integer_arithmetic_ops!(u32);
460impl_integer_arithmetic_ops!(i64);
461impl_integer_arithmetic_ops!(u64);
462
463macro_rules! impl_float_arithmetic_ops {
464    ($type:ty) => {
465        impl ArithmeticOps<Self> for $type {
466            #[inline]
467            fn add(self, other: Self) -> Self {
468                self + other
469            }
470            #[inline]
471            fn sub(self, other: Self) -> Self {
472                self - other
473            }
474            #[inline]
475            fn mul(self, other: Self) -> Self {
476                self * other
477            }
478        }
479    };
480}
481
482impl_float_arithmetic_ops!(f32);
483impl_float_arithmetic_ops!(f64);
484impl_float_arithmetic_ops!(F32);
485impl_float_arithmetic_ops!(F64);
486
487macro_rules! impl_integer {
488    ($type:ty) => {
489        impl Integer<Self> for $type {
490            #[inline]
491            #[allow(clippy::cast_lossless)]
492            fn leading_zeros(self) -> Self {
493                self.leading_zeros() as _
494            }
495            #[inline]
496            #[allow(clippy::cast_lossless)]
497            fn trailing_zeros(self) -> Self {
498                self.trailing_zeros() as _
499            }
500            #[inline]
501            #[allow(clippy::cast_lossless)]
502            fn count_ones(self) -> Self {
503                self.count_ones() as _
504            }
505            #[inline]
506            fn rotl(self, other: Self) -> Self {
507                self.rotate_left(other as u32)
508            }
509            #[inline]
510            fn rotr(self, other: Self) -> Self {
511                self.rotate_right(other as u32)
512            }
513            #[inline]
514            fn div(self, other: Self) -> Result<Self, TrapCode> {
515                if unlikely(other == 0) {
516                    return Err(TrapCode::IntegerDivisionByZero);
517                }
518                let (result, overflow) = self.overflowing_div(other);
519                if unlikely(overflow) {
520                    return Err(TrapCode::IntegerOverflow);
521                }
522                Ok(result)
523            }
524            #[inline]
525            fn rem(self, other: Self) -> Result<Self, TrapCode> {
526                if unlikely(other == 0) {
527                    return Err(TrapCode::IntegerDivisionByZero);
528                }
529                Ok(self.wrapping_rem(other))
530            }
531        }
532    };
533}
534impl_integer!(i32);
535impl_integer!(u32);
536impl_integer!(i64);
537impl_integer!(u64);
538
539// We cannot call the math functions directly, because they are not all available in `core`.
540// In no-std cases we instead rely on `libm`.
541// These wrappers handle that delegation.
542macro_rules! impl_float {
543    (type $type:ident as $repr:ty) => {
544        // In this particular instance we want to directly compare floating point numbers.
545        impl Float<Self> for $type {
546            #[inline]
547            fn abs(self) -> Self {
548                WasmFloatExt::abs(<$repr>::from(self)).into()
549            }
550            #[inline]
551            fn floor(self) -> Self {
552                WasmFloatExt::floor(<$repr>::from(self)).into()
553            }
554            #[inline]
555            fn ceil(self) -> Self {
556                WasmFloatExt::ceil(<$repr>::from(self)).into()
557            }
558            #[inline]
559            fn trunc(self) -> Self {
560                WasmFloatExt::trunc(<$repr>::from(self)).into()
561            }
562            #[inline]
563            fn nearest(self) -> Self {
564                WasmFloatExt::nearest(<$repr>::from(self)).into()
565            }
566            #[inline]
567            fn sqrt(self) -> Self {
568                WasmFloatExt::sqrt(<$repr>::from(self)).into()
569            }
570            #[inline]
571            fn div(self, other: Self) -> Self {
572                self / other
573            }
574            #[inline]
575            fn min(self, other: Self) -> Self {
576                // Note: equal to the unstable `f32::minimum` method.
577                //
578                // Once `f32::minimum` is stable we can simply use it here.
579                if self < other {
580                    self
581                } else if other < self {
582                    other
583                } else if self == other {
584                    if <$repr>::is_sign_negative(<$repr>::from(self))
585                        && <$repr>::is_sign_positive(<$repr>::from(other))
586                    {
587                        self
588                    } else {
589                        other
590                    }
591                } else {
592                    // At least one input is NaN. Use `+` to perform NaN propagation and quieting.
593                    self + other
594                }
595            }
596            #[inline]
597            fn max(self, other: Self) -> Self {
598                // Note: equal to the unstable `f32::maximum` method.
599                //
600                // Once `f32::maximum` is stable we can simply use it here.
601                if self > other {
602                    self
603                } else if other > self {
604                    other
605                } else if self == other {
606                    if <$repr>::is_sign_positive(<$repr>::from(self))
607                        && <$repr>::is_sign_negative(<$repr>::from(other))
608                    {
609                        self
610                    } else {
611                        other
612                    }
613                } else {
614                    // At least one input is NaN. Use `+` to perform NaN propagation and quieting.
615                    self + other
616                }
617            }
618            #[inline]
619            fn copysign(self, other: Self) -> Self {
620                WasmFloatExt::copysign(<$repr>::from(self), <$repr>::from(other)).into()
621            }
622        }
623    };
624}
625impl_float!( type F32 as f32 );
626impl_float!( type F64 as f64 );
627
628/// Low-level Wasm float interface to support `no_std` environments.
629///
630/// # Dev. Note
631///
632/// The problem is that in `no_std` builds the Rust standard library
633/// does not specify all of the below methods for `f32` and `f64`.
634/// Thus this trait serves as an adapter to import this functionality
635/// via `libm`.
636trait WasmFloatExt {
637    /// Equivalent to the Wasm `{f32,f64}.abs` instructions.
638    fn abs(self) -> Self;
639    /// Equivalent to the Wasm `{f32,f64}.ceil` instructions.
640    fn ceil(self) -> Self;
641    /// Equivalent to the Wasm `{f32,f64}.floor` instructions.
642    fn floor(self) -> Self;
643    /// Equivalent to the Wasm `{f32,f64}.trunc` instructions.
644    fn trunc(self) -> Self;
645    /// Equivalent to the Wasm `{f32,f64}.sqrt` instructions.
646    fn sqrt(self) -> Self;
647    /// Equivalent to the Wasm `{f32,f64}.nearest` instructions.
648    fn nearest(self) -> Self;
649    /// Equivalent to the Wasm `{f32,f64}.copysign` instructions.
650    fn copysign(self, other: Self) -> Self;
651}
652
653#[cfg(not(feature = "std"))]
654macro_rules! impl_wasm_float {
655    ($ty:ty) => {
656        impl WasmFloatExt for $ty {
657            #[inline]
658            fn abs(self) -> Self {
659                <libm::Libm<Self>>::fabs(self)
660            }
661
662            #[inline]
663            fn ceil(self) -> Self {
664                <libm::Libm<Self>>::ceil(self)
665            }
666
667            #[inline]
668            fn floor(self) -> Self {
669                <libm::Libm<Self>>::floor(self)
670            }
671
672            #[inline]
673            fn trunc(self) -> Self {
674                <libm::Libm<Self>>::trunc(self)
675            }
676
677            #[inline]
678            fn nearest(self) -> Self {
679                let round = <libm::Libm<Self>>::round(self);
680                if <Self as WasmFloatExt>::abs(self - <Self as WasmFloatExt>::trunc(self)) != 0.5 {
681                    return round;
682                }
683                let rem = round % 2.0;
684                if rem == 1.0 {
685                    <Self as WasmFloatExt>::floor(self)
686                } else if rem == -1.0 {
687                    <Self as WasmFloatExt>::ceil(self)
688                } else {
689                    round
690                }
691            }
692
693            #[inline]
694            fn sqrt(self) -> Self {
695                <libm::Libm<Self>>::sqrt(self)
696            }
697
698            #[inline]
699            fn copysign(self, other: Self) -> Self {
700                <libm::Libm<Self>>::copysign(self, other)
701            }
702        }
703    };
704}
705
706#[cfg(feature = "std")]
707macro_rules! impl_wasm_float {
708    ($ty:ty) => {
709        impl WasmFloatExt for $ty {
710            #[inline]
711            fn abs(self) -> Self {
712                self.abs()
713            }
714
715            #[inline]
716            fn ceil(self) -> Self {
717                self.ceil()
718            }
719
720            #[inline]
721            fn floor(self) -> Self {
722                self.floor()
723            }
724
725            #[inline]
726            fn trunc(self) -> Self {
727                self.trunc()
728            }
729
730            #[inline]
731            fn nearest(self) -> Self {
732                self.round_ties_even()
733            }
734
735            #[inline]
736            fn sqrt(self) -> Self {
737                self.sqrt()
738            }
739
740            #[inline]
741            fn copysign(self, other: Self) -> Self {
742                self.copysign(other)
743            }
744        }
745    };
746}
747
748impl_wasm_float!(f32);
749impl_wasm_float!(f64);
750
751#[cfg(test)]
752mod tests {
753    use super::*;
754
755    #[test]
756    fn wasm_float_min_regression_works() {
757        assert_eq!(
758            Float::min(F32::from(-0.0), F32::from(0.0)).to_bits(),
759            0x8000_0000,
760        );
761        assert_eq!(
762            Float::min(F32::from(0.0), F32::from(-0.0)).to_bits(),
763            0x8000_0000,
764        );
765    }
766
767    #[test]
768    fn wasm_float_max_regression_works() {
769        assert_eq!(
770            Float::max(F32::from(-0.0), F32::from(0.0)).to_bits(),
771            0x0000_0000,
772        );
773        assert_eq!(
774            Float::max(F32::from(0.0), F32::from(-0.0)).to_bits(),
775            0x0000_0000,
776        );
777    }
778
779    #[test]
780    fn copysign_regression_works() {
781        // This test has been directly extracted from a WebAssembly Specification assertion.
782        use Float as _;
783        assert!(F32::from_bits(0xFFC00000).is_nan());
784        assert_eq!(
785            F32::from_bits(0xFFC00000)
786                .copysign(F32::from_bits(0x0000_0000))
787                .to_bits(),
788            F32::from_bits(0x7FC00000).to_bits()
789        )
790    }
791}