diff --git a/.gitignore b/.gitignore index 4fa15125..dec0a36f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. +#claude +.claude + # vim .tags diff --git a/content/SUMMARY.md b/content/SUMMARY.md index 1ccb7b31..d9ee3906 100644 --- a/content/SUMMARY.md +++ b/content/SUMMARY.md @@ -98,6 +98,9 @@ * [Serving a JS Game](build-on-urbit/userspace/examples/flap.md) * [Ship Monitoring](build-on-urbit/userspace/examples/ahoy.md) * [Styled Text](build-on-urbit/userspace/examples/track7.md) +* [WebAssembly](build-on-urbit/wasm/README.md) + * [UrWasm Overview](build-on-urbit/wasm/overview.md) + * [Example Generator](build-on-urbit/wasm/generator.md) ## Urbit ID @@ -143,6 +146,15 @@ * [Poke Agent](urbit-os/base/threads/examples/poke-agent.md) * [Scry](urbit-os/base/threads/examples/scry.md) * [Take Fact](urbit-os/base/threads/examples/take-fact.md) + * [WebAssembly](urbit-os/base/wasm/README.md) + * [Lia Library](urbit-os/base/wasm/lib-wasm-lia.md) + * [Lia Types](urbit-os/base/wasm/lia-data-types.md) + * [Wasm Data Types](urbit-os/base/wasm/wasm-data-types.md) + * [Wasm Engine](urbit-os/base/wasm/lib-wasm-runner-engine.md) + * [Wasm Interpreter Types](urbit-os/base/wasm/wasm-interpreter-data-types.md) + * [Wasm Operator Definitions](urbit-os/base/wasm/lib-wasm-runner-op-def.md) + * [Wasm Parser](urbit-os/base/wasm/lib-wasm-parser.md) + * [Wasm Validator](urbit-os/base/wasm/lib-wasm-validator.md) * [Kernel](urbit-os/kernel/README.md) * [Arvo](urbit-os/kernel/arvo/README.md) * [Cryptography](urbit-os/kernel/arvo/cryptography.md) diff --git a/content/build-on-urbit/wasm/README.md b/content/build-on-urbit/wasm/README.md new file mode 100644 index 00000000..36d1ee87 --- /dev/null +++ b/content/build-on-urbit/wasm/README.md @@ -0,0 +1,23 @@ +--- +description: "WebAssembly tutorials" +layout: + title: + visible: true + description: + visible: false + tableOfContents: + visible: true + outline: + visible: true + pagination: + visible: true +--- + +# WebAssembly Walkthrough + +Urbit's WebAssembly affordances (collectively known as "UrWasm") enables Hoon developers to leverage pre-existing libraries from any Wasm-compatible language like Rust, Python, and Go. + +This section includes an [overview](./overview.md) of Wasm and how it can be run on Urbit. It also includes a trivial [example](./generator.md) of a Hoon generator using a Wasm module to sort a list. + +For a thorough description of UrWasm's types and libraries, see the [`%base`](../../urbit-os/base/wasm/README.md) docs. + diff --git a/content/build-on-urbit/wasm/generator.md b/content/build-on-urbit/wasm/generator.md new file mode 100644 index 00000000..7c41b34c --- /dev/null +++ b/content/build-on-urbit/wasm/generator.md @@ -0,0 +1,281 @@ +--- +description: "Example of a Hoon generator using an imported Wasm module" +layout: + title: + visible: true + description: + visible: false + tableOfContents: + visible: true + outline: + visible: true + pagination: + visible: true +--- + +# UrWasm Generator Example + +Let's use UrWasm to write a generator that can quickly sort a large list of 64-bit integers in ascending order. + +## Benchmark without UrWasm + +In pure Hoon, we would write something like this: + +```hoon +|= lit=(list @G) +^- (list @G) +~> %bout +(sort lit lth) +``` + +Let's run this and see how long it takes. (`__~` in the Dojo discards the product of the given expression and returns `~`). + +``` +> =l (flop (gulf 0 1.000)) +> +took ms/63.434 +> __~ +run l +~ +``` + +## Building the Wasm module + +Now let's sort the list using UrWasm, with the source code written in Rust. Initialize a new library cargo with `cargo new wasm_sort --lib` and edit `Cargo.toml`: + +```toml +[package] +name = "wasm_sort" +version = "0.1.0" +edition = "2024" + +[dependencies] +wasm-bindgen = "0.2" + +[lib] +crate-type = ["cdylib"] +``` + +Paste this source code into `sort.rs`: + +```rust +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub fn sort_u64(mut input: Vec) -> Vec { + input.sort(); + input +} +``` + +Run `wasm-pack build` and `wasm-pack` will compile the `wasm_sort.wasm` module from this file. The `wasm_bindgen` Rust library will be used to create a corresponding JS bindings file named something like `wasm_sort_bg.js`. + +## Writing Hoon bindings + +Let's see how the JS bindings file calls `sort_u64()`. Remember, this is a generated wrapper function that would be called from the web app. This wrapper function is what we'll have to reimplement in our new Hoon generator to call out to the compiled `wasm_sort.wasm` module. + +```javascript +// ... + +let WASM_VECTOR_LEN = 0; + +function passArray64ToWasm0(arg, malloc) { + const ptr = malloc(arg.length * 8, 8) >>> 0; + getBigUint64ArrayMemory0().set(arg, ptr / 8); + WASM_VECTOR_LEN = arg.length; + return ptr; +} + +function getArrayU64FromWasm0(ptr, len) { + ptr = ptr >>> 0; + return getBigUint64ArrayMemory0().subarray(ptr / 8, ptr / 8 + len); +} + +/** + * @param {BigUint64Array} input + * @returns {BigUint64Array} +**/ +export function sort_u64(input) { + const ptr0 = passArray64ToWasm0(input, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.sort_u64(ptr0, len0); + var v2 = getArrayU64FromWasm0(ret[0], ret[1]).slice(); + wasm.__wbindgen_free(ret[0], ret[1] * 8, 8); + return v2; +} + +// ... +``` + +What's going on in this `sortu64()` wrapper function? We see that it does the following: +1. Allocates memory for the input vector by calling `__wbindgen_malloc`. +2. Writes the contents of the array to Wasm memory. +3. Calls `sort_u64()` with the array pointer and length as parameters, which returns two values. +4. Uses those two values as the pointer and length of the resulting array, reads that from memory. +5. Frees the returned array from Wasm memory with `__wbindgen_free`. +6. Returns the sorted array. + +We don't need to reimplement step 5, since the whole Wasm VM will be freed when we're done. + +Our generator with Hoon "bindings" will look like this in full. We'll examine each part in detail below. + +{% code title="/gen/sort.hoon" overflow="nowrap" lineNumbers="true" %} + +```hoon +/+ *wasm-lia +/* wasm-bin %wasm /sort/wasm +:: +:- %say +|= [* [lit=(list @G) ~] *] +:- %noun +^- (list @G) +~> %bout +:: +=> |% + +$ yil-mold (list @G) + +$ acc-mold * + -- +%- yield-need =< - +%^ (run-once yil-mold acc-mold) [wasm-bin [~ ~]] %$ +=/ m (script yil-mold acc-mold) +=/ arr (arrows acc-mold) +=, arr +=/ len-vec=@ (lent lit) +=/ len-bytes=@ (mul 8 len-vec) +=/ vec=@ (rep 6 lit) +:: +;< ptr=@ try:m (call-1 '__wbindgen_malloc' len-bytes 8 ~) +;< ~ try:m (memwrite ptr len-bytes vec) +;< ptr-len=(list @) try:m (call 'sort_u64' ptr len-vec ~) +;< vec-out=octs try:m (memread &1.ptr-len (mul 8 &2.ptr-len)) +:: +=/ lit-out=(list @) (rip 6 q.vec-out) +=/ lent-out=@ (lent lit-out) +?: =(len-vec lent-out) + (return:m lit-out) +%- return:m +%+ weld lit-out +(reap (sub len-vec lent-out) 0) +``` + +{% endcode %} + +What's going on here? + +First, we import the Lia interpreter and the `.wasm` module, which we've copied in to the root of our desk. (If you're working through this example, the `%base` desk on a fakeship would be fine.) + +```hoon +/+ *wasm-lia +/+ wasm-bin %wasm /sort/wasm +``` + +Mostly generator boilerplate, but note the `.lit` parameter and the output `(list @G)`. (That is, a `+list` of `@`s where `G` indicates a bitwidth of 64.) + +```hoon +:- %say +|= [* [lit=(list @G) ~] *] +:- %noun +^- (list @G) +``` + +We use the `%bout` runtime hint to time the computation that follows. + +```hoon +~> %bout +``` + +Now we'll define the types for our yield of the main script, and the accumulator noun: +- The `$yil-mold` is the type of our yield, the result of the script. +- The `$acc-mold` is the type of the accumulator, which holds some arbitrary state we can read and write to during script execution. + +We don't need the accumulator for this example but it's required for `+run-once`, so we'll just call it a noun `*`. + +```hoon +=> |% + +$ yil-mold (list @G) :: type of the yield + +$ acc-mold * :: type of the accumulator + -- +``` + +Since Lia's `+run-once` returns a pair of \[yield accumulator], we grab the yield with [`=<`](../../../hoon/rune/tis.md#tisgal) to get the head (`-`) of the result. `+yield-need` is a Lia function that asserts that a yield is successful and returns the unwrapped result. + +Below, we build Lia's `+run-once` core and run it on our imported `.wasm-bin` module, which we give the empty argument `[~ ~]`. (That is, a pair of the initial accumulator state and a map of imports.) The `%$` is where we'd specify a runtime hint like `%bout`, but we stub it out here as we don't need it. + +```hoon +:: run +yield-need on the head of the result +%- yield-need =< - +:: +:: build Lia's +run-once core with our .yil-mold +:: and .acc-mold and run it with our .wasm-bin, which +:: will be given the empty state [~ ~] +%^ (run-once yil-mold acc-mold) [wasm-bin [~ ~]] %$ +``` + +Some more boilerplate. Hoon developers will recognize `.m` by analogy to the `.m` from the boilerplate often seen in [threads](../../urbit-os/base/threads/README.md). `.arrows` is our built [`+arrows`](../../urbit-os/base/wasm/lib-wasm-lia.md#arrows) core from Lia, and we expose that namespace with [`=,`](../../../hoon/rune/tis.md#tiscom) for convenient usage later. + +```hoon +:: define the monadic interface for the script +=/ m (script yil-mold acc-mold) +:: define basic operations +=/ arr (arrows acc-mold) +:: expose the .arr namespace +=, arr +``` + +We'll measure the input list and concatonate all of its elements into a single atom with [`+rep`](../../../hoon/stdlib/2c.md#rep). + +```hoon +:: number of items in the list +=/ len-vec=@ (lent lit) +:: byte-length of the list +=/ len-bytes=@ (mul 8 len-vec) +:: 2^6 = 64 bits per list element +=/ vec=@ (rep 6 lit) +``` + +With that out of the way we can now interact with Wasm VM, replicating steps 1-4 of the JS binding we're using as a reference. We make heavy use of Hoon's [`;<`](../../../hoon/rune/mic.md#micgal) monadic pipeline builder, running expressions and piping the result directly into the one that follows. + +```hoon +:: allocate memory +;< ptr=@ try:m (call-1 '__wbindgen_malloc' len-bytes 8 ~) +:: write the input vector +;< ~ try:m (memwrite ptr len-bytes vec) +:: call the sort_u64 function in the module +;< ptr-len=(list @) try:m (call 'sort_u64' ptr len-vec ~) +:: read the resulting vector from memory +;< vec-out=octs try:m (memread &1.ptr-len (mul 8 &2.ptr-len)) +``` + +Now we split the resulting octets atom (`$octs`, a cell of byte length and data) into a list of 64-bit atoms with [`+rip`](../../../hoon/stdlib/2c.md) and add missing trailing zeroes if necessary. (Note that UrWasm's [`+rope`](../../../urbit-os/base/wasm/lib-wasm-runner-op-def.md#rope) "BROKEN_LINK" would preserve the zeroes.) + +```hoon +:: rip the octet stream into a list of 64-bit atoms +=/ lit-out=(list @) (rip 6 q.vec-out) +:: measure the length of the list +=/ lent-out=@ (lent lit-out) +:: +:: check if .lent-out equals the length of the +:: original list we passed into the generator +?: =(len-vec lent-out) + :: if so, return the output list + (return:m lit-out) +:: +:: if not, use +return from the .m +script core to +:: return the output list with enough trailing zeroes +:: to match the length of the input list +%- return:m +%+ weld lit-out +(reap (sub len-vec lent-out) 0) +``` + +Once you have the `sort.wasm` module and `/gen/sort.hoon` in your `%base` desk, run `|commit %base` and run this `+sort` generator in the Dojo; again we'll see the timed computation with `%bout`. + +``` +> =l (flop (gulf 0 1.000)) +> +took ms/5.012 +> __~ +sort l +~ +``` + +This is a ~10x speedup compared to the pure Hoon implementation. + diff --git a/content/build-on-urbit/wasm/overview.md b/content/build-on-urbit/wasm/overview.md new file mode 100644 index 00000000..b86fd788 --- /dev/null +++ b/content/build-on-urbit/wasm/overview.md @@ -0,0 +1,120 @@ +--- +description: "Overview of the UrWasm project, goals, structure" +layout: + title: + visible: true + description: + visible: false + tableOfContents: + visible: true + outline: + visible: true + pagination: + visible: true +--- + +# UrWasm Overview + +Onboarding new developers onto Urbit necessarily involves teaching them [Hoon](../../hoon/why-hoon.md). Whatever Hoon's merits as a systems programming language, the time commitment to understanding the language enough to be productive is an obvious barrier to entry. + +Urbit allows developers to run server-side code in languages like Rust and Python by compiling that code to [WebAssembly](https://webassembly.org/) (Wasm). Today this still involves some knowledge of Hoon, but the developer can mostly use the examples here as boilerplate. + +Trivially, Urbit's Wasm affordances (collectively "UrWasm") enable Hoon developers to leverage pre-existing libraries for functionality that doesn't already exist in Hoon, or would be prohibitively slow without writing a C [runtime jet](../runtime/jetting.md). But one can imagine more ambitious use-cases like running complete Next.js or Rust apps on the Urbit ship. + +## WebAssembly + +WebAssembly is a small, hardware-independent assembly language for a stack-based virtual machine, primarily intended to be deployed on the web for client- and server-side applications that may be written in one of many programming languages other than Javascript. Wasm is supported out-of-the-box in all mainstream browsers, and it uses Web APIs wherever possible, but Wasm may be executed in other runtimes. + +Compiled Wasm code (a `.wasm` file) is structured as a module, which consists of import and export declarations, function definitions, global variable and memory declarations, etc. A Wasm module describes the starting state of a Wasm virtual machine, whose state is modified by calling its functions, writing directly to the memory of the module, or what have you. + +As a low-level language, Wasm's functions only return 32/64 bit integers and floating-point numbers. Higher-level information like structs and function signatures are stripped away during compliation and must be restored via "host language bindings", which reimplement the source code functions as wrappers around calls to the Wasm VM. These may be generated automatically by the Wasm compiler in addition to the `.wasm` module, usually in Javascript if the code is targeting a browser. + +For example, the following Rust code... + +```rust +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub fn process(input: String) -> String { + let output_string: String = input.chars().rev().collect(); + output_string +} +``` + +...will produce a `.wasm` module alongside the following language binding... + +```javascript +export function process(input) { + let deferred2_0; + let deferred2_1; + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0(input, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + wasm.process(retptr, ptr0, len0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + deferred2_0 = r0; + deferred2_1 = r1; + return getStringFromWasm0(r0, r1); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + wasm.__wbindgen_free(deferred2_0, deferred2_1, 1); + } +} +``` + +...which a web app could invoke like so: + +```javascript +import init, { process } from './our_wasm_module.js'; + +await init(); // init our_wasm_module.wasm +const result = process("hello world"); // run process() +console.log(result); // "dlrow olleh" +``` + +In our case, we'll have to write our own bindings manually in Hoon. We'll cover this later in these docs. + +## Lia + +The main theoretical blocker to executing non-Hoon code on Urbit was that doing so would violate Arvo's commitments to determinism. Some Wasm instructions like `memory.grow` and floating-point operators have non-deterministic behavior. + +UrWasm solves this by executing compiled Wasm in Lia ("Language for Invocation of (web)Assembly"), a tiny interpreter that manages Wasm's handful of nondeterministic edge-cases such that the same inputs to a Wasm function will always result in the same output. The Lia interpreter itself is small enough to be [jetted](../runtime/jetting.md), such that Urbit can execute Wasm code at near-native speeds. + +Lia handles Wasm's non-determinism like so: +- Some numerical operations may return an empty set of values; this is often equivalent to `undefined` in other languages, e.g. when the Wasm VM attempts to divide by zero. Whenever a function returns an empty set of values, Lia treats it as an error and deterministically crashes. +- Some numerical operations may legally return one of a set of values, from which the Wasm VM may select any. This typically occurs with floating-point operations that produce NaN results, where different implementations might return different NaN bit patterns. Lia preserves determinism by normalizing all NaN results to one type. +- Wasm's `memory.grow` may or may not grow the memory; in some cases the choice is left to the host environment like the browser. In Lia, `memory.grow` will always increase the length of linear memory if the module's limits allow. + +## UrWasm Structure + +UrWasm is structured as several nested cores, with each core in this list being in the [subject](../../hoon/why-hoon.md#subject-oriented-programming) of the core below. + +``` +/sur/wasm/wasm/hoon :: Wasm types +/sur/wasm/engine/hoon :: Wasm interpreter types +/sur/wasm/lia/hoon :: Lia types +/lib/wasm/parser/hoon :: Wasm parser +/lib/wasm/validator/hoon :: Wasm validator +/lib/wasm/runner/op-def/hoon :: Wasm operator definitions +/lib/wasm/runner/engine/hoon :: Wasm interpreter +/lib/wasm/lia/hoon :: Lia interpreter +``` + +All cores except `/lib/wasm/lia/hoon`, are additionally wrapped in one-armed cores for easy invocation: + +``` +/sur/wasm/wasm/hoon :: +wasm-sur +/sur/wasm/engine/hoon :: +engine-sur +/sur/wasm/lia/hoon :: +lia-sur +/lib/wasm/parser/hoon :: +parser +/lib/wasm/validator/hoon :: +validator +/lib/wasm/runner/op-def/hoon :: +op-def +/lib/wasm/runner/engine/hoon :: +engine +``` + +Thus if you imported `/lib/wasm/lia/hoon` as `wasm`, you can get the core with Lia types as `lia-sur:wasm`. + +UrWasm's data types and functionality are covered in detail in the [reference](../../urbit-os/base/wasm/README.md) section. + diff --git a/content/urbit-os/base/wasm/README.md b/content/urbit-os/base/wasm/README.md new file mode 100644 index 00000000..62037a86 --- /dev/null +++ b/content/urbit-os/base/wasm/README.md @@ -0,0 +1,43 @@ +--- +description: "UrWasm reference section, including data types and libraries" +layout: + title: + visible: true + description: + visible: false + tableOfContents: + visible: true + outline: + visible: true + pagination: + visible: true +--- + +# UrWasm Reference + +The UrWasm project is structured as a series of nested cores, innermost to outermost: + +``` +/sur/wasm/wasm/hoon :: Wasm types definition +/sur/wasm/engine/hoon :: Wasm interpreter types +/sur/wasm/lia/hoon :: Lia [Language for Invocation of (web)Assembly] types +/lib/wasm/parser/hoon :: Wasm parser +/lib/wasm/validator/hoon :: Wasm validator +/lib/wasm/runner/op-def/hoon :: Wasm operator definitions +/lib/wasm/runner/engine/hoon :: Wasm interpreter +/lib/wasm/lia/hoon :: Lia interpreter +``` + +This reference section documents UrWasm's data types and library functionality. + +## Data types +- [Wasm data types](./wasm-data-types.md) +- [Wasm interpreter data types](./wasm-interpreter-data-types.md) +- [Lia data types](./lia-data-types.md) + +## Libraries +- [Wasm parser](./lib-wasm-parser.md) +- [Wasm validator](./lib-wasm-validator.md) +- [Wasm operator definitions](./lib-wasm-runner-op-def.md) +- [Wasm interpreter](./wasm-interpreter-data-types.md) +- [Lia interpreter](./lib-wasm-lia.md) diff --git a/content/urbit-os/base/wasm/lia-data-types.md b/content/urbit-os/base/wasm/lia-data-types.md new file mode 100644 index 00000000..67f1cf44 --- /dev/null +++ b/content/urbit-os/base/wasm/lia-data-types.md @@ -0,0 +1,236 @@ +--- +description: "Data types for Lia, Urbit's deterministic Wasm interpreter" +layout: + title: + visible: true + description: + visible: false + tableOfContents: + visible: true + outline: + visible: true + pagination: + visible: true +--- + +# Lia Data Types + +The `+lia-sur` core builds on top of `+wasm-sur` and `+engine-sur` to define Lia's monadic, deterministic approach to making Wasm function calls and handling their results. + +### `+lia-sur` {#lia-sur} + +```hoon +++ lia-sur + =, wasm-sur + =, engine-sur + |% + :: ... +:: ... +``` + +Wrapper arm around the Lia core, making its arms and types addressable by limb resolution paths like `lia-value:lia-sur`. Also exposes the `+wasm-sur` and `+engine-sur` namespaces so this core can access the underlying Wasm types. + +### `+lia-value` {#lia-value} + +```hoon +++ lia-value + $~ [%i32 0] + $% [%octs octs] + $<(%ref coin-wasm) + == +``` + +Lia value type. Can be: +- 32-bit integer `0`. +- `%octs`: Byte data with length and content (see [`$octs`](./wasm-data-types.md#octs)). +- Any [`$coin-wasm`](./wasm-data-types.md#coin-wasm) except references. That leaves integers, floats, vectors, and binary data. + +### `+import` {#import} + +```hoon +++ import + |$ [acc] + %+ pair acc + %+ map (pair cord cord) + $- (list coin-wasm) + (script-raw-form (list coin-wasm) acc) +``` + +Wasm module import. + +Pair of the given `$acc` accumulator type with a map of imports to functions. Each `(pair cord cord)` key in the map is a cell of \[module-name export-name]. Each value in the map is a gate that takes a list of [`$coin-wasm`](./wasm-data-types.md#coin-wasm) arguments and returns the script. + +### `+lia-state` {#lia-state} + +```hoon +++ lia-state + |$ [acc] + (trel store (list (list lia-value)) (import acc)) +``` + +Execution state for a Lia script, returning a tuple of: +- [`$store`](./wasm-interpreter-data-types.md#store): Wasm module state. +- Stack of [`+lia-value`s](#lia-value) representing the current script state. +- [`+import`](#import): Available import functions and their implementations. + +### `+script-yield` {#script-yield} + +```hoon +++ script-yield + |$ [a] + $% [%0 p=a] + [%1 name=term args=(list lia-value)] + [%2 ~] + == +``` + +Result of executing a Lia script step: +- `%0`: Step completed with result of type `$a`. +- `%1`: Step blocked on external call to function `.name`, with the provided list of `.args` from the previous step. +- `%2`: Step crashed or errored. + +### `+script-result` {#script-result} + +```hoon +++ script-result + |$ [m-yil m-acc] + [(script-yield m-yil) (lia-state m-acc)] +``` + +Mold for the complete result of script execution including: +- [`$script-yield`](#script-yield) with the execution outcome. +- [`$lia-state`](#lia-state) with updated state. + +### `+script-raw-form` {#script-raw-form} + +```hoon +++ script-raw-form + |* [yil=mold acc=mold] + $-((lia-state acc) (script-result yil acc)) +``` + +Raw form of a Lia script: a gate that takes a [`$lia-state`](#lia-state) and produces a [`$script-result`](#script-result). This represents a stateful computation in the Lia monad. + +### `+script` {#script} + +```hoon +++ script + |* [m-yil=mold m-acc=mold] + |% + :: +output + :: +yield + :: +form + :: +m-sat + :: +return + :: +try + :: +catch + -- +``` + +Monadic interface for Lia scripts with yield mold `.m-yil` and accumulator mold `.m-acc`. + +#### `+output` {#output} + +```hoon +++ output (script-result m-yil m-acc) +``` + +Final `$script-result` of this script. + +#### `+yield` {#yield} + +```hoon +++ yield (script-yield m-yil) +``` + +Final `$script-yield` of this script. + +#### `+form` {#form} + +```hoon +++ form (script-raw-form m-yil m-acc) +``` + +Built script mold, analogous to `+form:m` from thread [strands](../../../urbit-os/base/threads/basics/fundamentals.md#strands). + +#### `+m-sat` {#m-sat} + +```hoon +++ m-sat (lia-state m-acc) +``` + +Execution state for this script with the provided `.m-acc`. + +#### `+return` {#return} + +```hoon +++ return :: pure + |= arg=m-yil + ^- form + |= s=m-sat + [0+arg s] +``` + +Monadic (pure) return: lifts a value into the Lia monad without changing script state. + +#### `+try` {#try} + +```hoon +++ try :: monadic bind + |* m-mond=mold + |= [mond=(script-raw-form m-mond m-acc) cont=$-(m-mond form)] + ^- form + |= s=m-sat + =^ mond-yil=(script-yield m-mond) s (mond s) + ^- output + ?. ?=(%0 -.mond-yil) [mond-yil s] + ((cont p.mond-yil) s) +``` + +Monadic bind: sequences two computations, passing the result of the first to the second if successful (`%0`). Handles early termination for blocked (`%1`) or crashed (`%2`) states. + +#### `+catch` {#catch} + +```hoon +++ catch :: bind either + |* m-mond=mold + |= $: $: try=(script-raw-form m-mond m-acc) + err=(script-raw-form m-mond m-acc) + == + cont=$-(m-mond form) + == + ^- form + |= s=m-sat + =^ try-yil=(script-yield m-mond) s (try s) + ^- output + ?: ?=(%0 -.try-yil) + ((cont p.try-yil) s) + ?: ?=(%1 -.try-yil) + [try-yil s] + =^ err-yil s (err s) + ?. ?=(%0 -.err-yil) [err-yil s] + ((cont p.err-yil) s) +``` + +Error handling combinator: attempts the first computation, and if it crashes (`%2`), tries the error handler. Blocked states (`%1`) are propagated immediately. + +### `$seed` {#seed} + +```hoon ++$ seed + $: + module=octs + past=(script-raw-form (list lia-value) *) + shop=(list (list lia-value)) + import=(import *) + == +``` + +Complete Lia interpreter state containing all necessary components for module execution: +- `.module`: Wasm module binary data. +- `.past`: Previously executed script that established the current state. +- `.shop`: External results accumulator - lists of values from resolved import function calls. +- `.import`: Map of available import functions that the module can call. + +This represents the "formal state" of the Lia interpreter, suitable for caching and resuming if necessary. + diff --git a/content/urbit-os/base/wasm/lib-wasm-lia.md b/content/urbit-os/base/wasm/lib-wasm-lia.md new file mode 100644 index 00000000..d660f246 --- /dev/null +++ b/content/urbit-os/base/wasm/lib-wasm-lia.md @@ -0,0 +1,306 @@ +--- +description: "Reference for UrWasm's Lia interpreter" +layout: + title: + visible: true + description: + visible: false + tableOfContents: + visible: true + outline: + visible: true + pagination: + visible: true +--- + +## Lia Interpreter + +The `/lib/wasm/lia.hoon` library implements Lia (Language for Invocation of webAssembly), a high-level scripting interface for executing WebAssembly modules within Urbit. Lia provides deterministic execution of Wasm code while maintaining performance through caching and efficient state management. + +Lia handles: +- Deterministic execution of Wasm modules. +- Memory read/write operations. +- Function invocation with automatic type conversion. +- Global variable access. +- Import function handling for external calls. + +## `$run-input` {#run-input} + +```hoon ++$ run-input + (each (script-raw-form (list lia-value) *) (list lia-value)) +``` + +Input type for [`+run`](#run). Either a new script to execute or cached values from previous execution. + +## `+run-once` {#run-once} + +```hoon +++ run-once + |* [type-yield=mold type-acc=mold] + |= [[binary=octs imp=(import type-acc)] hint=term script-in=form:m] + ^- [yield:m type-acc] + :: ... +``` + +Build a stateless execution arm for a Wasm module. Takes: +- `.type-yield`: Mold for the yield type. +- `.type-acc`: Mold for the accumulator type. +- `.binary`: WebAssembly module as [`$octs`](./wasm-data-types.md#octs). +- `.imp`: Import function map. +- `.hint`: Execution hint. +- `.script-in`: Lia script to execute. + +Returns the script yield and final accumulator state. + +### `+init:run-once` {#init-run-once} + +```hoon +++ init + =/ m (script ,~ type-acc) + ^- form:m + |= sat=(lia-state type-acc) + ^- output:m + :: ... +``` + +Initializes the Wasm module within [`+run-once`](#run-once). Handles module instantiation and processes any required imports by calling external functions. + +## `+run` {#run} + +```hoon +++ run + |= [input=run-input =seed hint=term] + ^- [yield:m * _seed] + :: ... +``` + +Stateful execution of a Wasm module. Can be used to run a new script or resume computation with the provided `$seed` state. + +### `+init` {#init-run} + +```hoon +++ init + =/ m (script ,~ *) + ^- form:m + |= sat=(lia-state) + ^- output:m +``` + +Initializes the Wasm module within [`+run`](#run). Similar to [`+init`](#init-run-once) in `+run-once` but operates on the monomorphic [`$lia-state`](./lia-data-types.md#lia-state). + +## `+arrows` {#arrows} + +```hoon +++ arrows + |* m-acc=mold + ^? |% + :: ... +``` + +Core containing Kleisli arrows (monadic operations) for Lia script operations. Type-polymorphic over the accumulator type `.m-acc`. + +### `$m-sat` {#m-sat} + +```hoon ++$ m-sat (lia-state m-acc) +``` + +Type alias for the polymorphic [`$lia-state`](./lia-data-types.md#lia-state) with accumulator type `.m-acc`. + +### `+call` {#call} + +```hoon +++ call + |= [name=cord args=(list @)] + =/ m (script (list @) m-acc) + ^- form:m +``` + +Invokes a WebAssembly function by name with the given arguments. Performs type conversion from Hoon atoms to [`$coin-wasm`](./wasm-data-types.md#coin-wasm) values and back. Handles import function resolution if the called function is not locally defined. + +### `+call-1` {#call-1} + +```hoon +++ call-1 + |= [name=cord args=(list @)] + =/ m (script @ m-acc) + ^- form:m +``` + +Convenience function for calling WebAssembly functions that return exactly one value. Wraps [`+call`](#call) and extracts the single return value. + +### `+memread` {#memread} + +```hoon +++ memread + |= [ptr=@ len=@] + =/ m (script octs m-acc) + ^- form:m +``` + +Reads `.len` bytes from WebAssembly linear memory starting at address `.ptr`. Returns the data as [`$octs`](./wasm-data-types.md#octs). Performs bounds checking against the current memory size. + +### `+memwrite` {#memwrite} + +```hoon +++ memwrite + |= [ptr=@ len=@ src=@] + =/ m (script ,~ m-acc) + ^- form:m +``` + +Writes `.len` bytes from atom `.src` to WebAssembly linear memory starting at address `.ptr`. Performs bounds checking and fails if the write would exceed memory bounds. + +### `+call-ext` {#call-ext} + +```hoon +++ call-ext + |= [name=term args=(list lia-value)] + =/ m (script (list lia-value) m-acc) + ^- form:m +``` + +Calls an external (import) function. If no cached result is available, yields the function call request to the host environment. Otherwise returns the cached result. + +### `+global-set` {#global-set} + +```hoon +++ global-set + |= [name=cord value=@] + =/ m (script ,~ m-acc) + ^- form:m +``` + +Sets the value of an exported global variable by name. Verifies the global is mutable and performs type conversion from atom to the appropriate [`$coin-wasm`](./wasm-data-types.md#coin-wasm) type. + +### `+global-get` {#global-get} + +```hoon +++ global-get + |= name=cord + =/ m (script @ m-acc) + ^- form:m +``` + +Gets the value of an exported global variable by name. Returns the value as an atom, performing type conversion from [`$coin-wasm`](./wasm-data-types.md#coin-wasm). + +### `+memory-size` {#memory-size} + +```hoon +++ memory-size + =/ m (script @ m-acc) + ^- form:m +``` + +Returns the current size of WebAssembly linear memory in pages. Each page is [`+page-size`](#page-size) bytes (65,536 bytes). + +### `+memory-grow` {#memory-grow} + +```hoon +++ memory-grow + |= delta=@ + =/ m (script @ m-acc) + ^- form:m +``` + +Grows WebAssembly linear memory by `.delta` pages. Returns the previous memory size in pages. Growing memory preserves existing data and zero-initializes the new pages. + +### `+get-acc` {#get-acc} + +```hoon +++ get-acc + =/ m (script m-acc m-acc) + ^- form:m +``` + +Returns the current accumulator value from the Lia state. + +### `+set-acc` {#set-acc} + +```hoon +++ set-acc + |= acc=m-acc + =/ m (script ,~ m-acc) + ^- form:m +``` + +Sets the accumulator value in the Lia state. + +### `+get-all-local-globals` {#get-all-local-globals} + +```hoon +++ get-all-local-globals + =/ m (script (list @) m-acc) + ^- form:m +``` + +Returns all local (non-import) global variable values as a list of atoms. + +### `+set-all-local-globals` {#set-all-local-globals} + +```hoon +++ set-all-local-globals + |= vals=(list @) + =/ m (script ,~ m-acc) + ^- form:m +``` + +Sets all local global variables from a list of atoms. Performs type conversion to the appropriate [`$coin-wasm`](./wasm-data-types.md#coin-wasm) types. + +## `+runnable` {#runnable} + +```hoon +++ runnable (script (list lia-value) *) +``` + +Type alias for the monomorphic script type used by [`+run`](#run). + +## `+cw-to-atom` {#cw-to-atom} + +```hoon +++ cw-to-atom + |= cw=coin-wasm:wasm-sur + ^- @ +``` + +Converts a [`$coin-wasm`](./wasm-data-types.md#coin-wasm) value to an atom. Fails if the coin represents a reference type. + +## `+types-atoms-to-coins` {#types-atoms-to-coins} + +```hoon +++ types-atoms-to-coins + |= [a=(list valtype:wasm-sur) b=(list @)] + ^- (list coin-wasm:wasm-sur) +``` + +Converts parallel lists of [`$valtype`s](./wasm-data-types.md#valtype) and atoms into a list of [`$coin-wasm`](./wasm-data-types.md#coin-wasm) values. Used for function argument conversion. + +## `+valtype-from-coin` {#valtype-from-coin} + +```hoon +++ valtype-from-coin + |= =cw + ^- valtype:wasm-sur +``` + +Extracts the [`$valtype`](./wasm-data-types.md#valtype) from a [`$coin-wasm`](./wasm-data-types.md#coin-wasm) value. + +## `+page-size` {#page-size} + +```hoon +++ page-size ^~((bex 16)) +``` + +WebAssembly page size constant: 65,536 bytes. + +## `+yield-need` {#yield-need} + +```hoon +++ yield-need + |* a=(script-yield *) + ?> ?=(%0 -.a) + p.a +``` + +Extracts the payload from a successful script yield, crashing if the yield indicates failure or blocking. diff --git a/content/urbit-os/base/wasm/lib-wasm-parser.md b/content/urbit-os/base/wasm/lib-wasm-parser.md new file mode 100644 index 00000000..cf689b87 --- /dev/null +++ b/content/urbit-os/base/wasm/lib-wasm-parser.md @@ -0,0 +1,1798 @@ +--- +description: "Reference for UrWasm's parsing library" +layout: + title: + visible: true + description: + visible: false + tableOfContents: + visible: true + outline: + visible: true + pagination: + visible: true +--- + +# Wasm Parser + +The `/lib/wasm/parser.hoon` library converts Wasm bytecode into UrWasm's Hoon types defined in [`+wasm-sur`](./wasm-data-types.md), which is nested in the imported [`/sur/wasm/lia.hoon`](./lia-data-types.md) file. This library implements parsing rules that follow the [WebAssembly Core Specification](https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/index.html) binary format. + +## `+parser` {#parser} + +```hoon +|% +++ parser + =/ sur wasm-sur + |% + :: ... + -- +-- +``` + +Parser core. Exposes the nested `+wasm-sur` namespace as `.sur`. + +## `+main` {#main} + +```hoon +++ main + |= wasm=octs + =, sur + ^- module + =| out=module + =/ bytes=tape (trip q.wasm) + :: add trailing zeros + =. bytes + %+ weld bytes + ^- tape + (reap (sub p.wasm (lent bytes)) '\00') + (scan bytes module:r) +``` + +Main parsing function that takes a Wasm binary as [`$octs`](./wasm-data-types.md#octs) and returns a parsed [`$module`](./wasm-data-types.md#module). Extends the octet stream with trailing zeros to ensure proper parsing then applies the [`+module:r`](#module) parsing rule. + +## `+r` {#r} + +Parsing rule core. Parsing rules often use the same name as the types in `/sur/wasm.hoon`, e.g. `+num-type:r` is the parsing rule that produces a `$num-type:sur`. + +### `+womp` {#womp} + +```hoon +++ womp + |* rul=rule + $_ =+ vex=(rul) + ?> ?=(^ q.vex) + p.u.q.vex +``` + +Returns a mold of the product of the given [`$rule`](../../../hoon/stdlib/3g.md#rule). + +### `+bild` {#bild} + +```hoon +++ bild + |* [vex=edge gat=_=>(rule |*(* *rule))] + ?~ q.vex + vex + %. [vex (gat p.u.q.vex)] + (comp |*([a=* b=*] b)) +``` + +Connects an [`$edge`](../../../hoon/stdlib/3g.md#edge) (parsing output) with a rule and a rule-producing gate, returning the result of the rule produced by slamming the gate with the result of the first rule. + +Used for conditional parsing based on previously parsed values. + +### `+bonk` {#bonk} + +```hoon +++ bonk + |* [tet=rule fes=rule] + |= tub=nail + ^+ (fes) + =+ try=(tet tub) + ?~ q.try try + =+ zen=(fes tub) + ?~ q.zen zen + ?: =(q.u.q.try q.u.q.zen) + zen + (fail tub) +``` + +Rule modifier. Applies two rules to a [`$nail`](../../../hoon/stdlib/3g.md#nail), failing if either rule fails to parse or if the rules return different continuations. Returns the result of the second rule otherwise. + +### `+feel` {#feel} + +```hoon +++ feel + |* [a=* sef=rule] + |= tub=nail + =+ vex=(sef tub) + ?~ q.vex vex + ?: =(a p.u.q.vex) + vex + [p=p.vex q=~] +``` + +Rule modifier. Tests equality of the parsing result with a given noun. Only succeeds if the parsed value `.p.u.q.vex` exactly matches the expected value `.a`. + +### `+u-n` {#u-n} + +```hoon +++ u-n + |= n-bits=@ + =* this $ + %+ knee *@ + |. ~+ + ;~ pose + :: multiple byte case + ?: (lte n-bits 7) fail + %+ cook + |= [n=@ m=@] + %+ add + (mul 128 m) + (sub n 128) + ;~ plug + (shim 128 255) + this(n-bits (sub n-bits 7)) + == + :: single byte case + (cook ,@ (shim 0 (dec (bex (min n-bits 7))))) + == +``` + +Parse `@` integer with bitwidth $$n$$ as an atom. Handles both single-byte and multi-byte cases recursively. + +### `+s-n` {#s-n} + +```hoon +++ s-n + |= n-bits=@ + =* this $ + %+ knee *@s + |. ~+ + ;~ pose + :: single byte: positive + (cook (cury new:si &) (shim 0 (dec (bex (min (dec n-bits) 6))))) + :: single byte: negative + %+ cook + |= n=@ + =, si + (dif (new & n) --128) + ;~ simu + (shim 64 127) + (shim (sub 128 (min 128 (bex (dec n-bits)))) 127) + == + :: multiple bytes + ?: (lte n-bits 7) fail + %+ cook + |= [n=@s m=@s] + =, si + (sum (dif n --128) (pro --128 m)) + ;~ plug + (cook (cury new:si &) (shim 128 255)) + this(n-bits (sub n-bits 7)) + == + == +``` + +Parse `@s` integer with bitwidth $$n$$. Implements WebAssembly's signed LEB128 integer encoding with proper sign extension handling for positive and negative integers. + +### `+u32` {#u32} + +```hoon +++ u32 (u-n 32) +``` + +Parse 32-bit unsigned integer. + +### `+u64` {#u64} + +```hoon +++ u64 (u-n 64) +``` + +Parse 64-bit unsigned integer. + +### `+f32` {#f32} + +```hoon +++ f32 + %+ cook + |= =(list @) + ;; @rs + (rep 3 list) + (stun [4 4] next) +``` + +Parse 32-bit float. + +Reads exactly 4 bytes and reconstructs them into a `@rs` atom. + +### `+f64` {#f64} + +```hoon +++ f64 + %+ cook + |= =(list @) + ;; @rd + (rep 3 list) + (stun [8 8] next) +``` + +Parse 64-bit float. + +Reads exactly 8 bytes and reconstructs them into a `@rd` atom. + +### `+vec` {#vec} + +```hoon +++ vec + |* rul=rule + ;~ bild + u32 + |= n=@ + (stun [n n] rul) + == +``` + +Parses a Wasm vector. + +First parses a u32 length, then parses exactly that many instances of the given rule. This is the standard WebAssembly encoding for variable-length sequences. + +### `+name` {#name} + +```hoon +++ name (cook crip (vec next)) +``` + +Parse Wasm name (UTF-8 string). + +Uses `+vec` to get the length and bytes, then converts the parsed `$rule` to a `@t`. + +### `+vec-u32` {#vec-u32} + +```hoon +++ vec-u32 (vec u32) +``` + +Parse vector of 32-bit unsigned integers. + +### `+num-type` {#num-type} + +```hoon +++ num-type + %+ cook |=(num-type:sur +<) + ;~ pose + (cold %i32 (just '\7f')) + (cold %i64 (just '\7e')) + (cold %f32 (just '\7d')) + (cold %f64 (just '\7c')) + == +``` + +Parse Wasm number type. + +Maps binary opcodes to [`$num-type`](./wasm-data-types.md#num-type) values. + +### `+vec-type` {#vec-type} + +```hoon +++ vec-type (cold %v128 (just '\7b')) +``` + +Parse Wasm vector type. + +### `+ref-type` {#ref-type} + +```hoon +++ ref-type + %+ cook |=(ref-type:sur +<) + ;~ pose + (cold %extn (just '\6f')) + (cold %func (just '\70')) + == +``` + +Parse Wasm reference type. + +Maps binary opcodes to [`$ref-type`](./wasm-data-types.md#ref-type) values. + +### `+valtype` {#valtype} + +```hoon +++ valtype + %+ cook |=(valtype:sur +<) + ;~(pose num-type vec-type ref-type) +``` + +Parse Wasm value type. + +Homogenizes results of `+num-type:r`, `+vec-type:r`, and `+ref-type:r` parsers into [`$valtype`s](./wasm-data-types.md#valtype). + +### `+func-type` {#func-type} + +```hoon +++ func-type + %+ cook |=(func-type:sur +<) + ;~ pfix + (just '\60') + ;~(plug (vec valtype) (vec valtype)) + == +``` + +Parse Wasm function. + +Expects the function type opcode `\60`, followed by parameter types and result types as vectors. (See [`$func-type`](./wasm-data-types.md#func-type).) + +### `+limits` {#limits} + +```hoon +++ limits + %+ cook |=(limits:sur +<) + ;~ pose + ;~(plug (cold %flor (just '\00')) u32) + ;~(plug (cold %ceil (just '\01')) u32 u32) + == +``` + +Parse Wasm memory/table limits. + +Either minimum only (`%flor`) or minimum and maximum (`%ceil`). + +## Instruction and Expression Parsing Rules + +### `+expr` {#expr} + +```hoon +++ expr + %+ knee *expression:sur + |. ~+ + ;~(sfix (star instr) end) +``` + +Parse Wasm expression as a sequence of instructions terminated by an [`+end`](#end) opcode. + +### `+expr-pair` {#expr-pair} + +```hoon +++ expr-pair + %+ knee [*expression:sur *expression:sur] + |. ~+ + ;~ plug + (star instr) + ;~ pose + (cold ~ end) + (ifix [else end] (star instr)) + == + == +``` + +Parse a pair of [`$expression`s](./wasm-data-types.md#expression) for [`+if`](#if) statements. + +First expression (true branch), followed by optional [`+else`](#else) and second expression (false branch). + +### `+end` {#end} + +```hoon +++ end (just '\0b') +``` + +Parse Wasm `end` opcode (`0x0B`). + +### `+else` {#else} + +```hoon +++ else (just '\05') +``` + +Parse Wasm `else` opcode (`0x05`). + +### `+const-i32` {#const-i32} + +```hoon +++ const-i32 (just '\41') +``` + +Parse Wasm `i32.const` opcode (`0x41`). + +### `+const-i64` {#const-i64} + +```hoon +++ const-i64 (just '\42') +``` + +Parse Wasm `i64.const` opcode (`0x42`). + +### `+const-f32` {#const-f32} + +```hoon +++ const-f32 (just '\43') +``` + +Parse Wasm `f32.const` opcode (`0x43`). + +### `+const-f64` {#const-f64} + +```hoon +++ const-f64 (just '\44') +``` + +Parse Wasm `f64.const` opcode (`0x44`). + +### `+block-op` {#block-op} + +```hoon +++ block-op (just '\02') +``` + +Parse Wasm `block` opcode (`0x02`). + +### `+loop-op` {#loop-op} + +```hoon +++ loop-op (just '\03') +``` + +Parse Wasm `loop` opcode (`0x03`). + +### `+if-op` {#if-op} + +```hoon +++ if-op (just '\04') +``` + +Parse Wasm `if` opcode (`0x04`). + +### `+form-ranges` {#form-ranges} + +```hoon +++ form-ranges + |= l=(list @) + ^- (list ?(@ [@ @])) + ?~ l ~ + =+ a=i.l + =+ b=i.l + |- ^- (list ?(@ [@ @])) + ?~ t.l + :_ ~ + ?: =(a b) a + [a b] + ?> (gth i.t.l b) + ?: =(i.t.l +(b)) + $(b i.t.l, t.l t.t.l) + :- ?: =(a b) a + [a b] + $(a i.t.l, b i.t.l, t.l t.t.l) +``` + +Helper function that converts a list of sorted integers into a list of ranges (`?(@ [@ @])`). + +### `+instr` {#instr} + +```hoon +++ instr + ~+ %- stew ^. stet + ;: welp + ^. limo + :~ + ['\fc' ;~(pfix next fc)] + ['\fd' ;~(pfix next fd)] + ['\1c' select-vec] + ['\0e' br-table] + ['\02' block] + ['\03' loop] + ['\04' if] + ['\d0' ;~(pfix next (stag %ref-null ref-type))] + ['\d2' ;~(pfix next (stag %ref-func u32))] + == + :: + %- turn :_ (late instr-zero) + %- form-ranges + %+ skim (gulf 0 255) + |=(n=@ ?=(^ (op-map n))) + :: + %+ turn + %- form-ranges + %+ skim (gulf 0 255) + |=(n=@ ?=(^ (handle-one-arg-i32 n 0))) + %- late + %+ sear handle-one-arg-i32 + ;~(plug next u32) + :: + ^. limo + :~ + ['\41' (cook handle-const-i32 ;~(pfix next (s-n 32)))] + ['\42' (cook handle-const-i64 ;~(pfix next (s-n 64)))] + ['\43' (cook handle-const-f32 ;~(pfix next f32))] + ['\44' (cook handle-const-f64 ;~(pfix next f64))] + == + :: + %+ turn + %- form-ranges + %+ skim (gulf 0 255) + |=(n=@ ?=(^ (handle-two-args-i32 n 0 0))) + %- late + %+ sear handle-two-args-i32 + ;~(plug next u32 u32) + :: + == +``` + +Main instruction parser. Uses `+stew` parser to switch on the given text to the appropriate parser. + +### `+select-vec` {#select-vec} + +```hoon +++ select-vec + %+ cook |=(instruction:sur +<) + ;~ pfix + next + %+ stag %select + %+ stag %~ + (vec valtype) + == +``` + +Parses a typed `select` instruction with explicit value types. + +### `+br-table` {#br-table} + +```hoon +++ br-table + %+ cook handle-br-table + ;~(pfix next ;~(plug vec-u32 u32)) +``` + +Parse Wasm `br_table` (branch table) expression with vector of labels and default label. + +### `+instr-zero` {#instr-zero} + +```hoon +++ instr-zero (sear op-map next) +``` + +Parse instructions with zero arguments by looking up the opcode in the [opcode map](#op-map). + +### `+block` {#block} + +```hoon +++ block + %+ cook handle-block + ;~(pfix next ;~(plug block-type expr)) +``` + +Parse Wasm `block` expression with [block type](#block-type) and body [expression](#expr). + +### `+loop` {#loop} + +```hoon +++ loop + %+ cook handle-loop + ;~(pfix next ;~(plug block-type expr)) +``` + +Parse Wasm `loop` expression with block type and body expression. + +### `+if` {#if} + +```hoon +++ if + %+ cook handle-if + ;~ pfix + next + ;~(plug block-type expr-pair) + == +``` + +Parse Wasm `if` expression with block type and true/false expression pair. + +### `+block-type` {#block-type} + +```hoon +++ block-type + %+ cook |=(block-type:sur +<) + ;~ pose + (cold [~ ~] (just '\40')) + ;~(plug (easy ~) (sear get-valtype next) (easy ~)) + :: + %+ sear + |= a=@s + ^- (unit @) + ?. (syn:si a) ~ + `(abs:si a) + (s-n 33) + :: + == +``` + +Parse Wasm [`$block-type`](./wasm-data-types.md#block-type). + +## Handler Functions + +### `+handle-one-arg-i32` {#handle-one-arg-i32} + +```hoon +++ handle-one-arg-i32 + |= [op=char arg=@] + ^- (unit instruction:sur) + ?+ op ~ + %0xc `[%br arg] + %0xd `[%br-if arg] + %0x10 `[%call arg] + %0x20 `[%local-get arg] + %0x21 `[%local-set arg] + %0x22 `[%local-tee arg] + %0x23 `[%global-get arg] + %0x24 `[%global-set arg] + %0x25 `[%table-get arg] + %0x26 `[%table-set arg] + %0x3f `[%memory-size ?>(=(arg 0) %0)] + %0x40 `[%memory-grow ?>(=(arg 0) %0)] + == +``` + +Handle Wasm instructions that take one 32-bit argument. + +Maps opcodes to their corresponding [`$instruction`](./wasm-data-types.md#instruction) representations. + +### `+handle-two-args-i32` {#handle-two-args-i32} + +```hoon +++ handle-two-args-i32 + |= [op=char arg1=@ arg2=@] + ^- (unit instruction:sur) + ?+ op ~ + %0x11 + `[%call-indirect arg1 arg2] + :: + %0x28 + `[%load %i32 [arg1 arg2] ~ ~] + :: + %0x29 + `[%load %i64 [arg1 arg2] ~ ~] + :: + %0x2a + `[%load %f32 [arg1 arg2] ~ ~] + :: + %0x2b + `[%load %f64 [arg1 arg2] ~ ~] + :: + %0x2c + `[%load %i32 [arg1 arg2] `%8 `%s] + :: + %0x2d + `[%load %i32 [arg1 arg2] `%8 `%u] + :: + %0x2e + `[%load %i32 [arg1 arg2] `%16 `%s] + :: + %0x2f + `[%load %i32 [arg1 arg2] `%16 `%u] + :: + %0x30 + `[%load %i64 [arg1 arg2] `%8 `%s] + :: + %0x31 + `[%load %i64 [arg1 arg2] `%8 `%u] + :: + %0x32 + `[%load %i64 [arg1 arg2] `%16 `%s] + :: + %0x33 + `[%load %i64 [arg1 arg2] `%16 `%u] + :: + %0x34 + `[%load %i64 [arg1 arg2] `%32 `%s] + :: + %0x35 + `[%load %i64 [arg1 arg2] `%32 `%u] + :: + %0x36 + `[%store %i32 [arg1 arg2] ~] + :: + %0x37 + `[%store %i64 [arg1 arg2] ~] + :: + %0x38 + `[%store %f32 [arg1 arg2] ~] + :: + %0x39 + `[%store %f64 [arg1 arg2] ~] + :: + %0x3a + `[%store %i32 [arg1 arg2] `%8] + :: + %0x3b + `[%store %i32 [arg1 arg2] `%16] + :: + %0x3c + `[%store %i64 [arg1 arg2] `%8] + :: + %0x3d + `[%store %i64 [arg1 arg2] `%16] + :: + %0x3e + `[%store %i64 [arg1 arg2] `%32] + == +:: +``` + +Handle Wasm instructions that take two 32-bit immediate arguments. + +### `+handle-br-table` {#handle-br-table} + +```hoon +++ handle-br-table + |= [vec=(list @) i=@] + ^- instruction:sur + [%br-table vec i] +``` + +Handle Wasm `br_table` expression (switch statement) by constructing the list of branches with a default index. + +### `+handle-block` {#handle-block} + +```hoon +++ handle-block + |= [type=block-type:sur body=expression:sur] + ^- $>(%block instruction:sur) + [%block type body] +``` + +Handle Wasm `block` expression. + +### `+get-valtype` {#get-valtype} + +```hoon +++ get-valtype + |= byte=@ + ^- (unit valtype:sur) + ?+ byte ~ + %0x7f `%i32 + %0x7e `%i64 + %0x7d `%f32 + %0x7c `%f64 + == +``` + +Map byte values to value types for block type parsing. + +Map opcodes to value types to construct [`$valtype`s](./wasm-data-types.md#valtype) + +### `+handle-loop` {#handle-loop} + +```hoon +++ handle-loop + |= [type=block-type:sur body=expression:sur] + ^- instruction:sur + [%loop type body] +``` + +Handle Wasm `loop` expression. + +### `+handle-if` {#handle-if} + +```hoon +++ handle-if + |= $: type=block-type:sur + body-true=expression:sur + body-false=expression:sur + == + ^- instruction:sur + [%if type body-true body-false] +``` + +Handle Wasm `if` expression construction with true and false branches. + +### `+handle-const-f64` {#handle-const-f64} + +```hoon +++ handle-const-f64 + |= i=@rd + ^- instruction:sur + [%const %f64 i] +``` + +Handle Wasm `f64.const` expression. + +### `+handle-const-f32` {#handle-const-f32} + +```hoon +++ handle-const-f32 + |= i=@rs + ^- instruction:sur + [%const %f32 i] +``` + +Handle Wasm `f32.const` expression. + +### `+handle-const-i32` {#handle-const-i32} + +```hoon +++ handle-const-i32 + |= i=@s + ^- instruction:sur + =; i-unsigned=@ + [%const %i32 i-unsigned] + =, si + ?: (syn i) + +:(old i) + (sub (bex 32) +:(old i)) +``` + +Handle Wasm `i32.const` expression with signed to unsigned conversion. + +### `+handle-const-i64` {#handle-const-i64} + +```hoon +++ handle-const-i64 + |= i=@s + ^- instruction:sur + =; i-unsigned=@ + [%const %i64 i-unsigned] + =, si + ?: (syn i) + +:(old i) + (sub (bex 64) +:(old i)) +``` + +Handle Wasm `i64.const` expression with signed to unsigned conversion. + +## `+fc` {#fc} + +`0xFC` extension parser for saturating truncation instructions. + +```hoon +++ fc + |^ + %+ cook |=(instruction:sur +<) + ;~ pose + zero-args + one-arg + two-args + == +``` + +Parser for Wasm instructions prefixed with `0xFC`, which includes memory and table operations. + +### `+zero-args` {#zero-args} + +```hoon +++ zero-args (sear handle-zero u32) +``` + +Parse extended instructions with zero additional arguments. + +### `+one-arg` {#one-arg} + +```hoon +++ one-arg (sear handle-one ;~(plug u32 u32)) +``` + +Parse extended instructions with one additional argument. + +### `+two-args` {#two-args} + +```hoon +++ two-args (sear handle-two ;~(plug u32 u32 u32)) +``` + +Parse extended instructions with two additional arguments. + +### `+handle-zero` {#handle-zero} + +```hoon +++ handle-zero + |= op=@ + ^- (unit instruction:sur) + ?. (lte op 7) ~ + :- ~ + :* + %trunc + :: Type + ?:((lte op 3) %i32 %i64) + :: Source type + `?:(=(0 (mod (div op 2) 2)) %f32 %f64) + :: Mode + `?:(=(0 (mod op 2)) %s %u) + :: + & :: saturated + == +``` + +Handle saturating truncation instructions (opcodes 0-7). + +### `+handle-one` {#handle-one} + +```hoon +++ handle-one + |= [op=@ arg=@] + ^- (unit instruction:sur) + ?+ op ~ + %9 `[%data-drop arg] + %11 `[%memory-fill ?>(?=(%0 arg) arg)] + %13 `[%elem-drop arg] + %15 `[%table-grow arg] + %16 `[%table-size arg] + %17 `[%table-fill arg] + == +``` + +Handle extended instructions with one argument. + +### `+handle-two` {#handle-two} + +```hoon +++ handle-two + |= [op=@ arg1=@ arg2=@] + ^- (unit instruction:sur) + ?+ op ~ + %8 `[%memory-init arg1 ?>(?=(%0 arg2) arg2)] + %10 `[%memory-copy ?>(?=(%0 arg1) arg1) ?>(?=(%0 arg2) arg2)] + %12 `[%table-init arg1 arg2] + %14 `[%table-copy arg1 arg2] + == +``` + +Handle extended instructions with two arguments. + +## `+fd` {#fd} + +0xFD extension parser for SIMD (vector) instructions. + +```hoon +++ fd + |^ + :: Opcode and immediate parameters + ;~ pose + (sear memarg ;~(plug u32 u32 u32)) + (sear mem-lane ;~(plug u32 ;~(plug u32 u32) next)) + (cook const ;~(pfix (feel 12 u32) (stun [16 16] next))) + (cook shuffle ;~(pfix (feel 13 u32) (stun [16 16] next))) + (sear lane ;~(plug u32 next)) + (sear simd-map u32) + == +``` + +Parser for Wasm SIMD instructions. + +### `+memarg` {#memarg} + +```hoon +++ memarg + |= [op=@ mem=[@ @]] + ^- (unit instruction:sur) + =; =(unit instr-vec:sur) + ?~ unit ~ + `[%vec u.unit] + ?+ op ~ + %0 `[%load mem ~] + %1 `[%load mem ~ %8 %extend %s] + %2 `[%load mem ~ %8 %extend %u] + %3 `[%load mem ~ %16 %extend %s] + %4 `[%load mem ~ %16 %extend %u] + %5 `[%load mem ~ %32 %extend %s] + %6 `[%load mem ~ %32 %extend %u] + %7 `[%load mem ~ %8 %splat] + %8 `[%load mem ~ %16 %splat] + %9 `[%load mem ~ %32 %splat] + %10 `[%load mem ~ %64 %splat] + %92 `[%load mem ~ %32 %zero] + %93 `[%load mem ~ %64 %zero] + %11 `[%store mem] + == +:: +``` + +Handle SIMD memory instructions (vector loads/stores with memory arguments). + +### `+mem-lane` {#mem-lane} + +```hoon +++ mem-lane + |= [op=@ mem=[@ @] l=@] + ^- (unit instruction:sur) + =; =(unit instr-vec:sur) + ?~ unit ~ + `[%vec u.unit] + ?+ op ~ + %84 `[%load-lane mem %8 l] + %85 `[%load-lane mem %16 l] + %86 `[%load-lane mem %32 l] + %87 `[%load-lane mem %64 l] + %88 `[%store-lane mem %8 l] + %89 `[%store-lane mem %16 l] + %90 `[%store-lane mem %32 l] + %91 `[%store-lane mem %64 l] + == +:: +``` + +Handle SIMD lane-specific memory operations. + +### `+const` {#const} + +```hoon +++ const + |= =(list @) + ^- instruction:sur + :^ %vec %const %v128 + (rep 3 list) +``` + +Handle SIMD constant vectors (`v128.const`). + +### `+shuffle` {#shuffle} + +```hoon +++ shuffle + |= =(list @) + ^- instruction:sur + [%vec %shuffle list] +``` + +Handle SIMD shuffle operations with lane indices. + +### `+lane` {#lane} + +```hoon +++ lane + |= [op=@ l=@] + ^- (unit instruction:sur) + =; =(unit instr-vec:sur) + ?~ unit ~ + `[%vec u.unit] + ?+ op ~ + %21 `[%extract %i8 l %s] + %22 `[%extract %i8 l %u] + %23 `[%replace %i8 l] + %24 `[%extract %i16 l %s] + %25 `[%extract %i16 l %u] + %26 `[%replace %i16 l] + %27 `[%extract %i32 l %u] + %28 `[%replace %i32 l] + %29 `[%extract %i64 l %u] + %30 `[%replace %i64 l] + %31 `[%extract %f32 l %u] + %32 `[%replace %f32 l] + %33 `[%extract %f64 l %u] + %34 `[%replace %f64 l] + == +``` + +Handle SIMD lane extract/replace operations. + +## Section Parsers + +### `+type-section` {#type-section} + +```hoon +++ type-section + %+ cook |=(type-section:sur +<) + (vec func-type) +``` + +Parse Wasm module [`$type-section`](./wasm-data-types.md#type-section) containing function signatures. + +### `+import-section` {#import-section} + +```hoon +++ import-section + %+ cook |=(import-section:sur +<) + (vec import) +``` + +Parse Wasm module [`$import-section`](./wasm-data-types.md#import-section) containing module dependencies. + +### `+import` {#import} + +```hoon +++ import + %+ cook |=(import:sur +<) + ;~ plug + name + name + import-desc + == +``` + +Parse individual module [`$import`](./wasm-data-types.md#import) entry with module name, import name, and description. + +### `+import-desc` {#import-desc} + +```hoon +++ import-desc + ;~ pose + import-func + import-tabl + import-memo + import-glob + == +``` + +Parse module import description (function, table, memory, or global). + +### `+import-func` {#import-func} + +```hoon +++ import-func ;~(plug (cold %func (just '\00')) u32) +``` + +Parse function import with type index. + +### `+import-tabl` {#import-tabl} + +```hoon +++ import-tabl ;~(plug (cold %tabl (just '\01')) ref-type limits) +``` + +Parse table import with [reference type](./wasm-data-types.md#ref-type) and [`$limits`](./wasm-data-types.md#limits). + +### `+import-memo` {#import-memo} + +```hoon +++ import-memo ;~(plug (cold %memo (just '\02')) limits) +``` + +Parse memory import with size limits. + +### `+import-glob` {#import-glob} + +```hoon +++ import-glob + ;~ plug + (cold %glob (just '\03')) + valtype + con-var + == +``` + +Parse global import with value type and mutability. + +### `+con-var` {#con-var} + +```hoon +++ con-var + ;~ pose + (cold %con (just '\00')) + (cold %var (just '\01')) + == +``` + +Parse constant/variable mutability flag. + +### `+function-section` {#function-section} + +```hoon +++ function-section + %+ cook |=(function-section:sur +<) + (vec u32) +``` + +Parse Wasm module [`$function-section`](./wasm-data-types.md#function-section) with type indices. + +### `+table-section` {#table-section} + +```hoon +++ table-section + %+ cook |=(table-section:sur +<) + (vec table) +``` + +Parse Wasm module [`$table-section`](./wasm-data-types.md#table-section). + +### `+table` {#table} + +```hoon +++ table ;~(plug ref-type limits) +``` + +Parse table definition with reference type and size limits. + +### `+memory-section` {#memory-section} + +```hoon +++ memory-section + %+ cook |=(memory-section:sur +<) + (vec limits) +``` + +Parse Wasm module [`$memory-section`](./wasm-data-types.md#memory-section) with size limits. + +### `+global-section` {#global-section} + +```hoon +++ global-section + %+ cook |=(global-section:sur +<) + (vec global) +``` + +Parse Wasm module [`$global-section`](./wasm-data-types.md#global-section). + +### `+global` {#global} + +```hoon +++ global + %+ cook |=(global:sur +<) + ;~ plug + valtype + con-var + const-expr + == +``` + +Parse [`$global`](./wasm-data-types.md#global) variable with type, mutability, and initial value. + +### `+const-expr` {#const-expr} + +```hoon +++ const-expr ;~(sfix const-instr end) :: single instruction +``` + +Parse constant [`$expression`](./wasm-data-types.md#expression) (single constant instruction followed by end). + +### `+const-instr` {#const-instr} + +```hoon +++ const-instr +%+ sear + |= i=instruction:sur + ^- (unit const-instr:sur) + ?. ?=(const-instr:sur i) + ~ + `i +instr +``` + +Parse [`$instruction`s](./wasm-data-types.md#instruction) that can appear in [constant instructions](./wasm-data-types.md#const-instr). + +### `+export-section` {#export-section} + +```hoon +++ export-section + %+ cook export-section:sur + (vec export) +``` + +Parse Wasm export section. + +### `+export` {#export} + +```hoon +++ export + ;~ plug + name + %+ cook |=(export-desc:sur +<) + ;~ pose + ;~(plug (cold %func (just '\00')) u32) + ;~(plug (cold %tabl (just '\01')) u32) + ;~(plug (cold %memo (just '\02')) u32) + ;~(plug (cold %glob (just '\03')) u32) + == + == +``` + +Parse export entry with name and description. + +### `+start-section` {#start-section} + +```hoon +++ start-section + %+ cook |=(start-section:sur +<) + (stag ~ u32) +``` + +Parse Wasm module [`$start-section`](./wasm-data-types.md#start-section) with start function index. + +### `+elem-section` {#elem-section} + +```hoon +++ elem-section + %+ cook |=(elem-section:sur +<) + (vec elem) +``` + +Parse Wasm module [`$elem-section`](./wasm-data-types.md#elem-section) for table initialization. + +### `+elem` {#elem} + +```hoon +++ elem + ;~ pose + elem-0 + elem-1 + elem-2 + elem-3 + elem-4 + elem-5 + elem-6 + elem-7 + == +``` + +Parse element segment in one of eight different formats. Each format has different combinations of: +- Active/passive/declarative mode. +- Table index specification. +- Element type annotation. +- Initialization [`$expression`](./wasm-data-types.md#expression) vs [`$function`](./wasm-data-types.md#function-section) indices. + +### `+elem-kind` {#elem-kind} + +```hoon +++ elem-kind (just '\00') +``` + +Parse element kind (currently only function references references supported). + +### `+elem-0` {#elem-0} + +```hoon +++ elem-0 + %+ cook handle-elem-0 + ;~ pfix (just '\00') + ;~(plug const-expr (vec u32)) + == +``` + +Parse active element segment with implicit table 0 and function indices. + +### `+elem-1` {#elem-1} + +```hoon +++ elem-1 + %+ cook handle-elem-1 + ;~ pfix (just '\01') + ;~(plug elem-kind (vec u32)) + == +``` + +Parse passive element segment with element kind and function indices. + +### `+elem-2` {#elem-2} + +```hoon +++ elem-2 + %+ cook handle-elem-2 + ;~ pfix (just '\02') + ;~(plug u32 const-expr elem-kind (vec u32)) + == +``` + +Parse active element segment with explicit table index, offset expression, element kind, and function indices. + +### `+elem-3` {#elem-3} + +```hoon +++ elem-3 + %+ cook handle-elem-3 + ;~ pfix (just '\03') + ;~(plug elem-kind (vec u32)) + == +``` + +Parse declarative element segment with element kind and function indices. + +### `+elem-4` {#elem-4} + +```hoon +++ elem-4 + %+ cook handle-elem-4 + ;~ pfix (just '\04') + ;~(plug const-expr (vec expr)) + == +``` + +Parse active element segment with implicit table 0 and initialization expressions. + +### `+elem-5` {#elem-5} + +```hoon +++ elem-5 + %+ cook handle-elem-5 + ;~ pfix (just '\05') + ;~(plug ref-type (vec expr)) + == +``` + +Parse passive element segment with reference type and initialization expressions. + +### `+elem-6` {#elem-6} + +```hoon +++ elem-6 + %+ cook handle-elem-6 + ;~ pfix (just '\06') + ;~(plug u32 const-expr ref-type (vec expr)) + == +``` + +Parse active element segment with explicit table index, offset expression, reference type, and initialization expressions. + +### `+elem-7` {#elem-7} + +```hoon +++ elem-7 + %+ cook handle-elem-7 + ;~ pfix (just '\07') + ;~(plug ref-type (vec expr)) + == +``` + +Parse declarative element segment with reference type and initialization expressions. + +### `+handle-elem-0` {#handle-elem-0} + +```hoon +++ handle-elem-0 + |= [e=const-instr:sur y=(list @)] + ^- elem:sur + :+ %func + %+ turn y + |= y=@ + ^- $>(?(%ref-func %ref-null) instruction:sur) + [%ref-func y] + [%acti 0 e] +``` + +Convert parsed elem-0 data into standard [`$elem`](./wasm-data-types.md#elem) format with active mode, table 0, and function references. + +### `+handle-elem-1` {#handle-elem-1} + +```hoon +++ handle-elem-1 + |= [et=@ y=(list @)] + ^- elem:sur + :+ ?+ et ~|(%unrecognized-elem-kind !!) + %0x0 %func + == + %+ turn y + |= y=@ + ^- $>(?(%ref-func %ref-null) instruction:sur) + [%ref-func y] + [%pass ~] +``` + +Convert parsed elem-1 data into standard [`$elem`](./wasm-data-types.md#elem) format with passive mode and function references. + +### `+handle-elem-2` {#handle-elem-2} + +```hoon +++ handle-elem-2 + |= [x=@ e=const-instr:sur et=@ y=(list @)] + ^- elem:sur + :+ ?+ et ~|(%unrecognized-elem-kind !!) + %0x0 %func + == + %+ turn y + |= y=@ + ^- $>(?(%ref-func %ref-null) instruction:sur) + [%ref-func y] + [%acti x e] +``` + +Convert parsed elem-2 data into standard [`$elem`](./wasm-data-types.md#elem) format with active mode, explicit table index, and function references. + +### `+handle-elem-3` {#handle-elem-3} + +```hoon +++ handle-elem-3 + |= [et=@ y=(list @)] + ^- elem:sur + :+ ?+ et ~|(%unrecognized-elem-kind !!) + %0x0 %func + == + %+ turn y + |= y=@ + ^- $>(?(%ref-func %ref-null) instruction:sur) + [%ref-func y] + [%decl ~] +``` + +Convert parsed elem-3 data into standard [`$elem`](./wasm-data-types.md#elem) format with declarative mode and function references. + +### `+handle-elem-4` {#handle-elem-4} + +```hoon +++ handle-elem-4 + |= [e=const-instr:sur el=(list expression:sur)] + ^- elem:sur + :+ %func + %+ turn el + |= ex=expression:sur + ^- $>(?(%ref-func %ref-null) instruction:sur) + ?> ?=([[%ref-func *] ~] ex) + i.ex + [%acti 0 e] +``` + +Convert parsed elem-4 data into standard [`$elem`](./wasm-data-types.md#elem) format with active mode, table 0, and expression-based initialization. + +### `+handle-elem-5` {#handle-elem-5} + +```hoon +++ handle-elem-5 + |= [et=ref-type:sur el=(list expression:sur)] + ^- elem:sur + :+ et + %+ turn el + |= ex=expression:sur + ^- $>(?(%ref-func %ref-null) instruction:sur) + ?> ?=([[%ref-func *] ~] ex) + i.ex + [%pass ~] +``` + +Convert parsed elem-5 data into standard [`$elem`](./wasm-data-types.md#elem) format with passive mode and expression-based initialization. + +### `+handle-elem-6` {#handle-elem-6} + +```hoon +++ handle-elem-6 + |= [x=@ e=const-instr:sur et=ref-type:sur el=(list expression:sur)] + ^- elem:sur + :+ et + %+ turn el + |= ex=expression:sur + ^- $>(?(%ref-func %ref-null) instruction:sur) + ?> ?=([[%ref-func *] ~] ex) + i.ex + [%acti x e] +``` + +Convert parsed elem-6 data into standard [`$elem`](./wasm-data-types.md#elem) format with active mode, explicit table index, and expression-based initialization. + +### `+handle-elem-7` {#handle-elem-7} + +```hoon +++ handle-elem-7 + |= [et=ref-type:sur el=(list expression:sur)] + ^- elem:sur + :+ et + %+ turn el + |= ex=expression:sur + ^- $>(?(%ref-func %ref-null) instruction:sur) + ?> ?=([[%ref-func *] ~] ex) + i.ex + [%decl ~] +``` + +Convert parsed elem-7 data into standard [`$elem`](./wasm-data-types.md#elem) format with declarative mode and expression-based initialization. + +### `+code-section` {#code-section} + +```hoon +++ code-section + %+ cook |=(code-section:sur +<) + (vec code) +``` + +Parse Wasm module [`$code-section`](./wasm-data-types.md#code-section) containing function implementations. + +### `+code` {#code} + +```hoon +++ code (bonk (vec next) ;~(pfix u32 func)) +``` + +Parse code entry with size validation and function body. + +### `+func` {#func} + +```hoon +++ func + ;~ plug + (cook handle-locals (vec locals)) + expr + == +``` + +Parse function implementation with local variable declarations and body. + +### `+locals` {#locals} + +```hoon +++ locals ;~(plug u32 valtype) +``` + +Parse local variable declaration (count and [`$valtype`](./wasm-data-types.md#valtype)). + +### `+handle-locals` {#handle-locals} + +```hoon +++ handle-locals + |= l=(list [n=@ v=valtype:sur]) + ^- (list valtype:sur) + ?~ l ~ + %+ weld (reap n.i.l v.i.l) + $(l t.l) +``` + +Expand compressed local variable declarations into full list. + +### `+data-section` {#data-section} + +```hoon +++ data-section + %+ cook |=(data-section:sur +<) + (vec data) +``` + +Parse Wasm module [`$data-section`](./wasm-data-types.md#data-section) section for memory initialization. + +### `+data` {#data} + +```hoon +++ data + %+ cook |=(data:sur +<) + ;~ pose + ;~ plug + (cold %acti (just '\00')) + const-expr + (cook to-octs (vec next)) + == + :: + ;~ plug + (cold %pass (just '\01')) + (cook to-octs (vec next)) + == + :: + ;~ plug + (cold %acti (just '\02')) + ;~ pfix + u32 + ;~ plug + const-expr + (cook to-octs (vec next)) + == == == == +``` + +Parse [`$data`](./wasm-data-types.md#data) segment in active or passive mode with initialization data. + +### `+to-octs` {#to-octs} + +```hoon +++ to-octs + |= =tape + ^- octs + :- (lent tape) + (rep 3 tape) +``` + +Convert tape to `$octs` format. + +### `+datacnt-section` {#datacnt-section} + +```hoon +++ datacnt-section + %+ cook |=(datacnt-section:sur +<) + (stag ~ u32) +``` + +Parse Wasm module [data count](./wasm-data-types.md#datacnt-section) section. + +### `+module` {#module} + +```hoon +++ module + %+ cook |=(module:sur +<) + ;~ pfix + magic + version + (ifix [customs customs] module-contents) + == +``` + +Parse complete Wasm [`$module`](./wasm-data-types.md#module) with [`+magic`](#magic) number, [`+version`](#version), and sections. + +### `+module-contents` {#module-contents} + +```hoon +++ module-contents + ;~ (glue customs) + (check 1 type-section *type-section:sur) + (check 2 import-section *import-section:sur) + (check 3 function-section *function-section:sur) + (check 4 table-section *table-section:sur) + (check 5 memory-section *memory-section:sur) + (check 6 global-section *global-section:sur) + (check 7 export-section *export-section:sur) + (check 8 start-section *start-section:sur) + (check 9 elem-section *elem-section:sur) + (check 12 datacnt-section *datacnt-section:sur) + (check 10 code-section *code-section:sur) + (check 11 data-section *data-section:sur) + == +``` + +Parse Wasm [`$module`](./wasm-data-types.md#module) contents. + +### `+check` {#check} + +```hoon +++ check + |* [id=@ sec=rule def=*] + ;~ pose + :: section present + ;~ pfix + (just `@`id) + (bonk (vec next) ;~(pfix u32 sec)) + == + :: section missing + (easy `(womp sec)`def) + == +``` + +Check for optional Wasm module sections, providing default values for missing sections. + +### `+customs` {#customs} + +```hoon +++ customs + %- star + ;~ plug + (just '\00') + (vec next) + == +``` + +Parse custom Wasm module sections (ignored during parsing). + +### `+magic` {#magic} + +```hoon +++ magic (jest '\00asm') +``` + +Parse Wasm magic number (`0x00`, `0x61`, `0x73`, `0x6D`) which identifies this file as a Wasm module. + +### `+version` {#version} + +```hoon +++ version + ;~ plug + (just '\01') + (just '\00') :: leading zeros shenanigans + (just '\00') + (just '\00') + == +``` + +Parse Wasm version number (currently version 1). + +## `+op-map` {#op-map} + +```hoon +++ op-map + |= op=char + ~+ + ^- (unit instruction:sur) + ?+ op ~ + %0x0 `[%unreachable ~] + %0x1 `[%nop ~] + %0xf `[%return ~] + %0x1a `[%drop ~] + %0x1b `[%select ~] + :: ... + == +``` + +Maps single-byte Wasm opcodes to their corresponding [`$instruction`](./wasm-data-types.md#instruction) representations. + +## `+simd-map` {#simd-map} + +```hoon +++ simd-map + |= op=@ + ~+ + |^ + ^- (unit instruction:sur) + =; =(unit instr-vec:sur) + ?~ unit ~ + `[%vec u.unit] + ?+ op ~ + %14 `[%swizzle ~] + %98 `[%popcnt ~] + :: ... + == +``` + +Maps SIMD instruction opcodes to their corresponding [vector instruction](./wasm-data-types.md#instr-vec) representations. Includes helper functions for different categories of vector operations. diff --git a/content/urbit-os/base/wasm/lib-wasm-runner-engine.md b/content/urbit-os/base/wasm/lib-wasm-runner-engine.md new file mode 100644 index 00000000..f9a238f5 --- /dev/null +++ b/content/urbit-os/base/wasm/lib-wasm-runner-engine.md @@ -0,0 +1,695 @@ +--- +description: "Reference for UrWasm's interpreter library" +layout: + title: + visible: true + description: + visible: false + tableOfContents: + visible: true + outline: + visible: true + pagination: + visible: true +--- + +# Wasm Runner Engine + +The `+engine` core implements the core WebAssembly interpreter engine. This runs module instantiation, function invocation, and the main evaluation loop for WebAssembly instructions. + +## `+engine` {#engine} + +```hoon +++ engine + =, engine-sur + =, op-def + |% + :: ... + -- +``` + +Exposes the `+engine-sur` and `+op-def` namespaces containing interpreter data types and instruction definitions. + +## `+get-types` {#get-types} + +```hoon +++ get-types + |= a=(list coin-wasm) + ^- (list valtype) + %+ turn a + |= c=coin-wasm + ^- valtype + ?. ?=([%ref r=*] c) + -.c + -.r.c +``` + +Extract [`$valtype`s](./wasm-data-types.md#valtype) from a list of [`$coin-wasm`](./wasm-data-types.md#coin-wasm) values. Used primarily for function parameter type checking. + +## `+mint` {#mint} + +```hoon +++ mint + |= a=(list valtype) + ^- (list val) + ?~ a ~ + :_ $(a t.a) + ^- val + ?- i.a + ?(num-type vec-type) *@ + ref-type :- %ref + ?- i.a + %extn [%extn ~] + %func [%func ~] + == == +``` + +Generate initial values for local variables. Creates default zero values for numeric and vector types, and null references for reference types. + +## `+make-export-map` {#make-export-map} + +```hoon +++ make-export-map + |= =export-section + =| out=(map cord export-desc) + |- ^+ out + ?~ export-section out + =, i.export-section + %= $ + out (~(put by out) name export-desc) + export-section t.export-section + == +``` + +Convert an [`$export-section`](./wasm-data-types.md#export-section) into a map from export names to their [`$export-desc`](./wasm-data-types.md#export-desc) for efficient lookup. + +## `+find-func-id` {#find-func-id} + +```hoon +++ find-func-id + |= [name=cord =module] + ^- @ + =, module + =/ =export-desc + (~(got by (make-export-map export-section)) name) + ?> ?=(%func -.export-desc) + i.export-desc +``` + +Find function index by export name. Looks up the function in the module's exports and returns its internal function index. + +## `+conv` {#conv} + +```hoon +++ conv + |= [m=^module sh=shop] + ^- store + =| st=store + =. shop.st sh + |^ + =. module.st + =, m + :* + type-section + (import-upd import-section) + (fuse function-section code-section) + table-section + memory-section + global-section + export-section + start-section + elem-section + data-section + == + st +``` + +Convert a parsed [`$module`](./wasm-data-types.md#module) into a [`$store`](./wasm-interpreter-data-types.md#store) suitable for execution. Restructures the module by: +- Reorganizing imports with [`+import-upd`](#import-upd). +- Fusing function signatures with their code implementations. +- Setting up the execution environment. + +### `+import-upd` {#import-upd} + +```hoon +++ import-upd + |= i=^import-section + =| out=import-section + |- ^- import-section + ?~ i + =, out + %_ out + funcs (flop funcs) + tables (flop tables) + memos (flop memos) + globs (flop globs) + == + =. out + =, i.i + =, out + ?- -.desc.i.i + %func out(funcs [[[mod name] type-id.desc] funcs]) + %tabl out(tables [[[mod name] t.desc] tables]) + %memo out(memos [[[mod name] l.desc] memos]) + %glob out(globs [[[mod name] +.desc] globs]) + == + $(i t.i) +``` + +Reorganize imports from the flat list structure in parsed modules to the categorized structure needed by the engine's [`$import-section`](./wasm-interpreter-data-types.md#import-section). + +## `+prep` {#prep} + +```hoon +++ prep + |= [m=^module sh=shop] + ^- result + (instantiate (conv m sh)) +``` + +Prepare a parsed [`$module`](./wasm-data-types.md#module) for execution. First converts it to the engine's internal representation, then instantiates it through the full WebAssembly instantiation process. + +## `+instantiate` {#instantiate} + +```hoon +++ instantiate + |= st=store + ^- result + |^ + ;< [* st=store] _wasm-bind (init-globals st) => +(st st) + ;< [* st=store] _wasm-bind (init-table st) => +(st st) + ;< [* st=store] _wasm-bind (init-elems st) => +(st st) + ;< [* st=store] _wasm-bind (init-mem st) => +(st st) + ;< [* st=store] _wasm-bind (init-data st) => +(st st) + ;< [* st=store] _wasm-bind (start st) => +(st st) + [%0 ~ st] +``` + +Main WebAssembly module instantiation process following the [WebAssembly Core Specification](https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/exec/modules.html#instantiation). Executes the instantiation steps in order: +1. Initialize global variables. +2. Initialize tables with null references. +3. Initialize table elements from element segments. +4. Initialize memory if present. +5. Initialize memory from data segments. +6. Execute the start function if present. + +Uses [`+wasm-bind`](#wasm-bind) to handle potential import blocking during initialization. + +### `+init-globals` {#init-globals} + +```hoon +++ init-globals + |= st=store + =/ m=module module.st + |- ^- result + ?~ global-section.m + [%0 ~ st(globals (flop globals.st))] + =* glob i.global-section.m + :: Const globals + :: + ?: ?=([%const p=$<(?(%v128 %ref) coin-wasm)] i.glob) + %= $ + global-section.m t.global-section.m + globals.st [p.i.glob globals.st] + == + ?: ?=([%vec %const p=$>(%v128 coin-wasm)] i.glob) + %= $ + global-section.m t.global-section.m + globals.st [p.i.glob globals.st] + == + ?: ?=([%ref-null t=ref-type] i.glob) + =/ null-ref=coin-wasm + ?: ?=(%func t.i.glob) [%ref %func ~] + [%ref %extn ~] + %= $ + global-section.m t.global-section.m + globals.st [null-ref globals.st] + == + ?: ?=([%ref-func func-id=@] i.glob) + %= $ + global-section.m t.global-section.m + globals.st [[%ref %func `func-id.i.glob] globals.st] + == + :: Imported globals. We assume here that %global-get + :: would not affect module store + :: + ?^ shop.st + %= $ + global-section.m t.global-section.m + globals.st [-.p.i.shop.st globals.st] + shop.st t.shop.st + == + :+ %1 + :- -:(snag index.i.glob globs.import-section.module.st) + [%glob ~ i.glob] + [*module ~ ~ ~] +``` + +Initialize global variables according to their constant initializer expressions. Handles various constant types and imported globals, potentially blocking on unresolved imports. + +### `+init-table` {#init-table} + +```hoon +++ init-table + |= st=store + =/ m=module module.st + |- ^- result + :+ %0 ~ + |- ^- store + ?~ table-section.m + st(tables (flop tables.st)) + =* tab i.table-section.m + %= $ + table-section.m t.table-section.m + :: + tables.st + :_ tables.st + (reap (lim-min q.tab) [%ref %func ~]) + == +``` + +Initialize tables by creating arrays filled with null function references according to each table's minimum size [`$limits`](./wasm-data-types.md#limits). + +### `+init-elems` {#init-elems} + +```hoon +++ init-elems + |= st=store + =/ m=module module.st + |- ^- result + :+ %0 ~ + |- ^- store + ?~ elem-section.m st + =* elem i.elem-section.m + ?. ?=(%acti -.m.elem) + $(elem-section.m t.elem-section.m) + :: Assert: only %func ref tables can be + :: initialized with an element segment + :: + ?> ?=(%func t.elem) + :: Assert: i32 value in the offset + :: (it theoretically can be a %global-get of import, revisit later?) + :: + =/ offset=(unit @) + ?: ?=([%const %i32 n=@] off.m.elem) `n.p.off.m.elem + ?. ?=(%global-get -.off.m.elem) ~ + =+ glob=(glob:grab index.off.m.elem st) + ?: ?=(%| -.glob) ~ + =/ coin=coin-wasm q.p.glob + ?: ?=(%i32 -.coin) `+.coin + ~ + ?> ?=(^ offset) + =/ tab-loc-id=@ + %+ sub tab.m.elem :: Assert: elems are instantiated locally + (lent tables.import-section.module.st) :: (to revisit?) + %= $ + elem-section.m t.elem-section.m + :: + tables.st + %^ shot tables.st tab-loc-id + %^ place (snag tab-loc-id tables.st) :: table to change + u.offset :: offset + %+ turn i.elem + |= in=instruction + ^- $>(%ref coin-wasm) + ?> ?=([%ref-func @] in) :: Assert: %func refs only + [%ref %func `func-id.in] + :: + == +``` + +Initialize active element segments by copying their contents into tables at the specified offsets. Only processes active segments; passive segments are handled by explicit instructions during execution. + +### `+init-mem` {#init-mem} + +```hoon +++ init-mem + |= st=store + =/ m=module module.st + |- ^- result + :+ %0 ~ + ?~ memory-section.m st + ?> ?=(@ t.memory-section.m) :: Assert: single memory + =* mem i.memory-section.m + st(mem `[0 (lim-min mem)]) +``` + +Initialize linear memory with the minimum number of pages specified in the memory section. WebAssembly currently supports only one memory per module. + +### `+init-data` {#init-data} + +```hoon +++ init-data + |= st=store + =/ m=module module.st + =+ id=`@`0 + |- ^- result + ?~ data-section.m [%0 ~ st] + =* data i.data-section.m + ?. ?=(%acti -.data) + $(data-section.m t.data-section.m, id +(id)) + :: Assert: const i32 value as offset + :: (it theoretically can be a %global-get of import, revisit later?) + :: + =/ offset=(unit @) + ?: ?=([%const %i32 n=@] off.data) `n.p.off.data + ?. ?=(%global-get -.off.data) ~ + =+ glob=(glob:grab index.off.data st) + ?: ?=(%| -.glob) ~ + =/ coin=coin-wasm q.p.glob + ?: ?=(%i32 -.coin) `+.coin + ~ + ?> ?=(^ offset) + =/ l=local-state + [[~ ~[-.b.data 0 u.offset]] ~ st] + =. l ((fetch-gate [%memory-init id %0]) l) + ?^ br.stack.l + ?+ br.stack.l !! + [%bloq p=*] [%1 p.br.stack.l] + [%trap ~] [%2 store.l] + == + %= $ + id +(id) + data-section.m t.data-section.m + st store.l + == +:: +``` + +Initialize active data segments by copying their contents into linear memory at specified offsets. Uses the `memory.init` instruction implementation for the actual memory copying. + +### `+start` {#start} + +```hoon +++ start + |= st=store + ^- result + =/ m=module module.st + ?~ start-section.m [%0 ~ st] + =/ [=stack * st-out=store] + (call u.start-section.m [[~ ~] ~ st]) + ?+ br.stack !! + ~ [%0 ~ st-out] + [%bloq p=*] [%1 p.br.stack] + [%trap ~] [%2 st-out] + == +``` + +Execute the start function if one is defined. The start function must take no parameters and return no values. + +## `+wasm-need` {#wasm-need} + +```hoon +++ wasm-need + |= a=result + ^- (quip coin-wasm store) + ?> ?=(%0 -.a) + +.a +``` + +Extract successful result from a [`$result`](./wasm-interpreter-data-types.md#result). Crashes if the result indicates an error or blocking state. + +## `+wasm-bind` {#wasm-bind} + +```hoon +++ wasm-bind + |= [a=result b=$-((quip coin-wasm store) result)] + ^- result + ?. ?=(%0 -.a) a + (b +.a) +``` + +Monadic bind operator for chaining [`$result`](./wasm-interpreter-data-types.md#result) computations. If the first result is successful, applies the continuation function; otherwise propagates the error or blocking state. + +## `+invoke` {#invoke} + +```hoon +++ invoke + |= [name=cord in=(list coin-wasm) st=store] + ^- result + =/ id=@ (find-func-id name module.st) + (invoke-id id in st) +``` + +Invoke an exported function by name with the given arguments. Looks up the function by name in the exports table, then delegates to [`+invoke-id`](#invoke-id). + +## `+invoke-id` {#invoke-id} + +```hoon +++ invoke-id + |= [id=@ in=(list coin-wasm) st=store] + ^- result + =/ id-local=@ + (sub id (lent funcs.import-section.module.st)) + :: Type check for the input values + :: + =, module.st + =/ =func-type + =/ func (func:grab id st) + ?: ?=(%& -.func) (snag type-id.p.func type-section) + (snag type-id.p.func type-section) + ?> =(params.func-type (get-types in)) + =/ [stack-out=stack * st-out=store] + %+ call id + ^- local-state + :+ stack=[~ (turn (flop in) coin-to-val)] + locals=~ + store=st + ?+ br.stack-out !! + ~ :+ %0 + (change results.func-type (flop va.stack-out)) + st-out + [%bloq p=*] [%1 p.br.stack-out] + [%trap ~] [%2 st-out] + == +``` + +Invoke a function by its index with type checking. Converts the input [`$coin-wasm`](./wasm-data-types.md#coin-wasm) values to stack values, calls the function, and converts the results back to typed values. + +## `+call` {#call} + +```hoon +++ call + |= [id=@ l=local-state] + ^- local-state + =, module.store.l + =+ f=(func:grab id store.l) :: (each function [[mod=cord name=cord] type-id=@]) + =/ type-id=@ =>(f ?:(?=(%& -) type-id.p type-id.p)) + =/ =func-type (snag type-id type-section) + :: import case + :: + ?: ?=(%| -.f) + %^ buy l(va.stack (slag (lent params.func-type) va.stack.l)) + :+ -.p.f %func + %+ change params.func-type + %- flop + (scag (lent params.func-type) va.stack.l) + results.func-type + :: local case + :: take input values + :: + =/ input-values=(pole val) + %- flop + (scag (lent params.func-type) va.stack.l) + :: save the rest of the stack and our locals + :: + =/ rest-vals=(pole val) + (slag (lent params.func-type) va.stack.l) + =/ our-locs=(list val) locals.l + :: update local state + :: + =. l + %+ eval expression.p.f + ^- local-state + :+ stack=[~ ~] + locals=(weld input-values (mint locals.p.f)) + store=store.l + :: If trap or bloq: forward + :: + ?: ?=([?(%trap %bloq) *] br.stack.l) l + :: Assert: no branch or branch with label 0 or return + :: + ?> ?| ?=(~ br.stack.l) + ?=([%retr ~] br.stack.l) + ?=([%targ %0] br.stack.l) + == + :: If return or targeted, take appropriate amount of vals from the stack + :: + =? va.stack.l ?| ?=([%retr ~] br.stack.l) + ?=([%targ %0] br.stack.l) + == + (scag (lent results.func-type) va.stack.l) + :: Push returned values on stack, bring back locals, empty out br + :: + %= l + va.stack (weld va.stack.l rest-vals) + locals our-locs + br.stack ~ + == +``` + +Call a function by ID within the interpreter. Handles both imported functions (which create import requests) and local functions (which are executed directly). + +For local functions: +1. Extracts parameters from the stack. +2. Sets up local variables (parameters + declared locals). +3. Evaluates the function body. +4. Handles return values and control flow. + +## `+eval` {#eval} + +```hoon +++ eval + |= [e=expression l=local-state] + ^- local-state + ?: |(=(~ e) !=(~ br.stack.l)) :: if navigating branch + l :: jump to the end of expression + $(l (apply -.e l), e +.e) +``` + +Evaluate a WebAssembly [`$expression`](./wasm-data-types.md#expression) (sequence of instructions) by applying each instruction in turn to the [`$local-state`](./wasm-interpreter-data-types.md#local-state). + +Short-circuits evaluation when: +- The expression is empty. +- There's an active branch signal (from `br`, `return`, etc.) + +## `+dec-br` {#dec-br} + +```hoon +++ dec-br + |= br=branch + ^- branch + ?+ br br + [%targ %0] ~ + [%targ i=@] [%targ (dec i.br)] + == +``` + +Decrement branch target labels when exiting nested control structures. Preserves absolute branching coordinates (`trap`, `return`, `block`) and safely decrements relative target indices. Used for control flow navigation when exiting blocks and loops. + +## `+apply` {#apply} + +```hoon +++ apply + |= [i=instruction l=local-state] + ^- local-state + !. + ?+ i ((fetch-gate i) l) + [%call func-id=@] + (call func-id.i l) + :: + [%block *] :: [%block type=block-type body=expression] + =/ n-params=@ + %- lent + ?^ type.i params.type.i + params:(snag type.i type-section.module.store.l) + :: save the current frame + :: + =/ rest-vals=(pole val) (slag n-params va.stack.l) + :: execute the block + :: + =. l (eval body.i l(va.stack (scag n-params va.stack.l))) + :: If the block was targeted, pop appropriate amount of vals + :: + =? va.stack.l ?=([%targ %0] br.stack.l) + =; n-results=@ + (scag n-results va.stack.l) + %- lent + ?^ type.i results.type.i + results:(snag type.i type-section.module.store.l) + :: Exit the block, navigate branch + :: + l(va.stack (weld va.stack.l rest-vals), br.stack (dec-br br.stack.l)) + :: + [%loop *] :: [%loop type=block-type body=expression] + |- ^- local-state :: not strictly necessary, but prevents from matching `i` again + =/ n-params=@ + %- lent + ?^ type.i params.type.i + params:(snag type.i type-section.module.store.l) + :: save the current frame + :: + =/ rest-vals=(pole val) (slag n-params va.stack.l) + :: execute the block + :: + =. l (eval body.i l(va.stack (scag n-params va.stack.l))) + :: If the loop was targeted, pop appropriate amount of vals, + :: push them on the stack, clear branching signal and jump to start + :: + ?: ?=([%targ %0] br.stack.l) + %= $ + va.stack.l (weld (scag n-params va.stack.l) rest-vals) + br.stack.l ~ + == + :: Exit the block, navigate branch + :: + l(va.stack (weld va.stack.l rest-vals), br.stack (dec-br br.stack.l)) + :: + [%call-indirect type-id=@ table-id=@] + =, module.store.l + ?> ?=([ref-id=@ rest=*] va.stack.l) + =, va.stack.l + =+ tab=(table:grab table-id.i store.l) :: (each (list $>(%ref coin-wasm)) [[mod=cord name=cord] t=table]) + :: import table + :: + ?: ?=(%| -.tab) + =/ =func-type (snag type-id.i type-section) + =/ input=(list coin-wasm) + %+ change params.func-type + (flop (scag (lent params.func-type) rest)) + %^ buy l(va.stack (slag (lent params.func-type) rest)) + [-.p.tab %tabl (weld input (change ~[%i32] ~[ref-id])) i] + results.func-type + :: local table + :: + :: Type check of reference + :: + =/ type-in-instr=func-type (snag type-id.i type-section) + =+ ref=(snag ref-id q.p.tab) + =/ type-of-ref=(unit func-type) + ?+ ref ~ + [%ref %func p=[~ @]] + =; =func-type + `func-type + =/ func (func:grab u.p.ref store.l) + ?: ?=(%& -.func) (snag type-id.p.func type-section) + (snag type-id.p.func type-section) + :: + [%ref %extn p=^] + `type.u.p.ref + == + ?~ type-of-ref l(br.stack [%trap ~]) + ?. =(type-in-instr u.type-of-ref) + l(br.stack [%trap ~]) + :: local func reference + :: + ?: ?=([%ref %func p=[~ @]] ref) + (call u.p.ref l(va.stack rest)) + :: external func reference + :: + ?. ?=([%ref %extn p=^] ref) + l(br.stack [%trap ~]) + %^ buy l(va.stack (slag (lent params.type.u.p.ref) rest)) + :- -.u.p.ref + =* params params.type.u.p.ref + [%func (change params (flop (scag (lent params) rest)))] + results.type.u.p.ref + :: + [%if *] :: [%if type=block-type branch-true=expression branch-false=expression] + ?> ?=([f=@ rest=*] va.stack.l) + =, va.stack.l + ?. =(0 f) + $(i [%block type branch-true]:i, va.stack.l rest) + $(i [%block type branch-false]:i, va.stack.l rest) + :: + == +``` + +Apply a single [`$instruction`](./wasm-data-types.md#instruction) to the [`$local-state`](./wasm-interpreter-data-types.md#local-state). Most instructions delegate to [`+fetch-gate:op-def`](./lib-wasm-runner-op-def.md#fetch-gate), but complex control flow instructions are implemented directly: + +- `%call`: Direct function calls. +- `%block`: Labeled blocks with proper value stack management. +- `%loop`: Loops with branch-back semantics. +- `%call-indirect`: Indirect calls through function tables with type checking. +- `%if`: Conditional execution choosing between true/false branches. + diff --git a/content/urbit-os/base/wasm/lib-wasm-runner-op-def.md b/content/urbit-os/base/wasm/lib-wasm-runner-op-def.md new file mode 100644 index 00000000..e6b3fdc5 --- /dev/null +++ b/content/urbit-os/base/wasm/lib-wasm-runner-op-def.md @@ -0,0 +1,1151 @@ +--- +description: "Reference for UrWasm's Wasm operation definitions" +layout: + title: + visible: true + description: + visible: false + tableOfContents: + visible: true + outline: + visible: true + pagination: + visible: true +--- + +# Wasm Runner Operation Definitions + +The `/lib/wasm/runner/op-def` library implements WebAssembly instruction definitions as functions that transform the [`$local-state`](./wasm-interpreter-data-types.md#local-state) of the UrWasm interpreter. This library serves as the operational semantics for WebAssembly instructions, defining how each instruction modifies the interpreter's execution state. + +## `+op-def` {#op-def} + +```hoon +++ op-def + =, engine-sur + |% + :: ... + -- +``` + +Core instruction definitions namespace. Implements WebAssembly instructions as gates that transform [`$local-state`](./wasm-interpreter-data-types.md#local-state) to [`$local-state`](./wasm-interpreter-data-types.md#local-state). Uses round-to-nearest ties-to-even semantics for floating-point operations except where otherwise specified. + +## `+mayb` {#mayb} + +```hoon +++ mayb + |* gat=$-(* *) + |: a=+6.gat + ^- (unit _$:gat) + `(gat a) +``` + +Wrap output of a gate in a unit. + +## `+sure` {#sure} + +```hoon +++ sure + |* gat=$-(* (unit)) + |* a=_+6.gat + (need (gat a)) +``` + +Unwrap output of gate, crashing if unit is empty. + +## `+torn` {#torn} + +```hoon +++ torn + |* [a=(list) b=$-(* (unit))] + =| l=(list _(need $:b)) + |- ^- (unit (list _(need $:b))) + ?~ a `(flop l) + =+ c=(b i.a) + ?~ c ~ + $(a t.a, l [u.c l]) +``` + +Map a list with a gate, collapsing units. Returns `~` if any application produces `~`. + +## `+lane-size` {#lane-size} + +```hoon +++ lane-size + |= lt=lane-type + ^- @ + (slav %ud (rsh 3 lt)) +``` + +Extract the bit size from a [`$lane-type`](./wasm-data-types.md#lane-type). + +## `+fuse` {#fuse} + +```hoon +++ fuse + |* [a=(list) b=(list)] + ^- (list [_?>(?=(^ a) i.a) _?>(?=(^ b) i.b)]) + ?~ a ~ + ?~ b ~ + :- [i.a i.b] + $(a t.a, b t.b) +``` + +Zip two lists together, stopping when either list is exhausted. + +## `+chap` {#chap} + +```hoon +++ chap + |= [l=local-state instr=$-(local-state local-state)] + ^- local-state + ?^ br.stack.l l + (instr l) +``` + +Check branching signal and conditionally apply instruction. If there's a branch signal in the local state, return unchanged; otherwise apply the instruction. + +## `+page-size` {#page-size} + +```hoon +++ page-size ^~((bex 16)) +``` + +WebAssembly memory page size (65,536 bytes). + +## `+place` {#place} + +```hoon +++ place + |* [a=(list) off=@ b=(list)] + |- ^+ b + ?~ b a + ?> (lth off (lent a)) + $(a (shot a off i.b), b t.b, off +(off)) +``` + +Place list `.b` into list `.a` starting at offset `.off`, overwriting contents of `.a`. + +## `+lim-min` {#lim-min} + +```hoon +++ lim-min + |= l=limits + ^- @ + ?: ?=(%flor -.l) + p.l + p.l +``` + +Extract minimum value from [`$limits`](./wasm-data-types.md#limits). + +## `+lim-max` {#lim-max} + +```hoon +++ lim-max + |= l=limits + ^- (unit @) + ?: ?=(%flor -.l) + ~ + `q.l +``` + +Extract maximum value from [`$limits`](./wasm-data-types.md#limits). Returns `~` if no maximum is specified. + +## `+lte-lim` {#lte-lim} + +```hoon +++ lte-lim + |= [a=@ l=limits] + ^- ? + ?: ?=(%flor -.l) + & + (lte a q.l) +``` + +Test if value `.a` is within the specified [`$limits`](./wasm-data-types.md#limits). + +## `+change` {#change} + +```hoon +++ change + |= [a=(list valtype) b=(list val)] + ^- (list coin-wasm) + ?. &(?=(^ a) ?=(^ b)) + ?> &(?=(~ a) ?=(~ b)) + ~ + :_ $(a t.a, b t.b) + ;; coin-wasm + ?- i.a + ?(num-type vec-type) + [i.a ?>(?=(@ i.b) i.b)] + :: + %extn + ?>(?=([%ref %extn ~] i.b) i.b) + :: + %func + ?>(?=([%ref %func *] i.b) i.b) + == +``` + +Convert stack values to a list of [`$coin-wasm`](./wasm-data-types.md#coin-wasm) for type checking. + +## `+to-si` {#to-si} + +```hoon +++ to-si + |= [base=@ n=@] + ^- @s + =. n (mod n (bex base)) + =/ sign=? (lth n (bex (dec base))) + %+ new:si sign + ?: sign n + (sub (bex base) n) +``` + +Convert unsigned integer to signed integer with specified bit width. + +## `+en-si` {#en-si} + +```hoon +++ en-si + |= [base=@ s=@s] + ^- @ + ?: (syn:si s) + +:(old:si s) + (sub (bex base) +:(old:si s)) +``` + +Convert signed integer to unsigned integer with specified bit width. + +## `+sat` {#sat} + +```hoon +++ sat + |= [size=@ s=@s mode=?(%u %s)] + ^- @ + =, si + =; sat-s=@s + (en-si size sat-s) + ?: =(%u mode) + ?: =(--1 (cmp s (sum (new & (bex size)) -1))) + (sum (new & (bex size)) -1) + ?: =(-1 (cmp s --0)) + --0 + s + ?: =(--1 (cmp s (sum (new & (bex (dec size))) -1))) + (sum (new & (bex (dec size))) -1) + ?: =(-1 (cmp s (new | (bex (dec size))))) + (new | (bex (dec size))) + s +``` + +Saturating conversion from signed integer with specified size and mode (`%u` for unsigned, `%s` for signed). + +## `+coin-to-val` {#coin-to-val} + +```hoon +++ coin-to-val + |= c=coin-wasm + ^- val + ?: ?=(%ref -.c) + c + ?- -.c + ?(%i32 %f32) (mod +.c ^~((bex 32))) + ?(%i64 %f64) (mod +.c ^~((bex 64))) + %v128 (mod +.c ^~((bex 128))) + == +``` + +Convert [`$coin-wasm`](./wasm-data-types.md#coin-wasm) to interpreter [`$val`](./wasm-interpreter-data-types.md#val). + +## `+val-to-coin` {#val-to-coin} + +```hoon +++ val-to-coin + |= [v=val ex=coin-wasm] + ^- coin-wasm + ?@ v + ?< ?=(%ref -.ex) + ;; coin-wasm + [-.ex v] + ?> ?=(%ref -.ex) + ?> =(+<.v +<.ex) :: assert: same reftypes + v +``` + +Convert interpreter [`$val`](./wasm-interpreter-data-types.md#val) to [`$coin-wasm`](./wasm-data-types.md#coin-wasm) using expected type. + +## `+snug` {#snug} + +```hoon +++ snug + |* [a=@ b=(list)] + |- ^- (unit _?>(?=(^ b) i.b)) + ?~ b ~ + ?: =(0 a) `i.b + $(b t.b, a (dec a)) +``` + +Unitized list indexing. Returns element at index `.a` or `~` if index is out of bounds. + +## `+shot` {#shot} + +```hoon +++ shot + |* [a=(list) b=@ c=*] + ^+ a + ?> (lth b (lent a)) + (snap a b c) +``` + +Replace existing item in a list at index `.b` with value `.c`. + +## `+buy` {#buy} + +```hoon +++ buy + |= [l=local-state req=[[mod=cord name=cord] =request] type=(list valtype)] + ^- local-state + ?~ shop.store.l + =, store.l + l(br.stack [%bloq req module mem tables globals]) + =/ valid-types=? + =/ res=(list coin-wasm) p.i.shop.store.l + |- ^- ? + ?: &(?=(~ res) ?=(~ type)) & + ?> &(?=(^ res) ?=(^ type)) + ?& =(-.i.res i.type) + $(res t.res, type t.type) + == + ?> valid-types + %= l + va.stack + %+ weld + %- flop + (turn p.i.shop.store.l coin-to-val) + va.stack.l + :: + store + =, store.l + [t.shop q.i.shop] + == +``` + +Resolve import. If no values are available in the `.shop`, creates a request and blocks execution. Otherwise, validates types and pushes values onto the stack. + +## `+grab` {#grab} + +```hoon +++ grab + |% + ++ func + |= [id=@ st=store] + ^- (each function [[mod=cord name=cord] type-id=@]) + =, import-section.module.st + =+ imp=(snug id funcs) + ?^ imp [%| u.imp] + :- %& + (snag (sub id (lent funcs)) function-section.module.st) + :: + ++ table + |= [id=@ st=store] + ^- %+ each (pair @ (list $>(%ref coin-wasm))) + [[mod=cord name=cord] t=^table] + =, import-section.module.st + =+ imp=(snug id tables) + ?^ imp [%| u.imp] + :- %& + =+ idx=(sub id (lent tables)) + :- idx + (snag idx tables.st) + :: + ++ memo + |= [id=@ st=store] + ^- %+ each [buffer=@ n-pages=@] + [[mod=cord name=cord] l=limits] + =, import-section.module.st + =+ imp=(snug id memos) + ?^ imp [%| u.imp] + [%& (need mem.st)] + :: + ++ glob + |= [id=@ st=store] + ^- %+ each (pair @ coin-wasm) + [[mod=cord name=cord] v=valtype m=?(%con %var)] + =, import-section.module.st + =+ imp=(snug id globs) + ?^ imp [%| u.imp] + :- %& + =+ idx=(sub id (lent globs)) + :- idx + (snag idx globals.st) + :: + -- +``` + +Import resolution utilities. Each arm returns either a local instance of an object or its external reference. +- `+func`: Resolve function by index. +- `+table`: Resolve table by index. Returns either a local table with its contents or an external reference. +- `+memo`: Resolve memory by index. Returns either local memory instance or an external reference. +- `+glob`: Resolve global variable by index. Returns either local global value or an external reference. + +## `+mem-store` {#mem-store} + +```hoon +++ mem-store + |= [index=@ size=@ content=@ buffer=@ n-pages=@] + ^- (unit [buffer=@ n-pages=@]) + ?. (lte (add index size) (mul n-pages page-size)) + ~ + `[(sew 3 [index size content] buffer) n-pages] +``` + +Store data in linear memory at the specified index. Returns updated memory or `~` if out of bounds. + +## `+mem-load` {#mem-load} + +```hoon +++ mem-load + |= [index=@ size=@ buffer=@ n-pages=@] + ^- (unit @) + ?. (lte (add index size) (mul n-pages page-size)) + ~ + `(cut 3 [index size] buffer) +``` + +Load data from linear memory at the specified index. Returns data or `~` if out of bounds. + +## `+kind` {#kind} + +```hoon +++ kind + |% + +$ nullary $? %unreachable %nop %return %drop == + +$ ref ?(%ref-null %ref-is-null %ref-func) + +$ get ?(%global-get %local-get) + +$ set ?(%global-set %local-set %local-tee) + +$ branch ?(%br %br-if %br-table) + +$ table $? %table-get %table-set %table-init %elem-drop + %table-copy %table-grow %table-size %table-fill == + +$ memo $? %memory-size %memory-grow %memory-init %data-drop + %memory-copy %memory-fill == + +$ unary-num $? %clz %ctz %popcnt %abs %neg %sqrt %ceil %floor + %trunc %nearest %eqz %wrap %extend %convert + %demote %promote %reinterpret == + +$ binary-num $? %add %sub %mul %div %rem %and %or %xor %shl %shr + %rotl %rotr %min %max %copysign %eq %ne %lt %gt + %le %ge == + -- +``` + +Instruction category types for organizing instruction implementations. + +## `+fetch-gate` {#fetch-gate} + +```hoon +++ fetch-gate + |= i=$<(?(%call %loop %call-indirect %block %if) instruction) + ^- $-(local-state local-state) + ?- -.i + %vec (simd +.i) + nullary:kind (null:fetch i) + ref:kind (ref:fetch i) + %load (load:fetch i) + %store (store:fetch i) + %const (const:fetch i) + get:kind (get:fetch i) + set:kind (set:fetch i) + branch:kind (branch:fetch i) + table:kind (table:fetch i) + memo:kind (memo:fetch i) + %select select:fetch + %dbug (dbug:fetch i) + :: + unary-num:kind + |= l=local-state + ^- local-state + ?> ?=([a=@ rest=*] va.stack.l) + =, va.stack.l + =+ val=((unar:fetch i) a) + ?~ val l(br.stack [%trap ~]) + l(va.stack [u.val rest]) + :: + binary-num:kind + |= l=local-state + ^- local-state + ?> ?=([b=@ a=@ rest=*] va.stack.l) + =, va.stack.l + =+ val=((bina:fetch i) a b) + ?~ val l(br.stack [%trap ~]) + l(va.stack [u.val rest]) + :: + == +``` + +Convert [`$instruction`](./wasm-data-types.md#instruction) to a gate that transforms local state. Handles different instruction categories appropriately. + +## `+fetch` {#fetch} + +```hoon +++ fetch + |% + ++ dbug :: Debug instructions + ++ select :: Parametric instructions + ++ null :: Control instructions with no operands + ++ ref :: Reference instructions + ++ load :: Memory load instructions + ++ store :: Memory store instructions + ++ const :: Constant instructions + ++ get :: Variable get instructions + ++ set :: Variable set instructions + ++ branch :: Branch instructions + ++ table :: Table instructions + ++ memo :: Memory instructions + ++ unar :: Unary numeric instructions + ++ bina :: Binary numeric instructions + -- +``` + +Core with instruction implementation definitions. Each arm contains implementations for specific categories of WebAssembly instructions. + +### `+dbug:fetch` {#dbug-fetch} + +```hoon +++ dbug + =- |= i=instruction + ((~(got by m) ;;(@tas +<.i)) i) + ^~ + ^= m + ^- (map @tas $-(instruction $-(local-state local-state))) + |^ + %- my + :~ print-tee+print-tee == + ++ print-tee + |= i=instruction + ?> ?=([%dbug %print-tee a=term] i) + |= l=local-state + ^- local-state + ~& [a.i ;;(@ux -.va.stack.l)] + l + -- +``` + +Debug instruction implementations. Currently supports `%print-tee` which prints the top stack value with a label. + +### `+select:fetch` {#select-fetch} + +```hoon +++ select + |= l=local-state + ^- local-state + ?> ?=([which=@ val2=* val1=* rest=*] va.stack.l) + =, va.stack.l + %= l + va.stack + [?.(=(0 which) val1 val2) rest] + == +``` + +Select instruction. Pops condition and two values, pushes the chosen value based on the condition. + +### `+null:fetch` {#null-fetch} + +```hoon +++ null + =- |= i=instruction + (~(got by m) ;;(@tas -.i)) + ^~ + ^= m + ^- (map @tas $-(local-state local-state)) + |^ + %- my + :~ + unreachable+unreachable + nop+nop + return+return + drop+drop + == + ++ unreachable + |= l=local-state + ^- local-state + l(br.stack [%trap ~]) + ++ nop |=(local-state +<) + ++ return + |= l=local-state + ^- local-state + l(br.stack [%retr ~]) + ++ drop + |= l=local-state + ^- local-state + l(va.stack +.va.stack.l) + -- +``` + +Control instructions with no operands: +- `unreachable`: Unconditionally trap. +- `nop`: No operation. +- `return`: Return from function. +- `drop`: Drop top value from stack. + +### `+ref:fetch` {#ref-fetch} + +```hoon +++ ref + =- |= i=instruction + ?> ?=(ref:kind -.i) + ^- $-(local-state local-state) + ((~(got by m) ;;(@tas -.i)) i) + ^~ + ^= m + ^- (map @tas $-(instruction $-(local-state local-state))) + |^ + %- my + :~ + ref-null+ref-null + ref-is-null+ref-is-null + ref-func+ref-func + == + ++ ref-null + |= i=instruction + ?> ?=(%ref-null -.i) + |= l=local-state + ^- local-state + %= l + va.stack + :_ va.stack.l + :- %ref + ?- t.i + %extn [%extn ~] + %func [%func ~] + == + == + ++ ref-is-null + |= * + |= l=local-state + ^- local-state + ?> ?=([ref=[%ref *] rest=*] va.stack.l) + =, va.stack.l + =/ out=@ + ?@(+>.ref 1 0) + l(va.stack [out rest]) + ++ ref-func + |= i=instruction + ?> ?=(%ref-func -.i) + |= l=local-state + ^- local-state + l(va.stack [[%ref %func ~ func-id.i] va.stack.l]) + -- +``` + +Reference instruction implementations: +- `ref-null`: Push null reference of specified type. +- `ref-is-null`: Test if reference is null. +- `ref-func`: Push function reference. + +## `+simd` {#simd} + +```hoon +++ simd + =< fetch-vec + |% + :: +rope + :: +fetch-vec + :: +load + :: +load-lane + :: +store + :: +store-lane + :: +const + :: +shuffle + :: +extract + :: +replace + :: +plain + -- +``` + +SIMD vector instruction implementations for 128-bit vectors. Handles vector load/store operations, lane manipulation, and parallel arithmetic operations on packed data. + +### `+rope` {#rope} + +```hoon +++ rope + |= [b=bloq s=step a=@] + ^- (list @) + ?: =(s 0) ~ + :- (end b a) + $(a (rsh b a), s (dec s)) +``` + +Helper for dissembling atoms with leading zeros. Similar to `+rip` but takes a specific number of blocks to extract. + +### `+fetch-vec` {#fetch-vec} + +```hoon +++ fetch-vec + |= i=instr-vec + ^- $-(local-state local-state) + ?+ -.i (plain i) + %load (load i) + %load-lane (load-lane i) + %store (store i) + %store-lane (store-lane i) + :: + %const (const i) + %shuffle (shuffle i) + %extract (extract i) + %replace (replace i) + :: + == +``` + +Vector instruction dispatcher that routes [`$instr-vec`](./wasm-data-types.md#instr-vec) to specific vector operation implementations. + +### `+load` {#load} + +```hoon +++ load + |= i=instr-vec + ?> ?=(%load -.i) + |= l=local-state + ^- local-state + ?> ?=([addr=@ rest=*] va.stack.l) + =, va.stack.l + =/ index=@ (add addr offset.m.i) + =+ mem=(memo:grab 0 store.l) + ?: ?=(%| -.mem) + %^ buy l(va.stack rest) + :* -.p.mem + %memo + (change ~[%i32] ~[addr]) + [%vec i] + == + ~[%v128] + ?~ kind.i + =+ loaded=(mem-load index 16 p.mem) + ?~ loaded l(br.stack [%trap ~]) + l(va.stack [u.loaded rest]) + ?- q.u.kind.i + [%extend p=?(%s %u)] + =; loaded=(unit @) + ?~ loaded l(br.stack [%trap ~]) + l(va.stack [u.loaded rest]) + =+ bloq=(xeb (dec p.u.kind.i)) + =+ get=(mem-load index 8 p.mem) + ?~ get ~ + =/ lanes=(list @) + (rope bloq (div 64 p.u.kind.i) u.get) + :- ~ + %+ rep +(bloq) + ?: ?=(%u p.q.u.kind.i) + lanes + %+ turn lanes + %+ cork (cury to-si p.u.kind.i) + (cury en-si (mul 2 p.u.kind.i)) + :: + %zero + =+ get=(mem-load index (div p.u.kind.i 8) p.mem) + ?~ get l(br.stack [%trap ~]) + l(va.stack [u.get rest]) + :: + %splat + =; loaded=(unit @) + ?~ loaded l(br.stack [%trap ~]) + l(va.stack [u.loaded rest]) + =+ lane=(mem-load index (div p.u.kind.i 8) p.mem) + ?~ lane ~ + `(fil (xeb (dec p.u.kind.i)) (div 128 p.u.kind.i) u.lane) + :: + == +``` + +Load vector from memory with optional operations like splat (broadcast), zero-extend, or sign-extend. + +### `+load-lane` {#load-lane} + +```hoon +++ load-lane + |= i=instr-vec + ?> ?=(%load-lane -.i) + |= l=local-state + ^- local-state + ?> ?=([vec=@ addr=@ rest=*] va.stack.l) + =, va.stack.l + =/ index=@ (add addr offset.m.i) + =+ mem=(memo:grab 0 store.l) + ?: ?=(%| -.mem) + %^ buy l(va.stack rest) + :* -.p.mem + %memo + (change ~[%i32 %v128] ~[addr vec]) + [%vec i] + == + ~[%v128] + =+ lane=(mem-load index (div p.i 8) p.mem) + ?~ lane l(br.stack [%trap ~]) + %= l + va.stack + [(sew (xeb (dec p.i)) [l.i 1 u.lane] vec) rest] + == +``` + +Load single value from memory into a specific lane of an existing vector. + +### `+store` {#store} + +```hoon +++ store + |= i=instr-vec + ?> ?=(%store -.i) + |= l=local-state + ^- local-state + ?> ?=([vec=@ addr=@ rest=*] va.stack.l) + =, va.stack.l + =+ memo=(memo:grab 0 store.l) + ?: ?=(%| -.memo) + %^ buy l(va.stack rest) + :* -.p.memo + %memo + (change ~[%i32 %v128] ~[addr vec]) + [%vec i] + == + ~ + =/ index=@ (add addr offset.m.i) + =+ mem-stored=(mem-store index 16 vec p.memo) + ?~ mem-stored l(br.stack [%trap ~]) + %= l + va.stack rest + :: + mem.store + `u.mem-stored + == +``` + +Store 128-bit vector to memory at the specified address. + +### `+store-lane` {#store-lane} + +```hoon +++ store-lane + |= i=instr-vec + ?> ?=(%store-lane -.i) + |= l=local-state + ^- local-state + ?> ?=([vec=@ addr=@ rest=*] va.stack.l) + =, va.stack.l + =+ memo=(memo:grab 0 store.l) + ?: ?=(%| -.memo) + %^ buy l(va.stack rest) + :* -.p.memo + %memo + (change ~[%i32 %v128] ~[addr vec]) + [%vec i] + == + ~ + =/ index=@ (add addr offset.m.i) + =+ lane=(cut (xeb (dec p.i)) [l.i 1] vec) + =+ mem-stored=(mem-store index (div p.i 8) lane p.memo) + ?~ mem-stored l(br.stack [%trap ~]) + %= l + va.stack rest + :: + mem.store + `u.mem-stored + == +``` + +Store single lane from vector to memory at the specified address. + +### `+const` {#const} + +```hoon +++ const + |= i=instr-vec + ?> ?=(%const -.i) + |= l=local-state + ^- local-state + l(va.stack [(coin-to-val p.i) va.stack.l]) +``` + +Push vector constant value onto the stack. + +### `+shuffle` {#shuffle} + +```hoon +++ shuffle + |= i=instr-vec + ?> ?=(%shuffle -.i) + |= l=local-state + ^- local-state + ?> ?=([c2=@ c1=@ rest=*] va.stack.l) + =, va.stack.l + =/ seq=(list @) + (weld (rope 3 16 c1) (rope 3 16 c2)) + %= l + va.stack + :_ rest + %+ rep 3 + %+ turn lane-ids.i + (curr snag seq) + == +``` + +Shuffle lanes between two vectors according to the lane indices specified in the instruction. + +### `+extract` {#extract} + +```hoon +++ extract + |= i=instr-vec + ?> ?=(%extract -.i) + |= l=local-state + ^- local-state + ?> ?=([vec=@ rest=*] va.stack.l) + =, va.stack.l + =+ size=(lane-size p.i) + =+ lane=(cut (xeb (dec size)) [l.i 1] vec) + =; to-put=@ + l(va.stack [to-put rest]) + ?: ?=(%u mode.i) lane + (en-si 32 (to-si size lane)) +``` + +Extract value from a specific lane of a vector, with optional sign extension. + +### `+replace` {#replace} + +```hoon +++ replace + |= i=instr-vec + ?> ?=(%replace -.i) + |= l=local-state + ^- local-state + ?> ?=([lane=@ vec=@ rest=*] va.stack.l) + =, va.stack.l + %= l + va.stack + :_ rest + (sew (xeb (dec (lane-size p.i))) [l.i 1 lane] vec) + == +``` + +Replace a specific lane in a vector with a new value. + +### `+plain` {#plain} + +```hoon +++ plain + |= i=instr-vec + ^- $-(local-state local-state) + ~+ + |^ + ?+ -.i !! + vec-unary:kind + |= l=local-state + ^- local-state + ?> ?=([a=@ rest=*] va.stack.l) + =, va.stack.l + =+ val=((vec-unar i) a) + ?~ val l(br.stack [%trap ~]) + l(va.stack [val rest]) + :: + vec-binary:kind + |= l=local-state + ^- local-state + ?> ?=([b=@ a=@ rest=*] va.stack.l) + =, va.stack.l + =+ val=((vec-bina i) a b) + ?~ val l(br.stack [%trap ~]) + l(va.stack [val rest]) + :: + lane-wise-unary:kind + =/ op=$-(@ (unit @)) (get-op-unar i) + =/ size=@ (get-size i) + |= l=local-state + ^- local-state + ?> ?=([vec=@ rest=*] va.stack.l) + =, va.stack.l + =; val=(unit @) + ?~ val l(br.stack [%trap ~]) + l(va.stack [u.val rest]) + ;< =(list @) _biff + (torn (rope (xeb (dec size)) (div 128 size) vec) op) + `(rep (xeb (dec size)) list) + :: + lane-wise-binary:kind + =/ op=$-([@ @] (unit @)) (get-op-bina i) + =/ size=@ (get-size i) + |= l=local-state + ^- local-state + ?> ?=([vec2=@ vec1=@ rest=*] va.stack.l) + =, va.stack.l + =; val=(unit @) + ?~ val l(br.stack [%trap ~]) + l(va.stack [u.val rest]) + ;< =(list @) _biff + %- torn :_ op + %+ fuse (rope (xeb (dec size)) (div 128 size) vec1) + (rope (xeb (dec size)) (div 128 size) vec2) + `(rep (xeb (dec size)) list) + :: + %bitselect + |= l=local-state + ^- local-state + ?> ?=([vec3=@ vec2=@ vec1=@ rest=*] va.stack.l) + =, va.stack.l + %= l + va.stack + :_ rest + :: ++con is ior, ++dis is iand + :: + (con (dis vec1 vec3) (dis vec2 (not 7 1 vec3))) + == + == +``` + +Plain vector operations dispatcher that handles different categories of SIMD instructions. + +#### `+kind:plain` {#kind-plain} + +```hoon +++ kind + |% + +$ vec-unary + $? %splat %not %any-true %all-true + %bitmask %extadd %extend %convert + %demote %promote + == + :: + +$ vec-binary + $? %swizzle %and %andnot %or %xor + %narrow %shl %shr %extmul %dot + == + :: + +$ lane-wise-unary + $? + %abs %neg %popcnt %ceil %floor %trunc + %nearest %sqrt + == + :: + +$ lane-wise-binary + $? %eq %ne %lt %gt %le %ge %add %sub + %min %max %avgr %q15mul-r-sat %mul %div + %pmin %pmax + == + -- +``` + +SIMD instruction type categories for organizing vector operations. + +#### `+vec-unar:plain` {#vec-unar-plain} + +```hoon +++ vec-unar + |: i=`$>(vec-unary:kind instr-vec)`[%not ~] + ^- $-(@ @) + ?- -.i + :: %splat + :: %not + :: %any-true + :: %all-true + :: %bitmask + :: %extadd + :: %extend + :: %convert + :: %demote + :: %promote + == +``` + +Unary vector operations including splat, bitwise not, truth testing, type conversions, and lane extensions. + +#### `+vec-bina:plain` {#vec-bina-plain} + +```hoon +++ vec-bina + |: i=`$>(vec-binary:kind instr-vec)`[%and ~] + ^- $-([@ @] @) + ?- -.i + :: %swizzle + :: %and + :: %andnot + :: %or + :: %xor + :: %narrow + :: %shl + :: %shr + :: %extmul + :: %dot + == +``` + +Binary vector operations including logical operations, lane narrowing, shifts, and multiplication. + +#### `+get-op-unar:plain` {#get-op-unar-plain} + +```hoon +++ get-op-unar + |: i=`$>(lane-wise-unary:kind instr-vec)`[%popcnt ~] + ^- $-(@ (unit @)) + ?- -.i + :: %abs + :: %neg + :: %popcnt + :: %ceil + :: %floor + :: %trunc + :: %nearest + :: %sqrt + == +``` + +Resolve unary operation implementations for lane-wise vector operations. + +#### `+get-op-bina:plain` {#get-op-bina-plain} + +```hoon +++ get-op-bina + |: i=`$>(lane-wise-binary:kind instr-vec)`[%q15mul-r-sat ~] + ^- $-([@ @] (unit @)) + ?- -.i + :: %eq + :: %ne + :: %lt + :: %gt + :: %le + :: %ge + :: %add + :: %sub + :: %min + :: %max + :: %avgr + :: %q15mul-r-sat + :: %mul + :: %div + :: %pmin + :: %pmax + == +``` + +Resolve binary operation implementations for lane-wise vector operations. + +#### `+get-size:plain` {#get-size-plain} + +```hoon +++ get-size + |= i=^ + ^- @ + ?+ +.i !! + ~ + ?+ -.i !! + %q15mul-r-sat 16 + %popcnt 8 + == + :: + lane-type + (lane-size +.i) + :: + [p=lane-type *] + (lane-size p.i) + == +``` + +Extract size information from lane-wise SIMD instruction types. diff --git a/content/urbit-os/base/wasm/lib-wasm-validator.md b/content/urbit-os/base/wasm/lib-wasm-validator.md new file mode 100644 index 00000000..89b7d332 --- /dev/null +++ b/content/urbit-os/base/wasm/lib-wasm-validator.md @@ -0,0 +1,715 @@ +--- +description: "Reference for UrWasm's Wasm module validator library" +layout: + title: + visible: true + description: + visible: false + tableOfContents: + visible: true + outline: + visible: true + pagination: + visible: true +--- + +# Wasm Validator + +The `+validator` library validates WebAssembly modules for correctness according to the [WebAssembly Core Specification](https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/valid/index.html). It performs static analysis to ensure type safety, proper memory access, and correct control flow before execution. + +The validator operates on parsed [`$module`](./wasm-data-types.md#module) structures from the [`+parser`](./lib-wasm-parser.md) core and provides error messages for invalid modules. + +## `+validator` {#validator} + +```hoon +++ validator + =, wasm-sur + |% + +$ glob-type [v=valtype m=?(%con %var)] + +$ glob-types (list glob-type) + :: ... + -- +``` + +Core validator namespace. Exposes the nested `+wasm-sur` namespace and defines validator-specific types. + +## `$glob-type` {#glob-type} + +```hoon ++$ glob-type [v=valtype m=?(%con %var)] +``` + +Global variable type combining a [`$valtype`](./wasm-data-types.md#valtype) with mutability: +- `.v`: Value type of the global variable. +- `.m`: Mutability - `%con` for constant, `%var` for variable. + +## `$glob-types` {#glob-types} + +```hoon ++$ glob-types (list glob-type) +``` + +List of all global variable types. + +## `+output` {#output} + +```hoon +++ output + |% + +$ import + $: + funcs=(list func-type) :: flopped + tables=(list table) :: flopped + memo=(unit limits) + globs=glob-types :: flopped + == + -- +``` + +Validation output types. The `.funcs`, `.tables`, and `.globs` lists are built in reverse order during parsing, so must be `+flop`ped to get the right order. +- `.funcs`: List of imported function types. +- `.tables`: List of imported [`$table`s](./wasm-data-types.md#table). +- `.memo`: Optional imported memory [`$limits`](./wasm-data-types.md#limits). +- `.globs`: List of imported global types. + +## `$store` {#store} + +```hoon ++$ store import:output :: right order +``` + +Validation store containing all available types in the correct order for validation context. Order matters because the store provides the validation context where imported items come first, followed by locally defined items, matching the WebAssembly spec's indexing requirements. + +## `+result-form` {#result-form} + +```hoon +++ result-form + |$ [mold] + (each mold cord) +``` + +Generic result type parameterized by return type. Either a result of the given `$mold` or an error message cord. + +## `+result` {#result} + +```hoon +++ result + |* m2=mold + |% + +$ form $+(form (result-form m2)) + ++ bind + |* m1=mold + |= [a=(result-form m1) b=$-(m1 form)] + ^- form + ?: ?=(%| -.a) a + (b p.a) + -- +``` + +Monadic error handling system for validation. If the first validation fails, returns the error message; otherwise applies the continuation function. + +## `+snug` {#snug} + +```hoon +++ snug + |= where=cord + |* [a=@ b=(list)] + =/ r (result _?>(?=(^ b) i.b)) + |- ^- form:r + ?~ b |+(cat 3 'index error in ' where) + ?: =(a 0) &+i.b + $(a (dec a), b t.b) +``` + +Safe list indexing. Returns element at index `.a` in list `.b`, or an error message indicating the failure location. + +## `+validate-module` {#validate-module} + +```hoon +++ validate-module + |= m=module + =/ r (result ,~) + ^- form:r + ;< import-out=import:output bind:r (v-import-section m) + =/ n-funcs-import=@ (lent funcs.import-out) + ;< functypes=(list func-type) bind:r + (v-function-section m funcs.import-out) + ;< tables=(list table) bind:r (v-table-section m tables.import-out) + ;< memo=(unit limits) bind:r (v-memory-section m memo.import-out) + =/ n-funcs=@ (lent functypes) + =/ n-tables=@ (lent tables) + ;< =glob-types bind:r + (v-global-section m globs.import-out n-funcs) + =/ =store [functypes tables memo glob-types] + ;< ~ bind:r + %: v-export-section + m + n-funcs + n-tables + ?^(memo 1 0) + (lent glob-types) + == + ;< ~ bind:r (v-start-section m functypes) + ;< ~ bind:r (v-elem-section m n-tables functypes store) + ;< datacnt=(unit @) bind:r (v-datacnt-section m) + ;< ~ bind:r (v-code-section m n-funcs-import store) + (v-data-section m datacnt store) +:: +``` + +Main module validation function. Validates a [`$module`](./wasm-data-types.md#module) by sequentially checking all sections in dependency order. + +## `+v-import-section` {#v-import-section} + +```hoon +++ v-import-section + |= m=module + =| out=import:output + =/ num-types=@ (lent type-section.m) + =/ r (result import:output) + |- ^- form:r + ?~ import-section.m + &+out + ?- -.desc.i.import-section.m + %func + =/ idx=@ type-id.desc.i.import-section.m + ;< type=func-type bind:r ((snug 'import functype') idx type-section.m) + %= $ + import-section.m t.import-section.m + funcs.out [type funcs.out] + == + :: + %tabl + ?. (validate-limits q.t.desc.i.import-section.m) + |+'invalid limits import table' + =/ =table t.desc.i.import-section.m + $(import-section.m t.import-section.m, tables.out [table tables.out]) + :: + %memo + ?. (validate-limits l.desc.i.import-section.m) + |+'invalid limits import memo' + ?^ memo.out |+'multiple memos' + %= $ + import-section.m t.import-section.m + memo.out `l.desc.i.import-section.m + == + :: + %glob + %= $ + import-section.m t.import-section.m + globs.out [+.desc.i.import-section.m globs.out] + == + == +``` + +Validates the module's [`$import-section`](./wasm-data-types.md#import-section). Checks: +- Function imports reference valid type indices. +- Table imports have valid [`$limits`](./wasm-data-types.md#limits). +- At most one memory can be imported. +- Global imports have proper types. + +## `+validate-limits` {#validate-limits} + +```hoon +++ validate-limits + |= l=limits + ^- ? + ?- -.l + %flor & + %ceil (gte q.l p.l) + == +``` + +Validates [`$limits`](./wasm-data-types.md#limits) ensuring maximum is greater than or equal to minimum. + +## `+v-function-section` {#v-function-section} + +```hoon +++ v-function-section + |= [m=module functypes-import=(list func-type)] + =/ functypes=(list func-type) functypes-import + =/ r (result (list func-type)) + |- ^- form:r + ?~ function-section.m &+(flop functypes) + =/ idx=@ i.function-section.m + ;< type=func-type bind:r ((snug 'local functype') idx type-section.m) + %= $ + function-section.m t.function-section.m + functypes [type functypes] + == +``` + +Validates the [`$function-section`](./wasm-data-types.md#function-section) by checking that all function type indices reference valid entries in the type section. + +## `+v-table-section` {#v-table-section} + +```hoon +++ v-table-section + |= [m=module tables=(list table)] + =/ r (result (list table)) + ^- form:r + ?~ table-section.m &+(flop tables) + ?. (validate-limits q.i.table-section.m) |+'invalid limits local table' + $(table-section.m t.table-section.m, tables [i.table-section.m tables]) +``` + +Validates the [`$table-section`](./wasm-data-types.md#table-section) by checking table [`$limits`](./wasm-data-types.md#limits). + +## `+v-memory-section` {#v-memory-section} + +```hoon +++ v-memory-section + |= [m=module memo=(unit limits)] + =/ r (result (unit limits)) + ^- form:r + =/ len-memos=@ (lent memory-section.m) + ?: (gth len-memos 1) |+'multiple memos' + ?: &(?=(^ memo) (gth len-memos 0)) |+'multiple memos' + ?^ memo &+memo + ?: =(len-memos 0) &+~ + =/ lim=limits -.memory-section.m + ?. (validate-limits lim) |+'invalid limits local memory' + ?: &(?=(%ceil -.lim) (gth q.lim (bex 16))) |+'mem limit too big' + &+`-.memory-section.m +``` + +Validates the [`$memory-section`](./wasm-data-types.md#memory-section). Ensures: +- At most one memory is defined. +- Memory limits are valid. +- Maximum memory size doesn't exceed 2^16 pages (4GB). + +## `+v-global-section` {#v-global-section} + +```hoon +++ v-global-section + |= [m=module gt=glob-types n-funcs=@] + =/ n-glob-import=@ (lent gt) + =/ r (result glob-types) + |- ^- form:r + ?~ global-section.m &+(flop gt) + =/ glob i.global-section.m + ?- -.i.glob + %const + ?. =(v.glob -.p.i.glob) |+'global type mismatch' + $(global-section.m t.global-section.m, gt [[v m]:glob gt]) + :: + %vec + ?. ?=(%v128 v.glob) |+'global type mismatch' + $(global-section.m t.global-section.m, gt [[v m]:glob gt]) + :: + %ref-null + $(global-section.m t.global-section.m, gt [[v m]:glob gt]) + :: + %ref-func + ?: (gte func-id.i.glob n-funcs) |+'invalid funcref' + $(global-section.m t.global-section.m, gt [[v m]:glob gt]) + :: + %global-get + ?: (gte index.i.glob n-glob-import) + |+'non-import or nonexisting const global initializer' + $(global-section.m t.global-section.m, gt [[v m]:glob gt]) + == +``` + +Validates the [`$global-section`](./wasm-data-types.md#global-section). Checks: +- Initializer expressions match declared types. +- Function references are valid. +- Global references in initializers refer to imported constants. + +## `+v-export-section` {#v-export-section} + +```hoon +++ v-export-section + |= [m=module n-funcs=@ n-tables=@ n-memos=@ n-globs=@] + =| names=(set cord) + =/ r (result ,~) + |- ^- form:r + ?~ export-section.m &+~ + =/ exp i.export-section.m + ?: (~(has in names) name.exp) |+'name duplicate' + =; [i=@ num=@] + ?: (gte i num) |+'invalid export index' + $(export-section.m t.export-section.m, names (~(put in names) name.exp)) + ?- -.export-desc.exp + %func [i.export-desc.exp n-funcs] + %tabl [i.export-desc.exp n-tables] + %memo [i.export-desc.exp n-memos] + %glob [i.export-desc.exp n-globs] + == +``` + +Validates the [`$export-section`](./wasm-data-types.md#export-section). Checks: +- Export names are unique. +- Export indices are within bounds for their respective types. + +## `+v-start-section` {#v-start-section} + +```hoon +++ v-start-section + |= [m=module functypes=(list func-type)] + =/ r (result ,~) + ^- form:r + ?~ start-section.m &+~ + =/ func-idx=@ u.start-section.m + ;< type=func-type bind:r ((snug 'start section') func-idx functypes) + ?. ?=([~ ~] type) + |+'non-void start function' + &+~ +``` + +Validates the [`$start-section`](./wasm-data-types.md#start-section) ensuring the start function has no parameters and no return values. + +## `+v-elem-section` {#v-elem-section} + +```hoon +++ v-elem-section + :: elems are additionaly restricted by the parser: offset + :: expression is limited to a single const instruction, + :: and init expression are limited to a single %ref* instruction + :: + |= [m=module n-tables=@ functypes=(list func-type) =store] + =/ r (result ,~) + ^- form:r + ?~ elem-section.m &+~ + =/ elem i.elem-section.m + ;< ~ bind:r + =/ r (result ,~) + |- ^- form:r + ?~ i.elem &+~ + ?: ?=(%ref-null -.i.i.elem) + ?: =(t.elem t.i.i.elem) $(i.elem t.i.elem) + |+'%ref-null type mismatch in element' + ?. ?=(%func t.elem) |+'%ref-null type mismatch in element' + =/ idx=@ func-id.i.i.elem + ;< * bind:r ((snug 'elem section funcref') idx functypes) + $(i.elem t.i.elem) + ?. ?=(%acti -.m.elem) $(elem-section.m t.elem-section.m) + ?: (gte tab.m.elem n-tables) |+'element index error' + :: ?. ?=(%i32 -.p.off.m.elem) |+'type error in element offset' + ?: ?=(?(%ref-null %ref-func %vec) -.off.m.elem) + |+'type error in element offset' + ?: ?=(%const -.off.m.elem) + ?. ?=(%i32 -.p.off.m.elem) |+'type error in element offset' + $(elem-section.m t.elem-section.m) + :: %global-get + ;< glob=glob-type bind:r + ((snug 'global section') index.off.m.elem globs.store) + ?. ?=(%i32 v.glob) |+'type error in element offset' + $(elem-section.m t.elem-section.m) +``` + +Validates the [`$elem-section`](./wasm-data-types.md#elem-section). Checks: +- Element initializers match table reference types. +- Function references are valid. +- Table indices are within bounds. +- Offset expressions have correct types. + +## `+v-datacnt-section` {#v-datacnt-section} + +```hoon +++ v-datacnt-section + |= m=module + =/ r (result (unit @)) + ^- form:r + &+datacnt-section.m +``` + +Returns the data count section, no additional validation needed. + +## `+v-code-section` {#v-code-section} + +```hoon +++ v-code-section + |= $: m=module + n-funcs-import=@ + =store + == + ?. =((lent code-section.m) (lent function-section.m)) + |+'mismatching lengths of function and code sections' + =/ idx=@ n-funcs-import + =/ r (result ,~) + |- ^- form:r + ?~ code-section.m &+~ + ;< type=func-type bind:r ((snug 'code section') idx funcs.store) + ;< ~ bind:r (validate-code idx i.code-section.m type m store) + $(idx +(idx), code-section.m t.code-section.m) +``` + +Validates the [`$code-section`](./wasm-data-types.md#code-section). Checks: +- Code and function sections have matching lengths. +- Each function body validates against its declared type. + +## `+v-data-section` {#v-data-section} + +```hoon +++ v-data-section + :: data section is additionaly restrained by the parser: + :: offset expression may only be a single const instruction + :: + |= [m=module datacnt=(unit @) =store] + =/ r (result ,~) + ^- form:r + ?: &(?=(^ datacnt) !=(u.datacnt (lent data-section.m))) + |+'wrong datacnt' + |- ^- form:r + ?~ data-section.m + &+~ + =/ data i.data-section.m + ?: ?=(%pass -.data) + $(data-section.m t.data-section.m) + ?~ memo.store |+'no memory to copy data to' + ?: ?=(%const -.off.data) + ?. ?=(%i32 -.p.off.data) |+'type error in data offset' + $(data-section.m t.data-section.m) + ?: ?=(?(%ref-null %ref-func %vec) -.off.data) + |+'type error in data offset' + :: global-get + ;< glob=glob-type bind:r + ((snug 'global-section') index.off.data globs.store) + ?. ?=(%i32 v.glob) |+'type error in data offset' + $(data-section.m t.data-section.m) +``` + +Validates the [`$data-section`](./wasm-data-types.md#data-section). Checks: +- Data count matches actual data segments. +- Memory exists for active data segments. +- Offset expressions have correct types. + +## `+validate-code` {#validate-code} + +```hoon +++ validate-code + |= $: idx=@ + =code + type=func-type + =module + =store + == + =/ r (result ,~) + ^- form:r + =/ locals (weld params.type locals.code) + =/ stack=(list valtype) ~ + =/ frames=(list (list valtype)) ~[results.type] + =/ res + %: validate-expr + expression.code + module + store + locals + stack + frames + == + ?- -.res + %& res + %| |+(rap 3 'func ' (scot %ud idx) ': ' p.res ~) + == +``` + +Validates a single function's [`$code`](./wasm-data-types.md#code) against its expected [`$func-type`](./wasm-data-types.md#func-type). Sets up the validation context with parameters and local variables, then validates the function body. + +## `+validate-expr` {#validate-expr} + +```hoon +++ validate-expr + |= $: expr=expression + =module + =store + $= args + $: locals=(list valtype) + stack=(list valtype) + frames=(list (list valtype)) + == == + =/ r (result ,~) + ^- form:r + ?~ expr + ?. =(-.frames.args (flop stack.args)) + ~& [frames.args (flop stack.args)] + |+'type error in result' + &+~ + =/ instr i.expr + :: stack-polymorphic instructions (unconditional control transfer) + :: + ?: ?=(%unreachable -.instr) &+~ + ?: ?=(%br -.instr) + ;< results=(list valtype) bind:r + ((snug 'br frames') label.instr frames.args) + ?. =(results (flop (scag (lent results) stack.args))) |+'br type error' + &+~ + ?: ?=(%br-table -.instr) + =/ labels=(list @) [label-default label-vec]:instr + ?. =(%i32 -.stack.args) |+'br-table index type error' + =. stack.args +.stack.args + |- ^- form:r + ?~ labels &+~ + ;< results=(list valtype) bind:r + ((snug 'br-table frames') i.labels frames.args) + ?. =(results (flop (scag (lent results) stack.args))) + |+'br-table type error' + $(labels t.labels) + ?: ?=(%return -.instr) + ?: =(~ frames.args) |+'no frames' + =/ results=(list valtype) (rear frames.args) + ?. =(results (flop (scag (lent results) stack.args))) + |+'return type error' + &+~ + ;< [stack1=_stack.args] bind:r + (validate-instr instr module store args) + $(expr t.expr, stack.args stack1) +``` + +Validates an [`$expression`](./wasm-data-types.md#expression) (sequence of instructions) within a given context. Validates: +- Local variable types. +- Stack type state. +- Control flow frames for branch validation. + +Handles stack-polymorphic instructions (`unreachable`, `br`, `br-table`, `return`) specially as they don't return normally. + +## `+validate-instr` {#validate-instr} + +```hoon +++ validate-instr + |= $: $= instr + $~ [%nop ~] + $<(?(%unreachable %br %br-table %return) instruction) + :: + =module + =store + locals=(list valtype) + stack=(pole valtype) + frames=(list (list valtype)) + == + =/ r (result _stack) + ^- form:r + :: ... +``` + +Validates a single [`$instruction`](./wasm-data-types.md#instruction) against the current stack and context. Handles: +- Value-polymorphic instructions (`drop`, `select`). +- Block instructions (`block`, `loop`, `if`). +- Control flow instructions (`br-if`, `call`, `call-indirect`). +- Reference instructions (`ref-is-null`). + +For most instructions, delegates to [`+get-type`](#get-type) to determine the instruction's type signature. + +## `+get-type` {#get-type} + +```hoon +++ get-type + |= $: $= instr + $~ [%nop ~] + $< $? %unreachable + %br + %br-table + %return + %drop + %select + %block + %loop + %if + %br-if + %call + %call-indirect + %ref-is-null + == + instruction + :: + =module + =store + locals=(list valtype) + == + ~+ + =/ r (result func-type) + ^- form:r + ?- -.instr + :: ... + == +``` + +Returns the type signature of an [`$instruction`](./wasm-data-types.md#instruction) as a [`$func-type`](./wasm-data-types.md#func-type) (parameter types and result types). Handles: +- Constant instructions (`const`). +- Unary numeric operations (`eqz`, `clz`, `ctz`, `popcnt`, `abs`, `neg`, etc.). +- Binary numeric operations (`add`, `sub`, `mul`, `div`, `rem`, etc.). +- Comparison operations (`eq`, `ne`, `lt`, `gt`, `le`, `ge`). +- Reference operations (`ref-null`, `ref-func`). +- Variable operations (`local-get`, `local-set`, `local-tee`, `global-get`, `global-set`). +- Table operations (`table-get`, `table-set`, `table-init`, etc.). +- Memory operations (`load`, `store`, `memory-size`, `memory-grow`, etc.). +- Vector instructions (delegated to [`+get-type-vec`](#get-type-vec)). + +## `+get-type-vec` {#get-type-vec} + +```hoon +++ get-type-vec + |= [instr=instr-vec =module =store] + =/ r (result func-type) + ^- form:r + ?- -.instr + :: ... + == +``` + +Returns the type signature for SIMD vector instructions. Handles: +- Vector memory operations (`load`, `store`, `load-lane`, `store-lane`). +- Vector constants and manipulation (`const`, `shuffle`, `extract`, `replace`). +- Vector logical operations (`swizzle`, `splat`). +- Vector comparison and arithmetic operations. +- Vector conversion operations. + +## `+from-coin` {#from-coin} + +```hoon +++ from-coin + |= coin=coin-wasm + ^- valtype + ?- -.coin + valtype -.coin + %ref +<.coin + == +``` + +Extracts the [`$valtype`](./wasm-data-types.md#valtype) from a [`$coin-wasm`](./wasm-data-types.md#coin-wasm). + +## `+byte-width` {#byte-width} + +```hoon +++ byte-width + |= v=?(num-type vec-type) + ^- @ + ?- v + ?(%i32 %f32) 4 + ?(%i64 %f64) 8 + %v128 16 + == +``` + +Returns the byte width of a numeric or vector type. + +## `+dim-lane` {#dim-lane} + +```hoon +++ dim-lane + |= l=lane-type + ^- @ + ?- l + %i8 16 + %i16 8 + ?(%i32 %f32) 4 + ?(%i64 %f64) 2 + == +``` + +Returns the number of lanes in a 128-bit vector for the given [`$lane-type`](./wasm-data-types.md#lane-type). + +## `+unpack` {#unpack} + +```hoon +++ unpack + |= l=lane-type + ^- num-type + ?- l + num-type l + ?(%i8 %i16) %i32 + == +``` + +Converts a [`$lane-type`](./wasm-data-types.md#lane-type) to its corresponding [`$num-type`](./wasm-data-types.md#num-type), promoting `%i8` and `%i16` to `%i32`. diff --git a/content/urbit-os/base/wasm/wasm-data-types.md b/content/urbit-os/base/wasm/wasm-data-types.md new file mode 100644 index 00000000..bfdf68a9 --- /dev/null +++ b/content/urbit-os/base/wasm/wasm-data-types.md @@ -0,0 +1,1518 @@ +--- +description: "Foundational Wasm types" +layout: + title: + visible: true + description: + visible: false + tableOfContents: + visible: true + outline: + visible: true + pagination: + visible: true +--- + +# Wasm Data Types + +UrWasm's inner core, `+wasm-sur`, contains the foundational Wasm types. + +The first part implements the [Structure chapter](https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/syntax/index.html) of the [WebAssembly Core Specification](https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/syntax/index.html). + +The second part implements Wasm's binary-format opcodes. + +### `$octs` {#octs} + +```hoon ++$ octs (pair @ud @) +``` + +Pair of byte-length and octet stream data. + +### `+wasm-sur` {#wasm-sur} + +Wrapper arm around the Wasm types. This just makes the types addressable by limb resolution paths like `num-type:wasm-sur`. + +### `$num-type` {#num-type} + +```hoon ++$ num-type ?(%i32 %i64 %f32 %f64) +``` + +Type annotation for the four types of number Wasm calls can return: +- 32-bit integers (`@F`). +- 64-bit integers (`@G`). +- 32-bit floating point numbers (`@rs`). +- 64-bit floating point numbers (`@rd`). + +### `$vec-type` {#vec-type} + +```hoon ++$ vec-type %v128 +``` + +Type annotation for a 128-bit (`@H`) register. + +### `$ref-type` {#ref-type} + +```hoon ++$ ref-type ?(%extn %func) :: externref and funcref +``` + +Type annotation for references in the Wasm state, be those functions or data. + +### `$valtype` {#valtype} + +```hoon ++$ valtype + $~ %i32 + $? num-type + vec-type + ref-type + == +``` + +Type annotation for all the types that Wasm can accept: numbers, vectors, and references. + +### `$coin-wasm` {#coin-wasm} + +```hoon ++$ coin-wasm + $~ [%i32 *@F] + $% [%i32 @F] + [%i64 @G] + [%f32 @rs] + [%f64 @rd] + [vec-type @H] + $: %ref :: function reference, null or not + $% [%func (unit @)] :: local + $: %extn :: external + %- unit + $: [mod=cord name=cord] + type=func-type + == == == == + :: + == +``` + +Type-annotated Wasm value. A `$coin-wasm` is UrWasm's version of Wasm's [result types](https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/syntax/types.html#result-types), which look something like Hoon's [`$coin`](https://docs.urbit.org/hoon/stdlib/3g#coin) nouns. + +These are the [`$valtype`](#valtype) type annotation terms with extra specification for atom size and object references. + +### `$limits` {#limits} + +```hoon ++$ limits + $% [%flor p=@] :: min only + [%ceil p=@ q=@] :: min and max + == +``` + +Limits the range of available storage for [`$memarg`](#memarg)s and [`$table`](#table)s. + +### `$memarg` {#memarg} + +```hoon ++$ memarg + $+ memarg + [align=@ offset=@] +``` + +Defines the minimum and maximum size of a memory instance. The `.align` and `.offset` values represent units of Wasm's page size, which is 128 bits (so, a `@H`). + +### `$block-type` {#block-type} + +```hoon ++$ block-type $@(@ func-type) :: typeidx in type section or func-type +``` + +Function signature for control flow blocks (`if`, `loop`, etc.). This may be defined inline ([`$func-type`](#func-type)) or referred to by its index in the [`$type-section`](#type-section). + +### `$func-type` {#func-type} + +```hoon ++$ func-type + $: params=(list valtype) + results=(list valtype) + == +``` + +Function signature. + +### `$lane-type` {#lane-type} + +```hoon ++$ lane-type ?(%i8 %i16 num-type) +``` + +Type annotation for lanes (memory elements) in a 128-bit (`@H`) register. Includes the usual [`$num-type`s](#num-type) and additional `%i8` and `%i16` types for smaller lanes. + +### `$instruction` {#instruction} + +```hoon ++$ instruction + $%([%vec instr-vec] instr-short instr-num instr-dbug) +``` + +Type union of all Wasm instructions. + +Instructions are categorized here by their operand patterns: +- `%vec`: Vector instructions. (See [`$instr-vec`](#instr-vec).) +- [`$instr-short`](#instr-short): Non-numeric Wasm instructions. +- [`$instr-num`](#instr-num): Standard Wasm instructions categorized by arity. +- [`$instr-dbug`](#instr-dbug): Debugging. + +### `$instr-dbug` {#instr-dbug} + +```hoon ++$ instr-dbug + $% + [%dbug %print-tee term] + == +``` + +Debugging instructions. The only one supported here is `%print-tee`, which prints a value to output with the given `$term` as a label. + +### `$instr-num` {#instr-num} + +```hoon ++$ instr-num ?(instr-num-zero instr-num-one instr-num-two) +``` + +Type union of all standard Wasm instructions, categorized here by arity. + +### `$instr-num-zero` {#instr-num-zero} + +```hoon ++$ instr-num-zero + $% + [%const p=$<(?(%v128 %ref) coin-wasm)] + == +``` + +Wasm `const` instruction, whose value is any [`$coin-wasm`](#coin-wasm) that may represent a constant value. So... + +```hoon +$? [%i32 @F] + [%i64 @G] + [%f32 @rs] + [%f64 @rd] +== +``` + +### `$instr-num-one` {#instr-num-one} + +```hoon ++$ instr-num-one + $% + [%eqz type=?(%i32 %i64)] + [%clz type=?(%i32 %i64)] + [%ctz type=?(%i32 %i64)] + [%popcnt type=?(%i8 %i32 %i64)] + [%abs type=lane-type] + [%neg type=lane-type] + [%ceil type=?(%f32 %f64)] + [%floor type=?(%f32 %f64)] + :: + $: %trunc + type=num-type + source-type=(unit ?(%f32 %f64)) + mode=(unit ?(%s %u)) + sat=? + == + :: + [%nearest type=?(%f32 %f64)] + [%sqrt type=?(%f32 %f64)] + [%wrap ~] + :: + $: %extend + type=?(%i32 %i64) + source-type=?(%i32 %i64) + source-size=?(%8 %16 %32) + mode=?(%s %u) + == + :: + [%convert type=?(%f32 %f64) source-type=?(%i32 %i64) mode=?(%s %u)] + [%demote ~] + [%promote ~] + [%reinterpret type=num-type source-type=num-type] + == +``` + +Standard Wasm instructions with one parameter each: +- `eqz`: Check if equals zero. +- `clz`: Count leading zeroes. +- `ctz`: Count trailing zeroes. +- `popcnt`: Population count (number of set bits). +- `abs`: Absolute value. +- `neg`: Negate. +- `ceil`: Round a float up to nearest integer. +- `floor`: Round a float down to nearest integer. +- `nearest`: Round a float to nearest integer. +- `sqrt`: Square root. +- `trunc`: Truncate float to integer (with optional saturation). +- `wrap`: Wrap larger integer to smaller type. +- `extend`: Extend smaller integer to larger type. +- `convert`: Convert between integer and float. +- `demote`: Convert `f64` (`@rd`) to `f32` `(@rs`). +- `promote`: Convert `f32` (`@rs`) to `f64` (`@rd`). +- `reinterpret`: Reinterpret bit pattern as different type. + +### `$instr-num-two` {#instr-num-two} + +```hoon ++$ instr-num-two + $% + [%eq type=lane-type] + [%ne type=lane-type] + [%lt type=lane-type mode=(unit ?(%s %u))] + [%gt type=lane-type mode=(unit ?(%s %u))] + [%le type=lane-type mode=(unit ?(%s %u))] + [%ge type=lane-type mode=(unit ?(%s %u))] + [%add type=lane-type] + [%sub type=lane-type] + [%mul type=lane-type] + [%div type=lane-type mode=(unit ?(%s %u))] + [%rem type=?(%i32 %i64) mode=?(%s %u)] + [%and type=?(%i32 %i64)] + [%or type=?(%i32 %i64)] + [%xor type=?(%i32 %i64)] + [%shl type=?(%i8 %i16 %i32 %i64)] + [%shr type=?(%i8 %i16 %i32 %i64) mode=?(%s %u)] + [%rotl type=?(%i32 %i64)] + [%rotr type=?(%i32 %i64)] + [%min type=?(%f32 %f64)] + [%max type=?(%f32 %f64)] + [%copysign type=?(%f32 %f64)] + == +``` + +Wasm's binary numeric instructions: +- `eq`: Test equality. +- `ne`: Test inequality. +- `lt`: Less than. +- `gt`: Greater than. +- `le`: Less than or equal. +- `ge`: Greater than or equal. +- `add`: Addition. +- `sub`: Subtraction. +- `mul`: Multiplication. +- `div`: Division. +- `rem`: Remainder. +- `and`: Bitwise AND. +- `or`: Bitwise OR. +- `xor`: Bitwise XOR. +- `shl`: Shift left. +- `shr`: Shift right (logical or arithmetic). +- `rotl`: Rotate left. +- `rotr`: Rotate right. +- `min`: Minimum value. +- `max`: Maximum value. +- `copysign`: Copy sign bit from second operand to first. + +### `$instr-short` {#instr-short} + +```hoon ++$ instr-short + $% + :: Control instructions + :: + [%unreachable ~] + [%nop ~] + [%block type=block-type body=expression] + [%loop type=block-type body=expression] + $: %if + type=block-type + branch-true=expression + branch-false=expression + == + :: + [%br label=@] + [%br-if label=@] + [%br-table label-vec=(list @) label-default=@] + [%return ~] + [%call func-id=@] + [%call-indirect type-id=@ table-id=@] + :: Reference instructions + :: + [%ref-null t=ref-type] + [%ref-is-null ~] + [%ref-func func-id=@] + :: Parametric instructions + :: + [%drop ~] + [%select (unit (list valtype))] + :: Variable instructions + :: + [%local-get index=@] + [%local-set index=@] + [%local-tee index=@] + [%global-get index=@] + [%global-set index=@] + :: Table instructions + :: + [%table-get tab-id=@] + [%table-set tab-id=@] + [%table-init elem-id=@ tab-id=@] + [%elem-drop elem-id=@] + [%table-copy tab-id-x=@ tab-id-y=@] + [%table-grow tab-id=@] + [%table-size tab-id=@] + [%table-fill tab-id=@] + :: Memory instructions + :: + $: %load + type=num-type + m=memarg + n=(unit ?(%8 %16 %32)) + mode=(unit ?(%s %u)) + == + :: + $: %store + type=num-type + m=memarg + n=(unit ?(%8 %16 %32)) + == + :: + [%memory-size mem-id=%0] + [%memory-grow mem-id=%0] + [%memory-init x=@ mem-id=%0] + [%data-drop x=@] + [%memory-copy mem-1=%0 mem-2=%0] + [%memory-fill mem-id=%0] + == :: $instr-short +``` + +Non-numeric WebAssembly instructions organized by category: +- `unreachable`: Trap execution unconditionally. +- `nop`: No operation. +- `block`: Start a block with optional result type. +- `loop`: Start a loop with optional result type. +- `if`: Conditional execution with true/false branches. +- `br`: Unconditional branch to label. +- `br-if`: Conditional branch to label. +- `br-table`: Multi-way branch (switch statement). +- `return`: Return from current function. +- `call`: Call function by index. +- `call-indirect`: Call function indirectly through table. +- `ref-null`: Create null reference. +- `ref-is-null`: Test if reference is null. +- `ref-func`: Get function reference. +- `drop`: Remove top stack value. +- `select`: Choose between two values based on condition. +- `local-get`: Get local variable value. +- `local-set`: Set local variable value. +- `local-tee`: Set local variable and return the value. +- `global-get`: Get global variable value. +- `global-set`: Set global variable value. +- `table-get`: Get element from table. +- `table-set`: Set element in table. +- `table-init`: Initialize table from element segment. +- `table-copy`: Copy elements between tables. +- `table-grow`: Grow table size. +- `table-size`: Get table size. +- `table-fill`: Fill table range with value. +- `elem-drop`: Drop element segment. +- `load`: Load value from memory. +- `store`: Store value to memory. +- `memory-size`: Get memory size in pages. +- `memory-grow`: Grow memory size. +- `memory-init`: Initialize memory from data segment. +- `data-drop`: Drop data segment. +- `memory-copy`: Copy memory regions. +- `memory-fill`: Fill memory range with byte value. + +### `$instr-vec` {#instr-vec} + +```hoon ++$ instr-vec + $% + :: Load + :: + $: %load + m=memarg + $= kind %- unit + $: p=?(%8 %16 %32 %64) + q=?(%splat %zero [%extend ?(%s %u)]) + == == + :: + [%load-lane m=memarg p=?(%8 %16 %32 %64) l=@] + :: Store + :: + [%store m=memarg] + [%store-lane m=memarg p=?(%8 %16 %32 %64) l=@] + :: Misc + [%const p=$>(%v128 coin-wasm)] + [%shuffle lane-ids=(list @)] + [%extract p=lane-type l=@ mode=?(%s %u)] + [%replace p=lane-type l=@] + :: Plain + :: + [%swizzle ~] + [%splat p=lane-type] + [%eq p=lane-type] + [%ne p=lane-type] + [%lt p=lane-type mode=?(%u %s)] + [%gt p=lane-type mode=?(%u %s)] + [%le p=lane-type mode=?(%u %s)] + [%ge p=lane-type mode=?(%u %s)] + [%not ~] + [%and ~] + [%andnot ~] + [%or ~] + [%xor ~] + [%bitselect ~] + [%any-true ~] + [%abs p=lane-type] + [%neg p=lane-type] + [%popcnt ~] + [%all-true p=?(%i8 %i16 %i32 %i64)] + [%bitmask p=?(%i8 %i16 %i32 %i64)] + [%narrow p=?(%i8 %i16) mode=?(%u %s)] + [%shl p=?(%i8 %i16 %i32 %i64)] + [%shr p=?(%i8 %i16 %i32 %i64) mode=?(%u %s)] + [%add p=lane-type sat=(unit ?(%u %s))] + [%sub p=lane-type sat=(unit ?(%u %s))] + [%min p=lane-type mode=?(%u %s)] + [%max p=lane-type mode=?(%u %s)] + [%avgr p=?(%i8 %i16) mode=%u] + [%extadd p=?(%i16 %i32) mode=?(%u %s)] + [%q15mul-r-sat ~] + [%extend p=?(%i16 %i32 %i64) mode=?(%u %s) half=?(%high %low)] + [%mul p=lane-type] + [%extmul p=?(%i16 %i32 %i64) mode=?(%u %s) half=?(%high %low)] + [%dot ~] + [%ceil p=?(%f32 %f64)] + [%floor p=?(%f32 %f64)] + [%trunc p=?(%i32 %f32 %f64) from=?(%f32 %f64) mode=?(%u %s)] + [%nearest p=?(%f32 %f64)] + [%sqrt p=?(%f32 %f64)] + [%div p=?(%f32 %f64)] + [%pmin p=?(%f32 %f64)] + [%pmax p=?(%f32 %f64)] + [%convert p=?(%f32 %f64) mode=?(%u %s)] + [%demote ~] + [%promote ~] + == :: $instr-vec +``` + +Wasm SIMD vector instructions for 128-bit vectors: +- `load`: Load vector from memory (with optional splat/zero-extend/sign-extend). +- `load-lane`: Load single value into specific lane. +- `store`: Store vector to memory. +- `store-lane`: Store single lane to memory. +- `const`: Vector constant. +- `shuffle`: Rearrange lanes by index list. +- `extract`: Get value from specific lane. +- `replace`: Set value in specific lane. +- `swizzle`: Rearrange lanes using second vector as indices. +- `splat`: Broadcast scalar to all lanes. +- `eq`: Lane-wise equality comparison. +- `ne`: Lane-wise inequality comparison. +- `lt`: Lane-wise less than comparison. +- `gt`: Lane-wise greater than comparison. +- `le`: Lane-wise less than or equal comparison. +- `ge`: Lane-wise greater than or equal comparison. +- `not`: Bitwise NOT on entire vector. +- `and`: Bitwise AND on entire vector. +- `andnot`: Bitwise AND-NOT on entire vector. +- `or`: Bitwise OR on entire vector. +- `xor`: Bitwise XOR on entire vector. +- `bitselect`: Select bits based on mask. +- `any-true`: Test if any lanes are true. +- `all-true`: Test if all lanes are true. +- `bitmask`: Extract high bit from each lane. +- `abs`: Lane-wise absolute value. +- `neg`: Lane-wise negation. +- `popcnt`: Population count on each lane. +- `narrow`: Pack two vectors into one with narrower lanes. +- `shl`: Lane-wise left bit shift. +- `shr`: Lane-wise right bit shift. +- `add`: Lane-wise addition (with optional saturation). +- `sub`: Lane-wise subtraction (with optional saturation). +- `min`: Lane-wise minimum. +- `max`: Lane-wise maximum. +- `avgr`: Averaging with rounding. +- `mul`: Lane-wise multiplication. +- `extend`: Extend lane width. +- `extmul`: Extended multiplication. +- `extadd`: Extended addition. +- `q15mul-r-sat`: Q15 fixed-point multiplication. +- `dot`: Dot product. +- `ceil`: Lane-wise ceiling (round up). +- `floor`: Lane-wise floor (round down). +- `nearest`: Lane-wise round to nearest. +- `sqrt`: Lane-wise square root. +- `div`: Lane-wise division. +- `trunc`: Lane-wise truncate to integer. +- `convert`: Lane-wise type conversion. +- `demote`: Lane-wise `f64` to `f32` conversion. +- `promote`: Lane-wise `f32` to `f64` conversion. +- `pmin`: Propagating minimum (NaN handling). +- `pmax`: Propagating maximum (NaN handling). + +### `$expression` {#expression} + +```hoon ++$ expression (list instruction) +``` + +An ordered sequence of [`$instruction`](#instruction)s. + +### `$const-instr` {#const-instr} + +```hoon ++$ const-instr + $~ [%const %i32 `@`0] + $? [%vec $>(%const instr-vec)] + $>(?(%const %global-get %ref-null %ref-func) instruction) + == +``` + +Instructions that produce constant values e.g. global variables. Unlike [`$instr-num-zero`](#instr-num-zero) which only handles scalar numeric constants, this includes vector constants, global variable reads, and reference constants. + +### `$module` {#module} + +```hoon ++$ module + $: + =type-section + =import-section + =function-section + =table-section + =memory-section + =global-section + =export-section + =start-section + =elem-section + =datacnt-section + =code-section + =data-section + == +``` + +A Wasm module. Note code and function sections are separated here to simplify parsing. + +### `$type-section` {#type-section} + +```hoon ++$ type-section + $+ type-section + (list func-type) +``` + +List of function signatures in the module. + +### `$import-section` {#import-section} + +```hoon ++$ import-section + $+ import-section + (list import) +``` + +List of [`$import`s](#import) this module requires from other modules. + +### `$import` {#import} + +```hoon ++$ import + $: mod=cord + name=cord + $= desc + $% + [%func type-id=@] + [%tabl t=table] + [%memo l=limits] + [%glob v=valtype m=?(%con %var)] :: constant or variable + == == +``` + +Import from another Wasm module, including: +- `.mod`: Module name. +- `.name`: Name for the entity in the module we'd like to import. +- `.desc`: Importable definitions within the module, including functions, tables, global variables, etc. + +### `$function-section` {#function-section} + +```hoon ++$ function-section + $+ function-section + (list type-id=@) +``` + +List of functions in this module, referenced by index. + +### `$table-section` {#table-section} + +```hoon ++$ table-section (list table) +``` + +List of [`$table`s](#table) in the module. + +### `$table` {#table} + +```hoon ++$ table (pair ref-type limits) +``` + +Wasm table, referenced by its [`$ref-type`](#ref-type) and describing the [`$limits`](#limits) of its size. + +### `$memory-section` {#memory-section} + +```hoon ++$ memory-section (list limits) +``` + +List of the module's memory arrays, defined by the [`$limits`](#limits) of their size. + +### `$global-section` {#global-section} + +```hoon ++$ global-section (list global) +``` + +List of the module's [`$global`](#global) variables. + +### `$global` {#global} + +```hoon ++$ global + $: v=valtype + m=?(%con %var) + i=const-instr + == +``` + +Global variable, including: +- `.val`: [`$valtype`](#valtype), type of the variable's value. +- `.m`: Constant or variable. +- `.i`: Initial value. (Note that this is a constant instruction and not a `(list instruction)` as Wasm has no global value type that would take multiple constant values.) + +### `$export-section` {#export-section} + +```hoon ++$ export-section + $+ export-section + (list export) +``` + +List of [`$export`s](#export) in the module. + +### `$export` {#export} + +```hoon ++$ export [name=cord =export-desc] +``` + +Element to be exportable for use in other modules: +- `.name`: Name of the export. +- `.export-desk`: [`$export-desc`](#export-desc), type-annotated value of the export. + +### `$export-desc` {#export-desc} + +```hoon ++$ export-desc + $% [%func i=@] + [%tabl i=@] + [%memo i=@] + [%glob i=@] + == +``` + +Type-annotated value of an export addressed by its index: +- `%func`: Function. +- `%tabl`: Table. +- `%memo`: Memory. +- `%glob`: Global variable. + +In practice these indexes will always be 32-bit integers (`@F`s). + +### `$start-section` {#start-section} + +```hoon ++$ start-section (unit @) +``` + +The initialization (or "start") function for the module, if one exists. + +### `$elem-section` {#elem-section} + +```hoon ++$ elem-section (list elem) +``` + +List of [`$elem`s](#elem). + +### `$elem` {#elem} + +```hoon ++$ elem + $~ [*ref-type ~ %pass ~] + $: t=ref-type + i=(list $>(?(%ref-func %ref-null) instruction)) + $= m + $% [%pass ~] + [%decl ~] + [%acti tab=@ off=const-instr] + == == +``` + +Wasm element segment: +- `.t`: Reference type. (Currently Wasm only supports function references (`%func` [`$ref-type`s](#ref-type)).) +- `.i`: Sequence of [`$instruction`s](#instruction) to produce function references. +- `.m`: Element segment mode: + - `%pass`: Passive, elements copied manually via table initialization. + - `%decl`: Declarative, declares references which aren't copied to tables. + - `%acti`: Active, automatically copies element data to the table `.tab` at offset `.off` when the module is instantiated. + +Element segments store static data with which to populate [`$table`s](#table) when the module is initialized. + +### `$code-section` {#code-section} + +```hoon ++$ code-section + $+ code-section + (list code) +``` + +List of [`$code`](#code) entries. + +### `$code` {#code} + +```hoon ++$ code + $: locals=(list valtype) + =expression + == +``` + +Function implementation stored in binary format: +- `.locals`: List of local variables in this function. +- `.expression`: The function body as a sequence of instructions. (See [`$expression`](#expression).) + +### `$data-section` {#data-section} + +```hoon ++$ data-section (list data) +``` + +List of the module's [`$data`](#data) segments. + +### `$data` {#data} + +```hoon ++$ data + $% + [%acti off=const-instr b=octs] + [%pass b=octs] + == +``` + +Data segment for initializing the module's state. +- `%acti`: Active segments automatically copy `$octs` `.b` to the module's memory at offset `.off` during instantiation. +- `%pass`: Passive segments are copied manually when instructed. + +### `+datacnt-section` {#datacnt-section} + +```hoon +++ datacnt-section (unit @) +``` + +Data count section of the Wasm module, which may optionally contain the number of data segments ([`$data`](#data)) in the module. This allows validators to check the index validity of the data section before trying to access it. + +### `$opcode` {#opcode} + +```hoon ++$ opcode $? bin-opcodes-zero-args + bin-opcodes-one-arg + bin-opcodes-two-args + bin-opcodes-blocks + pseudo-opcode + == +``` + +Type union of all Wasm's binary instruction opcodes. + +### `$bin-opcodes-zero-args` {#bin-opcodes-zero-args} + +```hoon ++$ bin-opcodes-zero-args + $? +:: trap nop return drop select wrap demote promote + %0x0 %0x1 %0xf %0x1a %0x1b %0xa7 %0xb6 %0xbb +:: + eqz-opcodes eq-opcodes ne-opcodes lt-opcodes gt-opcodes le-opcodes + ge-opcodes clz-opcodes ctz-opcodes popcnt-opcodes add-opcodes + sub-opcodes mul-opcodes div-opcodes rem-opcodes and-opcodes or-opcodes + xor-opcodes shl-opcodes shr-opcodes rotl-opcodes rotr-opcodes + abs-opcodes neg-opcodes ceil-opcodes floor-opcodes trunc-opcodes + nearest-opcodes sqrt-opcodes min-opcodes max-opcodes copysign-opcodes + extend-opcodes convert-opcodes reinterpret-opcodes + == +``` + +Wasm binary instruction opcodes that take no immediate arguments. + +A few simple opcodes are defined here: +- `%0x0`: `unreachable` - causes an unconditional trap when executed. +- `%0x1`: `nop` - no-op, do nothing. +- `%0xf`: `return` - return from current function with values from stack. + - If there's nothing in the stack to return, this returns nothing. + - If there are more values on the stack than allowed by the function's return type, the first $$n$$ values are returned (where $$n$$ is the number of values allowed) and the rest are discarded. +- `%0x1a`: `drop` - remove top value from stack. +- `%0x1b`: `select` - choose between two values based on condition. +- `%0xa7`: `i64.wrap_i32` - convert `i64` to `i32` by wrapping (truncating high bits). +- `%0xb6`: `f32.demote_f64` - convert `f64` to `f32`. +- `%0xbb`: `f64.promote_f32` - convert `f32` to `f64`. + +### `$pseudo-opcode` {#pseudo-opcode} + +```hoon ++$ pseudo-opcode ?(%0x5 %0xb) :: else, end +``` + +Pseudo-opcodes for control flow constructs: +- `%0x5`: `else` - marks the else branch of an if block. +- `%0xb`: `end` - terminates blocks, loops, if-statements, and functions. + +### `$bin-opcodes-one-arg` {#bin-opcodes-one-arg} + +```hoon ++$ bin-opcodes-one-arg + $? +:: br br_if call local.get local.set local.tee global.get global.set + %0xc %0xd %0x10 %0x20 %0x21 %0x22 %0x23 %0x24 +:: + const-opcodes + %0x3f :: memory.size + %0x40 :: memory.grow + == +``` + +Instructions taking one immediate argument: +- `%0xc`: `br` - unconditional branch to label. +- `%0xd`: `br_if` - conditional branch to label. +- `%0x10`: `call` - invoke function by index. +- `%0x20`: `local.get` - read local variable. +- `%0x21`: `local.set` - write local variable. +- `%0x22`: `local.tee` - write local variable and return value. +- `%0x23`: `global.get` - read global variable. +- `%0x24`: `global.set` - write global variable. +- `%0x3f`: `memory.size` - query memory size. +- `%0x40`: `memory.grow` - grow memory by given delta. +- [`$const-opcodes`](#const-opcodes): constants for `i32`, `i64`, `f32`, `f64`. + +### `$bin-opcodes-two-args` {#bin-opcodes-two-args} + +```hoon ++$ bin-opcodes-two-args + $? + %0xe :: br_table + %0x11 :: call_indirect + load-opcodes + store-opcodes + == +``` + +Instructions taking two immediate arguments: +- `%0xe`: `br_table` - branch table for switch-like control flow. +- `%0x11`: `call_indirect` - invoke function indirectly through table. +- [`$load-opcodes`](#load-opcodes): memory load operations with alignment and offset. +- [`$store-opcodes`](#store-opcodes): memory store operations with alignment and offset. + +### `$bin-opcodes-blocks` {#bin-opcodes-blocks} + +```hoon ++$ bin-opcodes-blocks + $? + %0x2 :: block + %0x3 :: loop + %0x4 :: if + == +``` + +Structured control flow instructions that create blocks with labels: +- `%0x2`: `block` - creates a block construct that can be targeted by branch instructions. +- `%0x3`: `loop` - creates a loop construct where branches target the beginning of the block. +- `%0x4`: `if` - creates a conditional block that executes based on a condition value. + +### `$const-opcodes` {#const-opcodes} + +```hoon ++$ const-opcodes + $? + %0x41 :: i32 + %0x42 :: i64 + %0x43 :: f32 + %0x44 :: f64 + == +``` + +Constant value instructions that push literal values onto the stack: +- `%0x41`: `i32.const` - push 32-bit integer constant. +- `%0x42`: `i64.const` - push 64-bit integer constant. +- `%0x43`: `f32.const` - push 32-bit float constant. +- `%0x44`: `f64.const` - push 64-bit float constant. + +### `$load-opcodes` {#load-opcodes} + +```hoon ++$ load-opcodes + $? + %0x28 :: i32 + %0x29 :: i64 + %0x2a :: f32 + %0x2b :: f64 + %0x2c :: i32 8 s + %0x2d :: i32 8 u + %0x2e :: i32 16 s + %0x2f :: i32 16 u + %0x30 :: i64 8 s + %0x31 :: i64 8 u + %0x32 :: i64 16 s + %0x33 :: i64 16 u + %0x34 :: i64 32 s + %0x35 :: i64 32 u + == +``` + +Memory load instructions that read values from linear memory: +- `%0x28`: `i32.load` - load 32-bit integer. +- `%0x29`: `i64.load` - load 64-bit integer. +- `%0x2a`: `f32.load` - load 32-bit float. +- `%0x2b`: `f64.load` - load 64-bit float. +- `%0x2c`: `i32.load8_s` - load 8-bit signed, extend to `i32`. +- `%0x2d`: `i32.load8_u` - load 8-bit unsigned, extend to `i32`. +- `%0x2e`: `i32.load16_s` - load 16-bit signed, extend to `i32`. +- `%0x2f`: `i32.load16_u` - load 16-bit unsigned, extend to `i32`. +- `%0x30`: `i64.load8_s` - load 8-bit signed, extend to `i64`. +- `%0x31`: `i64.load8_u` - load 8-bit unsigned, extend to `i64`. +- `%0x32`: `i64.load16_s` - load 16-bit signed, extend to `i64`. +- `%0x33`: `i64.load16_u` - load 16-bit unsigned, extend to `i64`. +- `%0x34`: `i64.load32_s` - load 32-bit signed, extend to `i64`. +- `%0x35`: `i64.load32_u` - load 32-bit unsigned, extend to `i64`. + +### `$store-opcodes` {#store-opcodes} + +```hoon ++$ store-opcodes + $? + %0x36 :: i32 + %0x37 :: i64 + %0x38 :: f32 + %0x39 :: f64 + %0x3a :: i32 8 + %0x3b :: i32 16 + %0x3c :: i64 8 + %0x3d :: i64 16 + %0x3e :: i64 32 + == +``` + +Memory store instructions that write values to linear memory: +- `%0x36`: `i32.store` - store 32-bit integer. +- `%0x37`: `i64.store` - store 64-bit integer. +- `%0x38`: `f32.store` - store 32-bit float. +- `%0x39`: `f64.store` - store 64-bit float. +- `%0x3a`: `i32.store8` - store lower 8 bits of `i32`. +- `%0x3b`: `i32.store16` - store lower 16 bits of `i32`. +- `%0x3c`: `i64.store8` - store lower 8 bits of `i64`. +- `%0x3d`: `i64.store16` - store lower 16 bits of `i64`. +- `%0x3e`: `i64.store32` - store lower 32 bits of `i64`. + +### `$eqz-opcodes` {#eqz-opcodes} + +```hoon ++$ eqz-opcodes ?(%0x45 %0x50) :: i32, i64 +``` + +Test if value equals zero: +- `%0x45`: `i32.eqz` - test if `i32` equals zero. +- `%0x50`: `i64.eqz` - test if `i64` equals zero. + +### `$eq-opcodes` {#eq-opcodes} + +```hoon ++$ eq-opcodes ?(%0x46 %0x51 %0x5b %0x61) :: i32, i64, f32, f64 +``` + +Test if two values are equal: +- `%0x46`: `i32.eq` - `i32` equality comparison. +- `%0x51`: `i64.eq` - `i64` equality comparison. +- `%0x5b`: `f32.eq` - `f32` equality comparison. +- `%0x61`: `f64.eq` - `f64` equality comparison. + +### `$ne-opcodes` {#ne-opcodes} + +```hoon ++$ ne-opcodes ?(%0x47 %0x52 %0x5c %0x62) :: i32, i64, f32, f64 +``` + +Test if two values are not equal: +- `%0x47`: `i32.ne` - `i32` inequality comparison. +- `%0x52`: `i64.ne` - `i64` inequality comparison. +- `%0x5c`: `f32.ne` - `f32` inequality comparison. +- `%0x62`: `f64.ne` - `f64` inequality comparison. + +### `$lt-opcodes` {#lt-opcodes} + +```hoon ++$ lt-opcodes + $? + %0x48 :: i32 s + %0x49 :: i32 u + %0x53 :: i64 s + %0x54 :: i64 u + %0x5d :: f32 + %0x63 :: f64 + == +``` + +Test if first value is less than second: +- `%0x48`: `i32.lt_s` - `i32` signed less-than. +- `%0x49`: `i32.lt_u` - `i32` unsigned less-than. +- `%0x53`: `i64.lt_s` - `i64` signed less-than. +- `%0x54`: `i64.lt_u` - `i64` unsigned less-than. +- `%0x5d`: `f32.lt` - `f32` less-than. +- `%0x63`: `f64.lt` - `f64` less-than. + +### `$gt-opcodes` {#gt-opcodes} + +```hoon ++$ gt-opcodes + $? + %0x4a :: i32 s + %0x4b :: i32 u + %0x55 :: i64 s + %0x56 :: i64 u + %0x5e :: f32 + %0x64 :: f64 + == +``` + +Test if first value is greater than second: +- `%0x4a`: `i32.gt_s` - `i32` signed greater-than. +- `%0x4b`: `i32.gt_u` - `i32` unsigned greater-than. +- `%0x55`: `i64.gt_s` - `i64` signed greater-than. +- `%0x56`: `i64.gt_u` - `i64` unsigned greater-than. +- `%0x5e`: `f32.gt` - `f32` greater-than. +- `%0x64`: `f64.gt` - `f64` greater-than. + +### `$le-opcodes` {#le-opcodes} + +```hoon ++$ le-opcodes + $? + %0x4c :: i32 s + %0x4d :: i32 u + %0x57 :: i64 s + %0x58 :: i64 u + %0x5f :: f32 + %0x65 :: f64 + == +``` + +Test if first value is less than or equal to second: +- `%0x4c`: `i32.le_s` - `i32` signed less-than-or-equal. +- `%0x4d`: `i32.le_u` - `i32` unsigned less-than-or-equal. +- `%0x57`: `i64.le_s` - `i64` signed less-than-or-equal. +- `%0x58`: `i64.le_u` - `i64` unsigned less-than-or-equal. +- `%0x5f`: `f32.le` - `f32` less-than-or-equal. +- `%0x65`: `f64.le` - `f64` less-than-or-equal. + +### `$ge-opcodes` {#ge-opcodes} + +```hoon ++$ ge-opcodes + $? + %0x4e :: i32 s + %0x4f :: i32 u + %0x59 :: i64 s + %0x5a :: i64 u + %0x60 :: f32 + %0x66 :: f64 + == +``` + +Test if first value is greater than or equal to second: +- `%0x4e`: `i32.ge_s` - `i32` signed greater-than-or-equal. +- `%0x4f`: `i32.ge_u` - `i32` unsigned greater-than-or-equal. +- `%0x59`: `i64.ge_s` - `i64` signed greater-than-or-equal. +- `%0x5a`: `i64.ge_u` - `i64` unsigned greater-than-or-equal. +- `%0x60`: `f32.ge` - `f32` greater-than-or-equal. +- `%0x66`: `f64.ge` - `f64` greater-than-or-equal. + +### `$clz-opcodes` {#clz-opcodes} + +```hoon ++$ clz-opcodes ?(%0x67 %0x79) :: i32, i64 +``` + +Count leading zeros: +- `%0x67`: `i32.clz` - count leading zeros in `i32`. +- `%0x79`: `i64.clz` - count leading zeros in `i64`. + +### `$ctz-opcodes` {#ctz-opcodes} + +```hoon ++$ ctz-opcodes ?(%0x68 %0x7a) :: i32, i64 +``` + +Count trailing zeros: +- `%0x68`: `i32.ctz` - count trailing zeros in `i32`. +- `%0x7a`: `i64.ctz` - count trailing zeros in `i64`. + +### `$popcnt-opcodes` {#popcnt-opcodes} + +```hoon ++$ popcnt-opcodes ?(%0x69 %0x7b) :: i32, i64 +``` + +Count number of set bits (population count): +- `%0x69`: `i32.popcnt` - count set bits in `i32`. +- `%0x7b`: `i64.popcnt` - count set bits in `i64`. + +### `$add-opcodes` {#add-opcodes} + +```hoon ++$ add-opcodes ?(%0x6a %0x7c %0x92 %0xa0) :: i32, i64, f32, f64 +``` + +Addition operations: +- `%0x6a`: `i32.add` - `i32` addition. +- `%0x7c`: `i64.add` - `i64` addition. +- `%0x92`: `f32.add` - `f32` addition. +- `%0xa0`: `f64.add` - `f64` addition. + +### `$sub-opcodes` {#sub-opcodes} + +```hoon ++$ sub-opcodes ?(%0x6b %0x7d %0x93 %0xa1) :: i32, i64, f32, f64 +``` + +Subtraction operations: +- `%0x6b`: `i32.sub` - `i32` subtraction. +- `%0x7d`: `i64.sub` - `i64` subtraction. +- `%0x93`: `f32.sub` - `f32` subtraction. +- `%0xa1`: `f64.sub` - `f64` subtraction. + +### `$mul-opcodes` {#mul-opcodes} + +```hoon ++$ mul-opcodes ?(%0x6c %0x7e %0x94 %0xa2) :: i32, i64, f32, f64 +``` + +Multiplication operations: +- `%0x6c`: `i32.mul` - `i32` multiplication. +- `%0x7e`: `i64.mul` - `i64` multiplication. +- `%0x94`: `f32.mul` - `f32` multiplication. +- `%0xa2`: `f64.mul` - `f64` multiplication. + +### `$div-opcodes` {#div-opcodes} + +```hoon ++$ div-opcodes + $? + %0x6d :: i32 s + %0x6e :: i32 u + %0x7f :: i64 s + %0x80 :: i64 u + %0x95 :: f32 + %0xa3 :: f64 + == +``` + +Division operations: +- `%0x6d`: `i32.div_s` - `i32` signed division. +- `%0x6e`: `i32.div_u` - `i32` unsigned division. +- `%0x7f`: `i64.div_s` - `i64` signed division. +- `%0x80`: `i64.div_u` - `i64` unsigned division. +- `%0x95`: `f32.div` - `f32` division. +- `%0xa3`: `f64.div` - `f64` division. + +### `$rem-opcodes` {#rem-opcodes} + +```hoon ++$ rem-opcodes + $? + %0x6f :: i32 s + %0x70 :: i32 u + %0x81 :: i64 s + %0x82 :: i64 u + == +``` + +Remainder operations: +- `%0x6f`: `i32.rem_s` - `i32` signed remainder. +- `%0x70`: `i32.rem_u` - `i32` unsigned remainder. +- `%0x81`: `i64.rem_s` - `i64` signed remainder. +- `%0x82`: `i64.rem_u` - `i64` unsigned remainder. + +### `$and-opcodes` {#and-opcodes} + +```hoon ++$ and-opcodes ?(%0x71 %0x83) :: i32, i64 +``` + +Bitwise AND operations: +- `%0x71`: `i32.and` - `i32` bitwise AND. +- `%0x83`: `i64.and` - `i64` bitwise AND. + +### `$or-opcodes` {#or-opcodes} + +```hoon ++$ or-opcodes ?(%0x72 %0x84) :: i32, i64 +``` + +Bitwise OR operations: +- `%0x72`: `i32.or` - `i32` bitwise OR. +- `%0x84`: `i64.or` - `i64` bitwise OR. + +### `$xor-opcodes` {#xor-opcodes} + +```hoon ++$ xor-opcodes ?(%0x73 %0x85) :: i32, i64 +``` + +Bitwise XOR operations: +- `%0x73`: `i32.xor` - `i32` bitwise exclusive OR. +- `%0x85`: `i64.xor` - `i64` bitwise exclusive OR. + +### `$shl-opcodes` {#shl-opcodes} + +```hoon ++$ shl-opcodes ?(%0x74 %0x86) :: i32, i64 +``` + +Bitwise left shift operations: +- `%0x74`: `i32.shl` - `i32` shift left. +- `%0x86`: `i64.shl` - `i64` shift left. + +### `$shr-opcodes` {#shr-opcodes} + +```hoon ++$ shr-opcodes + $? + %0x75 :: i32 s + %0x76 :: i32 u + %0x87 :: i64 s + %0x88 :: i64 u + == +``` + +Bitwise right shift operations: +- `%0x75`: `i32.shr_s` - `i32` arithmetic right shift (sign-extending). +- `%0x76`: `i32.shr_u` - `i32` logical right shift (zero-filling). +- `%0x87`: `i64.shr_s` - `i64` arithmetic right shift (sign-extending). +- `%0x88`: `i64.shr_u` - `i64` logical right shift (zero-filling). + +### `$rotl-opcodes` {#rotl-opcodes} + +```hoon ++$ rotl-opcodes ?(%0x77 %0x89) :: i32, i64 +``` + +Rotate left operations: +- `%0x77`: `i32.rotl` - `i32` rotate left. +- `%0x89`: `i64.rotl` - `i64` rotate left. + +### `$rotr-opcodes` {#rotr-opcodes} + +```hoon ++$ rotr-opcodes ?(%0x78 %0x8a) :: i32, i64 +``` + +Rotate right operations: +- `%0x78`: `i32.rotr` - `i32` rotate right. +- `%0x8a`: `i64.rotr` - `i64` rotate right. + +### `$abs-opcodes` {#abs-opcodes} + +```hoon ++$ abs-opcodes ?(%0x8b %0x99) :: f32, f64 +``` + +Floating-point absolute value operations: +- `%0x8b`: `f32.abs` - `f32` absolute value. +- `%0x99`: `f64.abs` - `f64` absolute value. + +### `$neg-opcodes` {#neg-opcodes} + +```hoon ++$ neg-opcodes ?(%0x8c %0x9a) :: f32, f64 +``` + +Floating-point negation operations: +- `%0x8c`: `f32.neg` - `f32` negation. +- `%0x9a`: `f64.neg` - `f64` negation. + +### `$ceil-opcodes` {#ceil-opcodes} + +```hoon ++$ ceil-opcodes ?(%0x8d %0x9b) :: f32, f64 +``` + +Floating-point ceiling operations (round up to nearest integer): +- `%0x8d`: `f32.ceil` - `f32` ceiling. +- `%0x9b`: `f64.ceil` - `f64` ceiling. + +### `$floor-opcodes` {#floor-opcodes} + +```hoon ++$ floor-opcodes ?(%0x8e %0x9c) :: f32, f64 +``` + +Floating-point floor operations (round down to nearest integer): +- `%0x8e`: `f32.floor` - `f32` floor. +- `%0x9c`: `f64.floor` - `f64` floor. + +### `$trunc-opcodes` {#trunc-opcodes} + +```hoon ++$ trunc-opcodes + $? + %0x8f :: f32 + %0x9d :: f64 + %0xa8 :: f32 -> i32 s + %0xa9 :: f32 -> i32 u + %0xaa :: f64 -> i32 s + %0xab :: f64 -> i32 u + %0xae :: f32 -> i64 s + %0xaf :: f32 -> i64 u + %0xb0 :: f64 -> i64 s + %0xb1 :: f64 -> i64 u + == +``` + +Truncation operations (remove fractional part): +- `%0x8f`: `f32.trunc` - `f32` truncate toward zero. +- `%0x9d`: `f64.trunc` - `f64` truncate toward zero. +- `%0xa8`: `i32.trunc_f32_s` - convert `f32` to signed `i32` (truncate). +- `%0xa9`: `i32.trunc_f32_u` - convert `f32` to unsigned `i32` (truncate). +- `%0xaa`: `i32.trunc_f64_s` - convert `f64` to signed `i32` (truncate). +- `%0xab`: `i32.trunc_f64_u` - convert `f64` to unsigned `i32` (truncate). +- `%0xae`: `i64.trunc_f32_s` - convert `f32` to signed `i64` (truncate). +- `%0xaf`: `i64.trunc_f32_u` - convert `f32` to unsigned `i64` (truncate). +- `%0xb0`: `i64.trunc_f64_s` - convert `f64` to signed `i64` (truncate). +- `%0xb1`: `i64.trunc_f64_u` - convert `f64` to unsigned `i64` (truncate). + +### `$nearest-opcodes` {#nearest-opcodes} + +```hoon ++$ nearest-opcodes ?(%0x90 %0x9e) :: f32, f64 +``` + +Floating-point nearest integer operations (round to nearest, ties to even): +- `%0x90`: `f32.nearest` - `f32` round to nearest integer. +- `%0x9e`: `f64.nearest` - `f64` round to nearest integer. + +### `$sqrt-opcodes` {#sqrt-opcodes} + +```hoon ++$ sqrt-opcodes ?(%0x91 %0x9f) :: f32, f64 +``` + +Floating-point square root operations: +- `%0x91`: `f32.sqrt` - `f32` square root. +- `%0x9f`: `f64.sqrt` - `f64` square root. + +### `$min-opcodes` {#min-opcodes} + +```hoon ++$ min-opcodes ?(%0x96 %0xa4) :: f32, f64 +``` + +Floating-point minimum operations: +- `%0x96`: `f32.min` - `f32` minimum of two values. +- `%0xa4`: `f64.min` - `f64` minimum of two values. + +### `$max-opcodes` {#max-opcodes} + +```hoon ++$ max-opcodes ?(%0x97 %0xa5) :: f32, f64 +``` + +Floating-point maximum operations: +- `%0x97`: `f32.max` - `f32` maximum of two values. +- `%0xa5`: `f64.max` - `f64` maximum of two values. + +### `$copysign-opcodes` {#copysign-opcodes} + +```hoon ++$ copysign-opcodes ?(%0x98 %0xa6) :: f32, f64 +``` + +Floating-point copy sign operations (combine magnitude of first value with sign of second): +- `%0x98`: `f32.copysign` - `f32` copy sign. +- `%0xa6`: `f64.copysign` - `f64` copy sign. + +### `$extend-opcodes` {#extend-opcodes} + +```hoon ++$ extend-opcodes + $? + %0xac :: i32 -> i64 s + %0xad :: i32 -> i64 u + %0xc0 :: i8 -> i32 s + %0xc1 :: i16 -> i32 s + %0xc2 :: i8 -> i64 s + %0xc3 :: i16 -> i64 s + %0xc4 :: i32 -> i64 s ?? same as 0xac??? (yes) + == +``` + +Integer extension operations (widen integer types): +- `%0xac`: `i64.extend_i32_s` - extend signed `i32` to `i64`. +- `%0xad`: `i64.extend_i32_u` - extend unsigned `i32` to `i64`. +- `%0xc0`: `i32.extend8_s` - extend signed `i8` to `i32`. +- `%0xc1`: `i32.extend16_s` - extend signed `i16` to `i32`. +- `%0xc2`: `i64.extend8_s` - extend signed `i8` to `i64`. +- `%0xc3`: `i64.extend16_s` - extend signed `i16` to `i64`. +- `%0xc4`: `i64.extend32_s` - extend signed `i32` to `i64` (same as `%0xac`). + +### `$convert-opcodes` {#convert-opcodes} + +```hoon ++$ convert-opcodes + $? + %0xb2 :: i32 s -> f32 + %0xb3 :: i32 u -> f32 + %0xb4 :: i64 s -> f32 + %0xb5 :: i64 u -> f32 + %0xb7 :: i32 s -> f64 + %0xb8 :: i32 u -> f64 + %0xb9 :: i64 s -> f64 + %0xba :: i64 u -> f64 + == +``` + +Type conversion operations (integer to floating-point): +- `%0xb2`: `f32.convert_i32_s` - convert signed `i32` to `f32`. +- `%0xb3`: `f32.convert_i32_u` - convert unsigned `i32` to `f32`. +- `%0xb4`: `f32.convert_i64_s` - convert signed `i64` to `f32`. +- `%0xb5`: `f32.convert_i64_u` - convert unsigned `i64` to `f32`. +- `%0xb7`: `f64.convert_i32_s` - convert signed `i32` to `f64`. +- `%0xb8`: `f64.convert_i32_u` - convert unsigned `i32` to `f64`. +- `%0xb9`: `f64.convert_i64_s` - convert signed `i64` to `f64`. +- `%0xba`: `f64.convert_i64_u` - convert unsigned `i64` to `f64`. + +### `$reinterpret-opcodes` {#reinterpret-opcodes} + +```hoon ++$ reinterpret-opcodes + $? + %0xbc :: f32 -> i32 + %0xbd :: f64 -> i64 + %0xbe :: i32 -> f32 + %0xbf :: i64 -> f64 + == +``` + +Type reinterpretation operations (reinterpret bit pattern without conversion): +- `%0xbc`: `i32.reinterpret_f32` - reinterpret `f32` bits as `i32`. +- `%0xbd`: `i64.reinterpret_f64` - reinterpret `f64` bits as `i64`. +- `%0xbe`: `f32.reinterpret_i32` - reinterpret `i32` bits as `f32`. +- `%0xbf`: `f64.reinterpret_i64` - reinterpret `i64` bits as `f64`. + diff --git a/content/urbit-os/base/wasm/wasm-interpreter-data-types.md b/content/urbit-os/base/wasm/wasm-interpreter-data-types.md new file mode 100644 index 00000000..0680f029 --- /dev/null +++ b/content/urbit-os/base/wasm/wasm-interpreter-data-types.md @@ -0,0 +1,279 @@ +--- +description: "Types for Wasm code in its executable form" +layout: + title: + visible: true + description: + visible: false + tableOfContents: + visible: true + outline: + visible: true + pagination: + visible: true +--- + +# Wasm Interpreter Data Types + +UrWasm's `+engine-sur` types represent Wasm modules in their executable form. While `+wasm-sur` implements the WebAssembly Core Specification, `+engine-sur` provides the runtime representation needed for execution, with some optimizations. + +### `+engine-sur` {#engine-sur} + +```hoon +++ engine-sur + =, wasm-sur + |% + :: ... +:: ... +``` + +Wrapper arm around the engine types, making them addressable by limb resolution paths like `module:engine-sur`. Also exposes the `+wasm-sur` namespace so it can refer to types like `$type-section` directly. + +### `$module` {#module} + +```hoon ++$ module + $: + =type-section + :: + =import-section :: Changed + =function-section :: Changed to include code + :: + =table-section + =memory-section + =global-section + =export-section + =start-section + =elem-section + :: =datacnt-section :: Removed + :: =code-section :: Code moved to function section + =data-section + == +``` + +Wasm [`$module`](./wasm-data-types.md#module) as seen by the engine. Some sections have been removed or modified for efficiency: +- [`$code-section`](./wasm-data-types.md#code-section) is merged into the function section. +- [`$datacnt-section`](./wasm-data-types.md#datacnt-section) is removed as it's not needed during execution. +- [`$import-section`](./wasm-data-types.md#import-section) is restructured for runtime resolution. + +### `$function` {#function} + +```hoon ++$ function + $: type-id=@ + locals=(list valtype) + =expression + == +``` + +This includes both the function signature and its implementation merged together: +- `.type-id`: Index into the [`$type-section`](./wasm-data-types.md#type-section) for this function's signature. +- `.locals`: Local variables for this function. +- [`.expression`](./wasm-data-types.md#expression): The function body as a sequence of [`$instruction`s](./wasm-data-types.md#instruction). + +### `$function-section` {#function-section} + +```hoon ++$ function-section + (list function) +``` + +List of [`$function`s](#function) in the [`$module`](#module). + +### `$import-section` {#import-section} + +```hoon ++$ import-section + $: + funcs=(list [[mod=cord name=cord] type-id=@]) + tables=(list [[mod=cord name=cord] t=table]) + memos=(list [[mod=cord name=cord] l=limits]) + globs=(list [[mod=cord name=cord] v=valtype m=?(%con %var)]) + == +``` + +Restructured import section organized by import type for efficient runtime resolution: +- `.funcs`: Imported functions with module name, export name, and type signature by index. +- `.tables`: Imported [`$table`s](./wasm-data-types.md#table) with their specifications. +- `.memos`: Imported memories with size [`$limits`](./wasm-data-types.md#limits). +- `.globs`: Imported global variables with [value type](./wasm-data-types.md#valtype) and mutability. + +### `$store` {#store} + +```hoon ++$ store + $: =shop :: resolved imports + =module :: engine representation of module + mem=(unit [buffer=@ n-pages=@]) :: single membuffer + tables=(list (list $>(%ref coin-wasm))) :: tables + globals=(list coin-wasm) + == +``` + +Complete module store containing: +- `.shop`: Resolved import dependencies (see [`$shop`](#shop)). +- `.module`: The engine's [`$module`](#module). +- `.mem`: Optional memory buffer with page count. +- `.tables`: Table instances containing function references. +- `.globals`: Global variable values. + +### `$local-state` {#local-state} + +```hoon ++$ local-state [=stack locals=(list val) =store] +``` + +Execution state for a function call: +- `.stack`: Current execution stack (see [`$stack`](#stack)). +- `.locals`: Local variable values for current function. +- `.store`: Module store containing all persistent state. + +### `$stack` {#stack} + +```hoon ++$ stack [br=branch va=(pole val)] +``` + +Execution stack with: +- `.br`: Current branch state for control flow (see [`$branch`](#branch)). +- `.va`: Stack of values. + +### `$val` {#val} + +```hoon ++$ val + $~ `@`0 + $@ @ :: numerical value + $>(%ref coin-wasm) :: reference +``` + +Runtime value on the stack, which can be either: +- A raw atom. +- A typed reference. + +### `$branch` {#branch} + +```hoon ++$ branch + $~ ~ + $@ ~ + $% [%retr ~] :: return to the caller + [%targ i=@] :: targeted block + [%trap ~] :: deterministic crash + :: + $: %bloq :: blocked on import request + [[mod=cord name=cord] =request] + =module + mem=(unit [buffer=@ n-pages=@]) :: single membuffer + tables=(list (list $>(%ref coin-wasm))) :: tables + globals=(list coin-wasm) + == == +``` + +Branch state for control flow management: +- `~`: Normal execution (no branch pending). +- `%retr`: Return to caller function. +- `%targ`: Branch to specific block label `.i`. +- `%trap`: Execution trapped (deterministic crash). +- `%bloq`: Blocked waiting for import resolution, containing the [`$request`](#request) details and current execution state. + +### `$result` {#result} + +```hoon ++$ result + $% + [%0 out=(list coin-wasm) st=store] :: success + :: + $: %1 :: block + [[mod=cord name=cord] =request] :: /module/name, request + =module + mem=(unit [buffer=@ n-pages=@]) + tables=(list (list $>(%ref coin-wasm))) + globals=(list coin-wasm) + == + :: + [%2 st=store] :: trap, crash + == +``` + +Result of module instantiation or function invocation: +- `%0`: Successful execution with output values and updated [`$store`](#store). +- `%1`: Blocked on import [`$request`](#request), containing request details and current state for resumption. +- `%2`: Crashed with final `$store` state. + +### `$request` {#request} + +```hoon ++$ request + $% + [%func args=(list coin-wasm)] :: typed values for the call + :: + $: %tabl + args=(list coin-wasm) + $= instr + $~ [%table-size `@`0] + $> + $? %call-indirect %table-get + %table-set %table-init + %table-copy %table-grow + %table-size %table-fill + == + instr-short + == + :: + $: %memo + args=(list coin-wasm) + $= instr + $~ [%memory-size %0] + $? $> $? %load %store + %memory-size %memory-grow + %memory-init %memory-copy + %memory-fill + == + instr-short + :: + $: %vec + $> $? %load %load-lane + %store %store-lane + == + instr-vec + == == == + :: + $: %glob + args=(list coin-wasm) + $= instr + $~ [%global-get `@`0] + $>(?(%global-get %global-set) instr-short) + == + == +``` + +Import request types: +- `%func`: Function call with typed argument values. +- `%tabl`: Table operation with arguments and specific table instruction. +- `%memo`: Memory operation, including vector operations and standard memory instructions. +- `%glob`: Global variable. + +### `$shop` {#shop} + +```hoon ++$ shop (list item) +``` + +List of resolved import [`$item`s](#item) representing available external dependencies. + +### `$item` {#item} + +```hoon ++$ item + %+ pair (list coin-wasm) + $: =module + mem=(unit [buffer=@ n-pages=@]) :: single membuffer + tables=(list (list $>(%ref coin-wasm))) :: tables + globals=(list coin-wasm) + == +``` + +Resolved import item containing: +- A list of values to push onto the stack when this import is invoked. +- Complete module state including memory, tables, and globals that this import provides access to.