Skip to content

Commit

Permalink
stuck
Browse files Browse the repository at this point in the history
  • Loading branch information
ahuoguo committed Nov 25, 2024
1 parent 4865eab commit c2ffebb
Show file tree
Hide file tree
Showing 6 changed files with 252 additions and 75 deletions.
71 changes: 71 additions & 0 deletions benchmarks/wasm/wasmfx/test_cont-strip.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
(module
(type (;0;) (func (param i32)))
(type (;1;) (cont 0))
(type (;2;) (func (result i32)))
(type (;3;) (func (param i32 i32)))
(type (;4;) (func))
(import "spectest" "print_i32" (func (;0;) (type 0)))
(tag (;0;) (type 2) (result i32))
(export "_start" (func 4))
(start 4)
(elem (;0;) declare func 3)
(func (;1;) (type 0) (param i32)
local.get 0
call 0
suspend 0
call 0
)
(func (;2;) (type 3) (param i32 i32)
(local i32)
local.get 0
local.set 2
block ;; label = @1
loop ;; label = @2
local.get 2
local.get 1
i32.gt_u
br_if 1 (;@1;)
local.get 2
call 1
local.get 2
i32.const 1
i32.add
local.set 2
br 0 (;@2;)
end
end
)
(func (;3;) (type 0) (param i32)
local.get 0
i32.const 13
call 2
)
(func (;4;) (type 4)
(local (ref 1) i32)
ref.func 3
cont.new 1
local.set 0
i32.const 10
local.set 1
block ;; label = @1
loop ;; label = @2
block (result (ref 1)) ;; label = @3
local.get 1
local.get 0
resume 1 (on 0 0 (;@3;))
i32.const -2
call 0
br 2 (;@1;)
end
local.set 0
local.get 1
i32.const 1
i32.add
local.set 1
i32.const -1
call 0
br 0 (;@2;)
end
end
)
)
55 changes: 55 additions & 0 deletions benchmarks/wasm/wasmfx/test_cont.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
(module
(import "spectest" "print_i32" (func $print_i32 (param i32)))
(type $task (func (param i32)))
(type $cont (cont $task))

(tag $yield (result i32))
;; DH: tag's input + continuation
;; == block's output ((ref $cont))
;; == the values on the stack after suspend
;; tag's output
;; == continuation's input
;; == the values on the stack when suspended computation is resumed

(func $process (param $x i32)
(call $print_i32 (local.get $x))
(suspend $yield) ;; DH: levaes a continuation on the stack, jump to the tag $yield
;; when come back, the stack should contains a i32 value, since the return type of $yield is i32
(call $print_i32))

(func $range (param $from i32) (param $to i32)
(local $i i32)
(local.set $i (local.get $from))
(block $b
(loop $l
(br_if $b (i32.gt_u (local.get $i) (local.get $to)))
(call $process (local.get $i))
(local.set $i (i32.add (local.get $i) (i32.const 1)))
(br $l))))

(func $task1 (param $x i32) (call $range (local.get $x) (i32.const 13)))

(func $main (export "_start")
(local $k (ref $cont))
(local $i i32)
(local.set $k (cont.new $cont (ref.func $task1)))
(local.set $i (i32.const 10))
(block $h
(loop $l
(block $on_yield (result (ref $cont))
(resume $cont
(on $yield $on_yield)
(local.get $i)
(local.get $k))
(call $print_i32 (i32.const -2)) ;; this code is executed only when no suspend is called in $k
(br $h))
;; $on_yield lands here, with the continuation on the stack
(local.set $k) ;; grab the continuation and save it
(local.set $i (i32.add (local.get $i) (i32.const 1)))
(call $print_i32 (i32.const -1))
(br $l))))

(elem declare func $task1)

(start $main)
)
4 changes: 2 additions & 2 deletions src/main/scala/wasm/AST.scala
Original file line number Diff line number Diff line change
Expand Up @@ -318,8 +318,8 @@ trait Callable

