-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Description
I've been interested in having the Exception Handling proposal supported in Wasmtime, so I looked into possible ways to implement it. There's been some prior discussion in issues #2049 and #1677, but those issues focus on details that I think are less important than a high-level strategy.
As far as I see, the core difficulty in implementing this feature is that Wasmtime currently has only one non-local control flow construct, traps, and the only way to catch traps is the scoped wasmtime_setjmp
construct implemented in C. There is no particularly good way to use this construct to implement the Wasm exception handling opcodes; it is a C function that manages resources opaque to the rest of the runtime. You could translate try
blocks to wasmtime_setjmp
calls by splitting each Wasm function that uses exception handling into many Cranelift functions, but this is a very complex transformation that would interfere with optimizations on the fast path, and I expect that no one wants that.
So, a different non-local control flow construct is necessary for exception handling. I see two options here:
- Reusing the existing OS-specific stack unwinding mechanism (SEH on Windows, DWARF elsewhere), and
- Implementing a new Cranelift-specific stack unwinding mechanism.
Option (1) requires a large amount of platform-specific work. Wasmtime does already emit DWARF and SEH tables to be able to capture backtraces with Wasm frames, but a lot more work is necessary to extend that functionality to cover exception handling.
In this case, exceptions and traps would use disjoint mechanisms, which naturally aligns with the semantics specified in the proposal.
Option (2) makes it possible to use a mostly platform-independent mechanism. It doesn't make a lot of sense to implement a new zero-cost exception handling strategy (you're better off using DWARF/SEH in that case), and the other major approach is SjLj. For example, Wasmtime could maintain a linked list of registered exception handlers in VM context, and a function that has try
blocks would append an entry to this list in the prologue, containing the frame pointer and the address of the basic block that dispatches an in-flight exception for a particular try
body. (This is a bit similar to 32-bit SEH.) Then, on any control flow into or out of a try
body, the address recorded in the entry would be updated. The throw
instruction would capture the exception parameters and set SP and IP to the ones in the head of the list, while Cranelift would have to make sure that any SSA values live in the dispatch block are allocated to stack slots.
This option could actually eliminate the dependency on C setjmp
/longjmp
functions and unify unwinding due to exceptions and traps. However, while it requires less platform-specific work, it is more costly at runtime, and maybe not a good fit for Wasmtime in the long run.
What do you think?
Metadata
Metadata
Assignees
Labels
Type
Projects
Status