[graalvm-dev] [External] : Truffle Bytecode DSL production cutover with continuations and structured control flow

protos at guillermomolina.com protos at guillermomolina.com
Fri Sep 11 11:26:31 UTC 2026


Hi,

I'm Guillermo Adrián Molina, author of Protos, a prototype-based 
language implemented on Truffle 25.3.4.1.

I originally intended to ask about how to represent dynamic guest 
control flow around Bytecode DSL continuations, but the implementation 
has progressed quite a bit since then, so I thought a more useful 
question would be about the final production cutover and any pitfalls we 
may be missing.

Protos is migrating from an AST-based cooperative replay mechanism to a 
Bytecode DSL backend.

The Bytecode path now supports:

  	* source-backed closure calls composed across Bytecode roots;
  	* Task/Future suspension and resumption through ContinuationResult, 
without replaying completed guest effects;
  	* non-local return, guest Error propagation/handlers, suspendible 
ensure, cancellation and while;
  	* debugger scopes through TagTreeNode / NodeLibrary;
  	* StatementTag/CallTag source identity and logical BytecodeLocation 
preservation across suspension/resume.

The control-flow model we ended up with is:

  	* normal guest values remain ordinary values;
  	* suspension is distinct from unwind and is represented by the 
Bytecode continuation mechanism;
  	* guest Error remains a guest-exception lane;
  	* non-local return and cancellation are rare internal control-flow 
transfers;
  	* a narrow interceptControlFlowException bridge allows those internal 
transfers to cross Bytecode EH regions so that structured cleanup can 
run, while retaining the exact original transfer;
  	* pending transfers live in resumable Bytecode/frame state while 
cleanup executes, including when cleanup itself suspends;
  	* there is no replay tape, global continuation registry, semantic 
ThreadLocal, or parked thread on the Bytecode path.

At this point the remaining work is mostly production cutover: complete 
a couple of hosting/native boundaries, switch normal 
TruffleLanguage.parse() execution to the Bytecode backend, retire the 
old replay path, and then enable/measure the optimizing Truffle runtime 
in ordinary execution.

I would be interested in feedback on three points:

  	* Does the separation above -- Bytecode continuations for suspension, 
structured Bytecode EH/finally for unwind, and a narrow 
ControlFlowException bridge for internal transfers -- match the intended 
direction of the Bytecode DSL, or is there another pattern you would 
recommend for a production language?
  	* Are there known caveats when making a continuation-enabled Bytecode 
DSL root the normal production backend, particularly for runtime 
compilation / partial evaluation? For example, does enabling yield have 
meaningful cost for roots that never actually suspend?
  	* Are there specific diagnostics or invariants you recommend checking 
during the final AST-to-Bytecode cutover to make sure resumed paths, 
structured control flow, source metadata and runtime compilation remain 
optimization-friendly?

The project is guillermomolina/protos on GitHub if concrete 
implementation details are useful.

Thanks,

Guillermo Adrián Molina
Protos
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://oss.oracle.com/pipermail/graalvm-dev/attachments/20260911/90d5f0f2/attachment.htm>


More information about the GraalVM-Dev mailing list