[LLVMdev] [WinEH] A hiccup for the Windows C++ exception handling

Kaylor, Andrew andrew.kaylor at intel.com
Wed May 13 11:03:45 PDT 2015


I've been working recently to get the following test case working for an x86_64-pc-windows-msvc target.

void test1() {
  try {
    try {
      throw 1;
    } catch(...) {
      throw;
    }
  } catch (...) {
  }
}

I committed a patch earlier in the week to get the WinEHPrepare pass to produce IR that I thought was sufficient, but as I mentioned in the review I was still seeing runtime errors when executing a program compiled with this code.  I have since figured out why the program crashes at runtime, but fixing it is going to be a bit tricky.  It has to do with some wrinkles in how the Microsoft runtime handles the rethrown exceptions.

As the code above is written, the outer catch should handle both the inner catch and its try-block and the outer catch handler should return to a location in the parent function.

An ideal numbering of the EH states would look something like this:

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
}

That would give us the following try map entries:

Inner catch { TryLow = 1, TryHigh = 1, CatchHigh = 2 }
Outer catch { TryLow = 0, TryHigh = 2, CatchHigh = 3 }

Unfortunately, we can't deduce this from the IR because the outer catch handler is only ever seen to catch exceptions from within the inner catch.  So we end up producing a try map like this. (Even this requires a small patch to the current code to avoid dropping the second entry entirely.)

Inner catch { TryLow = 1, TryHigh = 1, CatchHigh = 3 }
Outer catch { TryLow = 2, TryHigh = 2, CatchHigh = 3 }

This causes the runtime to interpret the outer catch as if it were nested within the inner catch.  In other words, the code is indistinguishable from this:

void test2() {
  try {
    throw 1;
  } catch(...) {
    try {
      throw;
    } catch (...) {
    }
  }
}

And indeed, the prepared IR we produce for these two cases is the same.  (There is a critical difference before WinEHPrepare that I'll discuss in a bit.)

Here is where the runtime problem comes in.  Because the runtime thinks the second catch is nested within the first, it doesn't actually rethrow the exception.  It just passes it on to the second handler, which it expects to return to an address within the first catch handler.  However, we aren't currently generating code that does that (in either case) because when we outline the first handler we insert a stub for the nested handler and without knowing what the second handler does, the block to which it should return appears to be unreachable.

Knowing this, I can fix the simple case easily enough by changing the order in which the landing pads are processed so that I don't need to stub out the nested handler and can use its return targets to make the required block reachable.

However, this opens a huge can of worms for non-trivial cases.  There are scenarios (the original test case among them) where a nested handler should return to an block outside the enclosing handler.  So how can the enclosing handler determine which blocks it should clone and which it shouldn't?  I'm working on this now, but it is quickly becoming clear that our current cloning scheme is not well suited to handle it.

So there are two issues here.

1) We need a better way to figure out which blocks should be in which handlers and properly remap the return statements in each handler accordingly.
2) We need a better way to compute the EH states.

Anyway, I'm working on these problems right now.  I'm still trying to solve the problems without redesigning our current scheme, but I don't think this is going to be the right long term solution.

Thoughts?

-Andy

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20150513/c23442a8/attachment.html>


More information about the llvm-dev mailing list