-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposed instruction format for reintroducing exnref #281
Comments
@aheejin This looks good to me and matches my understanding. Two small text format questions:
|
Yes, |
Wouldn't renaming the old opcodes mean that old text-format wasm no longer compiles and produces confusing errors? Vs retiring the old opcodes with their names intact, and using new names for the new ones that allows straightforward 'x is deprecated, use y' error messages. I am not sure how I feel about Changing rethrow to |
I agree that it would be nice to rename the existing instructions. Practically speaking though, that breaks a lot of text format tests that we have right now, and is very disruptive to other users of the text format. I think we could consider it in the future, but for now I'd prefer to have a unique name for the 'new thing'. |
IIUC, the only ambiguity is the case of a try with no handlers. But in that case, both interpretations also happen to be semantically equivalent. Would it not work to stick to |
Yeah, that is the only grammatical ambiguity. It would be difficult to do though in the parser I work with (bytecode-alliance/wasm-tools), where no expression AST is built up for while parsing. There we can only see if the next tokens is an 'immediate handler' So we could interpret an empty try as the old opcode easily, but not as the new one. But aside from the implementation details, I would feel uncomfortable diverging the text format from the binary encoding details here. Even if we want to remove these instructions eventually, in the meantime we need to be able to precisely emit both of them for tests (even if semantically equivalent). And also see exactly which one a binary is using. |
I suppose another option could be to disallow an empty |
I added #282 for some details on the exnref type. Feedback appreciated. |
I see.
Let's not introduce special cases. I'd rather go with try_table in that case. :) |
I have a working implementation in SpiderMonkey that I hope to land soon. I had to fill in some binary details and some small execution semantics. I tried to summarize them here. Let me know if I got anything wrong. Typesexnref is encoded as Exception handling with GC proposal should define a bottom ref type of
InstructionsText format
Binary format
Validation
Execution
|
Should we also consider a case that unpacks and drops the exnref, such as |
These types and instructions are part of the exception handling rework tracked in (WebAssembly/exception-handling#281).
fwiw, we would use this in our JIT and (if llvm exposed it) I think we might use it in our AOT compiler too. We use exceptions more like signals ('something happened, please unwind') than values containing information |
These types and instructions are part of the exception handling rework tracked in (WebAssembly/exception-handling#281).
These types and instructions are part of the exception handling rework tracked in (WebAssembly/exception-handling#281).
Oh, I completely forgot you implemented this already. I adopted your opcodes for
That would be fine with me. I think the simplest way to do that would be to add a flag in the binary for each of the catches for whether it's a WDYT? |
@eqrion A flag for each catch sounds fine, and the |
Extra flag byte sounds fine to me and is extensible. (If we wanted to save the extra byte we could also use a hack and represent the labelidx (or the tagidx) as a signed LEB, and abuse the sign as the flag. But I don't think that's worth it.) Oops, yes, you are right about the type opcode. I changed it to -0x17 as well, thanks. |
Agree that flags byte is probably the best bet. |
About the try {
...
} catch (int i) {
} This kind of code needs Which is to say, not sure how effectively we can use the |
My understanding is this would only help in cases where the nearest enclosing try for a throw is a catch_drop. I agree, it seems not useful for C++, but possibly for others. If the binary size of the extra flag byte becomes an issue, we could maybe use Andreas suggestion (or some other idea) to minimize the cost for C++. I updated the previous comment that documents what SM is targeting. I chose [catch/catch_ref] and [catch_all/catch_all_ref] instead of [_drop] terminology, as I think that matches the rest of the language design better. But it's not that important. |
* wast: Add exnref, try_table, and throw_ref These types and instructions are part of the exception handling rework tracked in (WebAssembly/exception-handling#281). * Add catch_ref and catch_all_ref as catch variants that capture the exnref * Fix order of flag bytes
In Wasm EH, even if we set `-sASSERTION` or `-sEXCEPTION_STACK_TRACES`, if we rethrow an exception in the code, we lose the effect of that option because previously we called `__throw_exception_with_stack_trace` only in `__cxa_throw`: https://github.com/emscripten-core/emscripten/blob/9ce7020632aa6f7578c6e40e197b800a4dd7073f/system/lib/libcxxabi/src/cxa_exception.cpp#L294-L296 This adds the same mechanism to `__cxa_rethrow` (which is used for C++ `throw;`) and `__cxa_rethrow_primary_exception` (which is used for `std::rethrow_exception`). This does not solve the problem of losing stack traces _before_ the rethrowing. libc++abi's `__cxa_rethrow` and `__cxa_rethrow_primary_exception` are implemented as throwing a pointer in the same way we first throw it and they are not aware of any metadata. This happens even in the native platform with GDB; GDB's backtrace only shows stack traces after rethrowing. We may try to fix this later by passing `exnref` (WebAssembly/exception-handling#281) to the library, but this is likely to take some time. Partially fixes emscripten-core#20301.
I'm fine with or without dropping versions. I don't think the extra byte is an issue, since catch clauses shouldn't exactly be frequent, and their size is certainly dominated by the block structure needed for the jump targets. Even if we decided not to have the dropping versions now, it'd probably be wise to reserve the extra byte. |
In Wasm EH, even if we set `-sASSERTION` or `-sEXCEPTION_STACK_TRACES`, if we rethrow an exception in the code, we lose the effect of that option because previously we called `__throw_exception_with_stack_trace` only in `__cxa_throw`: https://github.com/emscripten-core/emscripten/blob/9ce7020632aa6f7578c6e40e197b800a4dd7073f/system/lib/libcxxabi/src/cxa_exception.cpp#L294-L296 This adds the same mechanism to `__cxa_rethrow` (which is used for C++ `throw;`) and `__cxa_rethrow_primary_exception` (which is used for `std::rethrow_exception`). This does not solve the problem of losing stack traces _before_ the rethrowing. libc++abi's `__cxa_rethrow` and `__cxa_rethrow_primary_exception` are implemented as throwing a pointer in the same way we first throw it and they are not aware of any metadata. This happens even in the native platform with GDB; GDB's backtrace only shows stack traces after rethrowing. We may try to fix this later by passing `exnref` (WebAssembly/exception-handling#281) to the library, but this is likely to take some time. Partially fixes #20301.
The exnref-1b branch now has the latest instruction format (Option B'). |
I also updated the spec document for Option B', see #283. That should check all the spec and interpreter boxes for moving Option B' to phase 4. Now it's up to implementations and toolchains. :) |
From one runtime-implementer perspective, it would be helpful if we could
|
This situation is deteriorating rapidly. Your repeated changes have significantly disrupted the implementation of EH in wavm. My efforts to incorporate exception handling into a wasm virtual machine are being hampered by your changes. It's disappointing and frustrating to see the obstacles you're creating. Also, you do not change llvm itself, how could i supposed to support this if you do not support LLVM? |
@trcrsired if you have concerns about the state of the EH proposal that go beyond the text/binary encoding, can we discuss them in this issue? |
It’s been a while since we posted #280 and did a presentation in the 8/1 CG meeting. I think this is time we kick off more concrete discussions on what to do for the next steps.
CG Meeting Schedule
We reserved two slots in September CG meeting:
On 9/12, we are going to make a brief announcement that we have @titzer as a ch-champion of the EH proposal, and let people know that discussions are taking place in Github. On 9/26, we will have a 15-min slot for the recap of our proposed option and some possible discussions. I hoped we could do it all on 9/12 but the slot had already been fully reserved. And we plan to do a vote on the actual switch at the October hybrid CG meeting (remote option is available).
Proposed Instruction Format
And we’d like to propose below as our endorsed option to reintroduce
exnref
.In the folded format, this can be also written as
When an exception is thrown within
instruction*
, it is checked against each of the tags in order in the list ofcatch
es, and if it is caught by one of thecatch
es, the control flow branches to the corresponding label with extracted values and the exception’sexnref
. So if a tag contains a singlei32
, its target label’s result type will be a multivalue of(i32, exnref)
. Thecatch_all
label’s return type, if present, is just anexnref
.So an example of a
try
with two tags and acatch_all
would be:This is a variant of Option B we presented on 8/1. The difference is we bundle the
catch
labels withtry
, preceding the instructions that they guard, for ease of decoding. Becausecatch
es are with thetry
, we end the block with a normalend
, as inblock
s andloop
s.This option doesn’t have the many-partitioned blocks of
try
-catch
-...-catch_all
anymore; instead nowtry
is a block that additionally has attachedcatch
andcatch_all
clauses. Note that we don’t need the(do)
syntax in the text format anymore. Also in binary format, the handler list can be just a vector of immediates as inbr_table
, so we don’t need actual opcodes forcatch
andcatch_all
.(Our presentation on 8/1 incorrectly included
br_on_exn
for Option B, which is not necessary becausecatch
pushes extracted values along with anexnref
. The slides have been fixed.)rethrow
will take an explicitexnref
, not the label. The currentrethrow
instruction, which takes a label, creates semantic dependency on the lexical context which has been a challenge to some compiler code transformations.throw
instruction will remain the same.try
-delegate
will be removed from the new spec because we can achieve the same semantics usingexnref
and branches.We chose this option because several stakeholders we talked to expressed preference for it. The reasons include:
try
-delegate
, no need forcatch
andcatch_all
opcodes)I was able to talk with several VM and toolchain authors and they said the plan seemed doable.
@rossberg prototyped this option in https://github.com/WebAssembly/exception-handling/tree/exnref-1b branch. You can also see the diff in main...exnref-1b#diff-667145a59d9b08f9350bb76322feec7bbe56dda640a84dd5a9f0a65097ec452e.
Migration Plan
If we choose to adopt this option, the migration will progress cautiously given the circumstance that the current EH instructions have been already shipped to the web. The VMs will be supporting both current and the new instructions for the foreseeable future so apps already deployed won’t be disrupted. Emscripten + LLVM toolchain will support the new option under a flag first, and switch to it after it is stabilized. We also plan to provide a converter tool from the old binary to the new format to facilitate the use of the new instructions without the need for recompilation. We have a tentative plan, or more of a hope, for the deprecation of the current instructions, but this will only happen when we can be sure that the usage of the old instructions is low enough, which may turn out infeasible. Before any deprecation, we will maintain the documentation of the current instructions in the supplemental Phase 3 documentation.
The text was updated successfully, but these errors were encountered: