[llvm-dev] [RFC] [Windows SEH] Local_Unwind (Jumping out of a _finally) and -EHa (Hardware Exception Handling)

Reid Kleckner via llvm-dev llvm-dev at lists.llvm.org
Fri Apr 3 15:36:24 PDT 2020


UHi Ten,

Thanks for the writeup and implementation, nice to meet you.

I wonder if it would be best to try to discuss the features separately. My
view is that catching hardware exceptions (/EHa) is critical functionality,
but it's not clear to me if local unwind is truly worth implementing.
Having looked at the code briefly, it seemed like a large portion of the
complexity comes from local unwind. Today, clang crashes on this small
example that jumps out of a __finally block, but the intention was to
reject the code and avoid implementing the functionality. Clang does, in
fact, emit a warning:
$ clang -c t.cpp
t.cpp:7:7: warning: jump out of __finally block has undefined behavior
[-Wjump-seh-finally]
      goto lu1;
      ^
Local unwind, in my view, is the user saying, "I wrote __finally, but
actually I decided I wanted to catch the exception, so let's transfer to
normal control flow now." It seems to me that the user already has a way to
express this: __except. I know the mapping isn't trivial and it's not
exactly the same, but it seems feasible to rewrite most uses of local
unwind this way.

Can you estimate the prevalence of local unwind? What percent of __finally
blocks in your experience use non-local control flow? I see a lot of value
in supporting catching hardware exceptions, but if we can avoid carrying
over the complexity of this local unwind feature, it seems to me that
future generations of compiler engineers will thank us.

---

Regarding trap / non-call / hardware exception handling, I guess I am a bit
more blase about precisely modeling the control flow. As Eli mentions, we
already have setjmp, and we already don't model it. Users file bugs about
problems with setjmp, and we essentially close them as "wontfix" and tell
them to put more "volatile" on the problem until it stops hurting.

One thing that I am very concerned about is the implications for basic
block layout. Right now, machine basic block layout is very free-handed.
Today, CodeGen puts labels around every potentially-throwing call, does
block layout without considering try regions, and then collapses adjacent
label regions with the same landingpad during AsmPrinting. For MSVC C++ EH,
state number stores and the ip2state table achieve the same goal.

I think we need rules about how LLVM is allowed to transform the following
code:
void foo(volatile int *pv) {
  __try {
    if (cond()) {
      ++*pv;
      __builtin_unreachable();
    }
  } __except(1) { }
  __try {
    if (cond()) {
      ++*pv;
      __builtin_unreachable();
    }
  } __except(1) { }
}

In this case, the *pv operation may throw, but I believe it would be
semantics preserving to merge the two identical if-then blocks. The
call.setup proposal I sent not long ago runs into the same issue. I have
written a patch to tail merge such similar blocks, but I have not landed it:
https://reviews.llvm.org/D29428
Even though it's not yet landed, I think we need to know if the transform
is valid. If it is, then we need to do more than volatilize the try region
to make EHa work.

For a long time I've wanted regions of some kind in LLVM IR, and this use
case has made me want to pick it up again. However, assuming that you want
to land support for hardware exceptions without some kind of generalized
region support in the IR, I think we do need to do something about these
blocks ending in unreachable in __try regions. The simplest thing that
could possibly work is to make clang end the try region before unreachable.
This would mean ending the block and adding `invoke void @seh_try_end`
after every unreachable. It would be redundant for noreturn calls, since
those will already have an unwind edge, ensuring they remain in the try
region.

---

Another interesting aspect of /EHa is how it affects C++ destructor
cleanups. I am personally comfortable with the requirement that LLVM avoid
moving around volatile instructions in __try blocks. LLVM is already
required to leave volatile operations in order. But I *am* concerned about
C++ destructor scopes, which are much more frequent than __try. As you have
described it, clang would invoke eha_scope_begin() / eha_scope_end() around
the object lifetime, but are you proposing to volatilize all memory
operations in the region? If not, I see nothing that would prevent LLVM
from moving potentially faulting operations in or out of this scope. We
cannot require passes to look for non-local EH regions before doing code
motion. Would that be acceptable behavior? It could lead to some strange
behavior, where a load is sunk to the point of use outside the cleanup
region, but maybe users don't care about this in practice.

---

To summarize, my feedback would be:
1. Focus on __try and hardware exceptions first, the value proposition is
clear and large. In particular, something has to be done about unreachable.
Clang should already thread other abnormal control flow through the region
exit.
2. Please gather some data on prevalence of local unwind to motivate the
feature
3. Please elaborate on the design for /EHa C++ destructor cleanups and code
motion

I hope that helps, and I'm sorry if I'm slow to respond, this is a tricky
problem, and it's not my first priority.

Reid

On Wed, Apr 1, 2020 at 8:22 AM Ten Tzen via llvm-dev <
llvm-dev at lists.llvm.org> wrote:

> Hi, all,
>
>
>
> The intend of this thread is to complete the support for Windows SEH.
>
> Currently there are two major missing features:  Jumping out of a _finally
> and Hardware exception handling.
>
>
>
> The document below is my proposed design and implementation to fully
> support SEH on LLVM.
>
> I have completely implemented this design on a branch in repo:
> https://github.com/tentzen/llvm-project
> <https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Ftentzen%2Fllvm-project&data=02%7C01%7Ctentzen%40microsoft.com%7Ced638e497aa74798b3f808d7d5e46775%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637213049272295023&sdata=Pd6gK%2B7JsIlfcyJLB%2FajWKdrbgqsITsseBfeB2Z5lgg%3D&reserved=0>.
>
>
> It now passes MSVC’s in-house SEH suite.
>
>
>
> Sorry for this long write-up.  For better readability, please read it on
> https://github.com/tentzen/llvm-project/wiki
> <https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Ftentzen%2Fllvm-project%2Fwiki&data=02%7C01%7Ctentzen%40microsoft.com%7Ced638e497aa74798b3f808d7d5e46775%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637213049272305020&sdata=SN9XBN6InU79U%2FEXnReyi9H1uPbVwTHgXhMkKODnA%2FM%3D&reserved=0>
>
>
>
> Special thanks to Joseph Tremoulet for his earlier comments and
> suggestions.
>
>
>
> Note: I just subscribed llvm-dev, probably not in the list yet.  So please
> reply with my email address (tentzen at microsoft.com) explicitly in
> To-list.
>
> Thanks,
>
>
>
> --Ten
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20200403/1d3f028e/attachment.html>


More information about the llvm-dev mailing list