Skip to content

Commit f3d890a

Browse files
docs: working selfhosted example
Signed-off-by: Henry Gressmann <[email protected]>
1 parent 461126c commit f3d890a

File tree

18 files changed

+159
-233
lines changed

18 files changed

+159
-233
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
/target
22
notes.md
3-
examples/tinywasm.wat
3+
examples/rust/out/*
44
examples/wast/*

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
[workspace]
2-
members=["crates/*"]
2+
members=["crates/*", "examples/rust"]
33
resolver="2"
44

5+
[profile.wasm]
6+
opt-level="z"
7+
lto="thin"
8+
codegen-units=1
9+
panic="abort"
10+
inherits="release"
11+
512
[workspace.package]
613
version="0.3.0-alpha.0"
714
edition="2021"

README.md

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
# Status
1414

15-
TinyWasm, starting from upcoming 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).
15+
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)).
1616

1717
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.
1818
Results of the tests can be found [here](https://github.com/explodingcamera/tinywasm/tree/main/crates/tinywasm/tests/generated).
@@ -56,16 +56,7 @@ $ cargo add tinywasm
5656
Enables the `tinywasm-parser` crate. This is enabled by default.
5757

5858
With all these features disabled, TinyWasm only depends on `core`, `alloc` and `libm` and can be used in `no_std` environments.
59-
60-
<!-- # 🎯 Goals
61-
62-
- Self-hosted (can run itself compiled to WebAssembly)
63-
- No unsafe code
64-
- Works on `no_std` (with `alloc` the feature and nightly compiler)
65-
- Fully support WebAssembly MVP (1.0)
66-
- Low Memory Usage (less than 10kb)
67-
- Fast Startup Time
68-
- Preemptive multitasking support -->
59+
Since `libm` is not as performant as the compiler's built-in math intrinsics, it is recommended to use the `std` feature if possible (at least [for now](https://github.com/rust-lang/rfcs/issues/2505)).
6960

7061
## Performance
7162

crates/tinywasm/src/error.rs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,8 @@ impl Display for Error {
186186
#[cfg(feature = "std")]
187187
Self::Io(err) => write!(f, "I/O error: {}", err),
188188

189-
Self::Trap(trap) => write!(f, "trap: {}", trap.message()),
190-
Self::Linker(err) => write!(f, "linking error: {}", err.message()),
189+
Self::Trap(trap) => write!(f, "trap: {}", trap),
190+
Self::Linker(err) => write!(f, "linking error: {}", err),
191191
Self::CallStackEmpty => write!(f, "call stack empty"),
192192
Self::InvalidLabelType => write!(f, "invalid label type"),
193193
Self::Other(message) => write!(f, "unknown error: {}", message),
@@ -200,6 +200,42 @@ impl Display for Error {
200200
}
201201
}
202202

203+
impl Display for LinkingError {
204+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
205+
match self {
206+
Self::UnknownImport { module, name } => write!(f, "unknown import: {}.{}", module, name),
207+
Self::IncompatibleImportType { module, name } => {
208+
write!(f, "incompatible import type: {}.{}", module, name)
209+
}
210+
}
211+
}
212+
}
213+
214+
impl Display for Trap {
215+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
216+
match self {
217+
Self::Unreachable => write!(f, "unreachable"),
218+
Self::MemoryOutOfBounds { offset, len, max } => {
219+
write!(f, "out of bounds memory access: offset={}, len={}, max={}", offset, len, max)
220+
}
221+
Self::TableOutOfBounds { offset, len, max } => {
222+
write!(f, "out of bounds table access: offset={}, len={}, max={}", offset, len, max)
223+
}
224+
Self::DivisionByZero => write!(f, "integer divide by zero"),
225+
Self::InvalidConversionToInt => write!(f, "invalid conversion to integer"),
226+
Self::IntegerOverflow => write!(f, "integer overflow"),
227+
Self::CallStackOverflow => write!(f, "call stack exhausted"),
228+
Self::UndefinedElement { index } => write!(f, "undefined element: index={}", index),
229+
Self::UninitializedElement { index } => {
230+
write!(f, "uninitialized element: index={}", index)
231+
}
232+
Self::IndirectCallTypeMismatch { expected, actual } => {
233+
write!(f, "indirect call type mismatch: expected={:?}, actual={:?}", expected, actual)
234+
}
235+
}
236+
}
237+
}
238+
203239
#[cfg(any(feature = "std", all(not(feature = "std"), nightly)))]
204240
impl crate::std::error::Error for Error {}
205241

examples/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Examples
2+
3+
## WasmRust
4+
5+
These are examples using WebAssembly generated from Rust code.
6+
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.
7+
This requires the `wasm32-unknown-unknown` target and `wasm-opt` to be installed (available via Binaryen).
8+
9+
```bash
10+
$ ./examples/rust/build.sh
11+
```
12+
13+
Then you can run the examples:
14+
15+
```bash
16+
$ cargo run --example wasm-rust <example>
17+
```
18+
19+
Where `<example>` is one of the following:
20+
21+
- `hello`: A simple example that prints a number to the console.
22+
- `tinywasm`: Runs `hello` using TinyWasm - inside of TinyWasm itself!

examples/rust/Cargo.toml

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ forced-target="wasm32-unknown-unknown"
77
edition="2021"
88

99
[dependencies]
10-
tinywasm={path="../../crates/tinywasm", default-features=false, features=["parser"]}
11-
embedded-alloc={version="0.5"}
10+
tinywasm={path="../../crates/tinywasm", features=["parser", "std"]}
1211

1312
[[bin]]
1413
name="hello"
@@ -17,7 +16,3 @@ path="src/hello.rs"
1716
[[bin]]
1817
name="tinywasm"
1918
path="src/tinywasm.rs"
20-
21-
[profile.release]
22-
opt-level="z"
23-
panic="abort"

examples/rust/build.sh

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!/usr/bin/env bash
2+
cd "$(dirname "$0")"
3+
4+
bins=("hello" "tinywasm")
5+
exclude_wat=("tinywasm")
6+
out_dir="../../target/wasm32-unknown-unknown/wasm"
7+
dest_dir="out"
8+
9+
for bin in "${bins[@]}"; do
10+
cargo build --target wasm32-unknown-unknown --package rust-wasm-examples --profile=wasm --bin "$bin"
11+
12+
cp "$out_dir/$bin.wasm" "$dest_dir/"
13+
wasm-opt "$dest_dir/$bin.wasm" -o "$dest_dir/$bin.wasm" -O --intrinsic-lowering -O
14+
15+
if [[ ! " ${exclude_wat[@]} " =~ " $bin " ]]; then
16+
wasm2wat "$dest_dir/$bin.wasm" -o "$dest_dir/$bin.wat"
17+
fi
18+
done

examples/rust/src/tinywasm.rs

Lines changed: 21 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,32 @@
1-
#![no_std]
21
#![no_main]
2+
use tinywasm::{Extern, FuncContext};
33

4-
use embedded_alloc::Heap;
5-
// use tinywasm::{Extern, FuncContext};
6-
7-
#[cfg(not(test))]
8-
#[panic_handler]
9-
fn panic(_info: &core::panic::PanicInfo) -> ! {
10-
core::arch::wasm32::unreachable()
4+
#[link(wasm_import_module = "env")]
5+
extern "C" {
6+
fn printi32(x: i32);
117
}
128

13-
#[global_allocator]
14-
static HEAP: Heap = Heap::empty();
15-
169
#[no_mangle]
17-
pub unsafe extern "C" fn _start() {
18-
// Initialize the allocator BEFORE you use it
19-
{
20-
use core::mem::MaybeUninit;
21-
const HEAP_SIZE: usize = 1024;
22-
static mut HEAP_MEM: [MaybeUninit<u8>; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE];
23-
unsafe { HEAP.init(HEAP_MEM.as_ptr() as usize, HEAP_SIZE) }
24-
}
25-
26-
// now the allocator is ready types like Box, Vec can be used.
10+
pub extern "C" fn hello() {
2711
let _ = run();
2812
}
2913

3014
fn run() -> tinywasm::Result<()> {
31-
// let module = tinywasm::Module::parse_bytes(include_bytes!("../out/hello.wasm"))?;
32-
// let mut store = tinywasm::Store::default();
33-
34-
// let mut imports = tinywasm::Imports::new();
35-
// imports.define("env", "printi32", Extern::typed_func(|_: FuncContext<'_>, _: i32| Ok(())))?;
36-
37-
// let instance = module.instantiate(&mut store, Some(imports))?;
38-
// let add_and_print = instance.typed_func::<(i32, i32), ()>(&mut store, "add_and_print")?;
39-
// add_and_print.call(&mut store, (1, 2))?;
15+
let module = tinywasm::Module::parse_bytes(include_bytes!("../../wasm/hello.wasm"))?;
16+
let mut store = tinywasm::Store::default();
17+
let mut imports = tinywasm::Imports::new();
18+
19+
imports.define(
20+
"env",
21+
"printi32",
22+
Extern::typed_func(|_: FuncContext<'_>, v: i32| {
23+
unsafe { printi32(v) }
24+
Ok(())
25+
}),
26+
)?;
27+
let instance = module.instantiate(&mut store, Some(imports))?;
28+
29+
let add_and_print = instance.typed_func::<(i32, i32), ()>(&mut store, "add_and_print")?;
30+
add_and_print.call(&mut store, (1, 2))?;
4031
Ok(())
4132
}

examples/wasm-rust.rs

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,35 +7,59 @@ fn main() -> Result<()> {
77
println!("Usage: cargo run --example wasm-rust <rust_example>");
88
println!("Available examples:");
99
println!(" hello");
10+
println!(" tinywasm");
1011
return Ok(());
1112
}
1213

1314
match args[1].as_str() {
1415
"hello" => hello()?,
16+
"tinywasm" => tinywasm()?,
1517
_ => {}
1618
}
1719

1820
Ok(())
1921
}
2022

23+
fn tinywasm() -> Result<()> {
24+
const TINYWASM: &[u8] = include_bytes!("./rust/out/tinywasm.wasm");
25+
let module = Module::parse_bytes(&TINYWASM)?;
26+
let mut store = Store::default();
27+
28+
let mut imports = Imports::new();
29+
imports.define(
30+
"env",
31+
"printi32",
32+
Extern::typed_func(|_: FuncContext<'_>, x: i32| {
33+
println!("{}", x);
34+
Ok(())
35+
}),
36+
)?;
37+
let instance = module.instantiate(&mut store, Some(imports))?;
38+
39+
let hello = instance.typed_func::<(), ()>(&mut store, "hello")?;
40+
hello.call(&mut store, ())?;
41+
42+
Ok(())
43+
}
44+
2145
fn hello() -> Result<()> {
22-
// const HELLO_WASM: &[u8] = include_bytes!("./rust/out/hello.wasm");
23-
// let module = Module::parse_bytes(&HELLO_WASM)?;
24-
// let mut store = Store::default();
25-
26-
// let mut imports = Imports::new();
27-
// imports.define(
28-
// "env",
29-
// "printi32",
30-
// Extern::typed_func(|_: FuncContext<'_>, x: i32| {
31-
// println!("{}", x);
32-
// Ok(())
33-
// }),
34-
// )?;
35-
36-
// let instance = module.instantiate(&mut store, Some(imports))?;
37-
// let add_and_print = instance.typed_func::<(i32, i32), ()>(&mut store, "add_and_print")?;
38-
// add_and_print.call(&mut store, (1, 2))?;
46+
const HELLO_WASM: &[u8] = include_bytes!("./rust/out/hello.wasm");
47+
let module = Module::parse_bytes(&HELLO_WASM)?;
48+
let mut store = Store::default();
49+
50+
let mut imports = Imports::new();
51+
imports.define(
52+
"env",
53+
"printi32",
54+
Extern::typed_func(|_: FuncContext<'_>, x: i32| {
55+
println!("{}", x);
56+
Ok(())
57+
}),
58+
)?;
59+
60+
let instance = module.instantiate(&mut store, Some(imports))?;
61+
let add_and_print = instance.typed_func::<(i32, i32), ()>(&mut store, "add_and_print")?;
62+
add_and_print.call(&mut store, (1, 2))?;
3963

4064
Ok(())
4165
}

examples/wasm/add.wasm

-65 Bytes
Binary file not shown.

examples/wasm/add.wat

Lines changed: 0 additions & 16 deletions
This file was deleted.

examples/wasm/call.wat

Lines changed: 0 additions & 42 deletions
This file was deleted.

examples/wasm/global.wat

Lines changed: 0 additions & 1 deletion
This file was deleted.

examples/wasm/helloworld.wasm

-115 Bytes
Binary file not shown.

examples/wasm/helloworld.wat

Lines changed: 0 additions & 15 deletions
This file was deleted.

0 commit comments

Comments
 (0)