Windows EH support

Hi Reid,

I’m trying to get a “big picture” view of everything that needs to be done for clang and llvm to support SEH and C++ exception handling for the “msvc” environment. I’ve put together a list, including work in progress, but I’m guessing that I’ve missed some details. Can you look this over and tell me if it matches your idea of what needs to be done?

Structured Exception Handling

is the cl feature "SEH-Exceptions as C++-Exceptions" (compile switch /EHa) also on your list?

http://msdn.microsoft.com/de-de/library/1deeycx5.aspx

is the cl feature "SEH-Exceptions as C++-Exceptions" (compile switch /EHa) also on your list?

That's a good question. I think the answer is "not exactly."

There was some discussion of this previously. You can find it in this thread:

    https://groups.google.com/d/msg/llvm-dev/ebIj2PJiIEs/rTq9684oglIJ

beginning with the responses on December 2.

-Andy

Hi Reid,

I’m trying to get a “big picture” view of everything that needs to be done
for clang and llvm to support SEH and C++ exception handling for the “msvc”
environment. I’ve put together a list, including work in progress, but I’m
guessing that I’ve missed some details. Can you look this over and tell me
if it matches your idea of what needs to be done?

Structured Exception Handling

---------------------------------------

* Add support for __try and __except in clang.

It looks like this is mostly implemented by your changes in the D5607
review, but there hasn’t been any activity on that review for quite a
while. You’re going to rewrite a lot of that to change the way filters are
implemented, right?

Yes. Basically I was trying to do things top-down at first, and then John
seemed to want me to propose the LLVM IR design first so he could review
it, so I switched over to doing things bottom up: MI, SDAG, IR, Clang.

* Add support for __finally in clang

I haven’t seen anything for this yet. Based on various discussions, it
seems like you intend to mix C++ scope cleanup with SEH handlers in ways
that MSVC specifically punts. Is that right?

Supporting this is a non-goal for me, but I was planning to use the same
vanilla LLVM IR cleanup model for __finally as for C++ cleanups. It
wouldn't be hard to add a not-very-faithful mode that runs C++ destructor
cleanups when non-inlined code faults.

* Add support for unwinding in frames with no exception handlers

I’m not clear as to what needs to be done for this. I know clang is
intentionally generating errors when you compile code with an
MSVC-environment target. I’m guessing that just removing this block will
be sufficient for clang to generate the IR we want. Then it will be up to
the back end to produce the correct unwinding support, which I think it may
already be doing.

Do we not do this already? This shouldn't be hard, we already know how to
emit the required pdata/xdata call frame information (CFI).

* Add support for frame allocation intrinsics.

You’ve started this in review D6493, which seems to be progressing. I
don’t know how much remains to be done there, but I trust that you have it
under control.

Yep.

* Add support for outlining filter functions.

This is partially implemented in my D6556 review. I think that will be
ready to check in once the expected for of the input IR settles. I still
need to add support for removing outlined code. It will also need to make
use of the frame allocation intrinsics to reference frame variables in the
outlined code. You’ve mentioned that we should also do analysis to
minimize the amount of duplicated code. I agree but I was hoping that
could wait until everything else is in place and working.

Yep.

* Add support for outlining __try bodies.

I don’t know what your plan is for identifying the beginning and end of
these regions, but I expect that we’ll be able to leverage the outlining
code from the task above to do most of the work for this task.

I've already done this in Clang in D5607. We want to do this before LLVM
optimizers remove or move around potentially trapping operations.

* Add support to emit .xdata table for SEH for 64-bit targets.

This is implemented in your D6300 review, but that looks like it has
stalled a bit. Is there anything holding this up other than lack of
reviews?

Just reviews. Eric took a look, and said he wants me to run around and
update lots of comments referring to DWARF EH that isn't really DWARF EH or
something.

* Add support to generate stack-based EH tables for 32-bit targets.

I haven’t seen any work on this. Am I correct in thinking that other than
the placement of the EH tables the 32-bit implementation will be able to
re-use all of the other SEH code (IR generation, outlining, etc.)?

Yep, this should be all backend work at this point.

* Add more tests.

C++ Exception Handling

-------------------------------

* Add support for MSVC-specific try-except IR emission to clang.

This seems fairly straight-forward. I saw some code in your D5607 review
to add a way to access the correct personality function. Other than that,
it looks like this will just be a matter of removing the “ErrorUnspported”
blocks and adding MSVC-specific support to the parts of the code that are
currently generating libc++abi calls for throwing and catching exceptions.
The general shape of the IR generated seems to be correct.

Yep, I want to use the same basic IR form.

* Add support for outlining C++ catch handlers

This will be very similar to the filter outlining in the D6556 review. I
think a bit of extra effort will be required to correctly identify and
group cleanup handlers and get them mixed with the exception handlers
correctly, but overall I feel like I have a handle on this.

* Add support to emit .xdata table for C++EH for 64-bit targets.

The analogous SEH code should provide a stable location for where these
changes need to be made. From there it seems to be a matter of plumbing to
get the handler addresses and new code to emit the table in the correct
format for the MSVC C++ personality function.

* Add support to generate stack-based C++EH tables for 32-bit targets

* Add more tests

------------------------------------------------

