From b0b4eba3b7ff5bfbcb8835c473ac3945c5160332 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Mon, 29 Jan 2024 17:31:01 +0100 Subject: feat: Bulk Memory Operations Proposal Signed-off-by: Henry Gressmann --- README.md | 4 +- crates/parser/src/conversion.rs | 1 - crates/parser/src/module.rs | 8 +- crates/tinywasm/src/imports.rs | 15 ++- crates/tinywasm/src/instance.rs | 30 ++++- crates/tinywasm/src/reference.rs | 126 +++++++++++++-------- crates/tinywasm/src/runtime/interpreter/mod.rs | 39 ++++++- .../src/runtime/interpreter/no_std_floats.rs | 16 +++ crates/tinywasm/src/store/mod.rs | 76 ++++++++----- crates/tinywasm/tests/generated/2.0.csv | 2 +- examples/rust/build.sh | 2 +- examples/wasm-rust.rs | 6 +- 12 files changed, 223 insertions(+), 102 deletions(-) diff --git a/README.md b/README.md index 9fd38ca..c899701 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ # Status -TinyWasm, starting from version `0.3.0`, passes all the WebAssembly 1.0 tests in the [WebAssembly Test Suite](https://github.com/WebAssembly/testsuite). The 2.0 tests are in progress (notably `simd` and `bulk-memory-operations` are not implemented yet). This is enough to run most WebAssembly programs, including TinyWasm itself compiled to WebAssembly (see [examples/wasm-rust.rs](./examples/wasm-rust.rs)). +TinyWasm, starting from version `0.3.0`, passes all the WebAssembly 1.0 tests in the [WebAssembly Test Suite](https://github.com/WebAssembly/testsuite). The 2.0 tests are in progress. This is enough to run most WebAssembly programs, including TinyWasm itself compiled to WebAssembly (see [examples/wasm-rust.rs](./examples/wasm-rust.rs)). Some APIs to interact with the runtime are not yet exposed, and the existing ones are subject to change, but the core functionality is mostly complete. Results of the tests can be found [here](https://github.com/explodingcamera/tinywasm/tree/main/crates/tinywasm/tests/generated). @@ -24,7 +24,7 @@ TinyWasm is not designed for performance, but rather for size and portability. H - [**Mutable Globals**](https://github.com/WebAssembly/mutable-global/blob/master/proposals/mutable-global/Overview.md) - **Fully implemented** - [**Multi-value**](https://github.com/WebAssembly/spec/blob/master/proposals/multi-value/Overview.md) - **Fully implemented** - [**Sign-extension operators**](https://github.com/WebAssembly/spec/blob/master/proposals/sign-extension-ops/Overview.md) - **Fully implemented** -- [**Bulk Memory Operations**](https://github.com/WebAssembly/spec/blob/master/proposals/bulk-memory-operations/Overview.md) - **_Partially implemented_** +- [**Bulk Memory Operations**](https://github.com/WebAssembly/spec/blob/master/proposals/bulk-memory-operations/Overview.md) - **Fully implemented** (as of version `0.4.0`) - [**Reference Types**](https://github.com/WebAssembly/reference-types/blob/master/proposals/reference-types/Overview.md) - **_Partially implemented_** - [**Multiple Memories**](https://github.com/WebAssembly/multi-memory/blob/master/proposals/multi-memory/Overview.md) - **_Partially implemented_** (not tested yet) - [**Memory64**](https://github.com/WebAssembly/memory64/blob/master/proposals/memory64/Overview.md) - **_Partially implemented_** (only 32-bit addressing is supported at the moment, but larger memories can be created) diff --git a/crates/parser/src/conversion.rs b/crates/parser/src/conversion.rs index 79069e1..7c10d62 100644 --- a/crates/parser/src/conversion.rs +++ b/crates/parser/src/conversion.rs @@ -107,7 +107,6 @@ pub(crate) fn convert_module_tables Result> { let table_type = table_types.into_iter().map(|table| convert_module_table(table?)).collect::>>()?; - Ok(table_type) } diff --git a/crates/parser/src/module.rs b/crates/parser/src/module.rs index 5bc6a7e..f5c01ac 100644 --- a/crates/parser/src/module.rs +++ b/crates/parser/src/module.rs @@ -112,7 +112,6 @@ impl ModuleReader { if !self.table_types.is_empty() { return Err(ParseError::DuplicateSection("Table section".into())); } - debug!("Found table section"); validator.table_section(&reader)?; self.table_types = conversion::convert_module_tables(reader)?; @@ -140,6 +139,13 @@ impl ModuleReader { validator.data_section(&reader)?; self.data = conversion::convert_module_data_sections(reader)?; } + DataCountSection { count, range } => { + debug!("Found data count section"); + if !self.data.is_empty() { + return Err(ParseError::DuplicateSection("Data count section".into())); + } + validator.data_count_section(count, &range)?; + } CodeSectionStart { count, range, .. } => { debug!("Found code section ({} functions)", count); if !self.code.is_empty() { diff --git a/crates/tinywasm/src/imports.rs b/crates/tinywasm/src/imports.rs index 0b16970..38d0707 100644 --- a/crates/tinywasm/src/imports.rs +++ b/crates/tinywasm/src/imports.rs @@ -62,13 +62,13 @@ pub struct FuncContext<'a> { } impl FuncContext<'_> { - /// Get a mutable reference to the store - pub fn store_mut(&mut self) -> &mut crate::Store { + /// Get a reference to the store + pub fn store(&self) -> &crate::Store { self.store } - /// Get a reference to the store - pub fn store(&self) -> &crate::Store { + /// Get a mutable reference to the store + pub fn store_mut(&mut self) -> &mut crate::Store { self.store } @@ -78,9 +78,14 @@ impl FuncContext<'_> { } /// Get a reference to an exported memory - pub fn memory(&mut self, name: &str) -> Result { + pub fn exported_memory(&mut self, name: &str) -> Result> { self.module().exported_memory(self.store, name) } + + /// Get a reference to an exported memory + pub fn exported_memory_mut(&mut self, name: &str) -> Result> { + self.module().exported_memory_mut(self.store, name) + } } impl Debug for HostFunction { diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs index ecd6ce8..ad6c0f1 100644 --- a/crates/tinywasm/src/instance.rs +++ b/crates/tinywasm/src/instance.rs @@ -3,7 +3,7 @@ use tinywasm_types::*; use crate::{ func::{FromWasmValueTuple, IntoWasmValueTuple}, - log, Error, FuncHandle, FuncHandleTyped, Imports, MemoryRef, Module, Result, Store, + log, Error, FuncHandle, FuncHandleTyped, Imports, MemoryRef, MemoryRefMut, Module, Result, Store, }; /// An instanciated WebAssembly module @@ -152,6 +152,11 @@ impl ModuleInstance { *self.0.mem_addrs.get(addr as usize).expect("No mem addr for mem, this is a bug") } + // resolve a data address to the global store address + pub(crate) fn resolve_data_addr(&self, addr: DataAddr) -> MemAddr { + *self.0.data_addrs.get(addr as usize).expect("No data addr for data, this is a bug") + } + // resolve a memory address to the global store address pub(crate) fn resolve_elem_addr(&self, addr: ElemAddr) -> ElemAddr { *self.0.elem_addrs.get(addr as usize).expect("No elem addr for elem, this is a bug") @@ -190,7 +195,7 @@ impl ModuleInstance { } /// Get an exported memory by name - pub fn exported_memory(&self, store: &mut Store, name: &str) -> Result { + pub fn exported_memory<'a>(&self, store: &'a mut Store, name: &str) -> Result> { let export = self.export_addr(name).ok_or_else(|| Error::Other(format!("Export not found: {}", name)))?; let ExternVal::Memory(mem_addr) = export else { return Err(Error::Other(format!("Export is not a memory: {}", name))); @@ -199,11 +204,28 @@ impl ModuleInstance { Ok(mem) } + /// Get an exported memory by name + pub fn exported_memory_mut<'a>(&self, store: &'a mut Store, name: &str) -> Result> { + let export = self.export_addr(name).ok_or_else(|| Error::Other(format!("Export not found: {}", name)))?; + let ExternVal::Memory(mem_addr) = export else { + return Err(Error::Other(format!("Export is not a memory: {}", name))); + }; + let mem = self.memory_mut(store, mem_addr)?; + Ok(mem) + } + /// Get a memory by address - pub fn memory(&self, store: &Store, addr: MemAddr) -> Result { + pub fn memory<'a>(&self, store: &'a mut Store, addr: MemAddr) -> Result> { + let addr = self.resolve_mem_addr(addr); + let mem = store.get_mem(addr as usize)?; + Ok(MemoryRef { instance: mem.borrow() }) + } + + /// Get a memory by address (mutable) + pub fn memory_mut<'a>(&self, store: &'a mut Store, addr: MemAddr) -> Result> { let addr = self.resolve_mem_addr(addr); let mem = store.get_mem(addr as usize)?; - Ok(MemoryRef { instance: mem.clone() }) + Ok(MemoryRefMut { instance: mem.borrow_mut() }) } /// Get the start function of the module diff --git a/crates/tinywasm/src/reference.rs b/crates/tinywasm/src/reference.rs index ed09530..a34e30b 100644 --- a/crates/tinywasm/src/reference.rs +++ b/crates/tinywasm/src/reference.rs @@ -1,5 +1,5 @@ use core::{ - cell::{Ref, RefCell}, + cell::{Ref, RefCell, RefMut}, ffi::CStr, }; @@ -15,91 +15,121 @@ use tinywasm_types::WasmValue; // This module essentially contains the public APIs to interact with the data stored in the store /// A reference to a memory instance -#[derive(Debug, Clone)] -pub struct MemoryRef { - pub(crate) instance: Rc>, +#[derive(Debug)] +pub struct MemoryRef<'a> { + pub(crate) instance: Ref<'a, MemoryInstance>, } /// A borrowed reference to a memory instance #[derive(Debug)] -pub struct BorrowedMemory<'a> { - pub(crate) instance: Ref<'a, MemoryInstance>, +pub struct MemoryRefMut<'a> { + pub(crate) instance: RefMut<'a, MemoryInstance>, } -impl<'a> BorrowedMemory<'a> { +impl<'a> MemoryRefLoad for MemoryRef<'a> { /// Load a slice of memory - pub fn load(&self, offset: usize, len: usize) -> Result<&[u8]> { + fn load(&self, offset: usize, len: usize) -> Result<&[u8]> { self.instance.load(offset, 0, len) } +} - /// Load a C-style string from memory - pub fn load_cstr(&self, offset: usize, len: usize) -> Result<&CStr> { - let bytes = self.load(offset, len)?; - CStr::from_bytes_with_nul(bytes).map_err(|_| crate::Error::Other("Invalid C-style string".to_string())) +impl<'a> MemoryRefLoad for MemoryRefMut<'a> { + /// Load a slice of memory + fn load(&self, offset: usize, len: usize) -> Result<&[u8]> { + self.instance.load(offset, 0, len) } +} - /// Load a C-style string from memory, stopping at the first nul byte - pub fn load_cstr_until_nul(&self, offset: usize, max_len: usize) -> Result<&CStr> { - let bytes = self.load(offset, max_len)?; - CStr::from_bytes_until_nul(bytes).map_err(|_| crate::Error::Other("Invalid C-style string".to_string())) +impl MemoryRef<'_> { + /// Load a slice of memory + pub fn load(&self, offset: usize, len: usize) -> Result<&[u8]> { + self.instance.load(offset, 0, len) } -} -impl MemoryRef { - /// Borrow the memory instance - /// - /// This is useful for when you want to load only a reference to a slice of memory - /// without copying the data. The borrow should be dropped before any other memory - /// operations are performed. - pub fn borrow(&self) -> BorrowedMemory<'_> { - BorrowedMemory { instance: self.instance.borrow() } + /// Load a slice of memory as a vector + pub fn load_vec(&self, offset: usize, len: usize) -> Result> { + self.load(offset, len).map(|x| x.to_vec()) } +} +impl MemoryRefMut<'_> { /// Load a slice of memory + pub fn load(&self, offset: usize, len: usize) -> Result<&[u8]> { + self.instance.load(offset, 0, len) + } + + /// Load a slice of memory as a vector pub fn load_vec(&self, offset: usize, len: usize) -> Result> { - self.instance.borrow().load(offset, 0, len).map(|x| x.to_vec()) + self.load(offset, len).map(|x| x.to_vec()) } /// Grow the memory by the given number of pages - pub fn grow(&self, delta_pages: i32) -> Option { - self.instance.borrow_mut().grow(delta_pages) + pub fn grow(&mut self, delta_pages: i32) -> Option { + self.instance.grow(delta_pages) } /// Get the current size of the memory in pages - pub fn page_count(&self) -> usize { - self.instance.borrow().page_count() + pub fn page_count(&mut self) -> usize { + self.instance.page_count() } /// Copy a slice of memory to another place in memory - pub fn copy_within(&self, src: usize, dst: usize, len: usize) -> Result<()> { - self.instance.borrow_mut().copy_within(src, dst, len) + pub fn copy_within(&mut self, src: usize, dst: usize, len: usize) -> Result<()> { + self.instance.copy_within(src, dst, len) } /// Fill a slice of memory with a value - pub fn fill(&self, offset: usize, len: usize, val: u8) -> Result<()> { - self.instance.borrow_mut().fill(offset, len, val) + pub fn fill(&mut self, offset: usize, len: usize, val: u8) -> Result<()> { + self.instance.fill(offset, len, val) + } + + /// Store a slice of memory + pub fn store(&mut self, offset: usize, len: usize, data: &[u8]) -> Result<()> { + self.instance.store(offset, 0, data, len) + } +} + +#[doc(hidden)] +pub trait MemoryRefLoad { + fn load(&self, offset: usize, len: usize) -> Result<&[u8]>; + fn load_vec(&self, offset: usize, len: usize) -> Result> { + self.load(offset, len).map(|x| x.to_vec()) + } +} + +/// Convenience methods for loading strings from memory +pub trait MemoryStringExt: MemoryRefLoad { + /// Load a C-style string from memory + fn load_cstr(&self, offset: usize, len: usize) -> Result<&CStr> { + let bytes = self.load(offset, len)?; + CStr::from_bytes_with_nul(bytes).map_err(|_| crate::Error::Other("Invalid C-style string".to_string())) + } + + /// Load a C-style string from memory, stopping at the first nul byte + fn load_cstr_until_nul(&self, offset: usize, max_len: usize) -> Result<&CStr> { + let bytes = self.load(offset, max_len)?; + CStr::from_bytes_until_nul(bytes).map_err(|_| crate::Error::Other("Invalid C-style string".to_string())) } /// Load a UTF-8 string from memory - pub fn load_string(&self, offset: usize, len: usize) -> Result { - let bytes = self.load_vec(offset, len)?; - String::from_utf8(bytes).map_err(|_| crate::Error::Other("Invalid UTF-8 string".to_string())) + fn load_string(&self, offset: usize, len: usize) -> Result { + let bytes = self.load(offset, len)?; + String::from_utf8(bytes.to_vec()).map_err(|_| crate::Error::Other("Invalid UTF-8 string".to_string())) } /// Load a C-style string from memory - pub fn load_cstring(&self, offset: usize, len: usize) -> Result { - Ok(CString::from(self.borrow().load_cstr(offset, len)?)) + fn load_cstring(&self, offset: usize, len: usize) -> Result { + Ok(CString::from(self.load_cstr(offset, len)?)) } /// Load a C-style string from memory, stopping at the first nul byte - pub fn load_cstring_until_nul(&self, offset: usize, max_len: usize) -> Result { - Ok(CString::from(self.borrow().load_cstr_until_nul(offset, max_len)?)) + fn load_cstring_until_nul(&self, offset: usize, max_len: usize) -> Result { + Ok(CString::from(self.load_cstr_until_nul(offset, max_len)?)) } /// Load a JavaScript-style utf-16 string from memory - pub fn load_js_string(&self, offset: usize, len: usize) -> Result { - let memref = self.borrow(); - let bytes = memref.load(offset, len)?; + fn load_js_string(&self, offset: usize, len: usize) -> Result { + let bytes = self.load(offset, len)?; let mut string = String::new(); for i in 0..(len / 2) { let c = u16::from_le_bytes([bytes[i * 2], bytes[i * 2 + 1]]); @@ -109,13 +139,11 @@ impl MemoryRef { } Ok(string) } - - /// Store a slice of memory - pub fn store(&self, offset: usize, len: usize, data: &[u8]) -> Result<()> { - self.instance.borrow_mut().store(offset, 0, data, len) - } } +impl MemoryStringExt for MemoryRef<'_> {} +impl MemoryStringExt for MemoryRefMut<'_> {} + /// A reference to a global instance #[derive(Debug, Clone)] pub struct GlobalRef { diff --git a/crates/tinywasm/src/runtime/interpreter/mod.rs b/crates/tinywasm/src/runtime/interpreter/mod.rs index 3c3b776..79abbf3 100644 --- a/crates/tinywasm/src/runtime/interpreter/mod.rs +++ b/crates/tinywasm/src/runtime/interpreter/mod.rs @@ -418,12 +418,39 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M mem.fill(dst as usize, size as usize, val as u8)?; } - // MemoryInit(data_index, mem_index) => {} - // DataDrop(data_index) => { - // // let data_idx = module.resolve_data_addr(*data_index); - // // let data = store.get_data(data_idx as usize)?; - // // data.borrow_mut().drop()?; - // } + MemoryInit(data_index, mem_index) => { + let size = stack.values.pop_t::()? as usize; + let offset = stack.values.pop_t::()? as usize; + let dst = stack.values.pop_t::()? as usize; + + let data_idx = module.resolve_data_addr(*data_index); + let Some(ref data) = store.get_data(data_idx as usize)?.data else { + cold(); + return Err(Trap::MemoryOutOfBounds { offset: 0, len: 0, max: 0 }.into()); + }; + + let mem_idx = module.resolve_mem_addr(*mem_index); + let mem = store.get_mem(mem_idx as usize)?; + + let data_len = data.len(); + if offset + size > data_len { + cold(); + return Err(Trap::MemoryOutOfBounds { offset, len: size, max: data_len }.into()); + } + + let mut mem = mem.borrow_mut(); + let data = &data[offset..(offset + size)]; + + // mem.store checks bounds + mem.store(dst, 0, data, size)?; + } + + DataDrop(data_index) => { + let data_idx = module.resolve_data_addr(*data_index); + let data = store.get_data_mut(data_idx as usize)?; + data.drop(); + } + I32Store(arg) => mem_store!(i32, arg, stack, store, module), I64Store(arg) => mem_store!(i64, arg, stack, store, module), F32Store(arg) => mem_store!(f32, arg, stack, store, module), diff --git a/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs b/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs index 095fe9f..5620249 100644 --- a/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs +++ b/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs @@ -10,67 +10,83 @@ pub(super) trait FExt { } impl FExt for f64 { + #[inline] fn round(self) -> Self { libm::round(self) } + #[inline] fn abs(self) -> Self { libm::fabs(self) } + #[inline] fn signum(self) -> Self { libm::copysign(1.0, self) } + #[inline] fn ceil(self) -> Self { libm::ceil(self) } + #[inline] fn floor(self) -> Self { libm::floor(self) } + #[inline] fn trunc(self) -> Self { libm::trunc(self) } + #[inline] fn sqrt(self) -> Self { libm::sqrt(self) } + #[inline] fn copysign(self, other: Self) -> Self { libm::copysign(self, other) } } impl FExt for f32 { + #[inline] fn round(self) -> Self { libm::roundf(self) } + #[inline] fn abs(self) -> Self { libm::fabsf(self) } + #[inline] fn signum(self) -> Self { libm::copysignf(1.0, self) } + #[inline] fn ceil(self) -> Self { libm::ceilf(self) } + #[inline] fn floor(self) -> Self { libm::floorf(self) } + #[inline] fn trunc(self) -> Self { libm::truncf(self) } + #[inline] fn sqrt(self) -> Self { libm::sqrtf(self) } + #[inline] fn copysign(self, other: Self) -> Self { libm::copysignf(self, other) } diff --git a/crates/tinywasm/src/store/mod.rs b/crates/tinywasm/src/store/mod.rs index f89b931..1526eb1 100644 --- a/crates/tinywasm/src/store/mod.rs +++ b/crates/tinywasm/src/store/mod.rs @@ -287,40 +287,38 @@ impl Store { let data_count = self.data.datas.len(); let mut data_addrs = Vec::with_capacity(data_count); for (i, data) in datas.into_iter().enumerate() { - use tinywasm_types::DataKind::*; - match data.kind { - Active { mem: mem_addr, offset } => { - // a. Assert: memidx == 0 - if mem_addr != 0 { - return Err(Error::UnsupportedFeature("data segments for non-zero memories".to_string())); - } + let data_val = + match data.kind { + tinywasm_types::DataKind::Active { mem: mem_addr, offset } => { + // a. Assert: memidx == 0 + if mem_addr != 0 { + return Err(Error::UnsupportedFeature("data segments for non-zero memories".to_string())); + } - let mem_addr = mem_addrs - .get(mem_addr as usize) - .copied() - .ok_or_else(|| Error::Other(format!("memory {} not found for data segment {}", mem_addr, i)))?; + let mem_addr = mem_addrs.get(mem_addr as usize).copied().ok_or_else(|| { + Error::Other(format!("memory {} not found for data segment {}", mem_addr, i)) + })?; - let offset = self.eval_i32_const(&offset)?; + let offset = self.eval_i32_const(&offset)?; - let mem = - self.data.memories.get_mut(mem_addr as usize).ok_or_else(|| { + let mem = self.data.memories.get_mut(mem_addr as usize).ok_or_else(|| { Error::Other(format!("memory {} not found for data segment {}", mem_addr, i)) })?; - // See comment for active element sections in the function above why we need to do this here - if let Err(Error::Trap(trap)) = - mem.borrow_mut().store(offset as usize, 0, &data.data, data.data.len()) - { - return Ok((data_addrs.into_boxed_slice(), Some(trap))); - } + // See comment for active element sections in the function above why we need to do this here + if let Err(Error::Trap(trap)) = + mem.borrow_mut().store(offset as usize, 0, &data.data, data.data.len()) + { + return Ok((data_addrs.into_boxed_slice(), Some(trap))); + } - // drop the data - continue; - } - Passive => {} - } + // drop the data + None + } + tinywasm_types::DataKind::Passive => Some(data.data.to_vec()), + }; - self.data.datas.push(DataInstance::new(data.data.to_vec(), idx)); + self.data.datas.push(DataInstance::new(data_val, idx)); data_addrs.push((i + data_count) as Addr); } @@ -413,6 +411,16 @@ impl Store { self.data.tables.get(addr).ok_or_else(|| Error::Other(format!("table {} not found", addr))) } + /// Get the data at the actual index in the store + pub(crate) fn get_data(&self, addr: usize) -> Result<&DataInstance> { + self.data.datas.get(addr).ok_or_else(|| Error::Other(format!("table {} not found", addr))) + } + + /// Get the data at the actual index in the store + pub(crate) fn get_data_mut(&mut self, addr: usize) -> Result<&mut DataInstance> { + self.data.datas.get_mut(addr).ok_or_else(|| Error::Other(format!("table {} not found", addr))) + } + /// Get the element at the actual index in the store pub(crate) fn get_elem(&self, addr: usize) -> Result<&ElementInstance> { self.data.elements.get(addr).ok_or_else(|| Error::Other(format!("element {} not found", addr))) @@ -621,12 +629,22 @@ impl ElementInstance { /// See #[derive(Debug)] pub(crate) struct DataInstance { - pub(crate) _data: Vec, + pub(crate) data: Option>, pub(crate) _owner: ModuleInstanceAddr, // index into store.module_instances } impl DataInstance { - pub(crate) fn new(data: Vec, owner: ModuleInstanceAddr) -> Self { - Self { _data: data, _owner: owner } + pub(crate) fn new(data: Option>, owner: ModuleInstanceAddr) -> Self { + Self { data, _owner: owner } + } + + pub(crate) fn drop(&mut self) -> Option<()> { + match self.data { + None => None, + Some(_) => { + let _ = self.data.take(); + Some(()) + } + } } } diff --git a/crates/tinywasm/tests/generated/2.0.csv b/crates/tinywasm/tests/generated/2.0.csv index 404fe16..fbf18ae 100644 --- a/crates/tinywasm/tests/generated/2.0.csv +++ b/crates/tinywasm/tests/generated/2.0.csv @@ -1,2 +1,2 @@ 0.3.0,26722,1161,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":156,"failed":0},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":112,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":174,"failed":0},{"name":"bulk.wast","passed":8,"failed":109},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":170,"failed":0},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":61,"failed":0},{"name":"elem.wast","passed":98,"failed":1},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":96,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":900,"failed":0},{"name":"float_literals.wast","passed":163,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":441,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":172,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":110,"failed":0},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":171,"failed":12},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":79,"failed":0},{"name":"memory_copy.wast","passed":3928,"failed":522},{"name":"memory_fill.wast","passed":64,"failed":36},{"name":"memory_grow.wast","passed":96,"failed":0},{"name":"memory_init.wast","passed":177,"failed":63},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"ref_func.wast","passed":9,"failed":8},{"name":"ref_is_null.wast","passed":4,"failed":12},{"name":"ref_null.wast","passed":1,"failed":2},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":148,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table-sub.wast","passed":2,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"table_copy.wast","passed":1613,"failed":115},{"name":"table_fill.wast","passed":23,"failed":22},{"name":"table_get.wast","passed":10,"failed":6},{"name":"table_grow.wast","passed":21,"failed":29},{"name":"table_init.wast","passed":594,"failed":186},{"name":"table_set.wast","passed":19,"failed":7},{"name":"table_size.wast","passed":8,"failed":31},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":7,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] -0.4.0-alpha.0,27464,419,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":156,"failed":0},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":112,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":174,"failed":0},{"name":"bulk.wast","passed":53,"failed":64},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":170,"failed":0},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":61,"failed":0},{"name":"elem.wast","passed":99,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":96,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":900,"failed":0},{"name":"float_literals.wast","passed":163,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":441,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":172,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":110,"failed":0},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":183,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":79,"failed":0},{"name":"memory_copy.wast","passed":4450,"failed":0},{"name":"memory_fill.wast","passed":100,"failed":0},{"name":"memory_grow.wast","passed":96,"failed":0},{"name":"memory_init.wast","passed":177,"failed":63},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"ref_func.wast","passed":9,"failed":8},{"name":"ref_is_null.wast","passed":4,"failed":12},{"name":"ref_null.wast","passed":1,"failed":2},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":148,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table-sub.wast","passed":2,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"table_copy.wast","passed":1613,"failed":115},{"name":"table_fill.wast","passed":23,"failed":22},{"name":"table_get.wast","passed":10,"failed":6},{"name":"table_grow.wast","passed":21,"failed":29},{"name":"table_init.wast","passed":720,"failed":60},{"name":"table_set.wast","passed":19,"failed":7},{"name":"table_size.wast","passed":8,"failed":31},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":7,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] +0.4.0-alpha.0,27549,334,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":156,"failed":0},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":112,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":174,"failed":0},{"name":"bulk.wast","passed":75,"failed":42},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":170,"failed":0},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":61,"failed":0},{"name":"elem.wast","passed":99,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":96,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":900,"failed":0},{"name":"float_literals.wast","passed":163,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":441,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":172,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":110,"failed":0},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":183,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":79,"failed":0},{"name":"memory_copy.wast","passed":4450,"failed":0},{"name":"memory_fill.wast","passed":100,"failed":0},{"name":"memory_grow.wast","passed":96,"failed":0},{"name":"memory_init.wast","passed":240,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"ref_func.wast","passed":9,"failed":8},{"name":"ref_is_null.wast","passed":4,"failed":12},{"name":"ref_null.wast","passed":1,"failed":2},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":148,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table-sub.wast","passed":2,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"table_copy.wast","passed":1613,"failed":115},{"name":"table_fill.wast","passed":23,"failed":22},{"name":"table_get.wast","passed":10,"failed":6},{"name":"table_grow.wast","passed":21,"failed":29},{"name":"table_init.wast","passed":720,"failed":60},{"name":"table_set.wast","passed":19,"failed":7},{"name":"table_size.wast","passed":8,"failed":31},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":7,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] diff --git a/examples/rust/build.sh b/examples/rust/build.sh index a13357d..5450345 100755 --- a/examples/rust/build.sh +++ b/examples/rust/build.sh @@ -10,7 +10,7 @@ dest_dir="out" mkdir -p "$dest_dir" for bin in "${bins[@]}"; do - RUSTFLAGS="-C target-feature=+reference-types -C panic=abort" cargo build --target wasm32-unknown-unknown --package rust-wasm-examples --profile=wasm --bin "$bin" + RUSTFLAGS="-C target-feature=+reference-types,+bulk-memory -C panic=abort" cargo build --target wasm32-unknown-unknown --package rust-wasm-examples --profile=wasm --bin "$bin" cp "$out_dir/$bin.wasm" "$dest_dir/" wasm-opt "$dest_dir/$bin.wasm" -o "$dest_dir/$bin.wasm" -O --intrinsic-lowering -O diff --git a/examples/wasm-rust.rs b/examples/wasm-rust.rs index 468ab2e..96e3419 100644 --- a/examples/wasm-rust.rs +++ b/examples/wasm-rust.rs @@ -1,5 +1,5 @@ use color_eyre::eyre::Result; -use tinywasm::{Extern, FuncContext, Imports, Module, Store}; +use tinywasm::{Extern, FuncContext, Imports, MemoryStringExt, Module, Store}; fn main() -> Result<()> { let args = std::env::args().collect::>(); @@ -56,7 +56,7 @@ fn hello() -> Result<()> { "env", "print_utf8", Extern::typed_func(|mut ctx: FuncContext<'_>, args: (i64, i32)| { - let mem = ctx.memory("memory")?; + let mem = ctx.exported_memory("memory")?; let ptr = args.0 as usize; let len = args.1 as usize; let string = mem.load_string(ptr, len)?; @@ -69,7 +69,7 @@ fn hello() -> Result<()> { let arg_ptr = instance.exported_func::<(), i32>(&mut store, "arg_ptr")?.call(&mut store, ())?; let arg = b"world"; - instance.exported_memory(&mut store, "memory")?.store(arg_ptr as usize, arg.len(), arg)?; + instance.exported_memory_mut(&mut store, "memory")?.store(arg_ptr as usize, arg.len(), arg)?; let hello = instance.exported_func::(&mut store, "hello")?; hello.call(&mut store, arg.len() as i32)?; -- cgit v1.3.1