1use crate::{hint::unlikely, TrapCode};
2
3#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
9pub enum ValType {
10 I32,
12 I64,
14 F32,
16 F64,
18 V128,
20 FuncRef,
22 ExternRef,
24}
25
26impl ValType {
27 pub fn is_num(&self) -> bool {
32 matches!(self, Self::I32 | Self::I64 | Self::F32 | Self::F64)
33 }
34
35 pub fn is_ref(&self) -> bool {
39 matches!(self, Self::ExternRef | Self::FuncRef)
40 }
41}
42
43pub trait TryTruncateInto<T, E> {
50 fn try_truncate_into(self) -> Result<T, E>;
58}
59
60pub trait TruncateSaturateInto<T> {
72 fn truncate_saturate_into(self) -> T;
74}
75
76pub trait SignExtendFrom<T> {
78 fn sign_extend_from(self) -> Self;
80}
81
82pub trait Integer: Sized + Unsigned {
84 #[allow(clippy::wrong_self_convention)]
86 fn is_zero(self) -> bool;
87 fn leading_zeros(self) -> Self;
89 fn trailing_zeros(self) -> Self;
91 fn count_ones(self) -> Self;
93 fn shl(lhs: Self, rhs: Self) -> Self;
95 fn shr_s(lhs: Self, rhs: Self) -> Self;
97 fn shr_u(lhs: Self, rhs: Self) -> Self;
99 fn rotl(lhs: Self, rhs: Self) -> Self;
101 fn rotr(lhs: Self, rhs: Self) -> Self;
103 fn div_s(lhs: Self, rhs: Self) -> Result<Self, TrapCode>;
109 fn div_u(lhs: Self::Uint, rhs: Self::Uint) -> Result<Self::Uint, TrapCode>;
115 fn rem_s(lhs: Self, rhs: Self) -> Result<Self, TrapCode>;
121 fn rem_u(lhs: Self::Uint, rhs: Self::Uint) -> Result<Self::Uint, TrapCode>;
127}
128
129pub trait Unsigned {
131 type Uint;
133
134 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
154pub trait Float: Sized {
156 fn abs(self) -> Self;
158 fn floor(self) -> Self;
160 fn ceil(self) -> Self;
162 fn trunc(self) -> Self;
164 fn nearest(self) -> Self;
166 fn sqrt(self) -> Self;
168 fn min(lhs: Self, rhs: Self) -> Self;
170 fn max(lhs: Self, rhs: Self) -> Self;
172 fn copysign(lhs: Self, rhs: Self) -> Self;
174 #[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
326macro_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 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 lhs + rhs
374 }
375 }
376 #[inline]
377 fn max(lhs: Self, rhs: Self) -> Self {
378 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 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
411trait WasmFloatExt {
420 fn abs(self) -> Self;
422 fn ceil(self) -> Self;
424 fn floor(self) -> Self;
426 fn trunc(self) -> Self;
428 fn sqrt(self) -> Self;
430 fn nearest(self) -> Self;
432 fn copysign(self, other: Self) -> Self;
434 #[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#[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 pub fn as_u128(&self) -> u128 {
512 u128::from_ne_bytes(self.0)
513 }
514}
515
516#[cfg(feature = "std")]
518trait IntoQuietNan: Sized {
519 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 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}