diff options
| author | Henry Gressmann <mail@henrygressmann.de> | 2024-01-26 15:16:05 +0100 |
|---|---|---|
| committer | Henry Gressmann <mail@henrygressmann.de> | 2024-01-26 15:16:05 +0100 |
| commit | d8d439e6401607fab18feb5025f3b91f135e3a50 (patch) | |
| tree | f20f1d5961a5b0810cceca575a206ad45d2a3a9c | |
| parent | 67f0fd68f1f60f0629d997d5181e5e06440258d7 (diff) | |
feat: add more examples, work on new public api
Signed-off-by: Henry Gressmann <mail@henrygressmann.de>
| -rw-r--r-- | .cargo/config.toml | 1 | ||||
| -rw-r--r-- | .github/workflows/test.yaml | 8 | ||||
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | .vscode/settings.json | 6 | ||||
| -rw-r--r-- | Cargo.lock | 7 | ||||
| -rw-r--r-- | Cargo.toml | 6 | ||||
| -rw-r--r-- | crates/cli/src/bin.rs | 2 | ||||
| -rw-r--r-- | crates/tinywasm/src/export.rs | 1 | ||||
| -rw-r--r-- | crates/tinywasm/src/imports.rs | 9 | ||||
| -rw-r--r-- | crates/tinywasm/src/instance.rs | 36 | ||||
| -rw-r--r-- | crates/tinywasm/src/lib.rs | 5 | ||||
| -rw-r--r-- | crates/tinywasm/src/reference.rs | 17 | ||||
| -rw-r--r-- | crates/tinywasm/tests/testsuite/run.rs | 2 | ||||
| -rw-r--r-- | crates/tinywasm/tests/testsuite/util.rs | 4 | ||||
| -rw-r--r-- | crates/types/src/lib.rs | 6 | ||||
| -rw-r--r-- | examples/README.md | 7 | ||||
| -rw-r--r-- | examples/rust/Cargo.toml | 14 | ||||
| -rw-r--r-- | examples/rust/README.md | 5 | ||||
| -rwxr-xr-x | examples/rust/build.sh | 7 | ||||
| -rw-r--r-- | examples/rust/src/fibonacci.rs | 17 | ||||
| -rw-r--r-- | examples/rust/src/tinywasm.rs | 2 | ||||
| -rw-r--r-- | examples/wasm-rust.rs | 19 | ||||
| -rw-r--r-- | examples/wasm/add.wasm | bin | 0 -> 65 bytes |
23 files changed, 138 insertions, 45 deletions
diff --git a/.cargo/config.toml b/.cargo/config.toml index 19d47a2..807bc56 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,7 +1,6 @@ [alias] version-dev="workspaces version --no-git-commit --force tinywasm*" dev="run -- -l debug run" - test-mvp="test --package tinywasm --test test-mvp --release -- --enable " test-2="test --package tinywasm --test test-two --release -- --enable " test-wast="test --package tinywasm --test test-wast -- --enable " diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 92d56f4..05f7397 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -20,10 +20,10 @@ jobs: run: rustup update stable - name: Build (stable) - run: cargo +stable build --workspace --exclude wasm-testsuite + run: cargo +stable build --workspace - name: Run tests (stable) - run: cargo +stable test --workspace --exclude wasm-testsuite + run: cargo +stable test --workspace - name: Run MVP testsuite run: cargo +stable test-mvp @@ -41,10 +41,10 @@ jobs: run: rustup update nightly - name: Build (nightly, no default features) - run: cargo +nightly build --workspace --exclude wasm-testsuite --exclude rust-wasm-examples --no-default-features + run: cargo +nightly build --workspace --no-default-features - name: Run tests (nightly, no default features) - run: cargo +nightly test --workspace --exclude wasm-testsuite --exclude rust-wasm-examples --no-default-features + run: cargo +nightly test --workspace --no-default-features - name: Run MVP testsuite (nightly) run: cargo +nightly test-mvp @@ -1,4 +1,6 @@ /target notes.md examples/rust/out/* +examples/rust/target +examples/rust/Cargo.lock examples/wast/* diff --git a/.vscode/settings.json b/.vscode/settings.json index 4f9768f..51f36b4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,9 @@ { "search.exclude": { "**/wasm-testsuite/data": true - } + }, + "rust-analyzer.linkedProjects": [ + "./Cargo.toml", + "./examples/rust/Cargo.toml" + ] }
\ No newline at end of file @@ -1046,13 +1046,6 @@ dependencies = [ ] [[package]] -name = "rust-wasm-examples" -version = "0.0.0" -dependencies = [ - "tinywasm", -] - -[[package]] name = "rustc-demangle" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1,5 +1,5 @@ [workspace] -members=["crates/*", "examples/rust"] +members=["crates/*"] resolver="2" [profile.wasm] @@ -21,6 +21,10 @@ name="tinywasm-root" publish=false edition="2021" +[[example]] +name="wasm-rust" +test=false + [dev-dependencies] color-eyre="0.6" tinywasm={path="crates/tinywasm"} diff --git a/crates/cli/src/bin.rs b/crates/cli/src/bin.rs index 1c166cf..34d4bcd 100644 --- a/crates/cli/src/bin.rs +++ b/crates/cli/src/bin.rs @@ -112,7 +112,7 @@ fn run(module: Module, func: Option<String>, args: Vec<WasmValue>) -> Result<()> let instance = module.instantiate(&mut store, None)?; if let Some(func) = func { - let func = instance.exported_func_by_name(&store, &func)?; + let func = instance.exported_func_untyped(&store, &func)?; let res = func.call(&mut store, &args)?; info!("{res:?}"); } diff --git a/crates/tinywasm/src/export.rs b/crates/tinywasm/src/export.rs deleted file mode 100644 index 8b13789..0000000 --- a/crates/tinywasm/src/export.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/crates/tinywasm/src/imports.rs b/crates/tinywasm/src/imports.rs index a640081..9c5086a 100644 --- a/crates/tinywasm/src/imports.rs +++ b/crates/tinywasm/src/imports.rs @@ -77,6 +77,11 @@ impl FuncContext<'_> { pub fn module(&self) -> &crate::ModuleInstance { self.module } + + /// Get a reference to an exported memory + pub fn memory(&mut self, name: &str) -> Result<crate::MemoryRef> { + self.module.exported_memory(self.store, name) + } } impl Debug for HostFunction { @@ -276,7 +281,7 @@ impl Imports { if let Some(addr) = self.modules.get(&name.module) { let instance = store.get_module_instance(*addr)?; - return Some(ResolvedExtern::Store(instance.export(&import.name)?)); + return Some(ResolvedExtern::Store(instance.export_addr(&import.name)?)); } None @@ -398,7 +403,7 @@ impl Imports { Self::compare_table_types(import, &table.borrow().kind, ty)?; imports.tables.push(table_addr); } - (ExternVal::Mem(memory_addr), ImportKind::Memory(ty)) => { + (ExternVal::Memory(memory_addr), ImportKind::Memory(ty)) => { let mem = store.get_mem(memory_addr as usize)?; let (size, kind) = { let mem = mem.borrow(); diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs index d750cd5..dfee2ca 100644 --- a/crates/tinywasm/src/instance.rs +++ b/crates/tinywasm/src/instance.rs @@ -1,12 +1,9 @@ use alloc::{boxed::Box, format, string::ToString, sync::Arc}; -use tinywasm_types::{ - DataAddr, ElemAddr, Export, ExternVal, ExternalKind, FuncAddr, FuncType, GlobalAddr, Import, MemAddr, - ModuleInstanceAddr, TableAddr, -}; +use tinywasm_types::*; use crate::{ func::{FromWasmValueTuple, IntoWasmValueTuple}, - log, Error, FuncHandle, FuncHandleTyped, Imports, Module, Result, Store, + log, Error, FuncHandle, FuncHandleTyped, Imports, MemoryRef, Module, Result, Store, }; /// An instanciated WebAssembly module @@ -106,7 +103,7 @@ impl ModuleInstance { } /// Get a export by name - pub fn export(&self, name: &str) -> Option<ExternVal> { + pub fn export_addr(&self, name: &str) -> Option<ExternVal> { let exports = self.0.exports.iter().find(|e| e.name == name.into())?; let kind = exports.kind.clone(); let addr = match kind { @@ -162,12 +159,12 @@ impl ModuleInstance { } /// Get an exported function by name - pub fn exported_func_by_name(&self, store: &Store, name: &str) -> Result<FuncHandle> { + pub fn exported_func_untyped(&self, store: &Store, name: &str) -> Result<FuncHandle> { if self.0.store_id != store.id() { return Err(Error::InvalidStore); } - let export = self.export(name).ok_or_else(|| Error::Other(format!("Export not found: {}", name)))?; + let export = self.export_addr(name).ok_or_else(|| Error::Other(format!("Export not found: {}", name)))?; let ExternVal::Func(func_addr) = export else { return Err(Error::Other(format!("Export is not a function: {}", name))); }; @@ -179,15 +176,32 @@ impl ModuleInstance { } /// Get a typed exported function by name - pub fn typed_func<P, R>(&self, store: &Store, name: &str) -> Result<FuncHandleTyped<P, R>> + pub fn exported_func<P, R>(&self, store: &Store, name: &str) -> Result<FuncHandleTyped<P, R>> where P: IntoWasmValueTuple, R: FromWasmValueTuple, { - let func = self.exported_func_by_name(store, name)?; + let func = self.exported_func_untyped(store, name)?; Ok(FuncHandleTyped { func, marker: core::marker::PhantomData }) } + /// Get an exported memory by name + pub fn exported_memory(&self, store: &mut Store, name: &str) -> Result<MemoryRef> { + 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(store, mem_addr)?; + Ok(mem) + } + + /// Get a memory by address + pub fn memory(&self, store: &Store, addr: MemAddr) -> Result<MemoryRef> { + let addr = self.resolve_mem_addr(addr); + let mem = store.get_mem(addr as usize)?; + Ok(MemoryRef { instance: mem.clone() }) + } + /// Get the start function of the module /// /// Returns None if the module has no start function @@ -204,7 +218,7 @@ impl ModuleInstance { Some(func_index) => func_index, None => { // alternatively, check for a _start function in the exports - let Some(ExternVal::Func(func_addr)) = self.export("_start") else { + let Some(ExternVal::Func(func_addr)) = self.export_addr("_start") else { return Ok(None); }; diff --git a/crates/tinywasm/src/lib.rs b/crates/tinywasm/src/lib.rs index 36270a7..f2951b6 100644 --- a/crates/tinywasm/src/lib.rs +++ b/crates/tinywasm/src/lib.rs @@ -51,7 +51,7 @@ //! // Get a typed handle to the exported "add" function //! // Alternatively, you can use `instance.get_func` to get an untyped handle //! // that takes and returns [`WasmValue`]s -//! let func = instance.typed_func::<(i32, i32), i32>(&mut store, "add")?; +//! let func = instance.exported_func::<(i32, i32), i32>(&mut store, "add")?; //! let res = func.call(&mut store, (1, 2))?; //! //! assert_eq!(res, 3); @@ -99,6 +99,9 @@ pub use module::Module; mod instance; pub use instance::ModuleInstance; +mod reference; +pub use reference::*; + mod func; pub use func::{FuncHandle, FuncHandleTyped}; diff --git a/crates/tinywasm/src/reference.rs b/crates/tinywasm/src/reference.rs new file mode 100644 index 0000000..21471f6 --- /dev/null +++ b/crates/tinywasm/src/reference.rs @@ -0,0 +1,17 @@ +use core::cell::RefCell; + +use alloc::rc::Rc; + +use crate::{GlobalInstance, MemoryInstance}; + +/// A reference to a memory instance +#[derive(Debug, Clone)] +pub struct MemoryRef { + pub(crate) instance: Rc<RefCell<MemoryInstance>>, +} + +/// A reference to a global instance +#[derive(Debug, Clone)] +pub struct GlobalRef { + pub(crate) instance: Rc<RefCell<GlobalInstance>>, +} diff --git a/crates/tinywasm/tests/testsuite/run.rs b/crates/tinywasm/tests/testsuite/run.rs index 79d9acc..c44c3fb 100644 --- a/crates/tinywasm/tests/testsuite/run.rs +++ b/crates/tinywasm/tests/testsuite/run.rs @@ -428,7 +428,7 @@ impl TestSuite { continue; }; - let module_global = match match module.export(global) { + let module_global = match match module.export_addr(global) { Some(ExternVal::Global(addr)) => { store.get_global_val(addr as usize).map_err(|_| eyre!("failed to get global")) } diff --git a/crates/tinywasm/tests/testsuite/util.rs b/crates/tinywasm/tests/testsuite/util.rs index b74eec5..09a4769 100644 --- a/crates/tinywasm/tests/testsuite/util.rs +++ b/crates/tinywasm/tests/testsuite/util.rs @@ -25,7 +25,7 @@ pub fn exec_fn_instance( return Err(tinywasm::Error::Other("no instance found".to_string())); }; - let func = instance.exported_func_by_name(store, name)?; + let func = instance.exported_func_untyped(store, name)?; func.call(store, args) } @@ -42,7 +42,7 @@ pub fn exec_fn( let mut store = tinywasm::Store::new(); let module = tinywasm::Module::from(module); let instance = module.instantiate(&mut store, imports)?; - instance.exported_func_by_name(&store, name)?.call(&mut store, args) + instance.exported_func_untyped(&store, name)?.call(&mut store, args) } pub fn catch_unwind_silent<F: FnOnce() -> R, R>(f: F) -> std::thread::Result<R> { diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index d0d854c..365ead7 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -316,7 +316,7 @@ pub type ModuleInstanceAddr = Addr; pub enum ExternVal { Func(FuncAddr), Table(TableAddr), - Mem(MemAddr), + Memory(MemAddr), Global(GlobalAddr), } @@ -325,7 +325,7 @@ impl ExternVal { match self { Self::Func(_) => ExternalKind::Func, Self::Table(_) => ExternalKind::Table, - Self::Mem(_) => ExternalKind::Memory, + Self::Memory(_) => ExternalKind::Memory, Self::Global(_) => ExternalKind::Global, } } @@ -334,7 +334,7 @@ impl ExternVal { match kind { ExternalKind::Func => Self::Func(addr), ExternalKind::Table => Self::Table(addr), - ExternalKind::Memory => Self::Mem(addr), + ExternalKind::Memory => Self::Memory(addr), ExternalKind::Global => Self::Global(addr), } } diff --git a/examples/README.md b/examples/README.md index ce47073..94f974b 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,10 +1,10 @@ # Examples -## WasmRust +## Wasm-Rust These are examples using WebAssembly generated from Rust code. -To run these, you first need to build the Rust code into WebAssembly, since the wasm files are not included in the repository to keep it small. -This requires the `wasm32-unknown-unknown` target and `wasm-opt` to be installed (available via Binaryen). +To run these, you first need to build the Rust code, since the resulting wasm files are not included in the repository to keep it small. +This requires the `wasm32-unknown-unknown` target and `wasm-opt` to be installed (available via [Binaryen](https://github.com/WebAssembly/binaryen)). ```bash $ ./examples/rust/build.sh @@ -20,3 +20,4 @@ Where `<example>` is one of the following: - `hello`: A simple example that prints a number to the console. - `tinywasm`: Runs `hello` using TinyWasm - inside of TinyWasm itself! +- `fibonacci`: Calculates the x-th Fibonacci number. diff --git a/examples/rust/Cargo.toml b/examples/rust/Cargo.toml index 9ff80dc..d557392 100644 --- a/examples/rust/Cargo.toml +++ b/examples/rust/Cargo.toml @@ -1,5 +1,8 @@ cargo-features=["per-package-target"] +# treat this as an independent package +[workspace] + [package] publish=false name="rust-wasm-examples" @@ -16,3 +19,14 @@ path="src/hello.rs" [[bin]] name="tinywasm" path="src/tinywasm.rs" + +[[bin]] +name="fibonacci" +path="src/fibonacci.rs" + +[profile.wasm] +opt-level="s" +lto="thin" +codegen-units=1 +panic="abort" +inherits="release" diff --git a/examples/rust/README.md b/examples/rust/README.md index 8ecac52..1b6be2f 100644 --- a/examples/rust/README.md +++ b/examples/rust/README.md @@ -1 +1,4 @@ -# Examples using Rust compiled to WebAssembly +# WebAssembly Rust Examples + +This is a seperate crate that generates WebAssembly from Rust code. +It is used by the `wasm-rust` example. diff --git a/examples/rust/build.sh b/examples/rust/build.sh index 2c8069a..a8c587a 100755 --- a/examples/rust/build.sh +++ b/examples/rust/build.sh @@ -1,11 +1,14 @@ #!/usr/bin/env bash cd "$(dirname "$0")" -bins=("hello" "tinywasm") +bins=("hello" "tinywasm" "fibonacci") exclude_wat=("tinywasm") -out_dir="../../target/wasm32-unknown-unknown/wasm" +out_dir="./target/wasm32-unknown-unknown/wasm" dest_dir="out" +# ensure out dir exists +mkdir -p "$dest_dir" + for bin in "${bins[@]}"; do cargo build --target wasm32-unknown-unknown --package rust-wasm-examples --profile=wasm --bin "$bin" diff --git a/examples/rust/src/fibonacci.rs b/examples/rust/src/fibonacci.rs new file mode 100644 index 0000000..7493132 --- /dev/null +++ b/examples/rust/src/fibonacci.rs @@ -0,0 +1,17 @@ +#![no_std] +#![no_main] + +#[cfg(not(test))] +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + core::arch::wasm32::unreachable() +} + +#[no_mangle] +// The rust compiler will convert this to an iterative algorithm. +pub extern "C" fn fibonacci(n: i32) -> i32 { + if n <= 1 { + return n; + } + fibonacci(n - 1) + fibonacci(n - 2) +} diff --git a/examples/rust/src/tinywasm.rs b/examples/rust/src/tinywasm.rs index 0f18ab9..0fb9261 100644 --- a/examples/rust/src/tinywasm.rs +++ b/examples/rust/src/tinywasm.rs @@ -26,7 +26,7 @@ fn run() -> tinywasm::Result<()> { )?; let instance = module.instantiate(&mut store, Some(imports))?; - let add_and_print = instance.typed_func::<(i32, i32), ()>(&mut store, "add_and_print")?; + let add_and_print = instance.exported_func::<(i32, i32), ()>(&mut store, "add_and_print")?; add_and_print.call(&mut store, (1, 2))?; Ok(()) } diff --git a/examples/wasm-rust.rs b/examples/wasm-rust.rs index 3b8877e..3b26863 100644 --- a/examples/wasm-rust.rs +++ b/examples/wasm-rust.rs @@ -13,6 +13,7 @@ fn main() -> Result<()> { match args[1].as_str() { "hello" => hello()?, + "fibonacci" => fibonacci()?, "tinywasm" => tinywasm()?, _ => {} } @@ -36,7 +37,7 @@ fn tinywasm() -> Result<()> { )?; let instance = module.instantiate(&mut store, Some(imports))?; - let hello = instance.typed_func::<(), ()>(&mut store, "hello")?; + let hello = instance.exported_func::<(), ()>(&mut store, "hello")?; hello.call(&mut store, ())?; Ok(()) @@ -58,8 +59,22 @@ fn hello() -> Result<()> { )?; let instance = module.instantiate(&mut store, Some(imports))?; - let add_and_print = instance.typed_func::<(i32, i32), ()>(&mut store, "add_and_print")?; + let add_and_print = instance.exported_func::<(i32, i32), ()>(&mut store, "add_and_print")?; add_and_print.call(&mut store, (1, 2))?; Ok(()) } + +fn fibonacci() -> Result<()> { + const FIBONACCI_WASM: &[u8] = include_bytes!("./rust/out/fibonacci.wasm"); + let module = Module::parse_bytes(&FIBONACCI_WASM)?; + let mut store = Store::default(); + + let instance = module.instantiate(&mut store, None)?; + let fibonacci = instance.exported_func::<i32, i32>(&mut store, "fibonacci")?; + let n = 30; + let result = fibonacci.call(&mut store, n)?; + println!("fibonacci({}) = {}", n, result); + + Ok(()) +} diff --git a/examples/wasm/add.wasm b/examples/wasm/add.wasm Binary files differnew file mode 100644 index 0000000..92e3432 --- /dev/null +++ b/examples/wasm/add.wasm |
