Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ object TransformerCps extends Transformer {
val DEALLOC = Variable(JSName("DEALLOC"))
val TRAMPOLINE = Variable(JSName("TRAMPOLINE"))

val DEPTH = JSName("d")
val DEPTH_INC = js.RawExpr(List("", " + ", ""), List(Variable(DEPTH), js.RawLiteral("1")))

class RecursiveUsage(var jumped: Boolean)
case class RecursiveDefInfo(id: Id, label: Id, vparams: List[Id], bparams: List[Id], ks: Id, k: Id, used: RecursiveUsage)
case class ContinuationInfo(k: Id, vparams: List[Id], ks: Id)
Expand Down Expand Up @@ -98,7 +101,7 @@ object TransformerCps extends Transformer {
given TransformerContext = ctx.copy(scope = List(id -> block))
js.Const(nameDef(id), requiringThunk { toJS(id, block) })
case cps.ToplevelDefinition.Val(id, ks, k, binding) =>
js.Const(nameDef(id), Call(RUN_TOPLEVEL, js.Lambda(List(nameDef(ks), nameDef(k)), toJS(binding).stmts)))
js.Const(nameDef(id), Call(RUN_TOPLEVEL, js.Lambda(List(DEPTH, nameDef(ks), nameDef(k)), toJS(binding).stmts)))
case cps.ToplevelDefinition.Let(id, binding) =>
js.Const(nameDef(id), toJS(binding))
}
Expand Down Expand Up @@ -156,10 +159,10 @@ object TransformerCps extends Transformer {
val translatedBody = toJS(body)(using recursive(id, vparams, bparams, ks, k, label, used)).stmts

if used.jumped then
js.Lambda(vparams.map(nameDef) ++ bparams.map(nameDef) ++ List(nameDef(ks), nameDef(k)),
js.Lambda(vparams.map(nameDef) ++ bparams.map(nameDef) ++ List(DEPTH, nameDef(ks), nameDef(k)),
List(js.While(RawExpr("true"), translatedBody, Some(uniqueName(label)))))
else
js.Lambda(vparams.map(nameDef) ++ bparams.map(nameDef) ++ List(nameDef(ks), nameDef(k)),
js.Lambda(vparams.map(nameDef) ++ bparams.map(nameDef) ++ List(DEPTH, nameDef(ks), nameDef(k)),
translatedBody)

def toJS(b: cps.Block)(using TransformerContext): js.Expr = b match {
Expand All @@ -168,7 +171,7 @@ object TransformerCps extends Transformer {
case cps.New(handler) => toJS(handler)

case cps.BlockLit(vps, bps, ks, k, body) =>
js.Lambda(vps.map(nameDef) ++ bps.map(nameDef) ++ List(nameDef(ks), nameDef(k)), toJS(body).stmts)
js.Lambda(vps.map(nameDef) ++ bps.map(nameDef) ++ List(DEPTH, nameDef(ks), nameDef(k)), toJS(body).stmts)
}

def argumentToJS(b: cps.Block)(using TransformerContext): js.Expr = b match {
Expand All @@ -180,7 +183,7 @@ object TransformerCps extends Transformer {
case cps.Implementation(interface, operations) =>
js.Object(operations.map {
case cps.Operation(id, vps, bps, ks, k, body) =>
nameDef(id) -> js.Lambda(vps.map(nameDef) ++ bps.map(nameDef) ++ List(nameDef(ks), nameDef(k)), toJS(body)(using nonrecursive(ks)).stmts)
nameDef(id) -> js.Lambda(vps.map(nameDef) ++ bps.map(nameDef) ++ List(DEPTH, nameDef(ks), nameDef(k)), toJS(body)(using nonrecursive(ks)).stmts)
})
}

Expand All @@ -190,7 +193,7 @@ object TransformerCps extends Transformer {
case Cont.ContVar(id) =>
nameRef(id)
case Cont.ContLam(results, ks, body) =>
js.Lambda(results.map(nameDef) :+ nameDef(ks), toJS(body)(using nonrecursive(ks)).stmts)
js.Lambda(results.map(nameDef) ++ List(DEPTH, nameDef(ks)), toJS(body)(using nonrecursive(ks)).stmts)
case Cont.Abort => js.Undefined
}

Expand Down Expand Up @@ -301,8 +304,8 @@ object TransformerCps extends Transformer {
}

case cps.Stmt.Jump(k, vargs, ks) =>
pure(js.Return(maybeThunking(js.Call(nameRef(k),
vargs.map(toJS) ++ List(toJS(ks))))) :: Nil)
pure(js.Return(maybeThunking(d => js.Call(nameRef(k),
vargs.map(toJS) ++ List(d, toJS(ks))))) :: Nil)


case cps.Stmt.App(Recursive(id, label, vparams, bparams, ks1, k1, used), vargs, bargs, MetaCont(ks), k) =>
Expand Down Expand Up @@ -391,11 +394,11 @@ object TransformerCps extends Transformer {
}

case cps.Stmt.App(callee, vargs, bargs, ks, k) =>
pure(js.Return(js.Call(toJS(callee), vargs.map(toJS) ++ bargs.map(argumentToJS) ++ List(toJS(ks),
pure(js.Return(js.Call(toJS(callee), vargs.map(toJS) ++ bargs.map(argumentToJS) ++ List(DEPTH_INC, toJS(ks),
requiringThunk { toJS(k) }))) :: Nil)

case cps.Stmt.Invoke(callee, method, vargs, bargs, ks, k) =>
val args = vargs.map(toJS) ++ bargs.map(argumentToJS) ++ List(toJS(ks), toJS(k))
val args = vargs.map(toJS) ++ bargs.map(argumentToJS) ++ List(DEPTH_INC, toJS(ks), toJS(k))
pure(js.Return(MethodCall(toJS(callee), memberNameRef(method), args:_*)) :: Nil)

// const r = ks.arena.newRegion(); body
Expand Down Expand Up @@ -437,13 +440,13 @@ object TransformerCps extends Transformer {
}

case cps.Stmt.Reset(prog, ks, k) =>
pure(js.Return(Call(RESET, requiringThunk { toJS(prog)(using nonrecursive(prog)) }, toJS(ks), toJS(k))) :: Nil)
pure(js.Return(Call(RESET, Variable(DEPTH), requiringThunk { toJS(prog)(using nonrecursive(prog)) }, toJS(ks), toJS(k))) :: Nil)

case cps.Stmt.Shift(prompt, body, ks, k) =>
pure(js.Return(Call(SHIFT, nameRef(prompt), requiringThunk { toJS(body)(using nonrecursive(body)) }, toJS(ks), toJS(k))) :: Nil)
pure(js.Return(Call(SHIFT, nameRef(prompt), Variable(DEPTH), requiringThunk { toJS(body)(using nonrecursive(body)) }, toJS(ks), toJS(k))) :: Nil)

case cps.Stmt.Resume(r, b, ks2, k2) =>
pure(js.Return(js.Call(RESUME, nameRef(r), toJS(b)(using nonrecursive(b)), toJS(ks2), requiringThunk { toJS(k2) })) :: Nil)
pure(js.Return(js.Call(RESUME, nameRef(r), Variable(DEPTH), toJS(b)(using nonrecursive(b)), toJS(ks2), requiringThunk { toJS(k2) })) :: Nil)

case cps.Stmt.Hole(span) =>
pure(js.Return($effekt.call("hole", JsString(span.range.from.format))) :: Nil)
Expand Down Expand Up @@ -581,18 +584,28 @@ object TransformerCps extends Transformer {
// Thunking
// --------

def thunked(stmt: js.Stmt): js.Stmt = js.Return(js.Lambda(Nil, stmt))
def thunked(expr: js.Expr): js.Expr = js.Lambda(Nil, expr)
val MAX_DEPTH = 500

def thunked(stmt: js.Expr => js.Stmt): js.Stmt =
js.If(js.RawExpr(List("", " < ", ""), List(js.Variable(DEPTH), js.RawLiteral(MAX_DEPTH.toString))),
stmt(DEPTH_INC),
js.Return(js.Lambda(Nil, stmt(js.RawLiteral("0"))))
)
def thunked(expr: js.Expr => js.Expr): js.Expr =
js.IfExpr(js.RawExpr(List("", " < ", ""), List(js.Variable(DEPTH), js.RawLiteral(MAX_DEPTH.toString))),
expr(DEPTH_INC),
js.Lambda(Nil, expr(js.RawLiteral("0")))
)

def requiringThunk[T](prog: TransformerContext ?=> T)(using C: TransformerContext): T =
prog(using C.copy(requiresThunk = true))

def noThunking[T](prog: TransformerContext ?=> T)(using C: TransformerContext): T =
prog(using C.copy(requiresThunk = false))

def maybeThunking(stmt: js.Stmt)(using T: TransformerContext): js.Stmt =
if T.requiresThunk then thunked(stmt) else stmt
def maybeThunking(stmt: js.Expr => js.Stmt)(using T: TransformerContext): js.Stmt =
if T.requiresThunk then thunked(stmt) else stmt(DEPTH_INC)

def maybeThunking(expr: js.Expr)(using T: TransformerContext): js.Expr =
if T.requiresThunk then thunked(expr) else expr
def maybeThunking(expr: js.Expr => js.Expr)(using T: TransformerContext): js.Expr =
if T.requiresThunk then thunked(expr) else expr(DEPTH_INC)
}
18 changes: 9 additions & 9 deletions libraries/js/effekt_runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,22 +108,22 @@ function THUNK(f) {

function CAPTURE(body) {
return (ks, k) => {
const res = body(x => TRAMPOLINE(() => k(x, ks)))
const res = body(x => TRAMPOLINE(() => k(x, 0, ks)))
if (res instanceof Function) return res
else throw { computationIsDone: true, result: $effekt.unit }
}
}

const RETURN = (x, ks) => ks.rest.stack(x, ks.rest)
const RETURN = (x, d, ks) => ks.rest.stack(x, d, ks.rest)

// HANDLE(ks, ks, (p, ks, k) => { STMT })
function RESET(prog, ks, k) {
function RESET(d, prog, ks, k) {
const prompt = Symbol(); // gensym
const rest = { stack: k, prompt: ks.prompt, arena: ks.arena, rest: ks.rest }
return prog(prompt, { stack: null, prompt, arena: new Arena(), rest }, RETURN)
return prog(prompt, d + 1, { stack: null, prompt, arena: new Arena(), rest }, RETURN)
}

function SHIFT(p, body, ks, k) {
function SHIFT(p, d, body, ks, k) {

// TODO avoid constructing this object
let meta = { stack: k, prompt: ks.prompt, arena: ks.arena, rest: ks.rest }
Expand All @@ -143,11 +143,11 @@ function SHIFT(p, body, ks, k) {

const k1 = meta.stack
meta.stack = null
return body(cont, meta, k1)
return body(cont, d + 1, meta, k1)
}

// Rewind stack `cont` back onto `k` :: `ks` and resume with c
function RESUME(cont, c, ks, k) {
function RESUME(cont, d, c, ks, k) {
let meta = { stack: k, prompt: ks.prompt, arena: ks.arena, rest: ks.rest }
let toRewind = cont
while (!!toRewind) {
Expand All @@ -158,12 +158,12 @@ function RESUME(cont, c, ks, k) {

const k1 = meta.stack // TODO instead copy meta here, like elsewhere?
meta.stack = null
return () => c(meta, k1)
return () => c(0, meta, k1)
}

function RUN_TOPLEVEL(comp) {
try {
let a = comp(TOPLEVEL_KS, TOPLEVEL_K)
let a = comp(0, TOPLEVEL_KS, TOPLEVEL_K)
while (true) { a = a() }
} catch (e) {
if (e.computationIsDone) return e.result
Expand Down
Loading