diff --git a/examples/cont-stack/Shakefile.hs b/examples/cont-stack/Shakefile.hs new file mode 100644 index 0000000..612943b --- /dev/null +++ b/examples/cont-stack/Shakefile.hs @@ -0,0 +1,21 @@ +import Development.Shake +import Development.Shake.Command +import Development.Shake.FilePath +import Development.Shake.Util + +main = shakeArgs shakeOptions $ do + -- action $ do + -- sourceFiles <- getDirectoryFiles "" ["//*.luasm"] + -- need $ (-<.> "luac") <$> sourceFiles + want ["cont-stack.wasm"] + alternatives $ do + "*.wasm" %> \out -> do + let wat = out -<.> "wat" + need [wat] + cmd_ "wasm-tools parse" [wat] "-o" [out] + phony "run" $ do + need ["cont-stack.wasm"] + cmd_ "./run.js" + phony "clean" $ do + removeFilesAfter ".shake" ["//*"] + liftIO $ removeFiles "." ["*.wasm"] diff --git a/examples/cont-stack/cont-stack.wat b/examples/cont-stack/cont-stack.wat new file mode 100644 index 0000000..5a2d56f --- /dev/null +++ b/examples/cont-stack/cont-stack.wat @@ -0,0 +1,84 @@ +(module + ;; import console.log. + (func $print (import "guppy" "print") (param i32)) + + ;; declare a table with a reference to $halt in it. idk why, but this +is ;; necessary to use funcrefs lmfao. + (table 2 funcref) + (elem (i32.const 0) $halt) + + ;; an array of return continuations. + (type $cont (func (param i32))) + (type $cont-stack-type (array (mut (ref null $cont)))) + (global $cont-stack (ref $cont-stack-type) + (array.new_default $cont-stack-type (i32.const 128))) + (global $cont-stack-top (mut i32) (i32.const 0)) + + ;; a global array on which we pass arguments. + (type $arg-array-type (array (mut (ref null eq)))) + (global $arg-array (ref $arg-array-type) + (array.new_default $arg-array-type (i32.const 32))) + + ;; cps'd function. takes the number of scheme arguments as the sole + ;; wasm argument. arguments are read into locals from + ;; $arg-array. does not return, instead popping a continuation from + ;; $cont-stack and tail-calling it. + (func $add (param $nargs i32) + (local $x (ref eq)) + (local $y (ref eq)) + (local $return (ref $cont)) + (local.set $x (ref.as_non_null + (array.get $arg-array-type + (global.get $arg-array) + (i32.const 0)))) + (local.set $y (ref.as_non_null + (array.get $arg-array-type + (global.get $arg-array) + (i32.const 1)))) + (array.set $arg-array-type + (global.get $arg-array) + (i32.const 0) + (ref.i31 + (i32.add (i31.get_s (ref.cast (ref i31) (local.get $x))) + (i31.get_s (ref.cast (ref i31) (local.get $y)))))) + (return_call_ref + $cont + (i32.const 1) + (block (result (ref $cont)) + (ref.as_non_null + (array.get $cont-stack-type + (global.get $cont-stack) + (global.get $cont-stack-top))) + (global.set $cont-stack-top + (i32.sub (global.get $cont-stack-top) + (i32.const 1)))))) + ;; for demo purposes, this continuation calls console.log and stops. + (func $halt (param $nargs i32) + (call $print + (i31.get_s + (ref.cast + (ref i31) + (ref.as_non_null + (array.get $arg-array-type + (global.get $arg-array) + (i32.const 0))))))) + ;; entry point }:3 + (func (export "main") + ;; push args + (array.set $arg-array-type + (global.get $arg-array) + (i32.const 0) + (ref.i31 (i32.const 4))) + (array.set $arg-array-type + (global.get $arg-array) + (i32.const 1) + (ref.i31 (i32.const 5))) + ;; push return continuation + (array.set $cont-stack-type + (global.get $cont-stack) + (i32.const 0) + (ref.func $halt)) + ;; make call }:) + (return_call $add + ;; inform $add how many arguments we called it with + (i32.const 2)))) diff --git a/examples/cont-stack/run.js b/examples/cont-stack/run.js new file mode 100755 index 0000000..51fabc0 --- /dev/null +++ b/examples/cont-stack/run.js @@ -0,0 +1,14 @@ +#!/usr/bin/env node +const fs = require('node:fs'); + +const imports = { + guppy: { + print: (arg) => console.log (arg) + } +} + +const wasmBuffer = fs.readFileSync ('cont-stack.wasm'); +WebAssembly.instantiate (wasmBuffer, imports).then (wasmModule => { + const { main } = wasmModule.instance.exports; + main () +});