IR types and transformation passes¶
This section explains various IR types in Asterius, and hopefully presents a
clear picture of how information flows from Haskell to WebAssembly. (There’s a
similar section in
jsffi.md which explains implementation details of JSFFI)
Everything starts from Cmm, or more specifically, “raw” Cmm which satisfies:
All calls are tail calls, parameters are passed by global registers like R1 or on the stack.
All info tables are converted to binary data segments.
Cmm module in
ghc package to get started on Cmm.
Asterius obtains in-memory raw Cmm via:
cmmToRawCmmHookin our custom GHC fork. This allow us to lay our fingers on Cmm generated by either compiling Haskell modules, or
.cmmfiles (which are in
There is some abstraction in
ghc-toolkit, the compiler logic is actually in the
Compilerdatatype as some callbacks, and
ghc-toolkitconverts them to hooks, frontend plugins and
There is one minor annoyance with the Cmm types in GHC (or any other GHC IR type): it’s very hard to serialize/deserialize them without setting up complicated contexts related to package databases, etc. To experiment with new backends, it’s reasonable to marshal to a custom serializable IR first.
AsteriusStore type in
Asterius.Types. It’s an immutable data
structure that maps symbols to underlying entities in the expression IR for
every single module, and is a critical component of the linker.
Modeling the store as a self-contained data structure makes it pleasant to
write linker logic, at the cost of exploding RAM usage. So we implemented a
poor man’s KV store in
Asterius.Store which performs lazy-loading of modules:
when initializing the store, we only load the symbols, but not the actual
modules; only when a module is “requested” for the first time, we perform
deserialization for that module.
AsteriusStore supports merging. It’s a handy operation, since we can first
initialize a “global” store that represents the standard libraries, then make
another store based on compiling user input, simply merge the two and we can
start linking from the output store.
Generating binary code via binaryen¶
Once we have a
Module (which is essentially just Haskell modeling of binaryen
C API), we can invoke binaryen to validate it and generate Wasm binary code.
The low-level bindings are maintained in the
binaryen package, and
Asterius.Marshal contains the logic to call the imported functions to do
Generating binary code via wasm-toolkit¶
We can also convert
Module to IR types of
wasm-toolkit, which is our native
Haskell Wasm engine. It’s now the default backend of
ahc-link, but the
binaryen backend can still be chosen by
Common runtime which can be reused across different Asterius compiled modules. It’s in
Stub code which contains specific information like error messages, etc.
The linker generates stub script along with Wasm binary code, and concats the
or Chrome via