From 0a0f8707c47cfcab6dd613fbd371287dc2cc6377 Mon Sep 17 00:00:00 2001 From: Julius Rakow Date: Sun, 26 Aug 2018 14:05:30 +0200 Subject: [PATCH] use libm for float math in no_std --- Cargo.toml | 3 ++- src/lib.rs | 3 +++ src/value.rs | 45 +++++++++++++++++++++++++++++++++------------ 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 02876b3..ba2f1c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ default = ["std"] std = ["parity-wasm/std", "byteorder/std"] # Enable for no_std support # hashmap_core only works on no_std -core = ["hashmap_core"] +core = ["hashmap_core", "libm"] [dependencies] parity-wasm = { version = "0.31", default-features = false } @@ -24,6 +24,7 @@ byteorder = { version = "1.0", default-features = false } hashmap_core = { version = "0.1.9", optional = true } memory_units = "0.3.0" nan-preserving-float = "0.1.0" +libm = { version = "0.1.2", optional = true } [dev-dependencies] assert_matches = "1.1" diff --git a/src/lib.rs b/src/lib.rs index 206c6e4..cea7c3b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -131,6 +131,9 @@ use core::fmt; #[cfg(feature = "std")] use std::error; +#[cfg(not(feature = "std"))] +extern crate libm; + /// Error type which can be thrown by wasm code or by host environment. /// /// Under some conditions, wasm execution may produce a `Trap`, which immediately aborts execution. diff --git a/src/value.rs b/src/value.rs index 26a5af1..359f8d8 100644 --- a/src/value.rs +++ b/src/value.rs @@ -746,27 +746,48 @@ impl_integer!(u32); impl_integer!(i64); impl_integer!(u64); +// Use std float functions in std environment. +// And libm's implementation in no_std +#[cfg(feature = "std")] +macro_rules! call_math { + ($op:ident, $e:expr, $fXX:ident, $FXXExt:ident) => { + $fXX::$op($e) + }; +} +#[cfg(not(feature = "std"))] +macro_rules! call_math { + ($op:ident, $e:expr, $fXX:ident, $FXXExt:ident) => { + ::libm::$FXXExt::$op($e) + }; +} + +// We cannot call the math functions directly, because there are multiple available implementaitons in no_std. +// In std, there are only `Value::$op` and `std::$fXX:$op`. +// The `std` ones are preferred, because they are not from a trait. +// For `no_std`, the implementations are `Value::$op` and `libm::FXXExt::$op`, +// both of which are trait implementations and hence ambiguous. +// So we have to use a full path, which is what `call_math!` does. macro_rules! impl_float { - ($type:ident, $fXX:ident, $iXX:ident) => { + ($type:ident, $fXX:ident, $FXXExt:ident, $iXX:ident) => { impl Float<$type> for $type { fn abs(self) -> $type { - $fXX::abs(self.into()).into() + call_math!(abs, $fXX::from(self), $fXX, $FXXExt).into() } fn floor(self) -> $type { - $fXX::floor(self.into()).into() + call_math!(floor, $fXX::from(self), $fXX, $FXXExt).into() } fn ceil(self) -> $type { - $fXX::ceil(self.into()).into() + call_math!(ceil, $fXX::from(self), $fXX, $FXXExt).into() } fn trunc(self) -> $type { - $fXX::trunc(self.into()).into() + call_math!(trunc, $fXX::from(self), $fXX, $FXXExt).into() } fn round(self) -> $type { - $fXX::round(self.into()).into() + call_math!(round, $fXX::from(self), $fXX, $FXXExt).into() } fn nearest(self) -> $type { let round = self.round(); - if self.fract().abs() != 0.5 { + if call_math!(fract, $fXX::from(self), $fXX, $FXXExt).abs() != 0.5 { return round; } @@ -780,7 +801,7 @@ macro_rules! impl_float { } } fn sqrt(self) -> $type { - $fXX::sqrt(self.into()).into() + call_math!(sqrt, $fXX::from(self), $fXX, $FXXExt).into() } // This instruction corresponds to what is sometimes called "minNaN" in other languages. fn min(self, other: $type) -> $type { @@ -828,7 +849,7 @@ macro_rules! impl_float { }; } -impl_float!(f32, f32, i32); -impl_float!(f64, f64, i64); -impl_float!(F32, f32, i32); -impl_float!(F64, f64, i64); +impl_float!(f32, f32, F32Ext, i32); +impl_float!(f64, f64, F64Ext, i64); +impl_float!(F32, f32, F32Ext, i32); +impl_float!(F64, f64, F64Ext, i64);