Skip to content

Staged Miniwasm Interpreter #88

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

Merged
merged 62 commits into from
Jul 5, 2025
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
157aabf
try lms
butterunderflow Apr 22, 2025
a11a7ff
compose all parts
butterunderflow Apr 25, 2025
9408c85
Frame should be opaque
butterunderflow Apr 25, 2025
60c782b
function call
butterunderflow Apr 26, 2025
442d8d1
factor out getFuncType
butterunderflow Apr 26, 2025
ab679f3
fix: use restK when non-tail call
butterunderflow Apr 26, 2025
8ba8657
compile Block-like instructions(if-else, loop, block)
butterunderflow Apr 26, 2025
e2cc801
branching instructions
butterunderflow Apr 26, 2025
fa3d628
local set
butterunderflow Apr 27, 2025
d78a96b
operators
butterunderflow Apr 27, 2025
dac7e1c
global instructions
butterunderflow Apr 27, 2025
cc65f63
placeholder for mem instructions
butterunderflow Apr 27, 2025
cf3063a
scala code generation
butterunderflow Apr 27, 2025
80bfa68
some imported function
butterunderflow Apr 27, 2025
881eda4
polish
butterunderflow Apr 27, 2025
7a2bfd4
ci
butterunderflow Apr 27, 2025
294fcea
tweak
butterunderflow Apr 27, 2025
f450b5c
try some simplification
butterunderflow Apr 27, 2025
336eec5
improve runtime(the prelude)
butterunderflow Apr 27, 2025
6a666f3
some fixes
butterunderflow Apr 28, 2025
9947bec
fix: Frame creation is not optimizable
butterunderflow Apr 28, 2025
e7da823
demo br_table's attempts
butterunderflow Apr 29, 2025
2de28f5
fix: tail call
butterunderflow Apr 29, 2025
b5a69dc
fix global
ahuoguo Apr 29, 2025
de8f18e
fix: code generation for global.set
butterunderflow Apr 29, 2025
3bbd27e
brtable seems to work, but there is code duplication problem
Apr 29, 2025
a83eb06
effectful staged interpreter
butterunderflow May 4, 2025
b8a9aea
remove non-sense tests
butterunderflow May 4, 2025
b7b8786
scratch cpp backend
butterunderflow May 5, 2025
0a8339e
some tweaks
butterunderflow May 6, 2025
b4703c7
fix some of the nothing type
Kraks May 12, 2025
29acef0
manually supply the reflect's type arguments
butterunderflow May 13, 2025
67b077b
lift every function to top level & avoid lms's common subexpr elimina…
butterunderflow May 14, 2025
6e41521
stack pop example
butterunderflow May 14, 2025
9f04722
not inlining + shallow
Kraks May 15, 2025
ed9c8e4
an almost work runtime
butterunderflow May 17, 2025
d5ed20d
emit functions
butterunderflow May 17, 2025
4fb5424
read a dummy node to avoid lambda lifting
butterunderflow May 18, 2025
8e293b8
capture by value is not friendly with recursion
butterunderflow May 18, 2025
51dd632
redirect generated code to a file
butterunderflow May 18, 2025
2c6d5f6
fix printing logic in test
butterunderflow May 19, 2025
5dbc219
extract the dummy writing pattern as a function
butterunderflow May 19, 2025
a0d31e5
don't inline stack-pop to improve readability
butterunderflow May 19, 2025
39baa4a
make topFun work
butterunderflow May 20, 2025
5985803
update runtime
butterunderflow May 20, 2025
d314ebe
add all passed test cases
butterunderflow May 20, 2025
1f902e0
store/load operation
butterunderflow May 20, 2025
6a1db1d
more memory operations
butterunderflow May 20, 2025
a2b63f9
some fixes
butterunderflow May 21, 2025
d1ba899
some little polish
butterunderflow May 21, 2025
89d9a77
shift stack elements when exiting block instructions
butterunderflow May 21, 2025
8b4429f
fix: evalTop should be aware of frame size
butterunderflow May 23, 2025
8f4d6e0
comment IO statements
butterunderflow May 23, 2025
e68218c
benchmark code
butterunderflow May 23, 2025
46b4894
with std c++17
Kraks May 23, 2025
4dd702f
ensure the compiled program is executed correctly
butterunderflow May 23, 2025
f3861b1
utilize type information
butterunderflow May 16, 2025
4e907a3
lifting to the top
butterunderflow Jun 11, 2025
3adc60c
avoid re-registering top function
butterunderflow Jun 11, 2025
86061f1
remove std::vector usages & use O3 in benchmark
butterunderflow Jun 16, 2025
333a8d6
split header from prelude
butterunderflow Jun 20, 2025
251e014
move NewStagedEvalCPS.scala to attic
butterunderflow Jul 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/scala.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,4 @@ jobs:
sbt 'testOnly gensym.wasm.TestScriptRun'
sbt 'testOnly gensym.wasm.TestConcolic'
sbt 'testOnly gensym.wasm.TestDriver'
sbt 'testOnly gensym.wasm.TestStagedEval'
19 changes: 19 additions & 0 deletions benchmarks/wasm/global.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
(module
(type (;0;) (func (result i32)))
(type (;1;) (func))

(func (;0;) (type 0) (result i32)
i32.const 42
global.set 0
global.get 0
)
(func (;1;) (type 1)
call 0
;; should be 42
;; drop
)
(start 1)
(memory (;0;) 2)
(export "main" (func 1))
(global (;0;) (mut i32) (i32.const 0))
)
45 changes: 45 additions & 0 deletions benchmarks/wasm/staged/scratch.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
;; this file contains some wasm instructions to test if the compiler works,
;; and its execution is meaningless.
(module $push-drop
(global (;0;) (mut i32) (i32.const 1048576))
(func (;0;) (type 1) (result i32)
(local i32 i32)
i32.const 2
i32.const 2
local.get 0
local.get 1
local.set 0
local.tee 1
drop
drop
i32.add
nop
(call 1)
global.get 0
i32.const 3
global.set 0

if (result i32) ;; label = @1
i32.const 1
else
local.get 1
end
(block
(block
i32.const 4
i32.const 2
br_table 0 1 0 ;; the compilation of br_table is problematic now
)
)

(loop
i32.const 5
br 0)
return
i32.const 6
)
(func (;1;) (type 1) (param i32 i32) (result i32)
(local i32 i32)
local.get 0
local.get 1)
(start 0))
11 changes: 10 additions & 1 deletion src/main/scala/wasm/AST.scala
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,16 @@ case class RefType(kind: HeapType) extends ValueType