// https://webassembly.github.io/function-references/core/exec/runtime.html
abstract class Ref extends Value with Callable
case class RefNullV(t: HeapType) extends Ref {
def tipe(implicit m: ModuleInstance): ValueType = RefType(t)
case class RefNullV() extends Ref {
def tipe(implicit m: ModuleInstance): ValueType = ???
}
case class RefFuncV(funcAddr: Int) extends Ref {
def tipe(implicit m: ModuleInstance): ValueType =
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/wasm/MiniWasm.scala
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ object Primtives {
case F64Type => F64V(0)
}
case VecType(kind) => ???
case RefType(kind) => ???
case RefType(kind) => RefNullV()
}

def getFuncType(ty: BlockType): FuncType =
Expand Down
67 changes: 59 additions & 8 deletions src/main/scala/wasm/MiniWasmFX.scala
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,14 @@ case class EvaluatorFX(module: ModuleInstance) {
mkont: MCont[Ans],
trail: List[Cont[Ans]],
h: Handler[Ans]): Ans = {
// Note kont is only used in the base case, or when we are appending k and k1
if (insts.isEmpty) return kont(stack, mkont)

val inst = insts.head
val rest = insts.tail

// TODO: uncommenting this will fail tests that uses `testFileOutput`
// println(s"inst: ${inst} \t | ${frame.locals} | ${stack.reverse}" )
println(s"inst: ${inst} \t | ${frame.locals} | ${stack.reverse}" )

inst match {
case Drop => eval(rest, stack.tail, frame, kont, mkont, trail, h)
Expand Down Expand Up @@ -159,6 +160,8 @@ case class EvaluatorFX(module: ModuleInstance) {
case Block(ty, inner) =>
val funcTy = getFuncType(ty)
val (inputs, restStack) = stack.splitAt(funcTy.inps.size)
// why a block is introducing a new mknot1
// I feel like we will almost never need to change the mkont for a block
val restK: Cont[Ans] = (retStack, mkont1) => {
// kont -> mkont -> mkont1
eval(rest, retStack.take(funcTy.out.size) ++ restStack, frame, kont, mkont1, trail, h)
Expand All @@ -174,6 +177,8 @@ case class EvaluatorFX(module: ModuleInstance) {
eval(rest, retStack.take(funcTy.out.size) ++ restStack, frame, kont, mkont, trail, h)
def loop(retStack: List[Value], mkont: MCont[Ans]): Ans =
eval(inner, retStack.take(funcTy.inps.size), frame, restK, mkont, loop _ :: trail, h)
// for example, loop here doesn't change the meta-continuation at all
// compare with block
loop(inputs, mkont)
case If(ty, thn, els) =>
val funcTy = getFuncType(ty)
Expand Down Expand Up @@ -232,10 +237,10 @@ case class EvaluatorFX(module: ModuleInstance) {
val RefFuncV(f) :: newStack = stack
// should be similar to the contiuantion thrown by `throw`

// val k: Cont[Ans] = (s, mk) => evalCall(f, List(), s, frame, idK, mk, trail, h, false)
// TODO: where should kont go?
// TODO: this implementation is not right
def kr(s: Stack, k1: Cont[Ans], mk: MCont[Ans]): Ans = {
// k1 is rest for `resume`
// mk holds the handler for `suspend`
val kontK: Cont[Ans] = (s1, m1) => kont(s1, s2 => k1(s2, m1))
evalCall(f, List(), s, frame, kontK, mk, trail, h, false)
}
Expand All @@ -244,7 +249,16 @@ case class EvaluatorFX(module: ModuleInstance) {
// TODO: implement the following
case Suspend(tag_id) => {
// println(s"${RED}Unimplimented Suspending tag $tag_id")
throw new Exception("Suspend not implemented")
// add the continuation on the stack

val k = (s: Stack, k1: Cont[Ans], m: MCont[Ans]) => {
// k1 is the default handler
// so it should be konk ++ k1
eval(rest, s, frame, kont, m, trail, h)
}
val newStack = TCContV(k) :: stack
mkont(newStack)
// throw new Exception("Suspend not implemented")
// h(stack)
}

Expand All @@ -262,7 +276,19 @@ case class EvaluatorFX(module: ModuleInstance) {
f.k(inputs, k, mkont)
} else {
// TODO: attempt single tag first
throw new Exception("tags not supported")
if (handler.length > 1) {
throw new Exception("only single tag is supported")
}
val Handler(tagId, labelId) = handler.head
// if suspend happens, the label id holds the handler
// TODO: should handler be passed in an mkont??
val newHandler: Handler[Ans] = (newStack) => trail(labelId)(newStack, mkont)

// f might be handled by the default handler (namely kont), or by the
// handler specified by tags (newhandler, which has the same type as meta-continuation)
f.k(inputs, kont, newHandler)

// throw new Exception("tags not supported")

}

Expand Down Expand Up @@ -304,7 +330,30 @@ case class EvaluatorFX(module: ModuleInstance) {
case Export(`func_name`, ExportFunc(fid)) =>
System.err.println(s"Entering function $main")
module.funcs(fid) match {
case FuncDef(_, FuncBodyDef(_, _, _, body)) => body
case FuncDef(_, FuncBodyDef(_, _, locals, body)) => body
case _ => throw new Exception("Entry function has no concrete body")
}
case _ => List()
})
case None =>
module.defs.flatMap({
case Start(id) =>
System.err.println(s"Entering unnamed function $id")
module.funcs(id) match {
case FuncDef(_, FuncBodyDef(_, _, locals, body)) => body
case _ =>
throw new Exception("Entry function has no concrete body")
}
case _ => List()
})
}
val locals = main match {
case Some(func_name) =>
module.defs.flatMap({
case Export(`func_name`, ExportFunc(fid)) =>
System.err.println(s"Entering function $main")
module.funcs(fid) match {
case FuncDef(_, FuncBodyDef(_, _, locals, _)) => locals
case _ => throw new Exception("Entry function has no concrete body")
}
case _ => List()
Expand All @@ -314,7 +363,7 @@ case class EvaluatorFX(module: ModuleInstance) {
case Start(id) =>
System.err.println(s"Entering unnamed function $id")
module.funcs(id) match {
case FuncDef(_, FuncBodyDef(_, _, _, body)) => body
case FuncDef(_, FuncBodyDef(_, _, locals, body)) => locals
case _ =>
throw new Exception("Entry function has no concrete body")
}
Expand All @@ -323,7 +372,9 @@ case class EvaluatorFX(module: ModuleInstance) {
}
if (instrs.isEmpty) println("Warning: nothing is executed")
val handler0: Handler[Ans] = stack => throw new Exception(s"Uncaught exception: $stack")
eval(instrs, List(), Frame(ArrayBuffer(I32V(0))), halt, mhalt, List(halt), handler0)
// initialized locals
val frame = Frame(ArrayBuffer(locals.map(zero(_)): _*))
eval(instrs, List(), frame, halt, mhalt, List(halt), handler0)
}

def evalTop(m: ModuleInstance): Unit = {
Expand Down
Loading

0 comments on commit c2ffebb

Please sign in to comment.