[llvm-dev] What's the difference between llvm `resume` instruction and `__cxa_rethrow`?

John McCall via llvm-dev llvm-dev at lists.llvm.org
Wed Dec 1 13:38:47 PST 2021


On 1 Dec 2021, at 2:36, chuanqi.xcq wrote:
> Hi,
>
>    Recently I am checking the implementation of c++20 coroutine in 
> clang and I am wondering if it is possible to mark its status as 
> complete.
> (Now the status is partial althought it's been a while that the 
> clang's coroutine is used in production).
>
>    Here is the simple introduction, in the standard: 
> https://eel.is/c++draft/dcl.fct.def.coroutine#5, it says both 
> initial_suspend().await_ready()
> and initial_suspend().await_suspend() should be wrapped in `try` 
> statement  and if there is an exception happens, it would be rethown 
> by
> `throw;` statement. It may look like:
>
> ```C++
> try {
>    auto init_suspend = promise.init_suspend();
>    if (!init_suspend.await_ready())
>         init_suspend.await_suspend();
> } catch (...) {
>     throw;
> }
> ```
>
>     And the implementation didn't wrap them in `try...catch` 
> statement. The code generated by clang would look like:
>
> ```LLVM
> invoke promise.init_suspend()
>     to label %cont unwind label %lpad
>
> invoke init_suspend.await_ready()
>     to label %cont1 unwind label %lpad1
> invoke init_suspend.await_suspend()
>     to label %cont2 unwind label %lpad2
> ...
> %lpad:
>    ...
>    br label %eh.resume
> %lpad1:
>    ...
>    br label %eh.resume
>  %lpad2:
>    ...
>    br label %eh.resume
> eh.resume:
>    ....
>    resume
> ```
>
>    And I know that clang would generate `__cxa_rethrow` for `throw;`. 
> I did some simple test locally, the behavior now looks
> good for me. (I could catch the exception in the caller of the 
> coroutine). But I think it would be better to consult with the 
> experts.
> I am wondering what's the difference and if it would be a block issue 
> for conforming clang's implementation.

The landing pad is a region of the containing function, but really, in 
terms of its interactions with the unwinder, it’s important to think 
of it almost as if it were a separate function that’s “called” by 
the unwinder, with certain expected preconditions on entry and 
postconditions on “exit”, where the “exits” are various calls 
back into the unwinder.  Those pre/post-conditions are determined by the 
associated EH table and the exception selector.  Specifically:
- If the landing pad wants to be able to handle an exception, it needs 
the EH table for the covered region that leads to the landing pad to 
include an appropriate catch clause.
- If the EH selector corresponds to a catch clause, the landing pad is 
supposed to actually handle the exception before exiting.
- The landing pad handles an exception by calling the appropriate 
language routine for it, e.g. `__cxa_begin_catch`.
- Calling `__cxa_begin_catch` creates an additional requirement that the 
landing pad will call `__cxa_end_catch` to exit.  This call is an exit 
from the landing pad.
- If the EH selector corresponds to “just run cleanups”, the landing 
pad must not attempt to handle the exception and must eventually call 
`_Unwind_Resume`.

The `resume` instruction continues unwinding when you *haven’t* 
handled an exception yet.  Normally, it is run at the end of the landing 
pad after it’s checked for all of the exceptions that are caught 
there, so we know we’re not handling an exception, so it just turns 
into a call to `_Unwind_Resume`.  But in order to satisfy the 
requirements above, the inliner has to potentially merge landing pads in 
the caller and callee.  That means both (1) combining the EH entries for 
call sites in the inlined function with the EH entries for the inlined 
call site and (2) making the landing pads for the inlined function flow 
into the landing pad for the call site.  As a result, `resume` in the 
callee flows to the landing pad for the caller’s call site, which 
might then handle other exceptions.

`__cxa_rethrow` has to be called while an exception is handled and 
doesn’t itself exit the landing pad.  There’s supposed to be a 
cleanup to call `__cxa_end_catch` whenever you’re inside a `catch` 
block.

So it depends on what’s in the ellipses, but I believe this code is 
invalid, and landing pads are not supposed to do a `resume` when 
they’ve said they’re going to catch.

I assume the actual source code you’re processing doesn’t literally 
have a `catch (...) { throw; }`, and instead it calls some function that 
might do a `throw;`?

John.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20211201/8a50df66/attachment.html>


More information about the llvm-dev mailing list