case class GlobalType(ty: ValueType, mut: Boolean) extends WasmType

abstract class BlockType extends WIR
abstract class BlockType extends WIR {
def funcType: FuncType =
this match {
case VarBlockType(_, None) =>
??? // TODO: fill this branch until we handle type index correctly
case VarBlockType(_, Some(tipe)) => tipe
case ValBlockType(Some(tipe)) => FuncType(List(), List(), List(tipe))
case ValBlockType(None) => FuncType(List(), List(), List())
}
}
case class VarBlockType(index: Int, tipe: Option[FuncType]) extends BlockType
case class ValBlockType(tipe: Option[ValueType]) extends BlockType;

Expand Down
13 changes: 2 additions & 11 deletions src/main/scala/wasm/ConcolicMiniWasm.scala
Original file line number Diff line number Diff line change
Expand Up @@ -229,15 +229,6 @@ object Primitives {
case NumType(F32Type) => F32V(rng.nextFloat())
case NumType(F64Type) => F64V(rng.nextDouble())
}

def getFuncType(ty: BlockType): FuncType =
ty match {
case VarBlockType(_, None) =>
??? // TODO: fill this branch until we handle type index correctly
case VarBlockType(_, Some(tipe)) => tipe
case ValBlockType(Some(tipe)) => FuncType(List(), List(), List(tipe))
case ValBlockType(None) => FuncType(List(), List(), List())
}
}

