[LLVMdev] RFC: Native Windows C++ exception handling

Reid Kleckner rnk at google.com
Mon Jan 26 16:13:04 PST 2015


First, thanks for the help in this area. :)

I agree there's a big conceptual mismatch between the table-driven EH
strategy MSVC uses control-driven landing pad strategy used in the Itanium
C++ EH document. We need some way to represent these tables in IR, or we
ask the backend for too much.

I think your IR extension is insufficient. I'm looking at the 'nested.ll'
case, and I think I see a problem:

void test() {
  try {
    Outer outer;
    try {
      Inner inner;
      do_inner_thing();
    } catch (int) {
      handle_int();
    }
  } catch (float) {
    handle_float();
  }
  keep_going();
}

This is the landing pad for the do_inner_thing() invoke:
lpad3:                                            ; preds = %invoke.cont2
  %8 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)*
@__CxxFrameHandler3 to i8*)
          cleanup at @_Z4testv.unwind.1 ; ~Inner
          catch i8* bitcast (i8** @_ZTIi to i8*) at @_Z4testv.catch.1 ;
handle_int()
          catch i8* bitcast (i8** @_ZTIf to i8*) at @_Z4testv.catch.0 ;
handle_float()

The @_Z4testv.unwind.1 helper just calls ~Inner(), but not ~Outer.

I think we'd need something more complex like this:

lpad3:                                            ; preds = %invoke.cont2
  %8 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)*
@__CxxFrameHandler3 to i8*)
          cleanup at @_Z4testv.unwind.1 ; ~Inner
          catch i8* bitcast (i8** @_ZTIi to i8*) at @_Z4testv.catch.1 ;
handle_int(), returns to code before non-exceptional call to ~Outer
          cleanup at @_Z4testv.unwind.0 ; ~Outer
          catch i8* bitcast (i8** @_ZTIf to i8*) at @_Z4testv.catch.0 ;
handle_float()

Maybe we could make these changes, but once we do that it's not clear to me
that we should be using the same landing pad instruction anymore.

Even if we did make these changes, we'll have landing pads like:

  invoke @do_inner_thing() ... unwind label %lpad1
  invoke @~Inner() ... unwind label %lpad2
  invoke @~Outer() ... unwind label %lpad3

lpad1:
  landingpad ...
    cleanup at ~Inner
    catch @"typeinfo for int" at @handle_int
    cleanup at ~Outer
    catch @"typeinfo for float" at @handle_float
lpad2:
  landingpad ...
    catch @"typeinfo for int" at @handle_int
    cleanup at ~Outer
    catch @"typeinfo for float" at @handle_float
lpad3:
  landingpad ...
    catch @"typeinfo for float" at @handle_float

This is still pretty far away from the tables that _CxxFrameHandler3 wants.

I'm starting to think that what we really want to do is build an internal
LLVM global constant representing the action table at preparation time. I
don't fully understand the .xdata that MSVC emits for _CxxFrameHandler3
yet, but I suspect that 90% of it can be prepared ahead of time without
using PC values. All landing pads can be assigned a state number, and we
can insert a call to an intrinsic (@llvm.eh.state(i32 %n) maybe?) in each
landingpad. After that, we can truncate the block with unreachable and
prune away any unreachable blocks. Now all the backend has to do is fill in
a table mapping PC to state.

What do you think? One downside is that ConstantStructs are kind of
expensive.

On Mon, Jan 26, 2015 at 2:27 PM, Kaylor, Andrew <andrew.kaylor at intel.com>
wrote:

