1use crate::{
2 hint::unlikely,
3 nan_preserving_float::{F32, F64},
4 TrapCode,
5};
6
7#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
13pub enum ValType {
14 I32,
16 I64,
18 F32,
20 F64,
22 FuncRef,
24 ExternRef,
26}
27
28impl ValType {
29 pub fn is_num(&self) -> bool {
34 matches!(self, Self::I32 | Self::I64 | Self::F32 | Self::F64)
35 }
36
37 pub fn is_ref(&self) -> bool {
41 matches!(self, Self::ExternRef | Self::FuncRef)
42 }
43}
44
45pub trait WrapInto<T> {
47 fn wrap_into(self) -> T;
49}
50
51pub trait TryTruncateInto<T, E> {
58 fn try_truncate_into(self) -> Result<T, E>;
66}
67
68pub trait TruncateSaturateInto<T> {
80 fn truncate_saturate_into(self) -> T;
82}
83
84pub trait ExtendInto<T> {
86 fn extend_into(self) -> T;
88}
89
90pub trait SignExtendFrom<T> {
92 fn sign_extend_from(self) -> Self;
94}
95
96pub trait LoadInto {
98 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
119pub trait StoreFrom {
121 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
142pub trait LittleEndianConvert {
144 type Bytes: Default + LoadInto + StoreFrom;
146
147 fn into_le_bytes(self) -> Self::Bytes;
149
150 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
199pub trait ArithmeticOps<T>: Copy {
201 fn add(self, other: T) -> T;
203 fn sub(self, other: T) -> T;
205 fn mul(self, other: T) -> T;
207}
208
209pub trait Integer<T>: ArithmeticOps<T> {
211 fn leading_zeros(self) -> T;
213 fn trailing_zeros(self) -> T;
215 fn count_ones(self) -> T;
217 fn rotl(self, other: T) -> T;
219 fn rotr(self, other: T) -> T;
221 fn div(self, other: T) -> Result<T, TrapCode>;
227 fn rem(self, other: T) -> Result<T, TrapCode>;
233}
234
235pub trait Float<T>: ArithmeticOps<T> {
237 fn abs(self) -> T;
239 fn floor(self) -> T;
241 fn ceil(self) -> T;
243 fn trunc(self) -> T;
245 fn nearest(self) -> T;
247 fn sqrt(self) -> T;
249 fn div(self, other: T) -> T;
251 fn min(self, other: T) -> T;
253 fn max(self, other: T) -> T;
255 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
286impl_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
405impl_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
539macro_rules! impl_float {
543 (type $type:ident as $repr:ty) => {
544 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 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 self + other
594 }
595 }
596 #[inline]
597 fn max(self, other: Self) -> Self {
598 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 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
628trait WasmFloatExt {
637 fn abs(self) -> Self;
639 fn ceil(self) -> Self;
641 fn floor(self) -> Self;
643 fn trunc(self) -> Self;
645 fn sqrt(self) -> Self;
647 fn nearest(self) -> Self;
649 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 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}