case class Frame(module: ModuleInstance, locals: ArrayBuffer[Value], symLocals: ArrayBuffer[SymVal])
Expand Down Expand Up @@ -383,15 +374,15 @@ case class Evaluator(module: ModuleInstance) {
eval(rest, concStack, symStack, frame, kont, trail)
case Unreachable => throw new RuntimeException("Unreachable")
case Block(ty, inner) =>
val funcTy = getFuncType(ty)
val funcTy = ty.funcType
val (inputSize, outputSize) = (funcTy.inps.size, funcTy.out.size)
val (inputs, restStack) = concStack.splitAt(inputSize)
val (symInputs, restSymStack) = symStack.splitAt(inputSize)
val restK: Cont = (retStack, retSymStack, tree) =>
eval(rest, retStack.take(outputSize) ++ restStack, retSymStack.take(outputSize) ++ restSymStack, frame, kont, trail)(tree)
eval(inner, inputs, symInputs, frame, restK, restK :: trail)
case Loop(ty, inner) =>
val funcTy = getFuncType(ty)
val funcTy = ty.funcType
val (inputSize, outputSize) = (funcTy.inps.size, funcTy.out.size)
val (inputs, restStack) = concStack.splitAt(inputSize)
val (symInputs, restSymStack) = symStack.splitAt(inputSize)
Expand Down
19 changes: 5 additions & 14 deletions src/main/scala/wasm/MiniWasm.scala
Original file line number Diff line number Diff line change
Expand Up @@ -229,15 +229,6 @@ object Primtives {
case VecType(kind) => ???
case RefType(kind) => RefNullV(kind)
}

def getFuncType(ty: BlockType): FuncType =
ty match {
case VarBlockType(_, None) =>
??? // TODO: fill this branch until we handle type index correctly
case VarBlockType(_, Some(tipe)) => tipe
case ValBlockType(Some(tipe)) => FuncType(List(), List(), List(tipe))
case ValBlockType(None) => FuncType(List(), List(), List())
}
}

case class Frame(locals: ArrayBuffer[Value])
Expand All @@ -264,8 +255,8 @@ case class Evaluator(module: ModuleInstance) {
val frameLocals = args ++ locals.map(zero(_))
val newFrame = Frame(ArrayBuffer(frameLocals: _*))
if (isTail)
// when tail call, share the continuation for returning with the callee
eval(body, List(), newFrame, kont, List(kont))
// when tail call, return to the caller's return continuation
eval(body, List(), newFrame, trail.last, List(trail.last))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for spotting this!

else {
val restK: Cont[Ans] = (retStack) =>
eval(rest, retStack.take(ty.out.size) ++ newStack, frame, kont, trail)
Expand Down Expand Up @@ -380,7 +371,7 @@ case class Evaluator(module: ModuleInstance) {
eval(rest, stack, frame, kont, trail)
case Unreachable => throw Trap()
case Block(ty, inner) =>
val funcTy = getFuncType(ty)
val funcTy = ty.funcType
val (inputs, restStack) = stack.splitAt(funcTy.inps.size)
val restK: Cont[Ans] = (retStack) =>
eval(rest, retStack.take(funcTy.out.size) ++ restStack, frame, kont, trail)
Expand All @@ -389,15 +380,15 @@ case class Evaluator(module: ModuleInstance) {
// We construct two continuations, one for the break (to the begining of the loop),
// and one for fall-through to the next instruction following the syntactic structure
// of the program.
val funcTy = getFuncType(ty)
val funcTy = ty.funcType
val (inputs, restStack) = stack.splitAt(funcTy.inps.size)
val restK: Cont[Ans] = (retStack) =>
eval(rest, retStack.take(funcTy.out.size) ++ restStack, frame, kont, trail)
def loop(retStack: Stack): Ans =
eval(inner, retStack.take(funcTy.inps.size), frame, restK, loop _ :: trail)
loop(inputs)
case If(ty, thn, els) =>
val funcTy = getFuncType(ty)
val funcTy = ty.funcType
val I32V(cond) :: newStack = stack
val inner = if (cond != 0) thn else els
val (inputs, restStack) = newStack.splitAt(funcTy.inps.size)
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/wasm/Parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ class GSWasmVisitor extends WatParserBaseVisitor[WIR] {
else if (ctx.LOCAL_GET() != null) LocalGet(getVar(ctx.idx(0)).toInt)
else if (ctx.LOCAL_SET() != null) LocalSet(getVar(ctx.idx(0)).toInt)
else if (ctx.LOCAL_TEE() != null) LocalTee(getVar(ctx.idx(0)).toInt)
else if (ctx.GLOBAL_SET() != null) GlobalGet(getVar(ctx.idx(0)).toInt)
else if (ctx.GLOBAL_SET() != null) GlobalSet(getVar(ctx.idx(0)).toInt)
else if (ctx.GLOBAL_GET() != null) GlobalGet(getVar(ctx.idx(0)).toInt)
else if (ctx.load() != null) {
val ty = visitNumType(ctx.load.numType)
Expand Down
Loading