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
On 24 November 2010 13:27, Bill Wendling <wendling at apple.com> wrote:> 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.)Hi Bill, That's exactly how I pictured, glad I got it right... ;)> 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.I see. The front-end is responsible for knowing that baz, bar and foo cleanup area are chained together. So, if I got it right, the dispatch area is common for the whole function, all final cleanup areas point to the dispatch, and the back-end knows which dispatch "instruction" to call based on the region number. The dispatch instructions then would encode what type of dispatch they were just based on the filters, catches, etc.> 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()In the general case, that simplifies a lot. But in this case, if you were constructing the objects as you call them (as previous example), you'd have to tell the call to which cleanup landing pad you have to go. One option is to separate in basic blocks with different cleanup landing pads: bb: unwind to %FooCleanup call @f1.run() br bb1 bb1: unwind to %BarCleanup call @f2.run() br bb2 bb1: unwind to %BazCleanup call @f3.run() %0 = div i32 7 i32 0 ; this throws in Java and goes to BazCleanup br bb3 bb1: unwind to %Dispatch ; no cleanup call @f4.run() br bb4> 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.I hadn't quite got the role of the invoke in your intermediary model, now I get it. ;) I think we should ultimately treat calls like instructions and not mark them in any specific way regarding EH. cheers, --renato
On Nov 24, 2010, at 5:47 AM, Renato Golin wrote:>> 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. > > I see. The front-end is responsible for knowing that baz, bar and foo > cleanup area are chained together. > > So, if I got it right, the dispatch area is common for the whole > function, all final cleanup areas point to the dispatch, and the > back-end knows which dispatch "instruction" to call based on the > region number. The dispatch instructions then would encode what type > of dispatch they were just based on the filters, catches, etc. >That's correct. With the caveat that there can be more than one dispatch area per function and some may be nested.>> 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() > > In the general case, that simplifies a lot. > > But in this case, if you were constructing the objects as you call > them (as previous example), you'd have to tell the call to which > cleanup landing pad you have to go. > > One option is to separate in basic blocks with different cleanup landing pads: > > bb: unwind to %FooCleanup > call @f1.run() > br bb1 > bb1: unwind to %BarCleanup > call @f2.run() > br bb2 > bb1: unwind to %BazCleanup > call @f3.run() > %0 = div i32 7 i32 0 ; this throws in Java and goes to BazCleanup > br bb3 > bb1: unwind to %Dispatch ; no cleanup > call @f4.run() > br bb4 >Yeah, that's the basic idea. As John mentioned, this is an orthogonal change that we need to work out in the future. Chris had the above idea in the notes he wrote. But we will need to determine if it's sufficient or if there's a better solution.>> 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. > > I hadn't quite got the role of the invoke in your intermediary model, > now I get it. ;) > > I think we should ultimately treat calls like instructions and not > mark them in any specific way regarding EH. >I totally agree. :-) -bw