-
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
Proposal: using only catch_all to catch all exceptions #31
Comments
I think this problem is even bigger than this explanation makes it appear. |
I'm a bit confused. How is dealing with C++ frames related to whether you
want to use the tag mechanism for native Wasm handlers?
|
Have we considered adding a |
@eholk I don't understand how |
@rossberg I could be wrong, but I guess what @dschuff meant was, To make C++ destructors be correctly called when a JS exception is thrown,
|
C++ does have code that has to run whether an exception is thrown or now: destructors. With |
@eholk That wouldn't work because
|
Closing this, since we decided to use the second version of the proposal that uses a single |
…mory.init`, `memory.drop`, `table.init`, and `table.drop` identify passive segments; only that they index a valid segment. (WebAssembly#31)
TL;DR
The current form of separate catch instructions (catch i, catch j, ...,
catch_all) is very hard to generate from the compiler's side and possibly
detrimental for code size and performance. So I propose to merge all catch
blocks into one
catch_all
block that handles all tags (meaning both C++exceptions and foreign exceptions).
Problem
Suppose we have this original C++ code.
action 1
andaction 2
can be arbitrary user code.Itanium C++ ABI
specifies that
catch (...)
clause should be able to catch not only C++exceptions but also foreign exceptions that are not generated from one of C++
catch
clauses. It may not be necessary that we should strictly follow theItanium ABI spec, it makes most sense for
catch (...)
to handle also foreignexceptions anyway because that's the only way for a C++ programmer to specify
some action when it catches a foreign exception. That means, when there is
catch (...)
, we should generate acatch_all
instruction. Then the generatedWasm code will look like, in pseudocode,
Here
action 2
part is factored out so that it can be shared betweencatch i
and
catch_all
in order to prevent code duplications. But whether we duplicateaction 2
part of the code or factor it out, the requirement is that we shouldbe able to know which part of the code corresponds to the
catch (...)
so wecan generate correct code by factoring out or duplication.
Let's see another case. When the original C++ code is like
There is no
catch (...)
in this code, but that means we should generatecleanup code to call the destructor for
obj
. And that cleanup code shouldrun regardless of whether we catch a C++ exception or a foreign exception. So,
it should be either duplicated or factored out as well:
Now we face the same problem: we should know which part of the code corresponds
to corresponds to the cleanup code.
Separating code within
catch (...)
by examining and pattern matching LLVM IR(or any other compiler's IR) is not always possible because code can be
transformed or optimized in many different ways. Windows EH developers once
tried it and failed. Windows EH
requires identifying not only
catch (...)
but also all the catch clauses, butthe problem here is inherently the same. Pattern matching cleanup code is also
not simple, because from the IR's point of view, they are just function
(desturctor) calls.
Can this be done if we demarcate these parts in clang (or more generally,
frontend code generation phase)? The answer looks, maybe yes, but it will be
much hard, and I'm not sure if it's worth it. Basically what we need is the way
to demarcate some code parts, and prevent any code entering or escaping from
that regions in all of the IR-level passes and backend level passes. Code
hoisting or sinking across the boundaries should not occur in any pass, and
instruction scheduling in backend should treat these boundaries as fences for
not only memory instructions but also all instructions. Windows EH developers
faced similar problems and came up with new specialized
instructions,
but their objectives were different - they did this because they didn't use
Itanium ABI and they had to satisfy MSVC's spec -, it does not look possible to
reuse their approaches. Also, there will be more work that has to be done: such
as, matching each landing pad to its parent scope's cleanup code.
Even if it is possible by creating new instructions and doing more work on clang
side, it will also prevent code optimization opportunities, because it basically
separates certain parts of code and does not allow any optimization across their
border. For example, shared expressions may not be able to be merged.
Proposal
Considering the amount of work that needs to be done to satisfy the current spec
and the expected downside of code size and performance degradation, I think
having one
catch_all
instruction that handles all exceptions is the best wayto go. Actually we can do this even with the current spec by only using
catch_all
and not using othercatch tag
instructions, but that bringsanother point: is
catch tag
instruction ever useful?To use only
catch_all
, there should be a way to tell if the current exceptionis a C++ exception or not within a
catch_all
clause. While I think it can bedone by setting some variable within some libcxxabi functions (such as
__cxa_throw
and__cxa_begin_catch
), it would still be better if there is aneasy way to access the currently caught exception's tag within a
catch_all
block. Maybe
catch_all
block can return the caught exception's tag.Even if
catch_all
instruction does not put an exception object on top of Wasmstack, there are ways we can relay an exception object from
throw
tocatch_all
: one possible way is to use Wasm global.throw
instruction sets aWasm global with the pointer to an exception object so within a
catch_all
block we can retrieve it.
The only possible downside of this scheme is, when a foreign exception is thrown
and there is no cleanup code to run for a certain stack frame, anyway it should
stop at that frame because it is caught by
catch_all
instructions. But Ihardly imagine this case will be common enough to affect performance.
The text was updated successfully, but these errors were encountered: