-
Notifications
You must be signed in to change notification settings - Fork 19
Document UrWasm #253
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
bonbud-macryg
wants to merge
58
commits into
urbit:master
Choose a base branch
from
bonbud-macryg:bm/urwasm
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Document UrWasm #253
Changes from all commits
Commits
Show all changes
58 commits
Select commit
Hold shift + click to select a range
561fd08
Skeleton UrWASM docs
bonbud-macryg 1c7bc0c
Draft intro
bonbud-macryg 568b7cb
Draft overview
bonbud-macryg 8e4f98d
Skeleton examples section
bonbud-macryg 070de5d
Add UrWASM Structure to overview
bonbud-macryg a06cc32
Draft generator docs
bonbud-macryg f0c65fa
Add todo to generator
bonbud-macryg 6bce685
Change /ted to /gen
bonbud-macryg d046f44
Add l arg to dojo example
bonbud-macryg 1dd8b0c
Fix conclusion
bonbud-macryg 81a8f7e
UrWASM to UrWasm
bonbud-macryg ea76720
WASM to Wasm
bonbud-macryg e0c768c
Start Wasm data types
bonbud-macryg 607175a
Add atoms
bonbud-macryg 9a6cd8c
Break up Wasm reference section
bonbud-macryg 470b275
Add /sur links to Wasm reference
bonbud-macryg f05e2f5
Amend /sur/wasm
bonbud-macryg ca2036b
Moar draft Wasm data types
bonbud-macryg 5c87a41
Code wasm docs
bonbud-macryg 0bc20c7
Note on separate sections
bonbud-macryg b60ee4f
Draft instruction docs
bonbud-macryg 15db8bd
Draft binary opcodes docs
bonbud-macryg 3cdc641
Finish draft wasm data types
bonbud-macryg e20fa17
Draft /sur/engine docs
bonbud-macryg 15a35c1
Draft /sur/lia.hoon docs
bonbud-macryg e7589e5
Add /lib/wasm skeleton
bonbud-macryg 4d3f142
Ignore .claude
bonbud-macryg c68a266
Draft wasm-parser docs
bonbud-macryg dce96a5
Finish draft wasm parser
bonbud-macryg 6745080
Trim whitespace
bonbud-macryg e36d862
Draft wasm-validator docs
bonbud-macryg 930bcb2
Draft op-def docs
bonbud-macryg cccd2d8
Draft op-def docs
bonbud-macryg edd1d0e
Add jets to README index
bonbud-macryg 08b1344
Draft lib-wasm-lia docs
bonbud-macryg ff345b3
Draft Wasm engine docs
bonbud-macryg 792d12d
Restructure
bonbud-macryg 7e996fa
Draft new README
bonbud-macryg 06c4409
Generator TODOs
bonbud-macryg 53cfdb3
Better core notation in overview
bonbud-macryg 7829e7f
Expand on Lia
bonbud-macryg 3f1467f
Fix typo
bonbud-macryg 6b30b03
ABbreviated Core Descriptions
bonbud-macryg c7c1f82
Clarify Lia NaN handling
bonbud-macryg b4ee55e
Split elem parser H3s
bonbud-macryg 137d2b2
Fix binary opcode formatting
bonbud-macryg f22e38e
Remove last TODO
bonbud-macryg 71ac3c5
Fix intro
bonbud-macryg e99df0c
Clarify validator flops
bonbud-macryg 180e22b
Remove Wasm parser questions
bonbud-macryg c1b869c
Rename folders, update summary
bonbud-macryg bd8d87d
Move reference stuff to base docs
bonbud-macryg 4af426f
READMEs
bonbud-macryg fc2615b
Change overview title
bonbud-macryg 721f26b
Descriptions
bonbud-macryg d9525f0
Fix broken links
bonbud-macryg 61265a4
Fix some formatting
bonbud-macryg 83d78c2
Address the rest of the feedback
dozreg-toplud File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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. | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,295 @@ | ||
| --- | ||
| 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<u64>) -> Vec<u64> { | ||
| 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 %} | ||
|
dozreg-toplud marked this conversation as resolved.
|
||
|
|
||
| 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. | ||
|
dozreg-toplud marked this conversation as resolved.
|
||
|
|
||
| Below, we build Lia's `+run-once` gate and apply it to our imported `.wasm-bin` module, a pair of a noun accumulator and a map of imports, which will be just `[~ ~]`, an empty jet hint `%$` and finally the script itself. | ||
|
|
||
| ```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 [~ ~] | ||
|
dozreg-toplud marked this conversation as resolved.
|
||
| :: | ||
| %^ (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 concatenate 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. | ||
|
|
||
| ```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. | ||
|
dozreg-toplud marked this conversation as resolved.
|
||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.