[LLVMdev] RFC: Exception Handling Proposal II

Bill Wendling wendling at apple.com
Wed Nov 24 05:27:15 PST 2010


On Nov 24, 2010, at 2:59 AM, Renato Golin wrote:

> Hi Bill,
> 
> First, I like the idea of being more expressive in IR, since the old
> exception handling style didn't quite express exception handling, but
> alternate paths (resume/unwind). That made DwarfException.cpp a big
> hack. ;)
> 
> It also makes the front-end engineer's life a lot easier, since there
> is less need to keep a long list of global state for all current
> landing pads, clean up areas, terminate handlers, etc.
> 
That's a big goal of this rewrite. :-)

> If I got it right, the dispatch instruction will tell the
> instructions/calls to unwind to specific landing pads (cleanup areas,
> terminate), but the region number will encode try/catch areas, so that
> all those cleanup landing pads should ultimately end up in the catch
> area for that region.
> 
Yes, that's correct.

> If that's so, how do you encode which which landing pad is to be
> followed per region?
> 
> Consider the following code:
> 
> try {
>  Foo f();
>  f.run(); // can throw exception
>  Bar b();
>  b.run(); // can throw exception
>  Baz z();
>  z.run(); // can throw exception
> } catch (...) {
> }
> 
> The object 'f' is in a different cleanup area than 'b' which, in turn
> is in a different area than 'z'. These three regions should point to
> three different landing pads (or different offsets in the same landing
> pad), which (I believe) are encoded in IR by being declared after
> different dispatch instructions, all of which within the same region.
> 
> So, if the dispatch instruction points to a catch area, how do you
> differentiate between clean-up areas? If they point to clean-up areas,
> how to you specify a catch area per group, to go after cleaning?
> 
There are two different bits of information in the proposal that address this: the "unwind edge" from the invoke and the region number. It's the "unwind edge" which carries the bit of information that you're asking about. My mental view of this is that the region number correlates to the dispatch instruction (and, therefore, the catch blocks) and the unwind edge correlates to the cleanups. (This isn't exactly correct, but it helps to understand how all of this information will be used to generate the correct tables, etc.)

So the above code would look something like this:

entry:
  invoke @f.run() to %bb unwind to %FooCleanup region(1)
bb:
  invoke @b.run() to %bb2 unwind to %BarCleanup region(1)
bb2:
  invoke @z.run() to %bb3 unwind to %BazCleanup region(1)
...
BazCleanup:
  ; do stuff
  br %BarCleanup
BarCleanup:
  ; do stuff
  br %FooCleanup
FooCleanup:
  ; do stuff
  br %Dispatch
Dispatch:
  dispatch region(1) resume %block
    catches [ ... ]

and so on.

If we remove the invoke instructions, the "unwind to" information then goes onto the basic block itself. E.g.:

bb: unwind to %FooCleanup
 call @f1.run()
 call @f2.run()
 call @f3.run()
 call @f4.run()

> My second question is, why did you keep the invoke?
> 
> As far as I got it, you have a dispatch instruction inside a basic
> block, and whatever throws an exception there should unwind to the
> dispatch area (if it matches the filters), in order of appearance,
> ending in the "resume unwinding" path if none matches.
> 
> If that's so, why do you still have the invoke call? Why should you
> treat call-exceptions any differently than instruction-exceptions?
> 
> Since this is a major refactoring of the IR (new back-ends won't
> understand old IRs at all), there is no point of keeping
> compatibility...
> 
I kept it purely from a practical standpoint: it will be easier (and thus quicker) to do it this way. :-) Also, it could be done a bit more incrementally than completely removing the invoke. But I'm not opposed to removing the invoke.

>> * The "catchall", "catches", and "filters" clauses are optional. If none are
>>  specified, then the landing pad is implicitly a "cleanup."
>> 
>> * The <resumedest> basic block is the destination to unwind to if the type
>>  thrown isn't matched to any of the choices.
>> 
>> * The "catches" clause is a list of types which the region can catch and the
>>  destinations to jump to for each type.
>> 
>> * The "catchall" clause is the place to jump to if the exception type doesn't
>>  match any of the types in the "catches" clause.
>> 
>> * The "region" value is an integer, similar to the "addrspace" value, and is
>>  unique to each dispatch in a function. IR objects reference this value to
>>  indicate that they belong to the same region.
>> 
>> * The "filters" clause lists the types of exceptions which may be thrown by the
>>  region.
> 
> These are major enhancements over the current model, as the back-end
> doesn't have to guess anything. Both C++ and Java have those concepts
> explicit in the language and (AFAIK) that was somewhat lost in the
> "encoding".
> 
Thanks, Renato! :-)

-bw





More information about the llvm-dev mailing list