>  I am working on adding support for C++ exception handling when compiling
> for a native Windows target (that is a target with "MSVC" specified as the
> environment).  Because of differences between how the native Windows
> runtime handles exceptions and the Itanium-based model used by current LLVM
> exception handling code, I believe this will require some extensions to the
> LLVM IR, though I'm trying to leverage the existing mechanisms as much as
> possible.
>
>
>
> I'll discuss this below in more detail, but the summary is that I'm going
> to propose an extension to the syntax of the landing pad instruction to
> enable landing pad clauses to be outlined as external functions, and I'd
> like to introduce two new intrsinsics, llvm.eh.begin.catch and
> llvm.eh.end.catch, to replace calls to the libc++abi __cxa_begin_catch and
> __cxa_end_catch functions.
>
>
>
>
>
> Currently, LLVM supports 64-bit Windows exception handling for MinGW
> targets using a custom personality function and the libc++abi library.
> There are also LLVM clients, such as ldc, that provide Windows exception
> handling similar to what I am proposing by providing their own custom
> personality function.  However, what I would like is to support Windows C++
> exception handling using the __CxxFrameHandler3 function provided by the
> native Windows runtime library.
>
>
>
> Some of the primary challenges in supporting native Windows C++ exception
> handling are:
>
>
>
> 1. Catch and unwind handlers are called in a different frame context than
> the original function in which they are defined.
>
>
>
> 2. Windows exception handling is state driven rather than landing pad
> based.  The compiler must generate a table for each function mapping IP
> addresses within that function to the EH state at that address.  When an
> exception is thrown the runtime uses this table to determine which unwind
> and catch handlers should be invoked.
>
>
>
> 3. Windows catch and unwind handling is implemented using a series of
> calls to discrete handlers rather than a jump to a landing pad which uses
> runtime decisions to reach all relevant handler blocks as is done in LLVM's
> existing implementations.  LLVM's current landing pad structure frequently
> results in in catch handling blocks and cleanup blocks which are shared by
> multiple landing pads.  Windows expects each catch handler and unwind
> handler to be defined in a single location.  The runtime then determines
> which handlers should be called based on the EH state when an exception is
> thrown and makes a series of calls when multiple handlers are needed.
>
>
>
>
>
> The first challenge is relatively easy to address. The Microsoft C++
> compiler creates a psuedo-function for handlers which it embeds in the body
> of the parent function, but for LLVM I would like to try simply outlining
> the handler bodies into fully external functions.  The task of outlining
> the handler code is somewhat straightforward and can be done with the
> existing IR.  However, I need a way to link the landing pads from the
> parent function to the outlined handlers.  I propose doing this by
> extending the syntax of the landing pad instruction to allow the address of
> an outlined handler to be attached to catch and cleanup clauses.
>
>
>
> The current syntax for landingpad is:
>
>
>
>   <resultval> = landingpad <resultty> personality <type> <pers_fn>
> <clause>+
>
>   <resultval> = landingpad <resultty> personality <type> <pers_fn> cleanup
> <clause>*
>
>
>
>   <clause> := catch <type> <value>
>
>   <clause> := filter <array constant type> <array constant>
>
>
>
> I'd like to change that to:
>
>
>
>   <resultval> = landingpad <resultty> personality <type> <pers_fn>
> <clause>+
>
>   <resultval> = landingpad <resultty> personality <type> <pers_fn> cleanup
> [at handler] <clause>*
>
>
>
>   <clause> := catch <type> <value> [at handler]
>
>   <clause> := filter <array constant type> <array constant>
>
>
>
>
>
> Outlined handlers will reference frame variables from the parent function
> using the llvm.frameallocate and llvm.framerecover functions.  Any frame
> variable which must be referenced from a catch or cleanup handler will be
> moved into a block allocated by llvm.frameallocate.  When the handlers are
> called, the parent function's frame pointer is passed as the second
> argument to the call.  The handlers will use this frame pointer to find the
> frame allocation block from the parent function.  The frame allocation
> block will also contain space for an exception state variable and an
> exception object pointer.  These values are maintained by the runtime
> library.
>
>
>
> Current LLVM landing blocks use calls to __Cxa_begin_catch to get a
> pointer to the object associated with the exception.  This function is
> provided by the libc++abi library and is specific to the personality
> function being used.  I would like to introduce a new intrinsic
> (llvm.eh.being.catch) which accomplishes the same result in a
> personality-function independent way.  For consistency, I also propose
> introducing llvm.eh.end.catch to replace calls to __cxa_end_catch.
>
>
>
> I am attaching several examples showing the outlining transformation I am
> proposing.  Note that for simplicity I've used Linux type information in
> these examples, but the final implementation will need to use
> Microsoft-style RTTI.  I believe clang already has support for that.
>
>
>
> The 'simple.ll' example shows a function with a single catch-all handler.
> The 'catch-type.ll' example shows a function which catches a specific type
> of exception.  The 'min-unwind.ll' example shows a function which has no
> exception handlers but which requires an unwind handler.  The 'nested.ll'
> example shows a function which has nested try blocks.
>
>
>
> The nested example illustrates the challenge mentioned above with regard
> to inter-mingled handlers.  I think I know how I will accomplish the
> outlining shown in that example and generate the state tables needed by the
> __CxxFrameHandler3 personality function, but I'm going to skip discussion
> of the details for now.
>
>
>
> However, I do want to at least open discussion of the problem of EH state
> handling.  The native Windows C++ exception handling essentially needs an
> EH state assigned to each basic block.  I have an idea for how I might be
> able to infer the EH states based on the targets of invoke instructions.  I
> think I can make this work in a way that will produce correct results for
> synchronous C++ exception handling.  However, I don't think I can get it to
> map exactly to the actual C++ scopes in the original source code.  For this
> reason, assuming we would like to support asynchronous C++ exception
> handling at some future time, I think it may be preferable to have the EH
> states embedded by the front end, possibly as metadata.  I haven't thought
> through all of the possible problems here, and I am open to suggestions.
>
> _______________________________________________
> LLVM Developers mailing list
> LLVMdev at cs.uiuc.edu         http://llvm.cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20150126/9acffd05/attachment.html>


More information about the llvm-dev mailing list