What? Yet another EH proposal?! This one is different from the others in that I'm planning to start implementing this shortly. But I want your feedback! I've all ready gotten a lot of feedback from Chris, John, Jim, Eric, and many others. Now is your turn! Please read this proposal and send me your comments, suggestions, and concerns. -bw //===----------------------------------------------------------------------===// // LLVM Exception Handling Rewrite //===----------------------------------------------------------------------===// 7/16/2011 - Initial revision 7/18/2011 - Chris's feedback incorporated 7/19/2011 - John's feedback incorporated 7/22/2011 - Final revision: Chris's feedback incorporated The current exception handling system works for the most part. We have been able to work with our existing implementation now and get it to produce exception handling code for a wide variety of situations. However, we are reaching the limit of what we are able to support with it. In particular, it suffers from these deficiencies: 1. It's very hard to perform inlining through an `invoke' instruction. Because the information is stored in an intrinsic, it's very difficult to merge one function's exception handling with another. 2. The EH intrinsics, which contain the exception handling information for an invoke (e.g., 'llvm.eh.exception' and 'llvm.eh.selector'), can move out of the landing pad during normal code motion thus making retrieving EH information very difficult. 3. We are currently able to inline two functions which have incompatible personality functions. This breaks the semantics of the original program. 4. The exception handling ABI is not followed. Instead, we approximate it using catchalls and calls to non-standard APIs (e.g., '_Unwind_Resume_or_Rethrow'). 5. It's inefficient. Because of the constant rethrowing of exceptions, a normal exception takes much longer to execute. In order to address these issues, we need a much better way to represent exceptions in LLVM IR. //===----------------------------------------------------------------------===// // Proposal //===----------------------------------------------------------------------===// We start with the existing system and try to modify it to eliminate its negative aspects. This has many benefits, not the least of which is that it's easier for existing front-ends to adopt the new exception handling design. The heart of the proposal is to directly associate unwinding information for an invoke with the invoke, and to directly expose the values produced in the landing pad. We do this by introducing a new 'landingpad' instruction which is always required to the first non-phi instruction in the 'unwind' block of a landing pad block. Because of this direct association, it is always possible to find the invoke for a landing pad, and always possible to find the landing pad for an invoke. The 'landingpad' instruction is an instruction (not an intrinsic) because it has a variadic but highly structured argument list, and can return arbitrary types (specified by the personality function and ABI). //===-------------------------- // The 'landingpad' Instruction // The 'landingpad' instruction replaces the current 'llvm.eh.exception' and 'llvm.eh.selector' intrinsics. // Syntax: %res = landingpad <somety> personality <ty> <pers_fn> <clause>+ where <clause> : cleanup | catch <ty_1>, <ty_2>, ..., <ty_n> | filter <ty_1>, <ty_2>, ..., <ty_m> and the result has the type '<somety>'. The personality functions must be the same for all landingpad instructions in a given function. A landingpad instruction must contain at least one cleanup, catch, or filter clause. // Restrictions: There are several new invariants which will be enforced by the verifier: 1. A landing pad block is a basic block which is the unwind destination of an invoke instruction. 2. A landing pad block must have a landingpad instruction as its first non-PHI instruction. 3. The landingpad instruction must be the first non-PHI instruction in the landing pad block. 4. Like indirect branches, splitting the critical edge to a landing pad block requires considerable care, and SplitCriticalEdge will refuse to do it. 5. All landingpad instructions in a function must have the same personality function. // Semantics: The landingpad instruction defines the values which are set by the personality function upon reentry to the function, and therefore the "result type" of the landing pad instruction. With these changes, LLVM IR will be able to represent unusual personality functions that could return things in 6 registers for example. As with calling conventions, how the personality function results are represented in LLVM IR is target specific. // Examples: ;; A landing pad which can catch an integer or double and which can throw only ;; a const char *. %res = landingpad { i8*, i32 } personality i32 (...)* @__gxx_personality_v0 catch i8** @_ZTIi, i8** @_ZTId filter i8** @_ZTIPKc ;; A landing pad that is a cleanup. %res = landingpad { i8*, i32 } personality i32 (...)* @__gxx_personality_v0 cleanup ;; A landing pad which indicates that the personality function should call the ;; terminate function. %res = landingpad { i8*, i32 } personality i32 (...)* @__gxx_personality_v0 terminate //===-------------------------- // The 'resume' Instruction // The new "resume" instruction replaces the 'llvm.eh.resume' intrinsic and the old "unwind" instruction. The "unwind" instruction will be removed. // Syntax: resume <somety> <op> This is a terminator instruction that has no successors. Its operand must have the same type as the result of any landingpad instructions in the same function. // Semantics: Resumes propagation of an existing (in-flight) exception. // Example: resume { i8*, i32 } %eh.val ;; Resume exeception propagation out of ;; current function. Note that there is no way with this proposal for pure IR to actually start an exception throw. Instead, use of __cxa_throw or something similar is required. //===----------------------------------------------------------------------===// // Inlining //===----------------------------------------------------------------------===// During inlining through an invoke instruction, a 'resume' instruction will be replaced by a branch to the instruction immediately after the landingpad instruction associated with the invoke. The landingpad instructions, which supply the argument to the 'resume', will be updated with new types which they can catch, if any, from the invoke's landing pad block. The inliner will refuse to inline two functions which have landingpad instructions with incompatible personality functions. For example, an Ada function cannot be inlined into a C++ function, because Ada uses an incompatible personality function. However, a C++ function may be inlined into an Objective-C function (and vice-versa), because the Objective-C personality function contains all of the functionality of the C++ personality function. This proposal does not include a way for the optimizer to know that a superset relation exists, that can be added in the future with a named MDNode. For now, all such inlinings will be refused. //===----------------------------------------------------------------------===// // Cleanup and Catch Clauses //===----------------------------------------------------------------------===// If there is a cleanup that's inlined into a try-catch block, the exception handling table will still mark it as a cleanup, but will also indicate that it catches specific types. For example: struct A { A(); ~A(); }; void qux(); void bar() { A a; qux(); } void foo() { try { bar(); } catch (int) { } } If the call to bar is inlined into foo, the landing pads for A's constructor and destructor are marked as catching an int. The landing pad for qux(), which is marked as a cleanup in bar(), will remain marked as a cleanup, but also be marked as catching an int. //===----------------------------------------------------------------------===// // Future Landing Pad Instruction Optimizations //===----------------------------------------------------------------------===// In the future, the landing pad instruction could be modified to aide optimizations. E.g., if the personality function can support it, a landingpad instruction may indicate that the personality function should call the 'terminate' function: %res = landingpad <somety> personality <ty> <pers_fn> terminate This landingpad instruction would be followed by an 'unreachable' instruction. The exception handling table would be set up to have the personality function call the appropriate 'terminate' function. Support for such features would be done on a case-by-case basis. //===----------------------------------------------------------------------===// // Examples //===----------------------------------------------------------------------===// 1) A simple example: #include <cstdio> void bar(); void foo() throw (const char *) { try { bar(); } catch (int i) { printf("caught integer %d\n", i); } catch (double d) { printf("caught double %g\n", d); } } Produces: @_ZTIPKc = external constant i8* @_ZTIi = external constant i8* @_ZTId = external constant i8* @.str = private unnamed_addr constant [19 x i8] c"caught integer %d\0A\00", align 1 @.str1 = private unnamed_addr constant [18 x i8] c"caught double %g\0A\00", align 1 define i32 @_Z3foov() uwtable optsize ssp { entry: invoke void @_Z3barv() optsize to label %try.cont unwind label %lpad invoke.cont7: %tmp0 = tail call i8* @__cxa_begin_catch(i8* %exn) nounwind %tmp1 = bitcast i8* %tmp0 to i32* %exn.scalar = load i32* %tmp1, align 4 %call = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([19 x i8]* @.str, i64 0, i64 0), i32 %exn.scalar) optsize tail call void @__cxa_end_catch() nounwind br label %try.cont invoke.cont20: %tmp2 = tail call i8* @__cxa_begin_catch(i8* %exn) nounwind %tmp3 = bitcast i8* %tmp2 to double* %exn.scalar11 = load double* %tmp3, align 8 %call21 = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([18 x i8]* @.str1, i64 0, i64 0), double %exn.scalar11) optsize tail call void @__cxa_end_catch() nounwind br label %try.cont try.cont: ret i32 undef lpad: %exn.val = landingpad { i8*, i32 } personality i32 (...)* @__gxx_personality_v0 catch i8** @_ZTIi, i8** @_ZTId filter i8** @_ZTIPKc %exn = extractvalue { i8*, i32 } %exn.val, 0 %sel = extractvalue { i8*, i32 } %exn.val, 1 %tmp4 = tail call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) nounwind %tmp5 = icmp eq i32 %sel, %tmp4 br i1 %tmp5, label %invoke.cont7, label %eh.next eh.next: %tmp6 = tail call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTId to i8*)) nounwind %tmp7 = icmp eq i32 %sel, %tmp6 br i1 %tmp7, label %invoke.cont20, label %eh.next1 eh.next1: %ehspec.fails = icmp slt i32 %sel, 0 br i1 %ehspec.fails, label %ehspec.unexpected, label %eh.resume eh.resume: resume { i8*, i32 } %exn.val ehspec.unexpected: tail call void @__cxa_call_unexpected(i8* %exn) noreturn unreachable } 2) An example of inlining: void qux(); void bar() __attribute__((always_inline)); void bar() { try { qux(); } catch (char c) { printf("caught char %c\n", c); } } void foo() throw (const char *) { try { bar(); } catch (int i) { printf("caught integer %d\n", i); } catch (double d) { printf("caught double %g\n", d); } } Produces (see comments inline): @_ZTIc = external constant i8* @.str = private unnamed_addr constant [16 x i8] c"caught char %c\0A\00", align 1 @_ZTIPKc = external constant i8* @_ZTIi = external constant i8* @_ZTId = external constant i8* @.str1 = private unnamed_addr constant [19 x i8] c"caught integer %d\0A\00", align 1 @.str2 = private unnamed_addr constant [18 x i8] c"caught double %g\0A\00", align 1 define void @_Z3barv() uwtable optsize alwaysinline ssp { entry: invoke void @_Z3quxv() optsize to label %try.cont unwind label %lpad try.cont: ret void lpad: %exn.val = landingpad { i8*, i32 } personality i32 (...)* @__gxx_personality_v0 catch i8** @_ZTIc %exn = extractvalue { i8*, i32 } %exn.val, 0 %sel = extractvalue { i8*, i32 } %exn.val, 1 %tmp2 = tail call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIc to i8*)) nounwind %tmp3 = icmp eq i32 %sel, %tmp2 br i1 %tmp3, label %invoke.cont4, label %eh.resume invoke.cont4: %tmp0 = tail call i8* @__cxa_begin_catch(i8* %exn) nounwind %exn.scalar = load i8* %tmp0, align 1 %conv = sext i8 %exn.scalar to i32 %call = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([16 x i8]* @.str, i64 0, i64 0), i32 %conv) optsize tail call void @__cxa_end_catch() nounwind br label %try.cont eh.resume: resume { i8*, i32 } %exn.val } define i32 @_Z3foov() uwtable optsize ssp { entry: invoke void @_Z3quxv() optsize to label %try.cont.bar unwind label %lpad.bar try.cont.bar: ret void lpad.bar: ;; bar's landing pad is updated to indicate that it can catch an int or double ;; exception. %exn.val.bar = landingpad { i8*, i32 } personality i32 (...)* @__gxx_personality_v0 catch i8** @_ZTIc, i8** @_ZTIi, i8** @_ZTId %exn.bar = extractvalue { i8*, i32 } %exn.val.bar, 0 %sel.bar = extractvalue { i8*, i32 } %exn.val.bar, 1 %tmp2.bar = tail call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIc to i8*)) nounwind %tmp3.bar = icmp eq i32 %sel, %tmp2.bar br i1 %tmp3.bar, label %invoke.cont4.bar, label %eh.resume.bar invoke.cont4.bar: %tmp0.bar = tail call i8* @__cxa_begin_catch(i8* %exn) nounwind %exn.scalar.bar = load i8* %tmp0.bar, align 1 %conv.bar = sext i8 %exn.scalar.bar to i32 %call.bar = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([16 x i8]* @.str, i64 0, i64 0), i32 %conv.bar) optsize tail call void @__cxa_end_catch() nounwind br label %try.cont.bar eh.resume.bar: ;; bar's 'resume' instruction was replaced by an unconditional branch to the ;; foo's landing pad. br label %lpad.split invoke.cont7: %tmp0 = tail call i8* @__cxa_begin_catch(i8* %exn) nounwind %tmp1 = bitcast i8* %tmp0 to i32* %exn.scalar = load i32* %tmp1, align 4 %call = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([19 x i8]* @.str, i64 0, i64 0), i32 %exn.scalar) optsize tail call void @__cxa_end_catch() nounwind br label %try.cont invoke.cont20: %tmp2 = tail call i8* @__cxa_begin_catch(i8* %exn) nounwind %tmp3 = bitcast i8* %tmp2 to double* %exn.scalar11 = load double* %tmp3, align 8 %call21 = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([18 x i8]* @.str1, i64 0, i64 0), double %exn.scalar11) optsize tail call void @__cxa_end_catch() nounwind br label %try.cont try.cont: ret i32 undef lpad: %exn.val = landingpad { i8*, i32 } personality i32 (...)* @__gxx_personality_v0 catch i8** @_ZTIi, i8** @_ZTId filter i8** @_ZTIPKc br label %lpad.split lpad.split: ;; foo's landing pad is split after the 'landingpad' instruction so that bar's ;; 'landingpad' can continue processing the exception if it wasn't handled in ;; bar. %exn.val.phi = phi { i8*, i32 } [ %exn.val, %lpad ], [ %exn.val.bar, %lpad.bar ] %exn = extractvalue { i8*, i32 } %exn.val.phi, 0 %sel = extractvalue { i8*, i32 } %exn.val.phi, 1 %tmp4 = tail call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) nounwind %tmp5 = icmp eq i32 %sel, %tmp4 br i1 %tmp5, label %invoke.cont7, label %eh.next eh.next: %tmp6 = tail call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTId to i8*)) nounwind %tmp7 = icmp eq i32 %sel, %tmp6 br i1 %tmp7, label %invoke.cont20, label %eh.next1 eh.next1: %ehspec.fails = icmp slt i32 %sel, 0 br i1 %ehspec.fails, label %ehspec.unexpected, label %eh.resume eh.resume: resume { i8*, i32 } %exn.val.phi ehspec.unexpected: tail call void @__cxa_call_unexpected(i8* %exn) noreturn unreachable }
On Jul 22, 2011, at 10:29 PM, Bill Wendling wrote:> // Restrictions: > > There are several new invariants which will be enforced by the verifier: > > 1. A landing pad block is a basic block which is the unwind destination of an > invoke instruction. > 2. A landing pad block must have a landingpad instruction as its first non-PHI > instruction. > 3. The landingpad instruction must be the first non-PHI instruction in the > landing pad block. > 4. Like indirect branches, splitting the critical edge to a landing pad block > requires considerable care, and SplitCriticalEdge will refuse to do it. > 5. All landingpad instructions in a function must have the same personality > function.Could we add: - A landing pad block is not the destination of any other kind of terminator. Only unwind edges are allowed. - The landingpad instruction must only appear at the top of a landing pad. It cannot appear in any other block, or following non-phi instructions. Why won't SplitCriticalEdge work for landing pads? Does it require more than splitting the landing pad after the landingpad instruction, and duplicating the top half? Alternatively, could we get a SplitLandingPad function? Will it be possible to split landing pads during codegen? /jakob -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20110722/f89f2b53/attachment.html>
On Jul 22, 2011, at 11:44 PM, Jakob Stoklund Olesen wrote:> On Jul 22, 2011, at 10:29 PM, Bill Wendling wrote: > >> // Restrictions: >> >> There are several new invariants which will be enforced by the verifier: >> >> 1. A landing pad block is a basic block which is the unwind destination of an >> invoke instruction. >> 2. A landing pad block must have a landingpad instruction as its first non-PHI >> instruction. >> 3. The landingpad instruction must be the first non-PHI instruction in the >> landing pad block. >> 4. Like indirect branches, splitting the critical edge to a landing pad block >> requires considerable care, and SplitCriticalEdge will refuse to do it. >> 5. All landingpad instructions in a function must have the same personality >> function. > > Could we add: > > - A landing pad block is not the destination of any other kind of terminator. Only unwind edges are allowed.How do we lower SjLj exceptions as an IR -> IR pass then?> - The landingpad instruction must only appear at the top of a landing pad. It cannot appear in any other block, or following non-phi instructions.How does this differ from 2&3 above? Cameron -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20110722/b74adfc4/attachment.html>
On Jul 22, 2011, at 11:44 PM, Jakob Stoklund Olesen wrote:> On Jul 22, 2011, at 10:29 PM, Bill Wendling wrote: > >> // Restrictions: >> >> There are several new invariants which will be enforced by the verifier: >> >> 1. A landing pad block is a basic block which is the unwind destination of an >> invoke instruction. >> 2. A landing pad block must have a landingpad instruction as its first non-PHI >> instruction. >> 3. The landingpad instruction must be the first non-PHI instruction in the >> landing pad block. >> 4. Like indirect branches, splitting the critical edge to a landing pad block >> requires considerable care, and SplitCriticalEdge will refuse to do it. >> 5. All landingpad instructions in a function must have the same personality >> function. > > Could we add: > > - A landing pad block is not the destination of any other kind of terminator. Only unwind edges are allowed. > > - The landingpad instruction must only appear at the top of a landing pad. It cannot appear in any other block, or following non-phi instructions. >Most of this is covered by 2&3. But it would be good to explicitly state that a landingpad instruction can appear only in a landing pad block.> Why won't SplitCriticalEdge work for landing pads? Does it require more than splitting the landing pad after the landingpad instruction, and duplicating the top half? Alternatively, could we get a SplitLandingPad function?Splitting a critical edge, especially in this case, isn't necessary for correctness. It's an optimization. In general, the use of a value in the catch blocks will access that value via memory. It also complicates the SplitCriticalEdge function, like you outlined.> Will it be possible to split landing pads during codegen?Split a landing pad or split the critical edges to a landing pad? If the former, then yes. You can also split a landing pad in LLVM IR. You just can't split the landing pad block before the landingpad instruction. -bw
Hi Bill, Thanks for working on this. Is there a reference for the function attribute uwtable, or is it to be defined as part of this effort? Thanks in advance Garrison On Jul 23, 2011, at 1:29, Bill Wendling wrote:> What? Yet another EH proposal?! This one is different from the others in that > I'm planning to start implementing this shortly. But I want your feedback! I've > all ready gotten a lot of feedback from Chris, John, Jim, Eric, and many others. > Now is your turn! > > Please read this proposal and send me your comments, suggestions, and concerns. > > -bw > > //===----------------------------------------------------------------------===// > // LLVM Exception Handling Rewrite > //===----------------------------------------------------------------------===// > > 7/16/2011 - Initial revision > 7/18/2011 - Chris's feedback incorporated > 7/19/2011 - John's feedback incorporated > 7/22/2011 - Final revision: Chris's feedback incorporated > > The current exception handling system works for the most part. We have been able > to work with our existing implementation now and get it to produce exception > handling code for a wide variety of situations. However, we are reaching the > limit of what we are able to support with it. In particular, it suffers from > these deficiencies: > > 1. It's very hard to perform inlining through an `invoke' instruction. Because > the information is stored in an intrinsic, it's very difficult to merge one > function's exception handling with another. > 2. The EH intrinsics, which contain the exception handling information for an > invoke (e.g., 'llvm.eh.exception' and 'llvm.eh.selector'), can move out of > the landing pad during normal code motion thus making retrieving EH > information very difficult. > 3. We are currently able to inline two functions which have incompatible > personality functions. This breaks the semantics of the original program. > 4. The exception handling ABI is not followed. Instead, we approximate it using > catchalls and calls to non-standard APIs (e.g., '_Unwind_Resume_or_Rethrow'). > 5. It's inefficient. Because of the constant rethrowing of exceptions, a normal > exception takes much longer to execute. > > In order to address these issues, we need a much better way to represent > exceptions in LLVM IR. > > //===----------------------------------------------------------------------===// > // Proposal > //===----------------------------------------------------------------------===// > > We start with the existing system and try to modify it to eliminate its negative > aspects. This has many benefits, not the least of which is that it's easier for > existing front-ends to adopt the new exception handling design. > > The heart of the proposal is to directly associate unwinding information for an > invoke with the invoke, and to directly expose the values produced in the > landing pad. We do this by introducing a new 'landingpad' instruction which is > always required to the first non-phi instruction in the 'unwind' block of a > landing pad block. Because of this direct association, it is always possible to > find the invoke for a landing pad, and always possible to find the landing pad > for an invoke. > > The 'landingpad' instruction is an instruction (not an intrinsic) because it > has a variadic but highly structured argument list, and can return arbitrary > types (specified by the personality function and ABI). > > //===-------------------------- > // The 'landingpad' Instruction > // > > The 'landingpad' instruction replaces the current 'llvm.eh.exception' and > 'llvm.eh.selector' intrinsics. > > // Syntax: > > %res = landingpad <somety> personality <ty> <pers_fn> <clause>+ > > where > > <clause> :> cleanup > | catch <ty_1>, <ty_2>, ..., <ty_n> > | filter <ty_1>, <ty_2>, ..., <ty_m> > > and the result has the type '<somety>'. The personality functions must be the > same for all landingpad instructions in a given function. > > A landingpad instruction must contain at least one cleanup, catch, or filter > clause. > > // Restrictions: > > There are several new invariants which will be enforced by the verifier: > > 1. A landing pad block is a basic block which is the unwind destination of an > invoke instruction. > 2. A landing pad block must have a landingpad instruction as its first non-PHI > instruction. > 3. The landingpad instruction must be the first non-PHI instruction in the > landing pad block. > 4. Like indirect branches, splitting the critical edge to a landing pad block > requires considerable care, and SplitCriticalEdge will refuse to do it. > 5. All landingpad instructions in a function must have the same personality > function. > > // Semantics: > > The landingpad instruction defines the values which are set by the personality > function upon reentry to the function, and therefore the "result type" of the > landing pad instruction. With these changes, LLVM IR will be able to represent > unusual personality functions that could return things in 6 registers for > example. As with calling conventions, how the personality function results are > represented in LLVM IR is target specific. > > // Examples: > > ;; A landing pad which can catch an integer or double and which can throw only > ;; a const char *. > %res = landingpad { i8*, i32 } personality i32 (...)* @__gxx_personality_v0 > catch i8** @_ZTIi, i8** @_ZTId > filter i8** @_ZTIPKc > > ;; A landing pad that is a cleanup. > %res = landingpad { i8*, i32 } personality i32 (...)* @__gxx_personality_v0 > cleanup > > ;; A landing pad which indicates that the personality function should call the > ;; terminate function. > %res = landingpad { i8*, i32 } personality i32 (...)* @__gxx_personality_v0 > terminate > > //===-------------------------- > // The 'resume' Instruction > // > > The new "resume" instruction replaces the 'llvm.eh.resume' intrinsic and the old > "unwind" instruction. The "unwind" instruction will be removed. > > // Syntax: > > resume <somety> <op> > > This is a terminator instruction that has no successors. Its operand must have > the same type as the result of any landingpad instructions in the same function. > > // Semantics: > > Resumes propagation of an existing (in-flight) exception. > > // Example: > > resume { i8*, i32 } %eh.val ;; Resume exeception propagation out of > ;; current function. > > Note that there is no way with this proposal for pure IR to actually start an > exception throw. Instead, use of __cxa_throw or something similar is required. > > //===----------------------------------------------------------------------===// > // Inlining > //===----------------------------------------------------------------------===// > > During inlining through an invoke instruction, a 'resume' instruction will be > replaced by a branch to the instruction immediately after the landingpad > instruction associated with the invoke. The landingpad instructions, which > supply the argument to the 'resume', will be updated with new types which they > can catch, if any, from the invoke's landing pad block. > > The inliner will refuse to inline two functions which have landingpad > instructions with incompatible personality functions. For example, an Ada > function cannot be inlined into a C++ function, because Ada uses an incompatible > personality function. However, a C++ function may be inlined into an Objective-C > function (and vice-versa), because the Objective-C personality function contains > all of the functionality of the C++ personality function. > > This proposal does not include a way for the optimizer to know that a superset > relation exists, that can be added in the future with a named MDNode. For now, > all such inlinings will be refused. > > //===----------------------------------------------------------------------===// > // Cleanup and Catch Clauses > //===----------------------------------------------------------------------===// > > If there is a cleanup that's inlined into a try-catch block, the exception > handling table will still mark it as a cleanup, but will also indicate that it > catches specific types. For example: > > struct A { > A(); > ~A(); > }; > > void qux(); > void bar() { > A a; > qux(); > } > > void foo() { > try { > bar(); > } catch (int) { > } > } > > If the call to bar is inlined into foo, the landing pads for A's constructor and > destructor are marked as catching an int. The landing pad for qux(), which is > marked as a cleanup in bar(), will remain marked as a cleanup, but also be > marked as catching an int. > > //===----------------------------------------------------------------------===// > // Future Landing Pad Instruction Optimizations > //===----------------------------------------------------------------------===// > > In the future, the landing pad instruction could be modified to aide > optimizations. E.g., if the personality function can support it, a landingpad > instruction may indicate that the personality function should call the > 'terminate' function: > > %res = landingpad <somety> personality <ty> <pers_fn> > terminate > > This landingpad instruction would be followed by an 'unreachable' > instruction. The exception handling table would be set up to have the > personality function call the appropriate 'terminate' function. > > Support for such features would be done on a case-by-case basis. > > //===----------------------------------------------------------------------===// > // Examples > //===----------------------------------------------------------------------===// > > 1) A simple example: > > #include <cstdio> > > void bar(); > > void foo() throw (const char *) { > try { > bar(); > } catch (int i) { > printf("caught integer %d\n", i); > } catch (double d) { > printf("caught double %g\n", d); > } > } > > Produces: > > @_ZTIPKc = external constant i8* > @_ZTIi = external constant i8* > @_ZTId = external constant i8* > @.str = private unnamed_addr constant [19 x i8] c"caught integer %d\0A\00", align 1 > @.str1 = private unnamed_addr constant [18 x i8] c"caught double %g\0A\00", align 1 > > define i32 @_Z3foov() uwtable optsize ssp { > entry: > invoke void @_Z3barv() optsize > to label %try.cont unwind label %lpad > > invoke.cont7: > %tmp0 = tail call i8* @__cxa_begin_catch(i8* %exn) nounwind > %tmp1 = bitcast i8* %tmp0 to i32* > %exn.scalar = load i32* %tmp1, align 4 > %call = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([19 x i8]* @.str, i64 0, i64 0), > i32 %exn.scalar) optsize > tail call void @__cxa_end_catch() nounwind > br label %try.cont > > invoke.cont20: > %tmp2 = tail call i8* @__cxa_begin_catch(i8* %exn) nounwind > %tmp3 = bitcast i8* %tmp2 to double* > %exn.scalar11 = load double* %tmp3, align 8 > %call21 = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([18 x i8]* @.str1, i64 0, i64 0), > double %exn.scalar11) optsize > tail call void @__cxa_end_catch() nounwind > br label %try.cont > > try.cont: > ret i32 undef > > lpad: > %exn.val = landingpad { i8*, i32 } personality i32 (...)* @__gxx_personality_v0 > catch i8** @_ZTIi, i8** @_ZTId > filter i8** @_ZTIPKc > %exn = extractvalue { i8*, i32 } %exn.val, 0 > %sel = extractvalue { i8*, i32 } %exn.val, 1 > %tmp4 = tail call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) nounwind > %tmp5 = icmp eq i32 %sel, %tmp4 > br i1 %tmp5, label %invoke.cont7, label %eh.next > > eh.next: > %tmp6 = tail call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTId to i8*)) nounwind > %tmp7 = icmp eq i32 %sel, %tmp6 > br i1 %tmp7, label %invoke.cont20, label %eh.next1 > > eh.next1: > %ehspec.fails = icmp slt i32 %sel, 0 > br i1 %ehspec.fails, label %ehspec.unexpected, label %eh.resume > > eh.resume: > resume { i8*, i32 } %exn.val > > ehspec.unexpected: > tail call void @__cxa_call_unexpected(i8* %exn) noreturn > unreachable > } > > > 2) An example of inlining: > > void qux(); > > void bar() __attribute__((always_inline)); > void bar() { > try { > qux(); > } catch (char c) { > printf("caught char %c\n", c); > } > } > > void foo() throw (const char *) { > try { > bar(); > } catch (int i) { > printf("caught integer %d\n", i); > } catch (double d) { > printf("caught double %g\n", d); > } > } > > Produces (see comments inline): > > @_ZTIc = external constant i8* > @.str = private unnamed_addr constant [16 x i8] c"caught char %c\0A\00", align 1 > @_ZTIPKc = external constant i8* > @_ZTIi = external constant i8* > @_ZTId = external constant i8* > @.str1 = private unnamed_addr constant [19 x i8] c"caught integer %d\0A\00", align 1 > @.str2 = private unnamed_addr constant [18 x i8] c"caught double %g\0A\00", align 1 > > define void @_Z3barv() uwtable optsize alwaysinline ssp { > entry: > invoke void @_Z3quxv() optsize > to label %try.cont unwind label %lpad > > try.cont: > ret void > > lpad: > %exn.val = landingpad { i8*, i32 } personality i32 (...)* @__gxx_personality_v0 > catch i8** @_ZTIc > %exn = extractvalue { i8*, i32 } %exn.val, 0 > %sel = extractvalue { i8*, i32 } %exn.val, 1 > %tmp2 = tail call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIc to i8*)) nounwind > %tmp3 = icmp eq i32 %sel, %tmp2 > br i1 %tmp3, label %invoke.cont4, label %eh.resume > > invoke.cont4: > %tmp0 = tail call i8* @__cxa_begin_catch(i8* %exn) nounwind > %exn.scalar = load i8* %tmp0, align 1 > %conv = sext i8 %exn.scalar to i32 > %call = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([16 x i8]* @.str, i64 0, i64 0), i32 %conv) optsize > tail call void @__cxa_end_catch() nounwind > br label %try.cont > > eh.resume: > resume { i8*, i32 } %exn.val > } > > define i32 @_Z3foov() uwtable optsize ssp { > entry: > invoke void @_Z3quxv() optsize > to label %try.cont.bar unwind label %lpad.bar > > try.cont.bar: > ret void > > lpad.bar: > ;; bar's landing pad is updated to indicate that it can catch an int or double > ;; exception. > %exn.val.bar = landingpad { i8*, i32 } personality i32 (...)* @__gxx_personality_v0 > catch i8** @_ZTIc, i8** @_ZTIi, i8** @_ZTId > %exn.bar = extractvalue { i8*, i32 } %exn.val.bar, 0 > %sel.bar = extractvalue { i8*, i32 } %exn.val.bar, 1 > %tmp2.bar = tail call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIc to i8*)) nounwind > %tmp3.bar = icmp eq i32 %sel, %tmp2.bar > br i1 %tmp3.bar, label %invoke.cont4.bar, label %eh.resume.bar > > invoke.cont4.bar: > %tmp0.bar = tail call i8* @__cxa_begin_catch(i8* %exn) nounwind > %exn.scalar.bar = load i8* %tmp0.bar, align 1 > %conv.bar = sext i8 %exn.scalar.bar to i32 > %call.bar = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([16 x i8]* @.str, i64 0, i64 0), i32 %conv.bar) optsize > tail call void @__cxa_end_catch() nounwind > br label %try.cont.bar > > eh.resume.bar: > ;; bar's 'resume' instruction was replaced by an unconditional branch to the > ;; foo's landing pad. > br label %lpad.split > > invoke.cont7: > %tmp0 = tail call i8* @__cxa_begin_catch(i8* %exn) nounwind > %tmp1 = bitcast i8* %tmp0 to i32* > %exn.scalar = load i32* %tmp1, align 4 > %call = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([19 x i8]* @.str, i64 0, i64 0), > i32 %exn.scalar) optsize > tail call void @__cxa_end_catch() nounwind > br label %try.cont > > invoke.cont20: > %tmp2 = tail call i8* @__cxa_begin_catch(i8* %exn) nounwind > %tmp3 = bitcast i8* %tmp2 to double* > %exn.scalar11 = load double* %tmp3, align 8 > %call21 = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([18 x i8]* @.str1, i64 0, i64 0), > double %exn.scalar11) optsize > tail call void @__cxa_end_catch() nounwind > br label %try.cont > > try.cont: > ret i32 undef > > lpad: > %exn.val = landingpad { i8*, i32 } personality i32 (...)* @__gxx_personality_v0 > catch i8** @_ZTIi, i8** @_ZTId > filter i8** @_ZTIPKc > br label %lpad.split > > lpad.split: > ;; foo's landing pad is split after the 'landingpad' instruction so that bar's > ;; 'landingpad' can continue processing the exception if it wasn't handled in > ;; bar. > %exn.val.phi = phi { i8*, i32 } [ %exn.val, %lpad ], [ %exn.val.bar, %lpad.bar ] > %exn = extractvalue { i8*, i32 } %exn.val.phi, 0 > %sel = extractvalue { i8*, i32 } %exn.val.phi, 1 > %tmp4 = tail call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) nounwind > %tmp5 = icmp eq i32 %sel, %tmp4 > br i1 %tmp5, label %invoke.cont7, label %eh.next > > eh.next: > %tmp6 = tail call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTId to i8*)) nounwind > %tmp7 = icmp eq i32 %sel, %tmp6 > br i1 %tmp7, label %invoke.cont20, label %eh.next1 > > eh.next1: > %ehspec.fails = icmp slt i32 %sel, 0 > br i1 %ehspec.fails, label %ehspec.unexpected, label %eh.resume > > eh.resume: > resume { i8*, i32 } %exn.val.phi > > ehspec.unexpected: > tail call void @__cxa_call_unexpected(i8* %exn) noreturn > unreachable > } >
On Sat, Jul 23, 2011 at 2:36 AM, Garrison Venn <gvenn.cfe.dev at gmail.com> wrote:> Hi Bill, > > Thanks for working on this. > > Is there a reference for the function attribute uwtable, or is it to be defined as > part of this effort?It already exists; there's some limited documentation in the LLVM source, but Rafael apparently forgot to add it to LangRef... -Eli
On Fri, Jul 22, 2011 at 10:29 PM, Bill Wendling <wendling at apple.com> wrote: [...]> //===-------------------------- > // The 'landingpad' Instruction > // > > The 'landingpad' instruction replaces the current 'llvm.eh.exception' and > 'llvm.eh.selector' intrinsics. > > // Syntax: > > %res = landingpad <somety> personality <ty> <pers_fn> <clause>+ > > where > > <clause> :> cleanup > | catch <ty_1>, <ty_2>, ..., <ty_n> > | filter <ty_1>, <ty_2>, ..., <ty_m>| terminate ? You have an example referencing it, but it isn't in the grammar. -Eli
On Jul 23, 2011, at 9:49 AM, Eli Friedman <eli.friedman at gmail.com> wrote:> On Fri, Jul 22, 2011 at 10:29 PM, Bill Wendling <wendling at apple.com> wrote: > [...] >> //===-------------------------- >> // The 'landingpad' Instruction >> // >> >> The 'landingpad' instruction replaces the current 'llvm.eh.exception' and >> 'llvm.eh.selector' intrinsics. >> >> // Syntax: >> >> %res = landingpad <somety> personality <ty> <pers_fn> <clause>+ >> >> where >> >> <clause> :>> cleanup >> | catch <ty_1>, <ty_2>, ..., <ty_n> >> | filter <ty_1>, <ty_2>, ..., <ty_m> > > | terminate ? You have an example referencing it, but it isn't in the grammar. >Sorry for the confusion. The terminate is addressed later and I mention it as a potential optimization that could be added at a future time. But not for the initial rewrite. If it's in one of the examples, then please ignore it. I'll come up with a few other examples. -bw
> %exn.val = landingpad { i8*, i32 } personality i32 (...)* @__gxx_personality_v0 > catch i8** @_ZTIi, i8** @_ZTId > filter i8** @_ZTIPKc > br label %lpad.splitWhat is the semantics of filter? Is it undefined reference if an exception not matching ZTIi, ZTId or ZTIPKc passes by? Cheers, Rafael
On Jul 25, 2011, at 8:20 AM, Rafael Ávila de Espíndola wrote:>> %exn.val = landingpad { i8*, i32 } personality i32 (...)* @__gxx_personality_v0 >> catch i8** @_ZTIi, i8** @_ZTId >> filter i8** @_ZTIPKc >> br label %lpad.split > > What is the semantics of filter? Is it undefined reference if an > exception not matching ZTIi, ZTId or ZTIPKc passes by? >It results in a call if an exception is thrown or passes by this landing pad which isn't _ZTIPKc. It results in a call to std::unexpected() (in C++). The corresponding C++ feature is the "throw(const char *)" clause hanging off of functions: void foo() throw(const char*) { // ... } -bw
Hi Bill,> Please read this proposal and send me your comments, suggestions, and concerns.this proposal looks great to me. Thanks for working on it. I have a few minor comments, see below.> //===-------------------------- > // The 'landingpad' Instruction > // > > The 'landingpad' instruction replaces the current 'llvm.eh.exception' and > 'llvm.eh.selector' intrinsics. > > // Syntax: > > %res = landingpad<somety> personality<ty> <pers_fn> <clause>+ > > where > > <clause> :> cleanup > | catch<ty_1>,<ty_2>, ...,<ty_n> > | filter<ty_1>,<ty_2>, ...,<ty_m> > > and the result has the type '<somety>'. The personality functions must be the > same for all landingpad instructions in a given function.Is it intended that "cleanup ty_1, ty_2" potentially be different to "cleanup ty_1 cleanup ty_2"? Perhaps this is useful for funky personality functions. Also, there isn't much point to having multiple "cleanup" entries, but I guess it makes things more regular to allow it, and perhaps custom personality functions might want to do something special. If there is only a cleanup, the personality function doesn't really matter because all standard personality functions treat cleanups the same way. GCC has an optimization in which you don't have to specify a personality function in this case (when it comes to codegen time, if there is some personality function floating around, it uses that; if not it uses the C personality function; when inlining into something with a catch clause, the personality function from the catch is used etc). Something like this could be nice for LLVM too. For example the LLVM garbage collector codegen code wants to insert a simple cleanup: run some cleanup code then resume. It would be nice if it didn't have to specify a personality. For example by using the null pointer for the personality function. That said I suppose such code could be required to specify (eg) the C personality function. Another comment is: rather than using a function pointer for the personality function (and requiring a declaration for the personality), maybe it could just be a string? After all, you don't actually do anything much with it: you just dump the name into the assembler. Perhaps the same goes for catches and so on: is a global really needed and not its name? Finally, rather than baking cleanups, filters etc into the IR, I suppose the landingpad instruction could just be: %res = landingpad<some type> data<ty> For standard setups "data" could be a struct type, with fields for the personality function, a cleanup flag, lists of catches and filters and so on.> 5. All landingpad instructions in a function must have the same personality > function.And presumably the same type. Ciao, Duncan.
Hi Duncan, Just one question. On Jul 31, 2011, at 14:06, Duncan Sands wrote:> Hi Bill, > >> Please read this proposal and send me your comments, suggestions, and concerns. > > this proposal looks great to me. Thanks for working on it. I have a few minor > comments, see below. >snip ...> Another comment is: rather than using a function pointer for the personality > function (and requiring a declaration for the personality), maybe it could > just be a string? After all, you don't actually do anything much with it: > you just dump the name into the assembler. Perhaps the same goes for catches > and so on: is a global really needed and not its name?If I understand your query correctly, wouldn't the string--I am assuming symbol here, have to be known at compile time. For example, if the personality function was not extern Ced, and instead would be mangled by the compiler, wouldn't the developer not know what the symbol name was to supply to the API? I guess if this is what you mean, the API could additionally state that only C functions (extern C), could be used, and therefore the supplied name would match.> >snip ...> Ciao, Duncan.Just curious Garrison -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20110731/d9bff720/attachment.html>
On Sun, Jul 31, 2011 at 11:06 AM, Duncan Sands <baldrick at free.fr> wrote:> Another comment is: rather than using a function pointer for the personality > function (and requiring a declaration for the personality), maybe it could > just be a string? After all, you don't actually do anything much with it: > you just dump the name into the assembler. Perhaps the same goes for catches > and so on: is a global really needed and not its name?There isn't any guarantee that the relevant globals are externally visible; granted, this is unlikely for the personality function, but it's very easy to write C++ code where the type info for a class is an internal symbol. -Eli
On Jul 31, 2011, at 11:06 AM, Duncan Sands wrote:>> //===-------------------------- >> // The 'landingpad' Instruction >> // >> >> The 'landingpad' instruction replaces the current 'llvm.eh.exception' and >> 'llvm.eh.selector' intrinsics. >> >> // Syntax: >> >> %res = landingpad<somety> personality<ty> <pers_fn> <clause>+ >> >> where >> >> <clause> :>> cleanup >> | catch<ty_1>,<ty_2>, ...,<ty_n> >> | filter<ty_1>,<ty_2>, ...,<ty_m> >> >> and the result has the type '<somety>'. The personality functions must be the >> same for all landingpad instructions in a given function. > > Is it intended that "cleanup ty_1, ty_2" potentially be different to > "cleanup ty_1 cleanup ty_2"? Perhaps this is useful for funky personality > functions. >Yeah. One can basically interleave the catches and filters. But having two catch or two filter clauses in a row should be semantically the same as the clauses being merged into one. (E.g., your examples would be equivalent.)> Also, there isn't much point to having multiple "cleanup" entries, but I > guess it makes things more regular to allow it, and perhaps custom personality > functions might want to do something special. >I goofed on the BNF for the cleanup. I fixed it in later versions and in what I'm submitting to the LangRef.html.> If there is only a cleanup, the personality function doesn't really matter > because all standard personality functions treat cleanups the same way. GCC > has an optimization in which you don't have to specify a personality function > in this case (when it comes to codegen time, if there is some personality > function floating around, it uses that; if not it uses the C personality > function; when inlining into something with a catch clause, the personality > function from the catch is used etc). > > Something like this could be nice for LLVM too. For example the LLVM garbage > collector codegen code wants to insert a simple cleanup: run some cleanup code > then resume. It would be nice if it didn't have to specify a personality. For > example by using the null pointer for the personality function. That said I > suppose such code could be required to specify (eg) the C personality function. >There are a few personality function specific optimizations we could do – including setting up the EH tables to have the pers func call std::terminate for us C++ users automatically instead of having to code the call to std::terminate into the CFG. But those are to come later once we get the basic functionality up and running.> Another comment is: rather than using a function pointer for the personality > function (and requiring a declaration for the personality), maybe it could > just be a string? After all, you don't actually do anything much with it: > you just dump the name into the assembler. Perhaps the same goes for catches > and so on: is a global really needed and not its name? >I'm hesitant to do this because the machinery for printing out the correct global value's representation in assembly is already in the code. So it knows how to print: .long ___gxx_personality_v0+4 at GOTPCREL as opposed to .long L___gxx_personality_v0$non_lazy_ptr-. L___gxx_personality_v0$non_lazy_ptr: .indirect_symbol ___gxx_personality_v0 At least for the Mac, it's much easier to do it the GV way... :-)> Finally, rather than baking cleanups, filters etc into the IR, I suppose the > landingpad instruction could just be: > > %res = landingpad<some type> data<ty> > > For standard setups "data" could be a struct type, with fields for the > personality function, a cleanup flag, lists of catches and filters and so on. >True, but this makes the IR less readable for me. I would have to look at a global to understand what this instruction is doing. Plus, we have problems with the "funky personality function" situation. With the proposal, we can interleave the clauses. With the 'data' option, we would have to have a non-IR way of interpreting the struct. So we're kind of back to the situation we have with the intrinsics...>> 5. All landingpad instructions in a function must have the same personality >> function. > > And presumably the same type. >Yes. Good catch. :) -bw
Hi Duncan, On 31 July 2011 19:06, Duncan Sands <baldrick at free.fr> wrote:> Something like this could be nice for LLVM too. For example the LLVM garbage > collector codegen code wants to insert a simple cleanup: run some cleanup code > then resume. It would be nice if it didn't have to specify a personality. For > example by using the null pointer for the personality function. That said I > suppose such code could be required to specify (eg) the C personality function.I think this is too much for this first proposal. Using the C personality function as a default is easy to implement and easy to refactor later.> Perhaps the same goes for catches > and so on: is a global really needed and not its name?I can't see why we'd need the global. Maybe for IR validation (if you get the name wrong) but that's all.> Finally, rather than baking cleanups, filters etc into the IR, I suppose the > landingpad instruction could just be: > > %res = landingpad<some type> data<ty> > > For standard setups "data" could be a struct type, with fields for the > personality function, a cleanup flag, lists of catches and filters and so on.I still prefer the explicit fields in an explicit instruction. cheers, --renato
Maybe Matching Threads
- [LLVMdev] RFC: Exception Handling Rewrite
- [LLVMdev] RFC: How to represent SEH (__try / __except) in LLVM IR
- [LLVMdev] RFC: Exception Handling Rewrite
- [LLVMdev] Proposal: Loads/stores with deterministic trap/unwind behavior
- [LLVMdev] RFC: Exception Handling Proposal Revised