From 704f96e2e710d9bc4269ea6f9ce20ad1e1429268 Mon Sep 17 00:00:00 2001 From: Julius Rakow Date: Fri, 24 Aug 2018 23:47:20 +0200 Subject: [PATCH 01/17] add default-enabled std feature --- Cargo.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 3938432ddf..d5d67005af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,11 @@ description = "WebAssembly interpreter" keywords = ["wasm", "webassembly", "bytecode", "interpreter"] exclude = [ "/res/*", "/tests/*", "/fuzz/*", "/benches/*" ] +[features] +default = ["std"] +# Disable for no_std support +std = [] + [dependencies] parity-wasm = "0.31" byteorder = "1.0" From 606c006e1c69f26e494048bf6e5142342636b654 Mon Sep 17 00:00:00 2001 From: Julius Rakow Date: Fri, 24 Aug 2018 23:58:44 +0200 Subject: [PATCH 02/17] use parity-wasm/std feature only if std is enabled --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d5d67005af..63ea9c9fa7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,10 +13,10 @@ exclude = [ "/res/*", "/tests/*", "/fuzz/*", "/benches/*" ] [features] default = ["std"] # Disable for no_std support -std = [] +std = ["parity-wasm/std"] [dependencies] -parity-wasm = "0.31" +parity-wasm = { version = "0.31", default-features = false } byteorder = "1.0" memory_units = "0.3.0" nan-preserving-float = "0.1.0" From ffe54f967444e1ddd001f5fe9f3518d6f946026e Mon Sep 17 00:00:00 2001 From: Julius Rakow Date: Sat, 25 Aug 2018 00:22:51 +0200 Subject: [PATCH 03/17] drop dependency on std::io --- src/value.rs | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/value.rs b/src/value.rs index 9be2d41f7e..bddc9f6861 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,6 +1,5 @@ -use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{ByteOrder, LittleEndian, WriteBytesExt}; use nan_preserving_float::{F32, F64}; -use std::io; use std::mem::transmute; use std::{f32, i32, i64, u32, u64}; use TrapKind; @@ -585,8 +584,9 @@ impl LittleEndianConvert for i16 { } fn from_little_endian(buffer: &[u8]) -> Result { - io::Cursor::new(buffer).read_i16::() - .map_err(|_| Error::InvalidLittleEndianBuffer) + buffer.get(0..2) + .map(LittleEndian::read_i16) + .ok_or_else(|| Error::InvalidLittleEndianBuffer) } } @@ -597,8 +597,9 @@ impl LittleEndianConvert for u16 { } fn from_little_endian(buffer: &[u8]) -> Result { - io::Cursor::new(buffer).read_u16::() - .map_err(|_| Error::InvalidLittleEndianBuffer) + buffer.get(0..2) + .map(LittleEndian::read_u16) + .ok_or_else(|| Error::InvalidLittleEndianBuffer) } } @@ -609,8 +610,9 @@ impl LittleEndianConvert for i32 { } fn from_little_endian(buffer: &[u8]) -> Result { - io::Cursor::new(buffer).read_i32::() - .map_err(|_| Error::InvalidLittleEndianBuffer) + buffer.get(0..4) + .map(LittleEndian::read_i32) + .ok_or_else(|| Error::InvalidLittleEndianBuffer) } } @@ -621,8 +623,9 @@ impl LittleEndianConvert for u32 { } fn from_little_endian(buffer: &[u8]) -> Result { - io::Cursor::new(buffer).read_u32::() - .map_err(|_| Error::InvalidLittleEndianBuffer) + buffer.get(0..4) + .map(LittleEndian::read_u32) + .ok_or_else(|| Error::InvalidLittleEndianBuffer) } } @@ -633,8 +636,9 @@ impl LittleEndianConvert for i64 { } fn from_little_endian(buffer: &[u8]) -> Result { - io::Cursor::new(buffer).read_i64::() - .map_err(|_| Error::InvalidLittleEndianBuffer) + buffer.get(0..8) + .map(LittleEndian::read_i64) + .ok_or_else(|| Error::InvalidLittleEndianBuffer) } } @@ -645,9 +649,9 @@ impl LittleEndianConvert for f32 { } fn from_little_endian(buffer: &[u8]) -> Result { - io::Cursor::new(buffer).read_u32::() - .map(f32::from_bits) - .map_err(|_| Error::InvalidLittleEndianBuffer) + buffer.get(0..4) + .map(LittleEndian::read_f32) + .ok_or_else(|| Error::InvalidLittleEndianBuffer) } } @@ -658,9 +662,9 @@ impl LittleEndianConvert for f64 { } fn from_little_endian(buffer: &[u8]) -> Result { - io::Cursor::new(buffer).read_u64::() - .map(f64::from_bits) - .map_err(|_| Error::InvalidLittleEndianBuffer) + buffer.get(0..8) + .map(LittleEndian::read_f64) + .ok_or_else(|| Error::InvalidLittleEndianBuffer) } } From f4173ad8cac04df5ccf582213f1b0d2efc629b0c Mon Sep 17 00:00:00 2001 From: Julius Rakow Date: Sat, 25 Aug 2018 00:34:10 +0200 Subject: [PATCH 04/17] use hashmap_core instead of std::collections::HashMap --- Cargo.toml | 1 + src/imports.rs | 2 +- src/lib.rs | 1 + src/module.rs | 2 +- src/validation/mod.rs | 2 +- 5 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 63ea9c9fa7..2d0b833ddd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ std = ["parity-wasm/std"] [dependencies] parity-wasm = { version = "0.31", default-features = false } byteorder = "1.0" +hashmap_core = "0.1.9" memory_units = "0.3.0" nan-preserving-float = "0.1.0" diff --git a/src/imports.rs b/src/imports.rs index 5907cefdb4..1459ff2936 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use hashmap_core::HashMap; use global::GlobalRef; use memory::MemoryRef; use func::FuncRef; diff --git a/src/lib.rs b/src/lib.rs index bffaee056f..83b47dabd6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -104,6 +104,7 @@ extern crate assert_matches; extern crate parity_wasm; extern crate byteorder; +extern crate hashmap_core; extern crate memory_units as memory_units_crate; pub extern crate nan_preserving_float; diff --git a/src/module.rs b/src/module.rs index a61e866d82..af56bb333c 100644 --- a/src/module.rs +++ b/src/module.rs @@ -3,7 +3,7 @@ use Trap; use std::rc::Rc; use std::cell::RefCell; use std::fmt; -use std::collections::HashMap; +use hashmap_core::HashMap; use parity_wasm::elements::{External, InitExpr, Internal, Instruction, ResizableLimits, Type}; use {Module, Error, Signature, MemoryInstance, RuntimeValue, TableInstance}; use imports::ImportResolver; diff --git a/src/validation/mod.rs b/src/validation/mod.rs index d6e452f6cf..1620009e8e 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -1,6 +1,6 @@ use std::error; use std::fmt; -use std::collections::HashSet; +use hashmap_core::HashSet; use parity_wasm::elements::{ BlockType, External, GlobalEntry, GlobalType, Internal, MemoryType, Module, Instruction, ResizableLimits, TableType, ValueType, InitExpr, Type, From 33a0e6ec4cc890995f45d60e04cd3efa8025b824 Mon Sep 17 00:00:00 2001 From: Julius Rakow Date: Sat, 25 Aug 2018 00:43:42 +0200 Subject: [PATCH 05/17] disable std::error in no_std --- src/common/stack.rs | 2 ++ src/lib.rs | 3 +++ src/validation/mod.rs | 2 ++ 3 files changed, 7 insertions(+) diff --git a/src/common/stack.rs b/src/common/stack.rs index 2366adbd06..9479fc5b53 100644 --- a/src/common/stack.rs +++ b/src/common/stack.rs @@ -1,4 +1,5 @@ +#[cfg(feature = "std")] use std::error; use std::fmt; @@ -11,6 +12,7 @@ impl fmt::Display for Error { } } +#[cfg(feature = "std")] impl error::Error for Error { fn description(&self) -> &str { &self.0 diff --git a/src/lib.rs b/src/lib.rs index 83b47dabd6..7c86318c26 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -110,6 +110,7 @@ extern crate memory_units as memory_units_crate; pub extern crate nan_preserving_float; use std::fmt; +#[cfg(feature = "std")] use std::error; /// Error type which can be thrown by wasm code or by host environment. @@ -139,6 +140,7 @@ impl fmt::Display for Trap { } } +#[cfg(feature = "std")] impl error::Error for Trap { fn description(&self) -> &str { "runtime trap" @@ -309,6 +311,7 @@ impl fmt::Display for Error { } } +#[cfg(feature = "std")] impl error::Error for Error { fn description(&self) -> &str { match *self { diff --git a/src/validation/mod.rs b/src/validation/mod.rs index 1620009e8e..ed6f8ee79c 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "std")] use std::error; use std::fmt; use hashmap_core::HashSet; @@ -27,6 +28,7 @@ impl fmt::Display for Error { } } +#[cfg(feature = "std")] impl error::Error for Error { fn description(&self) -> &str { &self.0 From d38d268740d2f64209e3fd69bf9f63ae75558ac4 Mon Sep 17 00:00:00 2001 From: Julius Rakow Date: Sat, 25 Aug 2018 01:09:02 +0200 Subject: [PATCH 06/17] core and alloc all the things --- src/common/stack.rs | 4 +++- src/func.rs | 8 +++++--- src/global.rs | 6 +++--- src/host.rs | 4 ++-- src/imports.rs | 2 ++ src/isa.rs | 3 +++ src/lib.rs | 19 ++++++++++++++++++- src/memory.rs | 28 +++++++++++++++------------- src/module.rs | 10 ++++++---- src/runner.rs | 14 ++++++++------ src/table.rs | 12 +++++++----- src/tests/host.rs | 4 ++-- src/tests/mod.rs | 4 ++-- src/types.rs | 2 +- src/validation/context.rs | 2 ++ src/validation/func.rs | 8 +++++--- src/validation/mod.rs | 6 ++++-- src/validation/util.rs | 2 ++ src/value.rs | 12 ++++++------ 19 files changed, 96 insertions(+), 54 deletions(-) diff --git a/src/common/stack.rs b/src/common/stack.rs index 9479fc5b53..a96af35aa2 100644 --- a/src/common/stack.rs +++ b/src/common/stack.rs @@ -1,7 +1,9 @@ +#[allow(unused_imports)] +use alloc::prelude::*; #[cfg(feature = "std")] use std::error; -use std::fmt; +use core::fmt; #[derive(Debug)] pub struct Error(String); diff --git a/src/func.rs b/src/func.rs index 10162fa21d..802f16cc7d 100644 --- a/src/func.rs +++ b/src/func.rs @@ -1,5 +1,7 @@ -use std::rc::{Rc, Weak}; -use std::fmt; +#[allow(unused_imports)] +use alloc::prelude::*; +use alloc::rc::{Rc, Weak}; +use core::fmt; use parity_wasm::elements::Local; use {Trap, TrapKind, Signature}; use host::Externals; @@ -17,7 +19,7 @@ use isa; #[derive(Clone, Debug)] pub struct FuncRef(Rc); -impl ::std::ops::Deref for FuncRef { +impl ::core::ops::Deref for FuncRef { type Target = FuncInstance; fn deref(&self) -> &FuncInstance { &self.0 diff --git a/src/global.rs b/src/global.rs index aec71eda25..fe7e27683f 100644 --- a/src/global.rs +++ b/src/global.rs @@ -1,5 +1,5 @@ -use std::rc::Rc; -use std::cell::Cell; +use alloc::rc::Rc; +use core::cell::Cell; use value::RuntimeValue; use Error; use types::ValueType; @@ -13,7 +13,7 @@ use parity_wasm::elements::{ValueType as EValueType}; #[derive(Clone, Debug)] pub struct GlobalRef(Rc); -impl ::std::ops::Deref for GlobalRef { +impl ::core::ops::Deref for GlobalRef { type Target = GlobalInstance; fn deref(&self) -> &GlobalInstance { &self.0 diff --git a/src/host.rs b/src/host.rs index 65393e801a..745f207bf0 100644 --- a/src/host.rs +++ b/src/host.rs @@ -1,4 +1,4 @@ -use std::any::TypeId; +use core::any::TypeId; use value::{RuntimeValue, FromRuntimeValue}; use {TrapKind, Trap}; @@ -98,7 +98,7 @@ impl<'a> RuntimeArgs<'a> { /// _ => panic!(), /// } /// ``` -pub trait HostError: 'static + ::std::fmt::Display + ::std::fmt::Debug + Send + Sync { +pub trait HostError: 'static + ::core::fmt::Display + ::core::fmt::Debug + Send + Sync { #[doc(hidden)] fn __private_get_type_id__(&self) -> TypeId { TypeId::of::() diff --git a/src/imports.rs b/src/imports.rs index 1459ff2936..0ba8d2aec4 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -1,3 +1,5 @@ +#[allow(unused_imports)] +use alloc::prelude::*; use hashmap_core::HashMap; use global::GlobalRef; use memory::MemoryRef; diff --git a/src/isa.rs b/src/isa.rs index 39aaa9e7b1..4af093c38a 100644 --- a/src/isa.rs +++ b/src/isa.rs @@ -67,6 +67,9 @@ //! - Reserved immediates are ignored for `call_indirect`, `current_memory`, `grow_memory`. //! +#[allow(unused_imports)] +use alloc::prelude::*; + /// Should we keep a value before "discarding" a stack frame? /// /// Note that this is a `enum` since Wasm doesn't support multiple return diff --git a/src/lib.rs b/src/lib.rs index 7c86318c26..ecf600c410 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -96,6 +96,21 @@ #![warn(missing_docs)] +#![cfg_attr(not(feature = "std"), no_std)] + +//// alloc is required in no_std +#![cfg_attr(not(feature = "std"), feature(alloc))] + +#[cfg(not(feature = "std"))] +#[macro_use] +extern crate alloc; +#[cfg(feature = "std")] +extern crate std as alloc; + +#[cfg(feature = "std")] +#[macro_use] +extern crate core; + #[cfg(test)] extern crate wabt; #[cfg(test)] @@ -109,7 +124,9 @@ extern crate memory_units as memory_units_crate; pub extern crate nan_preserving_float; -use std::fmt; +#[allow(unused_imports)] +use alloc::prelude::*; +use core::fmt; #[cfg(feature = "std")] use std::error; diff --git a/src/memory.rs b/src/memory.rs index 0854d2a239..b1265160d2 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,9 +1,11 @@ -use std::u32; -use std::ops::Range; -use std::cmp; -use std::fmt; -use std::rc::Rc; -use std::cell::{Cell, RefCell}; +#[allow(unused_imports)] +use alloc::prelude::*; +use alloc::rc::Rc; +use core::u32; +use core::ops::Range; +use core::cmp; +use core::fmt; +use core::cell::{Cell, RefCell}; use parity_wasm::elements::ResizableLimits; use Error; use memory_units::{RoundUpTo, Pages, Bytes}; @@ -28,7 +30,7 @@ const LINEAR_MEMORY_MAX_PAGES: Pages = Pages(65536); #[derive(Clone, Debug)] pub struct MemoryRef(Rc); -impl ::std::ops::Deref for MemoryRef { +impl ::core::ops::Deref for MemoryRef { type Target = MemoryInstance; fn deref(&self) -> &MemoryInstance { &self.0 @@ -172,7 +174,7 @@ impl MemoryInstance { /// Get value from memory at given offset. pub fn get_value(&self, offset: u32) -> Result { let mut buffer = self.buffer.borrow_mut(); - let region = self.checked_region(&mut buffer, offset as usize, ::std::mem::size_of::())?; + let region = self.checked_region(&mut buffer, offset as usize, ::core::mem::size_of::())?; Ok(T::from_little_endian(&buffer[region.range()]).expect("Slice size is checked")) } @@ -216,7 +218,7 @@ impl MemoryInstance { /// Copy value in the memory at given offset. pub fn set_value(&self, offset: u32, value: T) -> Result<(), Error> { let mut buffer = self.buffer.borrow_mut(); - let range = self.checked_region(&mut buffer, offset as usize, ::std::mem::size_of::())?.range(); + let range = self.checked_region(&mut buffer, offset as usize, ::core::mem::size_of::())?.range(); value.into_little_endian(&mut buffer[range]); Ok(()) } @@ -254,7 +256,7 @@ impl MemoryInstance { } fn checked_region(&self, buffer: &mut B, offset: usize, size: usize) -> Result - where B: ::std::ops::DerefMut> + where B: ::core::ops::DerefMut> { let end = offset.checked_add(size) .ok_or_else(|| Error::Memory(format!("trying to access memory block of size {} from offset {}", size, offset)))?; @@ -275,7 +277,7 @@ impl MemoryInstance { fn checked_region_pair(&self, buffer: &mut B, offset1: usize, size1: usize, offset2: usize, size2: usize) -> Result<(CheckedRegion, CheckedRegion), Error> - where B: ::std::ops::DerefMut> + where B: ::core::ops::DerefMut> { let end1 = offset1.checked_add(size1) .ok_or_else(|| Error::Memory(format!("trying to access memory block of size {} from offset {}", size1, offset1)))?; @@ -314,7 +316,7 @@ impl MemoryInstance { let (read_region, write_region) = self.checked_region_pair(&mut buffer, src_offset, len, dst_offset, len)?; - unsafe { ::std::ptr::copy( + unsafe { ::core::ptr::copy( buffer[read_region.range()].as_ptr(), buffer[write_region.range()].as_ptr() as *mut _, len, @@ -343,7 +345,7 @@ impl MemoryInstance { return Err(Error::Memory(format!("non-overlapping copy is used for overlapping regions"))) } - unsafe { ::std::ptr::copy_nonoverlapping( + unsafe { ::core::ptr::copy_nonoverlapping( buffer[read_region.range()].as_ptr(), buffer[write_region.range()].as_ptr() as *mut _, len, diff --git a/src/module.rs b/src/module.rs index af56bb333c..94c2ec2875 100644 --- a/src/module.rs +++ b/src/module.rs @@ -1,8 +1,10 @@ +#[allow(unused_imports)] +use alloc::prelude::*; +use alloc::rc::Rc; use runner::check_function_args; use Trap; -use std::rc::Rc; -use std::cell::RefCell; -use std::fmt; +use core::cell::RefCell; +use core::fmt; use hashmap_core::HashMap; use parity_wasm::elements::{External, InitExpr, Internal, Instruction, ResizableLimits, Type}; use {Module, Error, Signature, MemoryInstance, RuntimeValue, TableInstance}; @@ -32,7 +34,7 @@ use memory_units::Pages; #[derive(Clone, Debug)] pub struct ModuleRef(pub(crate) Rc); -impl ::std::ops::Deref for ModuleRef { +impl ::core::ops::Deref for ModuleRef { type Target = ModuleInstance; fn deref(&self) -> &ModuleInstance { &self.0 diff --git a/src/runner.rs b/src/runner.rs index 5a9ec849a9..3f4d8098aa 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -1,7 +1,9 @@ -use std::ops; -use std::{u32, usize}; -use std::fmt; -use std::iter::repeat; +#[allow(unused_imports)] +use alloc::prelude::*; +use core::ops; +use core::{u32, usize}; +use core::fmt; +use core::iter::repeat; use parity_wasm::elements::Local; use {Error, Trap, TrapKind, Signature}; use module::ModuleRef; @@ -19,7 +21,7 @@ use nan_preserving_float::{F32, F64}; use isa; /// Maximum number of entries in value stack. -pub const DEFAULT_VALUE_STACK_LIMIT: usize = (1024 * 1024) / ::std::mem::size_of::(); +pub const DEFAULT_VALUE_STACK_LIMIT: usize = (1024 * 1024) / ::core::mem::size_of::(); // TODO: Make these parameters changeble. pub const DEFAULT_CALL_STACK_LIMIT: usize = 64 * 1024; @@ -122,7 +124,7 @@ impl Interpreter { } pub fn resume_execution<'a, E: Externals + 'a>(&mut self, return_val: Option, externals: &'a mut E) -> Result, Trap> { - use std::mem::swap; + use core::mem::swap; // Ensure that the VM is resumable. This is checked in `FuncInvocation::resume_execution`. assert!(self.state.is_resumable()); diff --git a/src/table.rs b/src/table.rs index 53dcc1019e..0e42196e11 100644 --- a/src/table.rs +++ b/src/table.rs @@ -1,7 +1,9 @@ -use std::u32; -use std::fmt; -use std::cell::RefCell; -use std::rc::Rc; +#[allow(unused_imports)] +use alloc::prelude::*; +use alloc::rc::Rc; +use core::u32; +use core::fmt; +use core::cell::RefCell; use parity_wasm::elements::ResizableLimits; use Error; use func::FuncRef; @@ -16,7 +18,7 @@ use module::check_limits; #[derive(Clone, Debug)] pub struct TableRef(Rc); -impl ::std::ops::Deref for TableRef { +impl ::core::ops::Deref for TableRef { type Target = TableInstance; fn deref(&self) -> &TableInstance { &self.0 diff --git a/src/tests/host.rs b/src/tests/host.rs index c0b5916041..4ba09d2559 100644 --- a/src/tests/host.rs +++ b/src/tests/host.rs @@ -12,8 +12,8 @@ struct HostErrorWithCode { error_code: u32, } -impl ::std::fmt::Display for HostErrorWithCode { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { +impl ::core::fmt::Display for HostErrorWithCode { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> Result<(), ::core::fmt::Error> { write!(f, "{}", self.error_code) } } diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 3ad8ba8c60..193e2743ee 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -23,10 +23,10 @@ fn assert_error_properties() { fn unsigned_to_runtime_value() { use super::RuntimeValue; - let overflow_i32: u32 = ::std::i32::MAX as u32 + 1; + let overflow_i32: u32 = ::core::i32::MAX as u32 + 1; assert_eq!(RuntimeValue::from(overflow_i32).try_into::().unwrap(), overflow_i32); - let overflow_i64: u64 = ::std::i64::MAX as u64 + 1; + let overflow_i64: u64 = ::core::i64::MAX as u64 + 1; assert_eq!(RuntimeValue::from(overflow_i64).try_into::().unwrap(), overflow_i64); } diff --git a/src/types.rs b/src/types.rs index de5e6738c1..6cf139a917 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,4 +1,4 @@ -use std::borrow::Cow; +use alloc::borrow::Cow; use parity_wasm::elements::{ FunctionType, ValueType as EValueType, GlobalType, TableType, MemoryType}; diff --git a/src/validation/context.rs b/src/validation/context.rs index 145ac18de1..19805e5396 100644 --- a/src/validation/context.rs +++ b/src/validation/context.rs @@ -1,3 +1,5 @@ +#[allow(unused_imports)] +use alloc::prelude::*; use parity_wasm::elements::{MemoryType, TableType, GlobalType, BlockType, ValueType, FunctionType}; use validation::Error; diff --git a/src/validation/func.rs b/src/validation/func.rs index 7107e05418..7c43bf771d 100644 --- a/src/validation/func.rs +++ b/src/validation/func.rs @@ -1,4 +1,6 @@ -use std::u32; +#[allow(unused_imports)] +use alloc::prelude::*; +use core::u32; use parity_wasm::elements::{Instruction, BlockType, ValueType, TableElementType, Func, FuncBody}; use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX}; use validation::context::ModuleContext; @@ -1726,7 +1728,7 @@ impl Sink { } fn emit_br_table(&mut self, targets: &[Target], default: Target) { - use std::iter; + use core::iter; let pc = self.cur_pc(); let mut isa_targets = Vec::new(); @@ -1757,7 +1759,7 @@ impl Sink { /// /// Panics if the label is already resolved. fn resolve_label(&mut self, label: LabelId) { - use std::mem; + use core::mem; if let (Label::Resolved(_), _) = self.labels[label.0] { panic!("Trying to resolve already resolved label"); diff --git a/src/validation/mod.rs b/src/validation/mod.rs index ed6f8ee79c..73794d8c93 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -1,6 +1,8 @@ +#[allow(unused_imports)] +use alloc::prelude::*; #[cfg(feature = "std")] use std::error; -use std::fmt; +use core::fmt; use hashmap_core::HashSet; use parity_wasm::elements::{ BlockType, External, GlobalEntry, GlobalType, Internal, MemoryType, Module, Instruction, @@ -47,7 +49,7 @@ pub struct ValidatedModule { pub module: Module, } -impl ::std::ops::Deref for ValidatedModule { +impl ::core::ops::Deref for ValidatedModule { type Target = Module; fn deref(&self) -> &Module { &self.module diff --git a/src/validation/util.rs b/src/validation/util.rs index acdec1b0c8..69d70ace46 100644 --- a/src/validation/util.rs +++ b/src/validation/util.rs @@ -1,3 +1,5 @@ +#[allow(unused_imports)] +use alloc::prelude::*; use parity_wasm::elements::{Local, ValueType}; use validation::Error; diff --git a/src/value.rs b/src/value.rs index bddc9f6861..18253ac097 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,7 +1,7 @@ use byteorder::{ByteOrder, LittleEndian, WriteBytesExt}; use nan_preserving_float::{F32, F64}; -use std::mem::transmute; -use std::{f32, i32, i64, u32, u64}; +use core::mem::transmute; +use core::{f32, i32, i64, u32, u64}; use TrapKind; #[derive(Debug)] @@ -780,7 +780,7 @@ macro_rules! impl_float { return round; } - use std::ops::Rem; + use core::ops::Rem; if round.rem(2.0) == 1.0 { self.floor() } else if round.rem(2.0) == -1.0 { @@ -795,7 +795,7 @@ macro_rules! impl_float { // This instruction corresponds to what is sometimes called "minNaN" in other languages. fn min(self, other: $type) -> $type { if self.is_nan() || other.is_nan() { - return ::std::$intermediate::NAN.into(); + return ::core::$intermediate::NAN.into(); } self.min(other) @@ -803,13 +803,13 @@ macro_rules! impl_float { // This instruction corresponds to what is sometimes called "maxNaN" in other languages. fn max(self, other: $type) -> $type { if self.is_nan() || other.is_nan() { - return ::std::$intermediate::NAN.into(); + return ::core::$intermediate::NAN.into(); } self.max(other) } fn copysign(self, other: $type) -> $type { - use std::mem::size_of; + use core::mem::size_of; if self.is_nan() { return self; From 3c75f65105d52c2f1c34e20a858ddf4998b2f94f Mon Sep 17 00:00:00 2001 From: Julius Rakow Date: Sat, 25 Aug 2018 01:15:04 +0200 Subject: [PATCH 07/17] mention no_std in readme --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 8d2b2f5ac0..e9c2c1ff1f 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,18 @@ cargo build cargo test ``` +# `no_std` support +This crate supports `no_std` environments. +If the `std` feature is disabled, `no_std` is supported. +Use like that: +```toml +[dependencies] +parity-wasm = { version = "0.31", default-features = false } +``` + +`no_std` requires the `core` and `alloc` libraries and a nightly compiler. +Also, code related to `std::error` is disabled. + ## Contribution Unless you explicitly state otherwise, any contribution intentionally submitted From 3b82da15dfe3bd3ff37cfb4f1e6edb63f523191c Mon Sep 17 00:00:00 2001 From: Julius Rakow Date: Sat, 25 Aug 2018 01:48:39 +0200 Subject: [PATCH 08/17] add no_std feature and use hashmap_core only on no_std --- Cargo.toml | 5 ++++- README.md | 9 ++++++--- src/imports.rs | 5 +++++ src/lib.rs | 1 + src/module.rs | 5 +++++ src/validation/mod.rs | 5 +++++ 6 files changed, 26 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2d0b833ddd..ff3f851614 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,11 +14,14 @@ exclude = [ "/res/*", "/tests/*", "/fuzz/*", "/benches/*" ] default = ["std"] # Disable for no_std support std = ["parity-wasm/std"] +# Enable for no_std support +# hashmap_core only works on no_std +no_std = ["hashmap_core"] [dependencies] parity-wasm = { version = "0.31", default-features = false } byteorder = "1.0" -hashmap_core = "0.1.9" +hashmap_core = { version = "0.1.9", optional = true } memory_units = "0.3.0" nan-preserving-float = "0.1.0" diff --git a/README.md b/README.md index e9c2c1ff1f..9d29ee6af6 100644 --- a/README.md +++ b/README.md @@ -29,11 +29,14 @@ cargo test # `no_std` support This crate supports `no_std` environments. -If the `std` feature is disabled, `no_std` is supported. -Use like that: +Enable the `no_std` feature and disable default features: ```toml [dependencies] -parity-wasm = { version = "0.31", default-features = false } +parity-wasm = { + version = "0.31", + default-features = false, + features = "no_std" +} ``` `no_std` requires the `core` and `alloc` libraries and a nightly compiler. diff --git a/src/imports.rs b/src/imports.rs index 0ba8d2aec4..bb52eb4b6b 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -1,6 +1,11 @@ #[allow(unused_imports)] use alloc::prelude::*; + +#[cfg(feature = "std")] +use std::collections::HashMap; +#[cfg(not(feature = "std"))] use hashmap_core::HashMap; + use global::GlobalRef; use memory::MemoryRef; use func::FuncRef; diff --git a/src/lib.rs b/src/lib.rs index ecf600c410..206c6e4e5b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -119,6 +119,7 @@ extern crate assert_matches; extern crate parity_wasm; extern crate byteorder; +#[cfg(not(feature = "std"))] extern crate hashmap_core; extern crate memory_units as memory_units_crate; diff --git a/src/module.rs b/src/module.rs index 94c2ec2875..4718bc512b 100644 --- a/src/module.rs +++ b/src/module.rs @@ -5,7 +5,12 @@ use runner::check_function_args; use Trap; use core::cell::RefCell; use core::fmt; + +#[cfg(feature = "std")] +use std::collections::HashMap; +#[cfg(not(feature = "std"))] use hashmap_core::HashMap; + use parity_wasm::elements::{External, InitExpr, Internal, Instruction, ResizableLimits, Type}; use {Module, Error, Signature, MemoryInstance, RuntimeValue, TableInstance}; use imports::ImportResolver; diff --git a/src/validation/mod.rs b/src/validation/mod.rs index 73794d8c93..fdb2d11ec3 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -3,7 +3,12 @@ use alloc::prelude::*; #[cfg(feature = "std")] use std::error; use core::fmt; + +#[cfg(feature = "std")] +use std::collections::HashSet; +#[cfg(not(feature = "std"))] use hashmap_core::HashSet; + use parity_wasm::elements::{ BlockType, External, GlobalEntry, GlobalType, Internal, MemoryType, Module, Instruction, ResizableLimits, TableType, ValueType, InitExpr, Type, From 99eabd7e9286dc62084ac81f6751c238b0a8cf59 Mon Sep 17 00:00:00 2001 From: Julius Rakow Date: Sat, 25 Aug 2018 14:18:52 +0200 Subject: [PATCH 09/17] rename the no_std feature to core --- Cargo.toml | 2 +- README.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ff3f851614..6a25deb5ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ default = ["std"] std = ["parity-wasm/std"] # Enable for no_std support # hashmap_core only works on no_std -no_std = ["hashmap_core"] +core = ["hashmap_core"] [dependencies] parity-wasm = { version = "0.31", default-features = false } diff --git a/README.md b/README.md index 9d29ee6af6..533bcbe3bf 100644 --- a/README.md +++ b/README.md @@ -29,17 +29,17 @@ cargo test # `no_std` support This crate supports `no_std` environments. -Enable the `no_std` feature and disable default features: +Enable the `core` feature and disable default features: ```toml [dependencies] parity-wasm = { version = "0.31", default-features = false, - features = "no_std" + features = "core" } ``` -`no_std` requires the `core` and `alloc` libraries and a nightly compiler. +The `core` feature requires the `core` and `alloc` libraries and a nightly compiler. Also, code related to `std::error` is disabled. ## Contribution From 86b1d3cf56261cf868f60165a6671e0b0b5d3b44 Mon Sep 17 00:00:00 2001 From: Julius Rakow Date: Sat, 25 Aug 2018 18:46:46 +0200 Subject: [PATCH 10/17] drop dependency on byteorder/std --- Cargo.toml | 4 ++-- src/value.rs | 37 +++++++++++++++---------------------- 2 files changed, 17 insertions(+), 24 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6a25deb5ff..02876b3e8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,14 +13,14 @@ exclude = [ "/res/*", "/tests/*", "/fuzz/*", "/benches/*" ] [features] default = ["std"] # Disable for no_std support -std = ["parity-wasm/std"] +std = ["parity-wasm/std", "byteorder/std"] # Enable for no_std support # hashmap_core only works on no_std core = ["hashmap_core"] [dependencies] parity-wasm = { version = "0.31", default-features = false } -byteorder = "1.0" +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" diff --git a/src/value.rs b/src/value.rs index 18253ac097..1a4ef8d2d0 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,4 +1,4 @@ -use byteorder::{ByteOrder, LittleEndian, WriteBytesExt}; +use byteorder::{ByteOrder, LittleEndian}; use nan_preserving_float::{F32, F64}; use core::mem::transmute; use core::{f32, i32, i64, u32, u64}; @@ -578,9 +578,8 @@ impl LittleEndianConvert for u8 { } impl LittleEndianConvert for i16 { - fn into_little_endian(self, mut buffer: &mut[u8]) { - buffer.write_i16::(self) - .expect("i16 is written without any errors"); + fn into_little_endian(self, buffer: &mut[u8]) { + LittleEndian::write_i16(buffer, self); } fn from_little_endian(buffer: &[u8]) -> Result { @@ -591,9 +590,8 @@ impl LittleEndianConvert for i16 { } impl LittleEndianConvert for u16 { - fn into_little_endian(self, mut buffer: &mut[u8]) { - buffer.write_u16::(self) - .expect("u16 is written without any errors"); + fn into_little_endian(self, buffer: &mut[u8]) { + LittleEndian::write_u16(buffer, self); } fn from_little_endian(buffer: &[u8]) -> Result { @@ -604,9 +602,8 @@ impl LittleEndianConvert for u16 { } impl LittleEndianConvert for i32 { - fn into_little_endian(self, mut buffer: &mut[u8]) { - buffer.write_i32::(self) - .expect("i32 is written without any errors"); + fn into_little_endian(self, buffer: &mut[u8]) { + LittleEndian::write_i32(buffer, self); } fn from_little_endian(buffer: &[u8]) -> Result { @@ -617,9 +614,8 @@ impl LittleEndianConvert for i32 { } impl LittleEndianConvert for u32 { - fn into_little_endian(self, mut buffer: &mut[u8]) { - buffer.write_u32::(self) - .expect("u32 is written without any errors"); + fn into_little_endian(self, buffer: &mut[u8]) { + LittleEndian::write_u32(buffer, self); } fn from_little_endian(buffer: &[u8]) -> Result { @@ -630,9 +626,8 @@ impl LittleEndianConvert for u32 { } impl LittleEndianConvert for i64 { - fn into_little_endian(self, mut buffer: &mut[u8]) { - buffer.write_i64::(self) - .expect("i64 is written without any errors"); + fn into_little_endian(self, buffer: &mut[u8]) { + LittleEndian::write_i64(buffer, self); } fn from_little_endian(buffer: &[u8]) -> Result { @@ -643,9 +638,8 @@ impl LittleEndianConvert for i64 { } impl LittleEndianConvert for f32 { - fn into_little_endian(self, mut buffer: &mut[u8]) { - buffer.write_f32::(self) - .expect("f32 is written without any errors"); + fn into_little_endian(self, buffer: &mut[u8]) { + LittleEndian::write_f32(buffer, self); } fn from_little_endian(buffer: &[u8]) -> Result { @@ -656,9 +650,8 @@ impl LittleEndianConvert for f32 { } impl LittleEndianConvert for f64 { - fn into_little_endian(self, mut buffer: &mut[u8]) { - buffer.write_f64::(self) - .expect("i64 is written without any errors"); + fn into_little_endian(self, buffer: &mut[u8]) { + LittleEndian::write_f64(buffer, self); } fn from_little_endian(buffer: &[u8]) -> Result { From cf67b161ce9e4c6537b37e8a4e94fff073d4383c Mon Sep 17 00:00:00 2001 From: Julius Rakow Date: Sun, 26 Aug 2018 13:11:28 +0200 Subject: [PATCH 11/17] simplify float impl macro --- src/value.rs | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/value.rs b/src/value.rs index 1a4ef8d2d0..e938b1352d 100644 --- a/src/value.rs +++ b/src/value.rs @@ -747,25 +747,22 @@ impl_integer!(i64); impl_integer!(u64); macro_rules! impl_float { - ($type:ident, $int_type:ident) => { - impl_float!($type, $type, $int_type); - }; - ($type:ident, $intermediate:ident, $int_type:ident) => { + ($type:ident, $fXX:ident, $iXX:ident) => { impl Float<$type> for $type { fn abs(self) -> $type { - $intermediate::abs(self.into()).into() + $fXX::abs(self.into()).into() } fn floor(self) -> $type { - $intermediate::floor(self.into()).into() + $fXX::floor(self.into()).into() } fn ceil(self) -> $type { - $intermediate::ceil(self.into()).into() + $fXX::ceil(self.into()).into() } fn trunc(self) -> $type { - $intermediate::trunc(self.into()).into() + $fXX::trunc(self.into()).into() } fn round(self) -> $type { - $intermediate::round(self.into()).into() + $fXX::round(self.into()).into() } fn nearest(self) -> $type { let round = self.round(); @@ -783,20 +780,26 @@ macro_rules! impl_float { } } fn sqrt(self) -> $type { - $intermediate::sqrt(self.into()).into() + $fXX::sqrt(self.into()).into() } // This instruction corresponds to what is sometimes called "minNaN" in other languages. fn min(self, other: $type) -> $type { - if self.is_nan() || other.is_nan() { - return ::core::$intermediate::NAN.into(); + if self.is_nan() { + return self; + } + if other.is_nan() { + return other; } self.min(other) } // This instruction corresponds to what is sometimes called "maxNaN" in other languages. fn max(self, other: $type) -> $type { - if self.is_nan() || other.is_nan() { - return ::core::$intermediate::NAN.into(); + if self.is_nan() { + return self; + } + if other.is_nan() { + return other; } self.max(other) @@ -808,9 +811,9 @@ macro_rules! impl_float { return self; } - let sign_mask: $int_type = 1 << ((size_of::<$int_type>() << 3) - 1); - let self_int: $int_type = self.transmute_into(); - let other_int: $int_type = other.transmute_into(); + let sign_mask: $iXX = 1 << ((size_of::<$iXX>() << 3) - 1); + let self_int: $iXX = self.transmute_into(); + let other_int: $iXX = other.transmute_into(); let is_self_sign_set = (self_int & sign_mask) != 0; let is_other_sign_set = (other_int & sign_mask) != 0; if is_self_sign_set == is_other_sign_set { @@ -825,7 +828,7 @@ macro_rules! impl_float { }; } -impl_float!(f32, i32); -impl_float!(f64, i64); +impl_float!(f32, f32, i32); +impl_float!(f64, f64, i64); impl_float!(F32, f32, i32); impl_float!(F64, f64, i64); From 2a6103c29f4e2b4e7d91a4b85d824da8d8e7f430 Mon Sep 17 00:00:00 2001 From: Julius Rakow Date: Sun, 26 Aug 2018 13:58:58 +0200 Subject: [PATCH 12/17] remove some trailing whitespace --- src/value.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/value.rs b/src/value.rs index e938b1352d..26a5af1e69 100644 --- a/src/value.rs +++ b/src/value.rs @@ -261,7 +261,7 @@ impl FromRuntimeValue for bool { } /// This conversion assumes that `i8` is represented as an [`I32`]. -/// +/// /// [`I32`]: enum.RuntimeValue.html#variant.I32 impl FromRuntimeValue for i8 { fn from_runtime_value(val: RuntimeValue) -> Option { @@ -275,7 +275,7 @@ impl FromRuntimeValue for i8 { } /// This conversion assumes that `i16` is represented as an [`I32`]. -/// +/// /// [`I32`]: enum.RuntimeValue.html#variant.I32 impl FromRuntimeValue for i16 { fn from_runtime_value(val: RuntimeValue) -> Option { @@ -289,7 +289,7 @@ impl FromRuntimeValue for i16 { } /// This conversion assumes that `u8` is represented as an [`I32`]. -/// +/// /// [`I32`]: enum.RuntimeValue.html#variant.I32 impl FromRuntimeValue for u8 { fn from_runtime_value(val: RuntimeValue) -> Option { @@ -303,7 +303,7 @@ impl FromRuntimeValue for u8 { } /// This conversion assumes that `u16` is represented as an [`I32`]. -/// +/// /// [`I32`]: enum.RuntimeValue.html#variant.I32 impl FromRuntimeValue for u16 { fn from_runtime_value(val: RuntimeValue) -> Option { From 0a0f8707c47cfcab6dd613fbd371287dc2cc6377 Mon Sep 17 00:00:00 2001 From: Julius Rakow Date: Sun, 26 Aug 2018 14:05:30 +0200 Subject: [PATCH 13/17] 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 02876b3e8c..ba2f1c24a4 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 206c6e4e5b..cea7c3b7cc 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 26a5af1e69..359f8d87ee 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); From 8854754ec60db010af3d6602cdc32327416c94c5 Mon Sep 17 00:00:00 2001 From: Julius Rakow Date: Sun, 26 Aug 2018 15:06:09 +0200 Subject: [PATCH 14/17] add note about no_std panics of libm to readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 533bcbe3bf..a8c48fa5f9 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,9 @@ parity-wasm = { The `core` feature requires the `core` and `alloc` libraries and a nightly compiler. Also, code related to `std::error` is disabled. +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). +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). + ## Contribution Unless you explicitly state otherwise, any contribution intentionally submitted From baa89afffecce87acd384c9c94ec4ae933f68765 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Sat, 20 Oct 2018 17:47:32 +0200 Subject: [PATCH 15/17] Embed nan-preserving-float crate. --- Cargo.toml | 1 - src/lib.rs | 3 +- src/nan_preserving_float.rs | 212 ++++++++++++++++++++++++++++++++++++ 3 files changed, 213 insertions(+), 3 deletions(-) create mode 100644 src/nan_preserving_float.rs diff --git a/Cargo.toml b/Cargo.toml index ba2f1c24a4..30d38ea50d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,6 @@ parity-wasm = { version = "0.31", default-features = false } 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] diff --git a/src/lib.rs b/src/lib.rs index cea7c3b7cc..c0d6162885 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -123,8 +123,6 @@ extern crate byteorder; extern crate hashmap_core; extern crate memory_units as memory_units_crate; -pub extern crate nan_preserving_float; - #[allow(unused_imports)] use alloc::prelude::*; use core::fmt; @@ -392,6 +390,7 @@ mod global; mod func; mod types; mod isa; +pub mod nan_preserving_float; #[cfg(test)] mod tests; diff --git a/src/nan_preserving_float.rs b/src/nan_preserving_float.rs new file mode 100644 index 0000000000..6579662097 --- /dev/null +++ b/src/nan_preserving_float.rs @@ -0,0 +1,212 @@ +#![allow(missing_docs)] + +#[cfg(not(feature = "std"))] +use libm::{F32Ext, F64Ext}; + +use core::ops::{Add, Div, Mul, Neg, Sub, Rem}; +use core::cmp::{Ordering, PartialEq, PartialOrd}; + +macro_rules! impl_binop { + ($for:ident, $is:ident, $op:ident, $func_name:ident) => { + impl> $op for $for { + type Output = Self; + + fn $func_name(self, other: T) -> Self { + $for( + $op::$func_name( + $is::from_bits(self.0), + $is::from_bits(other.into().0) + ).to_bits() + ) + } + } + } +} + +macro_rules! float { + ($for:ident, $rep:ident, $is:ident) => { + float!($for, $rep, $is, 1 << (::core::mem::size_of::<$is>() * 8 - 1)); + }; + ($for:ident, $rep:ident, $is:ident, $sign_bit:expr) => { + #[derive(Copy, Clone)] + pub struct $for($rep); + + impl_binop!($for, $is, Add, add); + impl_binop!($for, $is, Sub, sub); + impl_binop!($for, $is, Mul, mul); + impl_binop!($for, $is, Div, div); + impl_binop!($for, $is, Rem, rem); + + impl $for { + pub fn from_bits(other: $rep) -> Self { + $for(other) + } + + pub fn to_bits(self) -> $rep { + self.0 + } + + pub fn from_float(fl: $is) -> Self { + fl.into() + } + + pub fn to_float(self) -> $is { + self.into() + } + + pub fn is_nan(self) -> bool { + self.to_float().is_nan() + } + + pub fn abs(self) -> Self { + $for(self.0 & !$sign_bit) + } + + pub fn fract(self) -> Self { + self.to_float().fract().into() + } + + pub fn min(self, other: Self) -> Self { + Self::from(self.to_float().min(other.to_float())) + } + + pub fn max(self, other: Self) -> Self { + Self::from(self.to_float().max(other.to_float())) + } + } + + impl From<$is> for $for { + fn from(other: $is) -> $for { + $for(other.to_bits()) + } + } + + impl From<$for> for $is { + fn from(other: $for) -> $is { + <$is>::from_bits(other.0) + } + } + + impl Neg for $for { + type Output = Self; + + fn neg(self) -> Self { + $for(self.0 ^ $sign_bit) + } + } + + impl + Copy> PartialEq for $for { + fn eq(&self, other: &T) -> bool { + $is::from(*self) == $is::from((*other).into()) + } + } + + impl + Copy> PartialOrd for $for { + fn partial_cmp(&self, other: &T) -> Option { + $is::from(*self).partial_cmp(&$is::from((*other).into())) + } + } + + impl ::core::fmt::Debug for $for { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + $is::from(*self).fmt(f) + } + } + } +} + +float!(F32, u32, f32); +float!(F64, u64, f64); + +impl From for F32 { + fn from(other: u32) -> Self { + Self::from_bits(other) + } +} + +impl From for u32 { + fn from(other: F32) -> Self { + other.to_bits() + } +} + +impl From for F64 { + fn from(other: u64) -> Self { + Self::from_bits(other) + } +} + +impl From for u64 { + fn from(other: F64) -> Self { + other.to_bits() + } +} + +#[cfg(test)] +mod tests { + extern crate rand; + + use self::rand::Rng; + + use super::{F32, F64}; + + use core::ops::{Add, Div, Mul, Neg, Sub}; + use core::fmt::Debug; + use core::iter; + + fn test_ops(iter: I) + where + T: Add + + Div + + Mul + + Sub + + Neg + + Copy + + Debug + + PartialEq, + F: Into + + Add + + Div + + Mul + + Sub + + Neg + + Copy + + Debug, + I: IntoIterator, + { + for (a, b) in iter { + assert_eq!((a + b).into(), a.into() + b.into()); + assert_eq!((a - b).into(), a.into() - b.into()); + assert_eq!((a * b).into(), a.into() * b.into()); + assert_eq!((a / b).into(), a.into() / b.into()); + assert_eq!((-a).into(), -a.into()); + assert_eq!((-b).into(), -b.into()); + } + } + + #[test] + fn test_ops_f32() { + let mut rng = rand::thread_rng(); + let iter = iter::repeat(()).map(|_| rng.gen()); + + test_ops::(iter.take(1000)); + } + + #[test] + fn test_ops_f64() { + let mut rng = rand::thread_rng(); + let iter = iter::repeat(()).map(|_| rng.gen()); + + test_ops::(iter.take(1000)); + } + + #[test] + fn test_neg_nan_f32() { + assert_eq!((-F32(0xff80_3210)).0, 0x7f80_3210); + } + + #[test] + fn test_neg_nan_f64() { + assert_eq!((-F64(0xff80_3210_0000_0000)).0, 0x7f80_3210_0000_0000); + } +} From 5a54374330cf086bd64acb3dd070986ed958afe6 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Sat, 20 Oct 2018 17:51:21 +0200 Subject: [PATCH 16/17] Add no_std check to the Travis CI config --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 68a5cda026..be29218fd8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,6 +25,8 @@ script: # Make sure nightly targets are not broken. - if [ "$TRAVIS_RUST_VERSION" == "nightly" ]; then cargo check --tests --manifest-path=fuzz/Cargo.toml; fi - if [ "$TRAVIS_RUST_VERSION" == "nightly" ]; then cargo check --benches --manifest-path=benches/Cargo.toml; fi +# Make sure `no_std` version checks. +- if [ "$TRAVIS_RUST_VERSION" == "nightly" ]; then cargo +nightly check --no-default-features --features core; fi - ./test.sh - ./doc.sh after_success: | From 21bff51d03b81efa321e52723b0abb87fcb82ee3 Mon Sep 17 00:00:00 2001 From: Julius Rakow Date: Sun, 21 Oct 2018 20:55:31 +0100 Subject: [PATCH 17/17] add missing dev-dependency --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 30d38ea50d..fd7d9e7192 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,4 +27,5 @@ libm = { version = "0.1.2", optional = true } [dev-dependencies] assert_matches = "1.1" +rand = "0.4.2" wabt = "0.4"