Skip to content

Commit e6134c7

Browse files
authored
Upgrade libm and make no-std config testable (#241)
* Make everything build and test without 'std' feature Signed-off-by: Brian Anderson <andersrb@gmail.com> * Use faster constructor in vec_bytebuf. `vec![0; len]` uses a specialized intrinsic codepath, which, at least in debug mode, is extremely faster than `resize`. This makes the `alloc` test pass instantaneously instead of taking minutes. Signed-off-by: Brian Anderson <andersrb@gmail.com> * Fix a comment about core+mmap Signed-off-by: Brian Anderson <andersrb@gmail.com> * Don't use traits to call libm functions These no longer exist in the latest revision of libm. Signed-off-by: Brian Anderson <andersrb@gmail.com> * Upgrade libm to 0.2.1 Fixes #240 Signed-off-by: Brian Anderson <andersrb@gmail.com> * Remove warning from readme about debug-mode panics from libm Signed-off-by: Brian Anderson <andersrb@gmail.com> * Test no-std configuration under travis Signed-off-by: Brian Anderson <andersrb@gmail.com> * rustfmt Signed-off-by: Brian Anderson <andersrb@gmail.com>
1 parent edce5aa commit e6134c7

File tree

12 files changed

+127
-39
lines changed

12 files changed

+127
-39
lines changed

.travis.yml

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ script:
3030
# Check that `vec_memory` feature works.
3131
- cargo check --features vec_memory
3232
- travis_wait 60 ./test.sh
33+
- TEST_NO_STD=1 travis_wait 60 ./test.sh
3334
- ./doc.sh
3435

3536
after_success: |

Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ exclude = [ "/res/*", "/tests/*", "/fuzz/*", "/benches/*" ]
1515
validation = { package = "wasmi-validation", version = "0.3", path = "validation", default-features = false }
1616
parity-wasm = { version = "0.41.0", default-features = false }
1717
memory_units = "0.3.0"
18-
libm = { version = "0.1.2", optional = true }
18+
libm = { version = "0.2.1", optional = true }
1919
num-rational = { version = "0.2.2", default-features = false }
2020
num-traits = { version = "0.2.8", default-features = false }
2121
libc = { version = "0.2.58", optional = true}
@@ -41,7 +41,7 @@ std = [
4141
]
4242
# Enable for no_std support
4343
core = [
44-
# `core` doesn't support vec_memory
44+
# `core` doesn't support mmaped memory
4545
"vec_memory",
4646
"validation/core",
4747
"libm"

README.md

-3
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,6 @@ wasmi = {
3535

3636
When the `core` feature is enabled, code related to `std::error` is disabled.
3737

38-
Floating point operations in `no_std` use [`libm`](https://crates.io/crates/libm), which sometimes panics in debug mode (https://github.com/japaric/libm/issues/4).
39-
So make sure to either use release builds or avoid WASM with floating point operations, for example by using [`deny_floating_point`](https://docs.rs/wasmi/0.4.0/wasmi/struct.Module.html#method.deny_floating_point).
40-
4138
# License
4239

4340
`wasmi` is primarily distributed under the terms of both the MIT

examples/invoke.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ extern crate wasmi;
33

44
use std::env::args;
55

6-
use parity_wasm::elements::{External, FunctionType, Internal, Type, ValueType};
6+
use parity_wasm::elements::{External, FunctionType, Internal, Module, Type, ValueType};
77
use wasmi::{ImportsBuilder, ModuleInstance, NopExternals, RuntimeValue};
88

99
fn main() {
@@ -15,7 +15,7 @@ fn main() {
1515
let func_name = &args[2];
1616
let (_, program_args) = args.split_at(3);
1717

18-
let module = parity_wasm::deserialize_file(&args[1]).expect("File to be deserialized");
18+
let module = load_module(&args[1]);
1919

2020
// Extracts call arguments from command-line arguments
2121
let args = {
@@ -118,3 +118,14 @@ fn main() {
118118
.expect("")
119119
);
120120
}
121+
122+
#[cfg(feature = "std")]
123+
fn load_module(file: &str) -> Module {
124+
parity_wasm::deserialize_file(file).expect("File to be deserialized")
125+
}
126+
127+
#[cfg(not(feature = "std"))]
128+
fn load_module(file: &str) -> Module {
129+
let mut buf = std::fs::read(file).expect("Read file");
130+
parity_wasm::deserialize_buffer(&mut buf).expect("Deserialize module")
131+
}

src/memory/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ mod tests {
542542
use super::{MemoryInstance, MemoryRef, LINEAR_MEMORY_PAGE_SIZE};
543543
use crate::memory_units::Pages;
544544
use crate::Error;
545-
use std::rc::Rc;
545+
use alloc::rc::Rc;
546546

547547
#[test]
548548
fn alloc() {

src/memory/vec_bytebuf.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ pub struct ByteBuf {
88

99
impl ByteBuf {
1010
pub fn new(len: usize) -> Result<Self, String> {
11-
let mut buf = Vec::new();
12-
buf.resize(len, 0u8);
11+
let buf = vec![0; len];
1312
Ok(Self { buf })
1413
}
1514

src/prepare/tests.rs

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
// Test-only code importing std for no-std testing
2+
extern crate std;
3+
4+
use alloc::vec::Vec;
5+
use std::println;
6+
17
use super::{compile_module, CompiledModule};
28
use crate::isa;
39
use parity_wasm::{deserialize_buffer, elements::Module};

src/tests/host.rs

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// Test-only code importing std for no-std testing
2+
extern crate std;
3+
14
use super::parse_wat;
25
use crate::memory_units::Pages;
36
use crate::types::ValueType;
@@ -6,6 +9,8 @@ use crate::{
69
MemoryInstance, MemoryRef, ModuleImportResolver, ModuleInstance, ModuleRef, ResumableError,
710
RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef, Trap, TrapKind,
811
};
12+
use alloc::boxed::Box;
13+
use std::println;
914

1015
#[derive(Debug, Clone, PartialEq)]
1116
struct HostErrorWithCode {

src/tests/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ use super::Error;
88

99
fn assert_send<T: Send>() {}
1010
fn assert_sync<T: Sync>() {}
11+
#[cfg(feature = "std")]
1112
fn assert_std_err_impl<T: ::std::error::Error>() {}
13+
#[cfg(not(feature = "std"))]
14+
fn assert_std_err_impl<T>() {}
1215

1316
#[test]
1417
fn assert_error_properties() {

src/tests/wasm.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
// Test-only code importing std for no-std testing
2+
extern crate std;
3+
14
use crate::memory_units::Pages;
25
use crate::{
36
Error, FuncRef, GlobalDescriptor, GlobalInstance, GlobalRef, ImportsBuilder, MemoryDescriptor,
47
MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance, NopExternals,
58
RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef,
69
};
10+
use alloc::vec::Vec;
711
use std::fs::File;
812

913
struct Env {

src/value.rs

+85-28
Original file line numberDiff line numberDiff line change
@@ -828,48 +828,42 @@ impl_integer!(u32);
828828
impl_integer!(i64);
829829
impl_integer!(u64);
830830

831-
// Use std float functions in std environment.
832-
// And libm's implementation in no_std
833831
#[cfg(feature = "std")]
834-
macro_rules! call_math {
835-
($op:ident, $e:expr, $fXX:ident, $FXXExt:ident) => {
836-
$fXX::$op($e)
837-
};
832+
mod fmath {
833+
pub use f32;
834+
pub use f64;
838835
}
836+
839837
#[cfg(not(feature = "std"))]
840-
macro_rules! call_math {
841-
($op:ident, $e:expr, $fXX:ident, $FXXExt:ident) => {
842-
::libm::$FXXExt::$op($e)
843-
};
838+
mod fmath {
839+
pub use super::libm_adapters::f32;
840+
pub use super::libm_adapters::f64;
844841
}
845842

846-
// We cannot call the math functions directly, because there are multiple available implementaitons in no_std.
847-
// In std, there are only `Value::$op` and `std::$fXX:$op`.
848-
// The `std` ones are preferred, because they are not from a trait.
849-
// For `no_std`, the implementations are `Value::$op` and `libm::FXXExt::$op`,
850-
// both of which are trait implementations and hence ambiguous.
851-
// So we have to use a full path, which is what `call_math!` does.
843+
// We cannot call the math functions directly, because they are not all available in `core`.
844+
// In no-std cases we instead rely on `libm`.
845+
// These wrappers handle that delegation.
852846
macro_rules! impl_float {
853-
($type:ident, $fXX:ident, $FXXExt:ident, $iXX:ident) => {
847+
($type:ident, $fXX:ident, $iXX:ident) => {
854848
impl Float<$type> for $type {
855849
fn abs(self) -> $type {
856-
call_math!(abs, $fXX::from(self), $fXX, $FXXExt).into()
850+
fmath::$fXX::abs($fXX::from(self)).into()
857851
}
858852
fn floor(self) -> $type {
859-
call_math!(floor, $fXX::from(self), $fXX, $FXXExt).into()
853+
fmath::$fXX::floor($fXX::from(self)).into()
860854
}
861855
fn ceil(self) -> $type {
862-
call_math!(ceil, $fXX::from(self), $fXX, $FXXExt).into()
856+
fmath::$fXX::ceil($fXX::from(self)).into()
863857
}
864858
fn trunc(self) -> $type {
865-
call_math!(trunc, $fXX::from(self), $fXX, $FXXExt).into()
859+
fmath::$fXX::trunc($fXX::from(self)).into()
866860
}
867861
fn round(self) -> $type {
868-
call_math!(round, $fXX::from(self), $fXX, $FXXExt).into()
862+
fmath::$fXX::round($fXX::from(self)).into()
869863
}
870864
fn nearest(self) -> $type {
871865
let round = self.round();
872-
if call_math!(fract, $fXX::from(self), $fXX, $FXXExt).abs() != 0.5 {
866+
if fmath::$fXX::fract($fXX::from(self)).abs() != 0.5 {
873867
return round;
874868
}
875869

@@ -883,7 +877,7 @@ macro_rules! impl_float {
883877
}
884878
}
885879
fn sqrt(self) -> $type {
886-
call_math!(sqrt, $fXX::from(self), $fXX, $FXXExt).into()
880+
fmath::$fXX::sqrt($fXX::from(self)).into()
887881
}
888882
// This instruction corresponds to what is sometimes called "minNaN" in other languages.
889883
fn min(self, other: $type) -> $type {
@@ -931,7 +925,70 @@ macro_rules! impl_float {
931925
};
932926
}
933927

934-
impl_float!(f32, f32, F32Ext, i32);
935-
impl_float!(f64, f64, F64Ext, i64);
936-
impl_float!(F32, f32, F32Ext, i32);
937-
impl_float!(F64, f64, F64Ext, i64);
928+
impl_float!(f32, f32, i32);
929+
impl_float!(f64, f64, i64);
930+
impl_float!(F32, f32, i32);
931+
impl_float!(F64, f64, i64);
932+
933+
#[cfg(not(feature = "std"))]
934+
mod libm_adapters {
935+
pub mod f32 {
936+
pub fn abs(v: f32) -> f32 {
937+
libm::fabsf(v)
938+
}
939+
940+
pub fn floor(v: f32) -> f32 {
941+
libm::floorf(v)
942+
}
943+
944+
pub fn ceil(v: f32) -> f32 {
945+
libm::ceilf(v)
946+
}
947+
948+
pub fn trunc(v: f32) -> f32 {
949+
libm::truncf(v)
950+
}
951+
952+
pub fn round(v: f32) -> f32 {
953+
libm::roundf(v)
954+
}
955+
956+
pub fn fract(v: f32) -> f32 {
957+
v - trunc(v)
958+
}
959+
960+
pub fn sqrt(v: f32) -> f32 {
961+
libm::sqrtf(v)
962+
}
963+
}
964+
965+
pub mod f64 {
966+
pub fn abs(v: f64) -> f64 {
967+
libm::fabs(v)
968+
}
969+
970+
pub fn floor(v: f64) -> f64 {
971+
libm::floor(v)
972+
}
973+
974+
pub fn ceil(v: f64) -> f64 {
975+
libm::ceil(v)
976+
}
977+
978+
pub fn trunc(v: f64) -> f64 {
979+
libm::trunc(v)
980+
}
981+
982+
pub fn round(v: f64) -> f64 {
983+
libm::round(v)
984+
}
985+
986+
pub fn fract(v: f64) -> f64 {
987+
v - trunc(v)
988+
}
989+
990+
pub fn sqrt(v: f64) -> f64 {
991+
libm::sqrt(v)
992+
}
993+
}
994+
}

test.sh

+6-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
set -eux
44

55
EXTRA_ARGS=""
6+
NO_STD_ARGS=""
67

78
if [ -n "${TARGET-}" ]; then
89
# Tests build in debug mode are prohibitively
@@ -12,8 +13,12 @@ if [ -n "${TARGET-}" ]; then
1213
export RUSTFLAGS="--cfg debug_assertions"
1314
fi
1415

16+
if [ -n "${TEST_NO_STD-}" ]; then
17+
NO_STD_ARGS="--no-default-features --features=core"
18+
fi
19+
1520
cd $(dirname $0)
1621

17-
time cargo test --all ${EXTRA_ARGS}
22+
time cargo test --all ${EXTRA_ARGS} ${NO_STD_ARGS}
1823

1924
cd -

0 commit comments

Comments
 (0)