[LLVMdev] RFC: New EH representation for MSVC compatibility

Steve Cheng steve.ckp at gmail.com
Mon May 18 11:48:52 PDT 2015


On 2015-05-18 13:32:54 -0400, Reid Kleckner said:
> 
> Right, doing our own personality function is possible, but still has 
> half the challenge of using __CxxFrameHandler3, and introduces a new 
> runtime dependency that isn't there currently. Given that it won't save 
> that much work, I'd rather not introduce a dependency that wasn't there 
> before.
> 
> The reason it's still hard is that you have to split the main function 
> up into more than one subfunction.

I see, but I thought are able to outline cleanup code already?

And that the hiccup you are encountering is because __CxxFrameHandler3 
requires unwind tables with properly-ordered state transitions? The 
compiler SEH personality (_C_specific_handler) doesn't have that, 
right? If you could manage __try, __finally already, doesn't that 
provide the solution?

Let me be precise. Let's take your example with the "ambiguous IR lowering":

void test1() {
  // EH state = -1
  try {
    // EH state = 0
    try {
      // EH state = 1
      throw 1;
    } catch(...) {
      // EH state = 2
      throw;
    }
    // EH state = 0
  } catch (...) {
    // EH state = 3
  }
  // EH state = -1
}

If I were "lowering" to compiler SEH, I would do something like this:

If I were "lowering" to compiler SEH, I would do something like this:

  __try {
    // [0]
    // [1]
    __try {
      // [2]
      throw 1;
      // [3]
    } __except( MyCxxFilter1() ) {
      // [4]
      throw;
      // [5]
    }
    // [6]
    // [7]
  } __except( MyCxxFilter2() ) {
    // [8]
    // [9]
  }
  // [10]
  // [11]

My scope tables for _C_specific_handler look like this:

  BeginAddress EndAddress HandlerAddress JumpTarget
  [0]          [1]        MyCxxFilter2   [8]
  [2]          [3]        MyCxxFilter1   [4]
  [4]          [5]        MyCxxFilter2   [8]
  [6]          [7]        MyCxxFilter2   [8]

I'm "cheating" in that I can look at the source code,
but again, you already can lower __try, __except already using
_C_specific_handler.  There are no state transitions encoded
in the compiler SEH scope table so they aren't an issue...?

Now there is a subtle problem in my "lowering" in that the
there may be local objects with destructors, that have to
be lowered to __try, __finally.  Microsoft's compiler SEH,
and _C_specific_handler, does not allow a __try block
to have both __except and __finally following.  That's why
I suggest, writing a personality function, replacing
_C_specific_handler that does allow __finally + __except.


> The exception object is allocated in the frame of the function calling 
> __CxxThrow, and it has to stay alive until control leaves the catch 
> block receiving the exception.
> This is different from Itanium, where the exception object is 
> constructed in heap memory and the pointer is saved in TLS. If this 
> were not the case, we'd use the __gxx_personaltity_v0-style landingpad 
> approach and make a new personality variant that understands MS RTTI.

I'm surprised, I really want to check this myself later this week. I 
always thought that MSVCRT always copied your exception object because 
I have always seen it invoking the copy constructor on throw X. (It was 
a pain in my case because I didn't really want my exception objects to 
be copyable, only movable, and at least VS 2010 still insisted that I 
implement a copy constructor.)

Furthermore, the "catch info" in the MS ABI does have a field for the 
destructor that the catch block has to call. It's not theoretical, I've 
got code that calls that function pointer so I can properly catch C++ 
exceptions from a SEH handler. Though I might be mistaken in that the 
field points to just an in-place destructor and not a deleting 
destructor.

Also, I thought the stack already is unwinded completely when you reach 
the beginning of the catch block (but not a __finally block, i.e. the 
cleanup code). At least, that's the impression I get from reading 
reverse-engineered source code for the personality functions and the 
Windows API RtlUnwindEx.

> 
> We could try to do all this outlining in Clang, but that blocks a lot 
> of LLVM optimizations. Any object with a destructor (std::string) is 
> now escaped into the funclet that calls the destructor, and simple 
> transformations (SROA) require interprocedural analysis. This affects 
> the code on the normal code path and not just the exceptional path. 
> While EH constructs like try / catch are fairly rare in C++, destructor 
> cleanups are very, very common, and I'd rather not pessimize so much 
> code.

Right, but __CxxFrameHandler3 already forces you to outline destructor 
cleanups into funclets. So if you wanted to stop doing that you have to 
write your own personality function right?

What I am saying is, if you can design the personality function so that 
it works naturally with LLVM IR --- which can't see the source-level 
scopes --- that seems a whole lot less work versus:

* Changing the existing Itanium-based EH model in LLVM
* Incurring the wrath of people who like the Itanium model
* Having to maintain backwards compatibility or provide an upgrade path

Also, I think, if we want to eventually support trapped operations 
(some kind of invoke div_with_trap mentioned in another thread), 
wouldn't it be way easier to implement and optimize if the personality 
function can be designed in the right way?

Steve






More information about the llvm-dev mailing list