Skip to content

Commit d0c7425

Browse files
feat: initial support for the webassembly gc proposal (#50)
Signed-off-by: Henry <mail@henrygressmann.de>
1 parent 87f1da2 commit d0c7425

45 files changed

Lines changed: 2770 additions & 705 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎CHANGELOG.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111

1212
- Added support for the WebAssembly function-references proposal
13+
- Added support for the WebAssembly garbage-collection proposal
1314
- Added `WasmValue::ty` and `WasmValue::matches_type`
1415
- Added a `validate` feature to `tinywasm` and `tinywasm-parser` (enabled by default) to optionally skip wasmparser validation for faster parsing of trusted modules.
1516

1617
### Changed
1718

18-
- Function types are now stored separately and resolved through `Function::ty(&Store)`.
19+
- Module types now use one dense recursive type space, while function types are resolved through `Function::ty(&Store)`.
1920

2021
### Fixed
2122

‎Cargo.lock‎

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

‎Cargo.toml‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,10 @@ categories = ["compilers", "embedded", "no-std", "virtualization", "wasm"]
2121

2222
[workspace.dependencies]
2323
tinywasm = { path = "crates/tinywasm", version = "0.11.0-pre.0", default-features = false }
24-
tinywasm-cli = { path = "crates/cli", version = "0.11.0-pre.0", default-features = false }
2524
tinywasm-parser = { path = "crates/parser", version = "0.11.0-pre.0", default-features = false }
2625
tinywasm-types = { path = "crates/types", version = "0.11.0-pre.0", default-features = false }
2726

2827
eyre = "0.6"
29-
indexmap = "2.14"
3028
log = "0.4"
3129
owo-colors = { version = "4.3" }
3230
pretty_env_logger = "0.5"

‎README.md‎

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ See the [examples](./examples) directory and [documentation](https://docs.rs/tin
5757

5858
With default features disabled, `tinywasm` depends only on `core`, `alloc`, and `libm`[^libm], making it usable in `no_std + alloc` environments.
5959

60-
Use `Engine` and `engine::Config` when you need non-default runtime settings such as fuel accounting, stack sizing, memory backend selection, or trap-on-OOM behavior.
60+
Use `Engine` and `engine::Config` when you need non-default runtime settings such as fuel accounting, stack sizing, memory backend selection, the GC collection threshold, or trap-on-OOM behavior.
6161

6262
[^libm]: [rust-lang/rust#137578](https://github.com/rust-lang/rust/issues/137578) — tracking issue for floating-point math support in `no_std`.
6363

@@ -75,27 +75,31 @@ The internal `twasm` bytecode format is not currently validated as an untrusted
7575

7676
## Supported Proposals
7777

78-
| Proposal | Status | `tinywasm` Version |
79-
| --------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------------------ |
80-
| [**Multi-value**](https://github.com/WebAssembly/spec/blob/master/proposals/multi-value/Overview.md) | 🟢 | 0.2.0 |
81-
| [**Mutable Globals**](https://github.com/WebAssembly/mutable-global/blob/master/proposals/mutable-global/Overview.md) | 🟢 | 0.2.0 |
82-
| [**Non-trapping float-to-int Conversion**](https://github.com/WebAssembly/nontrapping-float-to-int-conversions) | 🟢 | 0.2.0 |
83-
| [**Sign-extension operators**](https://github.com/WebAssembly/sign-extension-ops) | 🟢 | 0.2.0 |
84-
| [**Bulk Memory Operations**](https://github.com/WebAssembly/spec/blob/master/proposals/bulk-memory-operations/Overview.md) | 🟢 | 0.4.0 |
85-
| [**Reference Types**](https://github.com/WebAssembly/reference-types/blob/master/proposals/reference-types/Overview.md) | 🟢 | 0.7.0 |
86-
| [**Multi-memory**](https://github.com/WebAssembly/multi-memory/blob/master/proposals/multi-memory/Overview.md) | 🟢 | 0.8.0 |
87-
| [**Custom Page Sizes**](https://github.com/WebAssembly/custom-page-sizes/blob/main/proposals/custom-page-sizes/Overview.md) | 🟢 | 0.9.0 |
88-
| [**Extended Const**](https://github.com/WebAssembly/extended-const/blob/main/proposals/extended-const/Overview.md) | 🟢 | 0.9.0 |
89-
| [**Fixed-Width SIMD**](https://github.com/WebAssembly/simd/blob/main/proposals/simd/Overview.md) | 🟢 | 0.9.0 |
90-
| [**Memory64**](https://github.com/WebAssembly/memory64/blob/master/proposals/memory64/Overview.md) | 🟢 | 0.9.0 |
91-
| [**Tail Call**](https://github.com/WebAssembly/tail-call/blob/main/proposals/tail-call/Overview.md) | 🟢 | 0.9.0 |
92-
| [**Relaxed SIMD**](https://github.com/WebAssembly/relaxed-simd/blob/main/proposals/relaxed-simd/Overview.md) | 🟢 | 0.9.0 |
93-
| [**Wide Arithmetic**](https://github.com/WebAssembly/wide-arithmetic/blob/main/proposals/wide-arithmetic/Overview.md) | 🟢 | 0.9.0 |
94-
| [**Typed Function References**](https://github.com/WebAssembly/function-references/blob/main/proposals/function-references/Overview.md) | 🚧 | `next` |
95-
| [**Exception Handling**](https://github.com/WebAssembly/exception-handling/blob/main/proposals/exception-handling/Exceptions.md) | 🌑 | - |
96-
| [**Garbage Collection**](https://github.com/WebAssembly/gc/blob/main/proposals/gc/Overview.md) | 🌑 | - |
97-
| [**Stack Switching**](https://github.com/WebAssembly/stack-switching/blob/main/proposals/stack-switching/Explainer.md) | 🌑 | - |
98-
| [**Threads**](https://github.com/WebAssembly/threads/blob/main-legacy/proposals/threads/Overview.md) | 🌑 | - |
78+
TinyWasm targets non-JavaScript core proposals through [phase 3](https://github.com/WebAssembly/proposals). JavaScript integrations and optional embedding or tooling APIs are not included here.
79+
80+
| Proposal | Status | `tinywasm` Version |
81+
| ---------------------------------------------------------------------------------------------------------------- | ------ | ------------------ |
82+
| [**Import/Export of Mutable Globals**](https://github.com/WebAssembly/mutable-global) | 🟢 | 0.2.0 |
83+
| [**Multi-value**](https://github.com/WebAssembly/multi-value) | 🟢 | 0.2.0 |
84+
| [**Non-trapping Float-to-int Conversions**](https://github.com/WebAssembly/nontrapping-float-to-int-conversions) | 🟢 | 0.2.0 |
85+
| [**Sign-extension Operators**](https://github.com/WebAssembly/sign-extension-ops) | 🟢 | 0.2.0 |
86+
| [**Bulk Memory Operations**](https://github.com/WebAssembly/bulk-memory-operations) | 🟢 | 0.4.0 |
87+
| [**Reference Types**](https://github.com/WebAssembly/reference-types) | 🟢 | 0.7.0 |
88+
| [**Fixed-width SIMD**](https://github.com/WebAssembly/simd) | 🟢 | 0.9.0 |
89+
| [**Tail Calls**](https://github.com/WebAssembly/tail-call) | 🟢 | 0.9.0 |
90+
| [**Extended Constant Expressions**](https://github.com/WebAssembly/extended-const) | 🟢 | 0.9.0 |
91+
| [**Multiple Memories**](https://github.com/WebAssembly/multi-memory) | 🟢 | 0.8.0 |
92+
| [**Relaxed SIMD**](https://github.com/WebAssembly/relaxed-simd) | 🟢 | 0.9.0 |
93+
| [**Custom Annotation Syntax**](https://github.com/WebAssembly/annotations) | 🟢 | 0.8.0 |
94+
| [**Memory64**](https://github.com/WebAssembly/memory64) | 🟢 | 0.9.0 |
95+
| [**Wide Arithmetic**](https://github.com/WebAssembly/wide-arithmetic) | 🟢 | 0.9.0 |
96+
| [**Custom Page Sizes**](https://github.com/WebAssembly/custom-page-sizes) | 🟢 | 0.9.0 |
97+
| [**Typed Function References**](https://github.com/WebAssembly/function-references) | 🟢 | `next` |
98+
| [**Garbage Collection**](https://github.com/WebAssembly/gc) | 🟢 | `next` |
99+
| [**Exception Handling**](https://github.com/WebAssembly/exception-handling) | 🌑 | - |
100+
| [**Stack Switching**](https://github.com/WebAssembly/stack-switching) | 🌑 | - |
101+
| [**Compact Import Section**](https://github.com/WebAssembly/compact-import-section) | 🌑 | - |
102+
| [**Threads**](https://github.com/WebAssembly/threads) | 🌑 | - |
99103

100104
**Legend**\
101105
🌑 -- not available\

‎crates/cli/src/wast_runner.rs‎

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ use std::time::Duration;
77

88
use eyre::{Context, Result, bail, eyre};
99
use log::{debug, error};
10-
use tinywasm::types::{ExternRef, FuncRef, MemoryType, RefType, RefValue, TableType, WasmType, WasmValue};
10+
use tinywasm::types::{
11+
AbstractHeapType as TinyAbstractHeapType, AnyRef, ExternRef, FuncRef, MemoryType, RefType, RefValue, TableType,
12+
WasmType, WasmValue,
13+
};
1114
use tinywasm::{ExecProgress, Global, HostFunction, Imports, Memory, Module, ModuleInstance, Store, Table};
1215
use wast::{QuoteWat, core::AbstractHeapType};
1316

@@ -459,7 +462,7 @@ impl WastRunner {
459462
let expected = expected_alternatives
460463
.iter()
461464
.filter_map(|alts| alts.first())
462-
.find(|exp| exp.matches(&module_global));
465+
.find(|exp| exp.matches(&module_global, &store));
463466
if expected.is_none() {
464467
test_group.add_result(
465468
&format!("AssertReturn(unsupported-{i})"),
@@ -508,7 +511,10 @@ impl WastRunner {
508511
}
509512
if expected_alternatives.iter().any(|expected| {
510513
expected.len() == outcomes.len()
511-
&& outcomes.iter().zip(expected.iter()).all(|(outcome, exp)| exp.matches(outcome))
514+
&& outcomes
515+
.iter()
516+
.zip(expected.iter())
517+
.all(|(outcome, exp)| exp.matches(outcome, &store))
512518
}) {
513519
Ok(())
514520
} else {
@@ -755,16 +761,18 @@ fn wastarg2tinywasmvalue(arg: wast::WastArg) -> Result<WasmValue> {
755761
I32(i) => WasmValue::I32(i),
756762
I64(i) => WasmValue::I64(i),
757763
V128(i) => WasmValue::V128(i.to_le_bytes()),
758-
RefExtern(v) => ExternRef::new(v).into(),
764+
RefExtern(v) => ExternRef::try_new(v).ok_or_else(|| eyre!("external reference address is too large"))?.into(),
759765
RefNull(t) => match t {
760766
wast::core::HeapType::Abstract { shared: false, ty: AbstractHeapType::Func } => RefValue::Null.into(),
761-
wast::core::HeapType::Abstract { shared: false, ty: AbstractHeapType::Extern } => RefValue::Null.into(),
767+
wast::core::HeapType::Abstract { shared: false, ty: AbstractHeapType::Extern | AbstractHeapType::Any } => {
768+
RefValue::Null.into()
769+
}
762770
_ => {
763771
bail!("unsupported arg type: refnull: {:?}", t);
764772
}
765773
},
766-
RefHost(_) => {
767-
bail!("unsupported arg type: RefHost");
774+
RefHost(value) => {
775+
RefValue::Any(AnyRef::from_host(value).ok_or_else(|| eyre!("host reference address is too large"))?).into()
768776
}
769777
})
770778
}
@@ -791,15 +799,29 @@ enum ExpectedValue {
791799
RefNull,
792800
RefFunc,
793801
RefExtern,
802+
RefAny,
803+
RefEq,
804+
RefI31,
805+
RefStruct,
806+
RefArray,
794807
}
795808

796809
impl ExpectedValue {
797-
fn matches(&self, value: &WasmValue) -> bool {
810+
fn matches(&self, value: &WasmValue, store: &Store) -> bool {
798811
match self {
799812
Self::Exact(expected) => value.eq_loose(expected),
800813
Self::RefNull => matches!(value, WasmValue::Ref(RefValue::Null)),
801814
Self::RefFunc => matches!(value, WasmValue::Ref(RefValue::Func(_))),
802815
Self::RefExtern => matches!(value, WasmValue::Ref(RefValue::Extern(_))),
816+
Self::RefAny => matches!(value, WasmValue::Ref(RefValue::Any(_))),
817+
Self::RefEq => {
818+
store.value_matches_type(*value, WasmType::Ref(RefType::new_abstract(false, TinyAbstractHeapType::Eq)))
819+
}
820+
Self::RefI31 => matches!(value, WasmValue::Ref(RefValue::Any(value)) if value.as_i31().is_some()),
821+
Self::RefStruct => store
822+
.value_matches_type(*value, WasmType::Ref(RefType::new_abstract(false, TinyAbstractHeapType::Struct))),
823+
Self::RefArray => store
824+
.value_matches_type(*value, WasmType::Ref(RefType::new_abstract(false, TinyAbstractHeapType::Array))),
803825
}
804826
}
805827
}
@@ -817,21 +839,34 @@ fn wastret2tinywasmvalues(ret: wast::WastRet) -> Result<Vec<ExpectedValue>> {
817839
}
818840

819841
fn wastretcore2tinywasmvalue(ret: wast::core::WastRetCore) -> Result<ExpectedValue> {
820-
use wast::core::WastRetCore::{F32, F64, I32, I64, RefExtern, RefFunc, RefNull, V128};
842+
use wast::core::WastRetCore::{
843+
F32, F64, I32, I64, RefAny, RefArray, RefEq, RefExtern, RefFunc, RefHost, RefI31, RefI31Shared, RefNull,
844+
RefStruct, V128,
845+
};
821846
Ok(match ret {
822847
F32(f) => ExpectedValue::Exact(nanpattern2tinywasmvalue(f)?),
823848
F64(f) => ExpectedValue::Exact(nanpattern2tinywasmvalue(f)?),
824849
I32(i) => ExpectedValue::Exact(WasmValue::I32(i)),
825850
I64(i) => ExpectedValue::Exact(WasmValue::I64(i)),
826851
V128(i) => ExpectedValue::Exact(WasmValue::V128(wast_v128_to_bytes(i))),
827852
RefNull(_) => ExpectedValue::RefNull,
828-
RefExtern(Some(v)) => ExpectedValue::Exact(ExternRef::new(v).into()),
853+
RefExtern(Some(v)) => ExpectedValue::Exact(
854+
ExternRef::try_new(v).ok_or_else(|| eyre!("external reference address is too large"))?.into(),
855+
),
829856
RefExtern(None) => ExpectedValue::RefExtern,
830857
RefFunc(Some(wast::token::Index::Num(n, _))) => ExpectedValue::Exact(FuncRef::new(n).into()),
831858
RefFunc(None) => ExpectedValue::RefFunc,
832859
RefFunc(v) => {
833860
bail!("unsupported arg type: reffunc: {:?}", v);
834861
}
862+
RefAny => ExpectedValue::RefAny,
863+
RefEq => ExpectedValue::RefEq,
864+
RefI31 | RefI31Shared => ExpectedValue::RefI31,
865+
RefStruct => ExpectedValue::RefStruct,
866+
RefArray => ExpectedValue::RefArray,
867+
RefHost(value) => ExpectedValue::Exact(
868+
RefValue::Any(AnyRef::from_host(value).ok_or_else(|| eyre!("host reference address is too large"))?).into(),
869+
),
835870
a => {
836871
bail!("unsupported arg type {:?}", a);
837872
}

0 commit comments

Comments
 (0)