Skip to content

Commit

Permalink
docs: working selfhosted example
Browse files Browse the repository at this point in the history
Signed-off-by: Henry Gressmann <mail@henrygressmann.de>
  • Loading branch information
explodingcamera committed Jan 26, 2024
1 parent 461126c commit f3d890a
Show file tree
Hide file tree
Showing 18 changed files with 159 additions and 233 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/target
notes.md
examples/tinywasm.wat
examples/rust/out/*
examples/wast/*
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 8 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
[workspace]
members=["crates/*"]
members=["crates/*", "examples/rust"]
resolver="2"

[profile.wasm]
opt-level="z"
lto="thin"
codegen-units=1
panic="abort"
inherits="release"

[workspace.package]
version="0.3.0-alpha.0"
edition="2021"
Expand Down
13 changes: 2 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

# Status

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).
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)).

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).
Expand Down Expand Up @@ -56,16 +56,7 @@ $ cargo add tinywasm
Enables the `tinywasm-parser` crate. This is enabled by default.

With all these features disabled, TinyWasm only depends on `core`, `alloc` and `libm` and can be used in `no_std` environments.

<!-- # 🎯 Goals
- Self-hosted (can run itself compiled to WebAssembly)
- No unsafe code
- Works on `no_std` (with `alloc` the feature and nightly compiler)
- Fully support WebAssembly MVP (1.0)
- Low Memory Usage (less than 10kb)
- Fast Startup Time
- Preemptive multitasking support -->
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)).

## Performance

Expand Down
40 changes: 38 additions & 2 deletions crates/tinywasm/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,8 @@ impl Display for Error {
#[cfg(feature = "std")]
Self::Io(err) => write!(f, "I/O error: {}", err),

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

impl Display for LinkingError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::UnknownImport { module, name } => write!(f, "unknown import: {}.{}", module, name),
Self::IncompatibleImportType { module, name } => {
write!(f, "incompatible import type: {}.{}", module, name)
}
}
}
}

impl Display for Trap {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Unreachable => write!(f, "unreachable"),
Self::MemoryOutOfBounds { offset, len, max } => {
write!(f, "out of bounds memory access: offset={}, len={}, max={}", offset, len, max)
}
Self::TableOutOfBounds { offset, len, max } => {
write!(f, "out of bounds table access: offset={}, len={}, max={}", offset, len, max)
}
Self::DivisionByZero => write!(f, "integer divide by zero"),
Self::InvalidConversionToInt => write!(f, "invalid conversion to integer"),
Self::IntegerOverflow => write!(f, "integer overflow"),
Self::CallStackOverflow => write!(f, "call stack exhausted"),
Self::UndefinedElement { index } => write!(f, "undefined element: index={}", index),
Self::UninitializedElement { index } => {
write!(f, "uninitialized element: index={}", index)
}
Self::IndirectCallTypeMismatch { expected, actual } => {
write!(f, "indirect call type mismatch: expected={:?}, actual={:?}", expected, actual)
}
}
}
}

#[cfg(any(feature = "std", all(not(feature = "std"), nightly)))]
impl crate::std::error::Error for Error {}

Expand Down
22 changes: 22 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Examples

## WasmRust

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).

```bash
$ ./examples/rust/build.sh
```

Then you can run the examples:

```bash
$ cargo run --example wasm-rust <example>
```

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!
7 changes: 1 addition & 6 deletions examples/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ forced-target="wasm32-unknown-unknown"
edition="2021"

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

[[bin]]
name="hello"
Expand All @@ -17,7 +16,3 @@ path="src/hello.rs"
[[bin]]
name="tinywasm"
path="src/tinywasm.rs"

[profile.release]
opt-level="z"
panic="abort"
18 changes: 18 additions & 0 deletions examples/rust/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env bash
cd "$(dirname "$0")"

bins=("hello" "tinywasm")
exclude_wat=("tinywasm")
out_dir="../../target/wasm32-unknown-unknown/wasm"
dest_dir="out"

for bin in "${bins[@]}"; do
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

if [[ ! " ${exclude_wat[@]} " =~ " $bin " ]]; then
wasm2wat "$dest_dir/$bin.wasm" -o "$dest_dir/$bin.wat"
fi
done
51 changes: 21 additions & 30 deletions examples/rust/src/tinywasm.rs
Original file line number Diff line number Diff line change
@@ -1,41 +1,32 @@
#![no_std]
#![no_main]
use tinywasm::{Extern, FuncContext};

use embedded_alloc::Heap;
// use tinywasm::{Extern, FuncContext};

#[cfg(not(test))]
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
core::arch::wasm32::unreachable()
#[link(wasm_import_module = "env")]
extern "C" {
fn printi32(x: i32);
}

#[global_allocator]
static HEAP: Heap = Heap::empty();

#[no_mangle]
pub unsafe extern "C" fn _start() {
// Initialize the allocator BEFORE you use it
{
use core::mem::MaybeUninit;
const HEAP_SIZE: usize = 1024;
static mut HEAP_MEM: [MaybeUninit<u8>; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE];
unsafe { HEAP.init(HEAP_MEM.as_ptr() as usize, HEAP_SIZE) }
}

// now the allocator is ready types like Box, Vec can be used.
pub extern "C" fn hello() {
let _ = run();
}

fn run() -> tinywasm::Result<()> {
// let module = tinywasm::Module::parse_bytes(include_bytes!("../out/hello.wasm"))?;
// let mut store = tinywasm::Store::default();

// let mut imports = tinywasm::Imports::new();
// imports.define("env", "printi32", Extern::typed_func(|_: FuncContext<'_>, _: i32| Ok(())))?;

// let instance = module.instantiate(&mut store, Some(imports))?;
// let add_and_print = instance.typed_func::<(i32, i32), ()>(&mut store, "add_and_print")?;
// add_and_print.call(&mut store, (1, 2))?;
let module = tinywasm::Module::parse_bytes(include_bytes!("../../wasm/hello.wasm"))?;
let mut store = tinywasm::Store::default();
let mut imports = tinywasm::Imports::new();

imports.define(
"env",
"printi32",
Extern::typed_func(|_: FuncContext<'_>, v: i32| {
unsafe { printi32(v) }
Ok(())
}),
)?;
let instance = module.instantiate(&mut store, Some(imports))?;

let add_and_print = instance.typed_func::<(i32, i32), ()>(&mut store, "add_and_print")?;
add_and_print.call(&mut store, (1, 2))?;
Ok(())
}
58 changes: 41 additions & 17 deletions examples/wasm-rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,59 @@ fn main() -> Result<()> {
println!("Usage: cargo run --example wasm-rust <rust_example>");
println!("Available examples:");
println!(" hello");
println!(" tinywasm");
return Ok(());
}

match args[1].as_str() {
"hello" => hello()?,
"tinywasm" => tinywasm()?,
_ => {}
}

Ok(())
}

fn tinywasm() -> Result<()> {
const TINYWASM: &[u8] = include_bytes!("./rust/out/tinywasm.wasm");
let module = Module::parse_bytes(&TINYWASM)?;
let mut store = Store::default();

let mut imports = Imports::new();
imports.define(
"env",
"printi32",
Extern::typed_func(|_: FuncContext<'_>, x: i32| {
println!("{}", x);
Ok(())
}),
)?;
let instance = module.instantiate(&mut store, Some(imports))?;

let hello = instance.typed_func::<(), ()>(&mut store, "hello")?;
hello.call(&mut store, ())?;

Ok(())
}

fn hello() -> Result<()> {
// const HELLO_WASM: &[u8] = include_bytes!("./rust/out/hello.wasm");
// let module = Module::parse_bytes(&HELLO_WASM)?;
// let mut store = Store::default();

// let mut imports = Imports::new();
// imports.define(
// "env",
// "printi32",
// Extern::typed_func(|_: FuncContext<'_>, x: i32| {
// println!("{}", x);
// Ok(())
// }),
// )?;

// let instance = module.instantiate(&mut store, Some(imports))?;
// let add_and_print = instance.typed_func::<(i32, i32), ()>(&mut store, "add_and_print")?;
// add_and_print.call(&mut store, (1, 2))?;
const HELLO_WASM: &[u8] = include_bytes!("./rust/out/hello.wasm");
let module = Module::parse_bytes(&HELLO_WASM)?;
let mut store = Store::default();

let mut imports = Imports::new();
imports.define(
"env",
"printi32",
Extern::typed_func(|_: FuncContext<'_>, x: i32| {
println!("{}", x);
Ok(())
}),
)?;

let instance = module.instantiate(&mut store, Some(imports))?;
let add_and_print = instance.typed_func::<(i32, i32), ()>(&mut store, "add_and_print")?;
add_and_print.call(&mut store, (1, 2))?;

Ok(())
}
Binary file removed examples/wasm/add.wasm
Binary file not shown.
16 changes: 0 additions & 16 deletions examples/wasm/add.wat

This file was deleted.

42 changes: 0 additions & 42 deletions examples/wasm/call.wat

This file was deleted.

1 change: 0 additions & 1 deletion examples/wasm/global.wat

This file was deleted.

Binary file removed examples/wasm/helloworld.wasm
Binary file not shown.
15 changes: 0 additions & 15 deletions examples/wasm/helloworld.wat

This file was deleted.

Loading

0 comments on commit f3d890a

Please sign in to comment.