I think I’ve mentioned before that C++ exception handling is my main
interest here. I’ve been planning to follow through on the remaining work
for the function outlining for both SEH and C++EH. Beyond that, I guess we
need to talk about what work you’ve already got in flight. Would you want
me to work on the other C++EH tasks while you finish up SEH, or is there
something there that overlaps with work you’ve already started?

Yeah, I think C++ EH would be a good task to split off here. Emitting the
64-bit C++ EH tables is probably a good first step, but it would be really
good if we can start landing patches and iterating...

A faithful implementation of /EHa is out of scope. We would have to emit EH
cleanups for code like this:

struct A { ~A(); };
int foo(int *p) {
  A a;
  return *p; // Make sure to run ~A() if *p faults!
}

LLVM and Clang still believe that *calls* are the only potentially
exceptional operations. Loads, stores, division, etc are still UB if they
trap.

* Add support for unwinding in frames with no exception handlers

I’m not clear as to what needs to be done for this. I know clang is intentionally generating errors when you compile code with an MSVC-environment target. I’m guessing that just removing this block will be sufficient for clang to generate the IR we want. Then it will be up to the back end to produce the correct unwinding support, which I think it may already be doing.

Do we not do this already? This shouldn't be hard, we already know how to emit the required pdata/xdata call frame information (CFI).

Like I said, clang spits out an error if you try to compile with exceptions enabled for an MSVC-environment target. If you compile without exceptions enabled, it doesn’t generate the IR necessary to cleanup during unwind if an exception is thrown across a function. I tested it by compiling functions with MSVC that call through a function compiled with clang and then throwing an exception across the clang-compiled function. It unwinds correctly, but doesn’t do any cleanup.

I know that llvm has code to handle this with some personality functions (mingw, at least), but I don’t think it supports the “native” MSVC personality functions yet (__CxxFrameHandler3/4 in this case). I suppose that will come out of the C++EH work with nothing extra to be done especially for SEH in the back end. I just wanted to call it out in case there was something else I had overlooked.

Here’s the test case I have in mind.

    * Add support for unwinding in frames with no exception handlers

I’m not clear as to what needs to be done for this. I know clang is
intentionally generating errors when you compile code with an
MSVC-environment target. I’m guessing that just removing this block will
be sufficient for clang to generate the IR we want. Then it will be up to
the back end to produce the correct unwinding support, which I think it may
already be doing.

>> Do we not do this already? This shouldn't be hard, we already know how
to emit the required pdata/xdata call frame information (CFI).

Like I said, clang spits out an error if you try to compile with
exceptions enabled for an MSVC-environment target. If you compile without
exceptions enabled, it doesn’t generate the IR necessary to cleanup during
unwind if an exception is thrown across a function. I tested it by
compiling functions with MSVC that call through a function compiled with
clang and then throwing an exception across the clang-compiled function.
It unwinds correctly, but doesn’t do any cleanup.

Ah, OK, I misunderstood what you meant. I guess you're asking about
emitting handlers for C++ destructors when no explicit EH construct (try or
__try) is present.

This shouldn't be hard. This should use the same IR as __finally and should
be outlined in the same way. The only difference is what personality
function gets used.

I know that llvm has code to handle this with some personality functions
(mingw, at least), but I don’t think it supports the “native” MSVC
personality functions yet (__CxxFrameHandler3/4 in this case). I suppose
that will come out of the C++EH work with nothing extra to be done
especially for SEH in the back end. I just wanted to call it out in case
there was something else I had overlooked.

Here’s the test case I have in mind.

==========================================

#include <windows.h>

#include <stdio.h>

class MyClass {

public:

  MyClass() { printf("Hello\n"); }

  ~MyClass() { printf("Goodbye\n"); }

};

void bar() {

  RaiseException(1, 0, 0, NULL);

}

void foo() {

  MyClass Fubar;

  bar();

}

DWORD filter() {

    printf("In filter\n");

    return EXCEPTION_EXECUTE_HANDLER;

}

int main() {

    __try {

        __try {

            foo();

        }

        __except (filter()) {

            printf("In exception handler\n");

        }

    }

    __finally {

        printf("Finally!\n\n");

    }

    return 0;

}

==========================================

Compiling that with MSVC, it doesn’t execute the MyClass destructor if I
compile with no /EH option, but it does execute the destructor with /EHa,
having called it from __CxxFrameHandler3 while unwinding, on its way to the
exception handler. I understand that you aren’t worried about faithfully
reproducing the behavior of /EHa, but we do at least want to clean up in
cases like this, right?

I think MSVC might default to EHs and not EHa because most code isn't
written to handle recovery from random access violations. The destructors
might clean things up successfully, or they might fault. Once we've gone to
async exceptions land, it's almost safer to not run C++ cleanups and just
run __finally blocks that were explicitly written to clean up after faults.

However, if someone does want to run C++ destructors, it's easy for us to
support it at a best-effort level by using the normal _CxxFrameHandler3/4
personality function and flipping the flag in the EH table that controls
this behavior. Some of the cleanups will get run, but probably not the ones
in the function that trapped. In the case that you've outlined, I think
everything will work as intended because RaiseException() is a potentially
throwing function call.

If we want to support this level of EHa, it's probably worth thinking about
how to represent it for LTO. My first idea is "use a function attribute",
but those can get lost with cross-module inlining. Then again, it reflects
the limitations of the _CxxFrameHandler3 flag, which operates at a
whole-function level.