This is a revision of the second exception handling proposal I sent out. You can see it here: http://lists.cs.uiuc.edu/pipermail/llvmdev/2010-November/036484.html After much discussion, there are some changes to the proposal – some significant and some minor. One major point, this proposal does not address the issue of catching an exception thrown from a non-invoke instruction. However if done properly, it should form the foundation for that. General Model ============ The unwind edge from an invoke instruction jumps to a landing pad. That landing pad contains code which performs optional cleanups, and then determines which catch handler to call (if any). If no catch handlers are applicable, the exception resumes propagation either to the next enclosing region or out of the function. Basic Block Attributes ===================== A basic block can be given an attribute. General Syntax: label: attr1 attr2 ... attrn For exception handling, we introduce the `landingpad' attribute. lp: landingpad A basic block that has the `landingpad' attribute is considered a (surprise!) "landing pad". • It may be branched to only by the unwind edge of an invoke instruction or by the resume edge of a dispatch instruction. • A landing pad must have exactly one dispatch instruction associated with it, and it must dominate that instruction. • If a landing pad is split, only the section containing the start of the basic block retains the `landingpad' attribute. • The critical edges may not be split. • The attribute may not be removed. Dispatch Instruction =================== The dispatch instruction is the work horse of the exception handling design. It replaces the llvm.eh.selector and llvm.eh.typeid.for intrinsics. Syntax: dispatch resume to label <resumedest> catches [ <type> <val>, label <dest1> ... ] catchall [ <type> <val>, label <dest> ] personality [<type> <value>] filters [ <type> <val>, <type> <val>, ... ] The `catches', `catchall', and `filters' clauses are optional. If neither `catches' nor `catchall' is specified, then the landing pad is implicitly a cleanup. • The `<resumedest>' basic block is the destination to unwind to if the exception cannot be handled by this landing pad. • The `catches' clause is a list of types which the landing pad can handle 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 `personality' clause indicates the personality function for the landing pad. • The `filters' clause lists the types of exceptions which may be thrown by the region. Invoke Instruction ================= The "invoke" instruction will not change. :-) Examples ======= Consider this program: $ cat t.cpp #include <cstdio> extern void foo(); struct A { ~A(); }; struct B { ~B(); }; struct C { ~C(); }; void bar() { try { foo(); A a; foo(); B b; foo(); C c; foo(); } catch (int i) { printf("caught int: %d\n", i); } catch (const char *c) { printf("caught string: %s\n", c); } catch (...) { printf("catchall\n"); } } Here's a simplified example of what code would be generated for this (there may be subtle errors...it's handcrafted): $ llvm-g++ -o - -emit-llvm -S t.cpp -Os @.str = private constant [16 x i8] c"caught int: %d\0A\00", align 1 @.str1 = private constant [19 x i8] c"caught string: %s\0A\00", align 1 @.str2 = private constant [9 x i8] c"catchall\00", align 1 @_ZTIi = external constant %struct.__fundamental_type_info_pseudo @_ZTIPKc = external constant %struct.__pointer_type_info_pseudo define void @_Z3barv() optsize ssp { entry: %a = alloca %struct.A, align 8 %b = alloca %struct.B, align 8 %c = alloca %struct.C, align 8 invoke void @_Z3foov() to label %invcont unwind label %catch.handlers invcont: invoke void @_Z3foov() to label %invcont1 unwind label %a.dtor invcont1: invoke void @_Z3foov() to label %invcont2 unwind label %b.dtor invcont2: invoke void @_Z3foov() to label %bb1 unwind label %c.dtor bb1: invoke void @_ZN1CD1Ev(%struct.C* %c) to label %bb2 unwind label %b.dtor bb2: invoke void @_ZN1BD1Ev(%struct.B* %b) to label %bb3 unwind label %a.dtor bb3: invoke void @_ZN1AD1Ev(%struct.A* %a) to label %return unwind label %catch.handlers return: ret void ;; Catch Handlers catch.handlers: landingpad %eh_ptr = call i8* @llvm.eh.exception() dispatch resume to label %... catches [ %struct.__fundamental_type_info_pseudo* @_ZTIi, label %ch.int %struct.__pointer_type_info_pseudo* @_ZTIPKc, label %ch.str ] catchall [i8* null, label % ch.ca] ch.int: %t0 = call i8* @__cxa_begin_catch(i8* %eh_ptr) nounwind %t1 = bitcast i8* %t0 to i32* %t2 = load i32* %t1, align 4 %t3 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([16 x i8]* @.str, i64 0, i64 0), i32 %t2) call void @__cxa_end_catch() ret void ch.string: %t4 = call i8* @__cxa_begin_catch(i8* %eh_ptr) nounwind %t5 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([19 x i8]* @.str1, i64 0, i64 0), i8* %t4) call void @__cxa_end_catch() ret void ch.ca: %t6 = call i8* @__cxa_begin_catch(i8* %eh_ptr) nounwind %t7 = call i32 @puts(i8* getelementptr inbounds ([9 x i8]* @.str2, i64 0, i64 0)) call void @__cxa_end_catch() ret void ;; C's d'tor c.dtor: invoke void @_ZN1CD1Ev(%struct.C* %c) to label %b.dtor unwind label %lpad.c.dtor ;; B's d'tor b.dtor: invoke void @_ZN1BD1Ev(%struct.B* %b) to label %a.dtor unwind label %lpad.b.dtor ;; A's d'tor a.dtor: landingpad invoke void @_ZN1AD1Ev(%struct.A* %a) to label %onto.catch.handlers unwind label %lpad.a.dtor onto.catch.handlers: dispatch resume to %lpad ;; Desperate times... lpad.c.dtor: landingpad dispatch resume to %yikes lpad.b.dtor: landingpad dispatch resume to %yikes lpad.a.dtor: landingpad dispatch resume to %yikes yikes: call void @_ZSt9terminatev() noreturn nounwind unreachable } Because of the invariant that a landing pad must dominate a dispatch and because a landing pad may be branched to only by an invoke and dispatch instructions, the CFG now has enough information in it to collect the information needed to create the exception handling tables. Well? What do you think? Pretty cool, eh? :-) -bw
On Nov 30, 2010, at 11:04 PM, Bill Wendling wrote:> • A landing pad must have exactly one dispatch instruction associated with it, > and it must dominate that instruction.Okay, but how do we find it? Looks good, though! John.
On Dec 1, 2010, at 12:10 AM, John McCall wrote:> On Nov 30, 2010, at 11:04 PM, Bill Wendling wrote: >> • A landing pad must have exactly one dispatch instruction associated with it, >> and it must dominate that instruction. > > Okay, but how do we find it? >We can modify the dispatch to point back to the landing pad it's associated with. Something like: lpad: landingpad dispatch region label %lpad ... or similar.> Looks good, though! >Thanks! -bw
On 1 December 2010 07:04, Bill Wendling <wendling at apple.com> wrote:> The unwind edge from an invoke instruction jumps to a landing pad. That landing pad contains code which performs optional cleanups, and then determines which catch handler to call (if any). If no catch handlers are applicable, the exception resumes propagation either to the next enclosing region or out of the function.Hi Bill, Looking good already! ;)> ch.int:shouldn't the catch handlers have "landingpad" attributes?> ;; C's d'tor > c.dtor:and the cleanups?> yikes: > call void @_ZSt9terminatev() noreturn nounwind > unreachableand yikes? Can we standardize "yikes" as the official terminate handler name? :D> Well? What do you think? Pretty cool, eh? :-)Yup. I think it's much more clear than the current scheme, at least for C++ Itanium ABI (AFAICS). Can anyone try an Ada (or any other language) example to see if it makes sense for them, too? cheers, --renato
On Dec 1, 2010, at 1:12 AM, Renato Golin wrote:> On 1 December 2010 07:04, Bill Wendling <wendling at apple.com> wrote: >> The unwind edge from an invoke instruction jumps to a landing pad. That landing pad contains code which performs optional cleanups, and then determines which catch handler to call (if any). If no catch handlers are applicable, the exception resumes propagation either to the next enclosing region or out of the function. > > Hi Bill, > > Looking good already! ;) >Thanks!>> ch.int: > > shouldn't the catch handlers have "landingpad" attributes? >I don't think they need it.>> ;; C's d'tor >> c.dtor: > > and the cleanups? >Nor these. Basically, I want the basic block that's labeled a "landing pad" to be jumped to by only a dispatch resume or unwind edge of invoke. We could do this with the c.dtor and ch.int here, but it would mean inserting useless "cleanup dispatches" that only resume to the block (see onto.catch.handlers for an example).>> yikes: >> call void @_ZSt9terminatev() noreturn nounwind >> unreachable > > and yikes? > > Can we standardize "yikes" as the official terminate handler name? :D >I wish! :-D>> Well? What do you think? Pretty cool, eh? :-) > > Yup. I think it's much more clear than the current scheme, at least > for C++ Itanium ABI (AFAICS). > > Can anyone try an Ada (or any other language) example to see if it > makes sense for them, too? >*Looks at Duncan* -bw
Hi Bill,> General Model > ============> > The unwind edge from an invoke instruction jumps to a landing pad. That landing pad contains code which performs optional cleanups, and then determines which catch handler to call (if any). If no catch handlers are applicable, the exception resumes propagation either to the next enclosing region or out of the function.I was immediately struck by the fact that you describe cleanups as being run before dispatching to handlers. In Ada handlers are run first and any cleanups are run afterwards. As far as I know it is exactly the same in C++. Take your example. I rewrite it with more explicit scopes: void bar() { try { foo(); { // new scope A a; foo(); { // new scope ... etc... } // end scope } // end scope } catch (int i) { ... etc ... } Here cleanups for "a", "b" etc are only run before the handlers are executed because they are in inner scopes, and so are cleaned by unwinding before the "try" scope is reached. Consider the following example instead: void bar() { try { A a; foo(); } catch (int i) { ... etc ... } If you check the assembler you will see that the destructor for "a" is run before branching to the handler. I will comment on the rest later, once I have absorbed it. Ciao, Duncan.
Hi Bill, this proposal seems strangely complicated. I don't see the advantage of the dispatch instruction over just attaching the information to each invoke. Right now you have invoke void @_Z3foov() to label %invcont unwind label %catch.handlers catch.handlers: landingpad dispatch resume to label %... catches [ %struct.__fundamental_type_info_pseudo* @_ZTIi, label %ch.int %struct.__pointer_type_info_pseudo* @_ZTIPKc, label %ch.str ] catchall [i8* null, label % ch.ca] Why not combine this into: invoke void @_Z3foov() to label %invcont unwind resume to label %... catches [ %struct.__fundamental_type_info_pseudo* @_ZTIi, label %ch.int %struct.__pointer_type_info_pseudo* @_ZTIPKc, label %ch.str ] catchall [i8* null, label % ch.ca] ? That would get rid of the "landingpad" basic block attribute, and rules about what can branch to landing pads, that these cannot be split etc. Your rules mean that you can uniquely associate a dispatch instruction with each invoke unwind edge, so why not directly attach the dispatch information to the edge, i.e. to the invoke?> Syntax: > > dispatch resume to label<resumedest>How do you say: if nothing is matched, keep unwinding out of the function? Actually I don't see why <resumedest> is useful at all, since you can always place a complete set of all handlers into one dispatch, so redispatching is not needed. That said, it might be handy for reducing code duplication.> catches [ > <type> <val>, label<dest1> > ... > ] > catchall [<type> <val>, label<dest> ] > personality [<type> <value>] > filters [ > <type> <val>,<type> <val>, ...You also need labels for filters. I know it may not look like it, but if you inline a function with a filter into a function with handlers then the filter ends up being nested inside outer handlers, resulting in the need for a destination. In fact inlining also means that you can't just list filters after catches, you need to consider maybe having some catches, then a filter, then some other catches etc. Or perhaps you want to take care of it by chaining dispatches via the resumedest?> ] > > The `catches', `catchall', and `filters' clauses are optional. If neither `catches' nor `catchall' is specified, then the landing pad is implicitly a cleanup.You may have cleanups to run even if there are handlers, how do you distinguish between "have handlers, plus a cleanup to run in any case" and "have handlers, no cleanup needs to be run if handlers don't match, just unwind straight through"?> • The `filters' clause lists the types of exceptions which may be thrown by the > region.What is "a region"?> onto.catch.handlers: > dispatch resume to %lpadWhat is %lpad? As a general remark, I don't see how this is superior to gcc's region scheme, which could be adapted to LLVM by (in essence) attaching to an invoke the list of all regions (and their info like list of catches) that contain the invoke. If you do that then you get something rather close to your proposal, only the dispatch instructions have become part of the invokes, you don't need the funny landing pad special semantics etc. Ciao, Duncan.
On Dec 1, 2010, at 9:52 AM, Duncan Sands wrote:> Hi Bill, > >> General Model >> ============>> >> The unwind edge from an invoke instruction jumps to a landing pad. That landing pad contains code which performs optional cleanups, and then determines which catch handler to call (if any). If no catch handlers are applicable, the exception resumes propagation either to the next enclosing region or out of the function. > > I was immediately struck by the fact that you describe cleanups as being run > before dispatching to handlers. In Ada handlers are run first and any cleanups > are run afterwards. As far as I know it is exactly the same in C++. Take your > example. I rewrite it with more explicit scopes: > > void bar() { > try { > foo(); > { // new scope > A a; > foo(); > { // new scope > ... etc... > } // end scope > } // end scope > } catch (int i) { > ... etc ... > } > > Here cleanups for "a", "b" etc are only run before the handlers are executed > because they are in inner scopes, and so are cleaned by unwinding before the > "try" scope is reached.I don't understand what you're trying to say here. I looked at the assembly for your first bar() function and it's exactly the same as would be emitted for one without the new scopes. Indeed, it's semantically identical. What do you mean by "before the try scope is reached"? If an exception is thrown by something in a nested scope, any variables live at that point must have their cleanups called before exiting the try block. And that occurs before the catch handlers are called.> Consider the following example instead: > > void bar() { > try { > A a; > foo(); > } catch (int i) { > ... etc ... > } > > If you check the assembler you will see that the destructor for "a" is run > before branching to the handler. >Correct. How is this a contradiction of what I wrote? I'm confused. -bw -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20101201/e1f5115d/attachment.html>
On Dec 1, 2010, at 10:20 AM, Duncan Sands wrote:> Hi Bill, this proposal seems strangely complicated. I don't see the advantage > of the dispatch instruction over just attaching the information to each invoke. > Right now you have > > invoke void @_Z3foov() > to label %invcont unwind label %catch.handlers > > catch.handlers: landingpad > dispatch resume to label %... > catches [ > %struct.__fundamental_type_info_pseudo* @_ZTIi, label %ch.int > %struct.__pointer_type_info_pseudo* @_ZTIPKc, label %ch.str > ] > catchall [i8* null, label % ch.ca] > > Why not combine this into: > > invoke void @_Z3foov() > to label %invcont unwind resume to label %... > catches [ > %struct.__fundamental_type_info_pseudo* @_ZTIi, label %ch.int > %struct.__pointer_type_info_pseudo* @_ZTIPKc, label %ch.str > ] > catchall [i8* null, label % ch.ca] > > ? That would get rid of the "landingpad" basic block attribute, and rules about > what can branch to landing pads, that these cannot be split etc. Your rules > mean that you can uniquely associate a dispatch instruction with each invoke > unwind edge, so why not directly attach the dispatch information to the edge, > i.e. to the invoke? >That was my first proposal (way back when). I ran into the problem of cleanups. Basically, they get in the way of the decision making process. Consider this: invoke void @_Z3foov() to label %invcont unwind resume to label %... catches [ %struct.__fundamental_type_info_pseudo* @_ZTIi, label %ch.int %struct.__pointer_type_info_pseudo* @_ZTIPKc, label %ch.str ] catchall [i8* null, label % ch.ca] Now consider that a cleanup must occur here. How do we represent this here and still keep the information that "int" goes to ch.int and "const char *" goes to ch.str? The cleanups must happen before we get to ch.int, ch.str, and ch.ca. And cleanup code can be arbitrarily complex – including the fact that it may not return, or it may have multiple exit points. You end up needing a dispatch-like instruction at the end of the cleanup region so that you can associated the invoke to the decision point – the point where we determine which handler we need to execute. After we realized this, this proposal became the natural way.>> Syntax: >> >> dispatch resume to label<resumedest> > > How do you say: if nothing is matched, keep unwinding out of the function? > Actually I don't see why <resumedest> is useful at all, since you can always > place a complete set of all handlers into one dispatch, so redispatching is > not needed. That said, it might be handy for reducing code duplication. >The <resumedest> will have code to handling unwinding out of the function. Normally, it will just call _Unwind_Resume(). As you saw in the example, though, it may call other things (like `terminate()'). I would love to have a language-neutral way of saying "resume unwinding". I don't know if it's sufficient to rely upon every implementation to call _Unwind_Resume().>> catches [ >> <type> <val>, label<dest1> >> ... >> ] >> catchall [<type> <val>, label<dest> ] >> personality [<type> <value>] >> filters [ >> <type> <val>,<type> <val>, ... > > You also need labels for filters. I know it may not look like it, but > if you inline a function with a filter into a function with handlers > then the filter ends up being nested inside outer handlers, resulting > in the need for a destination. In fact inlining also means that you > can't just list filters after catches, you need to consider maybe > having some catches, then a filter, then some other catches etc. Or > perhaps you want to take care of it by chaining dispatches via the > resumedest? >When I looked at code which inlined functions with filters, it seemed like they were attached to the throwing instructions. In that case, you will have the dispatch of the inlined function, which will be associated with its invokes, and the filter information will exist there. In that way, we have the "nesting" that you described.>> ] >> >> The `catches', `catchall', and `filters' clauses are optional. If neither `catches' nor `catchall' is specified, then the landing pad is implicitly a cleanup. > > You may have cleanups to run even if there are handlers, how do you distinguish > between "have handlers, plus a cleanup to run in any case" and "have handlers, > no cleanup needs to be run if handlers don't match, just unwind straight > through"? >The first case is my example. E.g.: lpad: landingpad call void @cleanup(%A* %a) dispatch resume to label %unwind catches [ . . . ] etc. The second one would be the job of the front-end to generate the correct dispatch. E.g: lpad: landingpad dispatch resume to label %unwind catches [ . . . ]>> • The `filters' clause lists the types of exceptions which may be thrown by the >> region. > > What is "a region"? >I defined it in the first document, which was referenced: A "region" is defined as a section of code where, if an exception is thrown anywhere within that section, control is passed to code that will handle the exception (called a "landing pad"). The code which handles the specific exception is unique to that region.>> onto.catch.handlers: >> dispatch resume to %lpad > > What is %lpad? >A typo. It should be %catch.handlers> As a general remark, I don't see how this is superior to gcc's region scheme, > which could be adapted to LLVM by (in essence) attaching to an invoke the list > of all regions (and their info like list of catches) that contain the invoke. > If you do that then you get something rather close to your proposal, only the > dispatch instructions have become part of the invokes, you don't need the funny > landing pad special semantics etc. >Could you describe GCC's region scheme? -bw -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20101201/cd9b5b03/attachment.html>
Hi Bill, there are a couple of things I didn't understand about your proposal, for example how it interacts with inlining, whether it is feasible to do the "turn invoke-of-Unwind_Resume into a branch" optimization and also whether in "resumedest" you still plan to use _Unwind_Resume to continue unwinding up the stack. Could you please show what the LLVM IR would look like for the following example both before and after inlining, and if the optimization I mentioned is compatible with the dispatch instruction can you please show how the IR would look after it is applied (right now it has the nasty habit of leaving calls to eh.selector far away from any landing pad, and I'm worried it might leave stray dispatch instructions in odd places, breaking the rules about where they may be placed). Thanks a lot, Duncan. #include <stdio.h> struct X { ~X() { printf("Running destructor!\n"); } }; void foo() { struct X x; throw 1; } int main() { try { foo(); } catch (int) { printf("Caught exception!\n"); } return 0; }
On Dec 7, 2010, at 12:30 PM, Duncan Sands wrote:> Hi Bill, there are a couple of things I didn't understand about your proposal, > for example how it interacts with inlining, whether it is feasible to do the > "turn invoke-of-Unwind_Resume into a branch" optimization and also whether in > "resumedest" you still plan to use _Unwind_Resume to continue unwinding up the > stack. Could you please show what the LLVM IR would look like for the following > example both before and after inlining, and if the optimization I mentioned is > compatible with the dispatch instruction can you please show how the IR would > look after it is applied (right now it has the nasty habit of leaving calls to > eh.selector far away from any landing pad, and I'm worried it might leave stray > dispatch instructions in odd places, breaking the rules about where they may be > placed). > > Thanks a lot, Duncan. > > #include <stdio.h> > > struct X { ~X() { printf("Running destructor!\n"); } }; > > void foo() { > struct X x; > throw 1; > } > > int main() { > try { > foo(); > } catch (int) { > printf("Caught exception!\n"); > } > return 0; > }Hi Duncan, I glossed over inlining a bit in my proposal, so let me step through your example to see how it would work. (I've elided some of the extraneous IR stuff – types, function decls, etc. – but none of the code.) Here's the initial, non-inlined code: @_ZTIi = external constant %struct.__fundamental_type_info_pseudo @.str = private constant [20 x i8] c"Running destructor!\00", align 1 @.str1 = private constant [18 x i8] c"Caught exception!\00", align 1 define void @_Z3foov() noreturn optsize ssp { entry: %tmp0 = tail call i8* @__cxa_allocate_exception(i64 4) nounwind %tmp1 = bitcast i8* %tmp0 to i32* store i32 1, i32* %tmp1, align 4 invoke void @__cxa_throw(i8* %tmp0, i8* bitcast (%struct.__fundamental_type_info_pseudo* @_ZTIi to i8*), void (i8*)* null) noreturn to label %invcont unwind label %invcont1 invcont: unreachable invcont1: landingpad %tmp2 = tail call i32 @puts(i8* getelementptr inbounds ([20 x i8]* @.str, i64 0, i64 0)) nounwind dispatch region label %invcont1 resume to label %Unwind personality [i32 (...)* @__gxx_personality_v0] Unwind: %eh_ptr = tail call i8* @llvm.eh.exception() tail call void @_Unwind_Resume_or_Rethrow(i8* %eh_ptr) unreachable } define i32 @main() optsize ssp { entry: invoke void @_Z3foov() optsize ssp to label %bb unwind label %lpad bb: ret i32 0 lpad: landingpad %eh_ptr = tail call i8* @llvm.eh.exception() dispatch region label %lpad resume to label %Unwind catches [ %struct.__fundamental_type_info_pseudo* @_ZTIi, label %ch.int ] personality [i32 (...)* @__gxx_personality_v0] ch.int: %tmp0 = tail call i8* @__cxa_begin_catch(i8* %eh_ptr) nounwind %tmp1 = tail call i32 @puts(i8* getelementptr inbounds ([18 x i8]* @.str1, i64 0, i64 0)) tail call void @__cxa_end_catch() ret i32 0 Unwind: tail call void @_Unwind_Resume(i8* %eh_ptr) unreachable } Here is a diagram of what the two functions look like: foo: .--------------------. | invoke __cxa_throw | `--------------------' | .----------------------. | normal | unwind v | unreachable v .-----------------------------. | print "Running destructor!" | | dispatch |----> resume `-----------------------------' main: .------------. | invoke foo | `------------' | .-----------------. | normal | unwind v | ret 0 v .----------. | dispatch |----> resume `----------' | catch ty: int v .---------------------------. | print "Caught exception!" | | ret 0 | `---------------------------'>From this, you can imagine chaining the dispatches together. I.e., the inlined normal edge will point to the unreachable instruction. The 'ret 0' block in main will have no predecessors. The inlined dispatch's 'resume' edge will be modified to jump to main's landing pad. The '_Unwind_Resume' in the inlined function will have no predecessors.Now in code (I won't replicate the "foo" function here since it doesn't change): define i32 @main() optsize ssp { entry.foo: %tmp0.foo = tail call i8* @__cxa_allocate_exception(i64 4) nounwind %tmp1.foo = bitcast i8* %tmp0.foo to i32* store i32 1, i32* %tmp1.foo, align 4 invoke void @__cxa_throw(i8* %tmp0.foo, i8* bitcast (%struct.__fundamental_type_info_pseudo* @_ZTIi to i8*), void (i8*)* null) noreturn to label %invcont.foo unwind label %invcont1.foo invcont.foo: unreachable invcont1.foo: landingpad %tmp2.foo = tail call i32 @puts(i8* getelementptr inbounds ([20 x i8]* @.str, i64 0, i64 0)) nounwind dispatch region label %invcont1.foo resume to label %lpad.main personality [i32 (...)* @__gxx_personality_v0] lpad.main: landingpad %eh_ptr = tail call i8* @llvm.eh.exception() dispatch region label %lpad.main resume to label %Unwind.main catches [ %struct.__fundamental_type_info_pseudo* @_ZTIi, label %ch.int.main ] personality [i32 (...)* @__gxx_personality_v0] ch.int.main: %tmp0.main = tail call i8* @__cxa_begin_catch(i8* %eh_ptr) nounwind %tmp1.main = tail call i32 @puts(i8* getelementptr inbounds ([18 x i8]* @.str1, i64 0, i64 0)) tail call void @__cxa_end_catch() ret i32 0 Unwind.main: tail call void @_Unwind_Resume(i8* %eh_ptr) unreachable ;;; The blocks below have no predecessors Unwind.foo: ;; << No more predecessors %eh_ptr = tail call i8* @llvm.eh.exception() tail call void @_Unwind_Resume_or_Rethrow(i8* %eh_ptr) unreachable bb.main: ;; << No more predecessors ret i32 0 } What the inliner would do in this case is look at the invoke/dispatch pair from foo. It would determine that it's a cleanup-only landing pad. And redirect the "resume to" edge to the resume destination for the inlinee's invoke/dispatch pair. -bw
Maybe Matching Threads
- [LLVMdev] RFC: Exception Handling Proposal Revised
- [LLVMdev] RFC: Exception Handling Proposal Revised
- [LLVMdev] RFC: Exception Handling Proposal Revised
- [LLVMdev] RFC: Exception Handling Proposal Revised
- [LLVMdev] Catching C++ exceptions, cleaning up, rethrowing