cont stack example

This commit is contained in:
2026-07-14 17:35:37 -06:00
parent cd56b176af
commit 42f17c0dac
3 changed files with 119 additions and 0 deletions
+21
View File
@@ -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"]
+84
View File
@@ -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))))
+14
View File
@@ -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 ()
});