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 @@ -300,8 +300,7 @@ 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(List(js.Return(js.Call(js.Variable(js.JSName("THUNK_K")), nameRef(k) :: vargs.map(toJS) ++ List(toJS(ks))))))
Comment on lines 302 to +303
Copy link
Copy Markdown
Contributor

@jiribenes jiribenes Mar 23, 2026

Choose a reason for hiding this comment

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

As an experiment, I wonder if it would be better to inline the call to THUNK_K completely, something horrible like, the following (if I wrote it correctly [!]) should work, I think:

return (++_depth > 512) ? (_depth = 0, () => k(v, ks)) : k(v, ks)

but I would love if this wasn't necessary. :)

Copy link
Copy Markdown
Contributor

@jiribenes jiribenes Mar 23, 2026

Choose a reason for hiding this comment

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

I looked at the code in a bit more detail: am I correct in that this always thunks? (or rather: defers it to the runtime?)

We have some maybeThunking and thunked operators that are not used if we're sure we don't need thunking. Could we still use them and say that thunked := THUNK_K, otherwise use the non-thunked version directly? In other words: if we think we might need a thunk, we use the dynamic one, otherwise if we're 100% sure no thunks are needed, then use the direct call?
(This assumption depends on maybeThunking and its associated infrastructure being conservative which I'm not sure about)

_EDIT: It doesn't matter which direction it's conservative in as long as it's conservative in one direction -- we can also do swap the sides, of course: "always needs a thunk" ~> () => k(v, vs) vs "might need a thunk" ~> THUNK_K(...) [?]



case cps.Stmt.App(Recursive(id, label, vparams, bparams, ks1, k1, used), vargs, bargs, MetaCont(ks), k) =>
Expand Down
21 changes: 21 additions & 0 deletions libraries/js/effekt_runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,27 @@ function RESUME(cont, c, ks, k) {
return () => c(meta, k1)
}

let _depth = 0

function THUNK_K(k, v, ks) {
const threshold = 512
if (_depth >= threshold) {
return () => k(v, ks)
} else {
_depth += 1
return k(v, ks)
}
}

function THUNK(prog) {
if (_depth >= threshold) {
Comment thread
dvdvgt marked this conversation as resolved.
return () => prog()
} else {
_depth += 1
return prog()
}
}
Comment thread
dvdvgt marked this conversation as resolved.

function RUN_TOPLEVEL(comp) {
try {
let a = comp(TOPLEVEL_KS, TOPLEVEL_K)
Expand Down
Loading