1 unstable release
| 0.6.0-alpha.1 | Nov 14, 2025 |
|---|
#7 in WebAssembly
Used in 8 crates
14KB
252 lines
WAFFLE: Wasm Analysis Framework for Lightweight Experimentation
A Wasm-to-Wasm compiler framework in Rust, built around an SSA IR. This is a fork of the original waffle by Chris Fallin, maintained under the portal-co organization and published as the portal-pc-waffle family of crates.
What it does
WAFFLE reads WebAssembly bytecode, converts it to an SSA (Static Single Assignment) intermediate representation, optionally transforms or optimizes it, and compiles it back to valid Wasm bytecode. The round-trip (Wasm -> IR -> Wasm) is the core operation. All other features (optimization passes, hooking, copying, lowering) are built on top of that.
The IR is a CFG of basic blocks where dataflow is explicit SSA values. Wasm locals are eliminated during parsing and replaced with SSA values and block parameters. All Wasm operators remain at the Wasm abstraction level — memories, tables, and reference types are first-class IR concepts rather than being lowered into implementation details.
Status
Version 0.6.0-alpha.1. The codebase has been refactored from a single crate into a Cargo workspace of smaller crates (see crates/). The round-trip pass is working and has been fuzzed. The IR supports Wasm MVP plus multivalue, SIMD, reference types (including non-nullable funcrefs and typed funcrefs), and GC proposals (arrays, structs). There is a built-in interpreter used for fuzzing differential testing.
The codebase compiles with #![no_std] (using alloc) in most crates and has #![forbid(unsafe_code)] in all library crates.
Repository
https://github.com/portal-co/waffle-
Crate structure
The workspace is split into the following crates, all published under the portal-pc- prefix:
| Crate | Published name | Purpose |
|---|---|---|
. (root) |
portal-pc-waffle |
Facade; re-exports and gates sub-crates via features |
crates/waffle-entity |
portal-pc-waffle-entity |
Type-safe arena indices (Func, Block, Value, Memory, etc.) and indexed containers |
crates/waffle-ir |
portal-pc-waffle-ir |
Core IR types: Module, FunctionBody, ValueDef, Operator, Terminator; also contains the built-in interpreter and a WASI stub |
crates/waffle-frontend |
portal-pc-waffle-frontend |
Wasm bytecode -> IR conversion (SSA construction, multivalue handling) |
crates/waffle-backend |
portal-pc-waffle-backend |
IR -> Wasm bytecode (reducify, stackify, treeify, localify) |
crates/waffle-passes-shared |
portal-pc-waffle-passes-shared |
Utilities shared between passes: max-SSA conversion, alias resolution, purity predicates |
crates/waffle-passes |
portal-pc-waffle-passes |
Optimization passes: GVN, constant propagation/folding, redundant blockparam elimination, inlining, reordering, ub_vaccum, func_rocket, importify |
crates/waffle-lowering |
portal-pc-waffle-lowering |
Memory-level lowering passes: mem_fusing and unmem |
crates/waffle-copying |
portal-pc-waffle-copying |
Utilities for copying/cloning module components (fcopy, fts, kts, module copy) |
crates/waffle-copying-passes |
portal-pc-waffle-copying-passes |
Passes that combine copying with optimization (mapping, tcore) |
crates/waffle-hooking |
portal-pc-waffle-hooking |
Function wrapping/swizzling: replaces a function body with a new body that calls the original, useful for instrumentation |
crates/waffle-fuzzing |
portal-pc-waffle-fuzzing |
Fuzzing helpers: ArbitraryModule wrapper, rejection filters |
Backend pipeline
Compiling a FunctionBody back to Wasm bytecode goes through four stages:
-
Reducify (
reducify.rs) — Handles irreducible control flow by duplicating code to make all CFG loops reducible. Uses RPO-based loop detection. The comments warn of potential exponential code blowup for pathological inputs (dense state machines, cliques). -
Stackify (
stackify.rs) — Converts the reducible CFG into a structured Wasm control-flow AST (blocks, loops, if-then) using Ramsey's algorithm. -
Treeify (
treeify.rs) — Identifies SSA values used exactly once and "trees" them: moves the computation inline to avoid materializing an intermediate local. -
Localify (
localify.rs) — Linear-scan register allocation over the remaining SSA values, assigning them to Wasm locals with no overlapping live ranges.
Frontend
The frontend (waffle-frontend) parses Wasm with wasmparser and builds SSA as it goes. When it discovers multiple reaching definitions for a Wasm local (e.g. at a join point), it inserts block parameters rather than phi nodes. Multivalue blocks (Wasm parameters and results for every control-flow construct) are fully handled. Function bodies are parsed lazily and expanded on demand.
Optimization passes
OptOptions (in waffle-passes) controls:
gvn— global value numbering (domtree walk with a scoped map)cprop— constant propagation and folding (uses the interpreter'sconst_eval)redundant_blockparams— removes block parameters that always carry the same valueinline_refs— inlines single-use pure valuesub_vaccum— removes unreachable/undefined-behavior code
Additional passes include function inlining, function reordering, importify (converts internal functions to imports under a feature flag), func_rocket (cancels paired call/inverse-call patterns), and memory-level operations in waffle-lowering.
Fuzzing
The fuzz/ directory contains six libfuzzer targets:
roundtrip— parses generated Wasm and compiles it back; checks for panicsroundtrip_roundtrip— checks that two round-trips produce identical outputdifferential— compares globals and memory of the original Wasm run against the round-tripped version under Wasmtime (with fuel limits)opt_diff— compares interpreter execution results before and after optimizationirreducible— exercises the reducify pass specificallyparse_ir— exercises IR parsing
Wasm feature support
- MVP instructions
- Multivalue (block params/returns)
- SIMD (via
wasmparser'ssimdfeature) - Reference types:
funcref,externref, non-nullable refs, typed funcrefs - GC proposal types: arrays, structs (subtype checking implemented in
ir_subtypes.rs) - Exception handling (unstable, behind
unstable-exceptionsfeature flag)
Optional features
frontend/backend— enable the parsing and code-generation halves independentlycopying— enablewaffle-copyinghooking— enablewaffle-hookingtcore— enablewaffle-copying-passesfuzzing— enablewaffle-fuzzingssa-traits-02/ssa-traits-03— implementssa-traitsandcfg-traitsinterfaces fromportal-co/codegen-utilsrkyv-impl— deriverkyvArchive/Serialize/Deserialize for IR typesimportify— enable theimportifypass inwaffle-passes
Related projects
- Binaryen — AST-based IR, C/C++ API only; supports CFG-to-Wasm but not Wasm-to-CFG
- Walrus — Rust, Wasm-to-Wasm transforms, but IR mirrors bytecode closely (no SSA)
- Cranelift — similar SSA IR with block params, but only Wasm-to-IR (no Wasm output), and it lowers Wasm abstractions
License
Apache-2.0 WITH LLVM-exception
Dependencies
~200–760KB
~17K SLoC