I may me wrong, but I think Nathan used ints for demonstration purposes only. unwind always takes i8* argument that ideally should be a pointer to exception structure, variable %x in invoke is also typed i8*, it's not "untyped". Probably more llvm-ish syntax would be unwind i8* %x to label %catch to show the type explicitly. However throwing a pointer to a structure raises questions about structure's ownership, so I think Nathan cheated a bit and threw an int to make the code snippet simpler. Landing pad code then checked the int, whereas real code would expect exception object. "Real" code would look more like: define i32 @v(i32 %o) { %r = icmp eq i32 %o, 0 br i1 %r, label %raise, label %ok ok: %m = mul i32 %o, 2 ret i32 %m raise: %ex = call i8* @allocate_exception() call void @init_div_by_0_eh(i8* %ex) ; unwind now takes an i8* "exception" pointer unwind i8* %ex } define i32 @g(i32 %o) { entry: ; invoke produces a different value depending on whether if ; branches to the success case or the failure case. %s = invoke i32 @v(i32 %o) to label %ok unwind %x to label %catch ok: ret i32 %s catch: %type = call i32 @exception_type(i8* %x) %r = icmp eq i32 %type, 255 ; 255 is DivisionByZeroException type br i1 %r, label %bad, label %worse bad: ret i32 -1 worse: ret i32 -2 } Nathan -- is this approach simpler than using intrinsics @eh.throw (assuming it's added) and @eh.exception? The latter seems more flexible in supporting various levels of ABI (I think ideally LLVM exception handling should follow general platform ABI and also allow front-ends for specific languages generate code in accordance with language specific ABI). I going with invoke instruction that returns exception pointer (which feels right to me) maybe this is a good candidate for using union type -- invoke can produce a single result which is either normal return value or an exception pointer, since only one of the two values can be actually produced. This sounds logical but may be taking us too far from ABIs. Eugene On Sun, Sep 26, 2010 at 12:19 PM, Renato Golin <rengolin at systemcall.org> wrote:> On 25 September 2010 23:46, Nathan Jeffords <blunted2night at gmail.com> wrote: >> catch: >> %v = ptrtoint i8 * %x to i32 >> %r = icmp eq i32 %v, 255 >> br i1 %r, label %bad, label %worse >> bad: >> ret i32 -1 >> worse: >> ret i32 -2 >> } > > If I understood correctly, you're trying to pass the clean-up flag > through %x directly on the invoke call. But later avoid the > eh.exception call, assuming that was in %x. > > The problem is that you're mixing two concepts: The exception > structure contains information about the object that was thrown and > not a "good" number. That's the role of the clean-up flag (in case the > catch blocks can't deal with the exception) or the landing pads (that > should reflect the return values the user asked for in their > programs). > > It's the users role to tell what's good and what's not (return values > included). the only thing you (compiler) can do is to explode > prematurely in case you can't properly catch the error (ie. throw > inside throw, throw inside delete, etc). > > If that's the case, your implementation will not work for dwarf > exceptions, and I wouldn't recommend having an *invoke* syntax for > each type of exception handling mechanism. > > Other question: why are you passing untyped %x? I haven't seen any > untyped variable in LLVM, so far, and I think it's good to be > redundant in this case. That alone would have caught the mistake. If > you need an i32 (for your bad/worse comparison), throwing i8* would > have hinted that you crossed the concepts. > > > On a side note... > > Exception handling was designed by the devil himself. Part of the flow > control is designed by the user (try/catch blocks, throw > specifications), part of it is designed by the compiler, in exception > tables (specific unwinding instructions and types), and part by the > library writers (unwinding and personality routines). All that, > decided in three different time frames, by three different kinds of > developers, have to communicate perfectly in run time. > > It'd be very difficult for the compiler to optimize automatically > without breaking run-time assumptions. All of that is controlled by > different ABIs, that make sure all three universes are talking the > same language. You can't change one without changing all the others... > > To be honest, I'm still surprised that it actually works at all! ;) > > -- > cheers, > --renato > > http://systemcall.org/ > > Reclaim your digital rights, eliminate DRM, learn more at > http://www.defectivebydesign.org/what_is_drm > > _______________________________________________ > LLVM Developers mailing list > LLVMdev at cs.uiuc.edu http://llvm.cs.uiuc.edu > http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev >
On Sun, Sep 26, 2010 at 5:08 AM, Eugene Toder <eltoder at gmail.com> wrote:> I may me wrong, but I think Nathan used ints for demonstration > purposes only. unwind always takes i8* argument that ideally should be > a pointer to exception structure, variable %x in invoke is also typed > i8*, it's not "untyped". Probably more llvm-ish syntax would be > > unwind i8* %x to label %catch >> to show the type explicitly. >Yes, I didn't specify a type because %x is an assignment (like '%x = call @eh.exception ...'). Admittedly this specify syntax is misleading. I think a syntax like: invoke i32 @v(i32 %o) success %s to label %ok unwind %x to label %catch would be more consistent and have the benefit better indicating what value is generated when branching to what label. [snip] Nathan -- is this approach simpler than using intrinsics @eh.throw> (assuming it's added) and @eh.exception? The latter seems more > flexible in supporting various levels of ABI (I think ideally LLVM > exception handling should follow general platform ABI and also allow > front-ends for specific languages generate code in accordance with > language specific ABI). >I don't know the specifics of all the platforms that LLVM may run on so this is a hard question to answer. I think looking at this as a representation and not an implementation may help. If it is assumed that all exception handling mechanisms fundamentally pass pointers to exception information than this representation would work, and it would be lowered to the appropriate platform specific representation by a platform specific pass. Another approach (that should be selectable by the compiler) would be for the code generator to lower this representation into what ever it would take to pass the unwind pointer argument to the catch block referenced by the invoke instruction to allow the compiler to be platform agnostic at the cost of some flexibility with regards to the types of options available with platform specific implementations. I do think that a direct representation is more desirable than one implemented via intrinsics. I think that that makes it easier for passes to deal with.> I going with invoke instruction that returns exception pointer (which > feels right to me) maybe this is a good candidate for using union type > -- invoke can produce a single result which is either normal return > value or an exception pointer, since only one of the two values can be > actually produced. This sounds logical but may be taking us too far > from ABIs. > >This could work too if unions are going to be supported. Perhaps this could be another reason why LLVM needs unions. I don't now any details on the semantics of the existing union support, but for this purpose I think I can see a set of semantics. It would revolve around have a way tie what field of the union is valid to one or more basic blocks. [snip] Thanks for the feedback, -Nathan -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20100926/aaa1d484/attachment.html>
On 26 September 2010 13:08, Eugene Toder <eltoder at gmail.com> wrote:> %s = invoke i32 @v(i32 %o) to label %ok > unwind %x to label %catch > ok: > ret i32 %s > > catch: > %type = call i32 @exception_type(i8* %x) > %r = icmp eq i32 %type, 255 ; 255 is DivisionByZeroException type > br i1 %r, label %bad, label %worse > > bad: > ret i32 -1 > > worse: > ret i32 -2 > }That could be enough for SJ/LJ (I have no idea), but it certainly is not for Dwarf exceptions. When an invoke throws an exception, the flow doesn't go directly to the catch area. First you go to a cleanup area, where you have to deallocate all temporaries, in which you can also throw again, or even terminate. Then you go the analysis of the exception structure, to see if that's valid in accordance with the function specification and the personality routine (when the EH table goes in context). It is only after that that you go through the catch blocks by: first checking the type is the same as the catch block is catching for and entering the block if it is, or jumping to the next type check/catch block. At the end, if it was not caught, you have to jump to another cleanup area before throwing again, and continue with the unwinding cycle on the caller function. ALL that is generated by the compiler and happen oblivious to the user's code. The flow only return to the user's code if you enter in a catch area. Which means that, %type is NOT a number, it is a TypeInfo, and it's not inside the catch area, but rather before it. I assume, in your example, that the *icmp* is user code. If %x is what came from the catch (Type& x) {}, you'll have to do one of two things to get %type: 1. Give the user an intrinsic, which breaks the EH contract 2. Give the type directly, which also breaks the contract (they also need the value) If the *icmp* is not user code, this is even worse, because an exception handling code automatically generated by the compiler will be returning a value from a function, completely breaking the EH contract. In other words, this approach will not work with Dwarf exceptions.> I going with invoke instruction that returns exception pointer (which > feels right to me) maybe this is a good candidate for using union typeI'm an advocate of re-introducing unions, but in this case I think it'll rather obfuscate things even more... Don't take me wrong, I'm not against the change, really. If the docs say there's something wrong, I thing we should fix it, but I fear that the issue is being seen from the sj/lj point of view only (or mainly)... Is there any language front-end (other than C++) using exceptions? We might consider their views as well... cheers, --renato
On 26 September 2010 19:49, Nathan Jeffords <blunted2night at gmail.com> wrote:> I do think that a direct representation > is more desirable than one implemented via intrinsics. I think that that > makes it easier for passes to deal with.Agreed! Exception handling is an important part of many languages and should be represented directly. Internally, most of the C++ unwinding functions are actually library calls (prolly why they used intrinsics), but using direct representation would ease not only validation passes, but also optimization passes. In the end, they'd be lowered to the same lib calls anyway... -- cheers, --renato http://systemcall.org/ Reclaim your digital rights, eliminate DRM, learn more at http://www.defectivebydesign.org/what_is_drm
On Sun, Sep 26, 2010 at 11:49 AM, Renato Golin <rengolin at systemcall.org>wrote:> On 26 September 2010 13:08, Eugene Toder <eltoder at gmail.com> wrote: > > %s = invoke i32 @v(i32 %o) to label %ok > > unwind %x to label %catch > > ok: > > ret i32 %s > > > > catch: > > %type = call i32 @exception_type(i8* %x) > > %r = icmp eq i32 %type, 255 ; 255 is DivisionByZeroException type > > br i1 %r, label %bad, label %worse > > > > bad: > > ret i32 -1 > > > > worse: > > ret i32 -2 > > } > > That could be enough for SJ/LJ (I have no idea), but it certainly is > not for Dwarf exceptions. When an invoke throws an exception, the flow > doesn't go directly to the catch area. First you go to a cleanup area, > where you have to deallocate all temporaries, in which you can also > throw again, or even terminate. > > Then you go the analysis of the exception structure, to see if that's > valid in accordance with the function specification and the > personality routine (when the EH table goes in context). It is only > after that that you go through the catch blocks by: first checking the > type is the same as the catch block is catching for and entering the > block if it is, or jumping to the next type check/catch block. > > At the end, if it was not caught, you have to jump to another cleanup > area before throwing again, and continue with the unwinding cycle on > the caller function. ALL that is generated by the compiler and happen > oblivious to the user's code. The flow only return to the user's code > if you enter in a catch area. > > Which means that, %type is NOT a number, it is a TypeInfo, and it's > not inside the catch area, but rather before it. I assume, in your > example, that the *icmp* is user code. If %x is what came from the > catch (Type& x) {}, you'll have to do one of two things to get %type: > > 1. Give the user an intrinsic, which breaks the EH contract > 2. Give the type directly, which also breaks the contract (they also > need the value) > > If the *icmp* is not user code, this is even worse, because an > exception handling code automatically generated by the compiler will > be returning a value from a function, completely breaking the EH > contract. > > In other words, this approach will not work with Dwarf exceptions. > >For dwarf exception handling tied to gcc's unwinding library, the minimal change to make it work with this new approach would be to replace the call to 'eh.exception' in the one landing pad with a phi node pulling in the 'exception' value of all the invoke instructions. The use of unwind to initiate the throwing of the exception could be bypassed just like it is currently.> > > > I going with invoke instruction that returns exception pointer (which > > feels right to me) maybe this is a good candidate for using union type > > I'm an advocate of re-introducing unions, but in this case I think > it'll rather obfuscate things even more... > > Don't take me wrong, I'm not against the change, really. If the docs > say there's something wrong, I thing we should fix it, but I fear that > the issue is being seen from the sj/lj point of view only (or > mainly)... > > Is there any language front-end (other than C++) using exceptions? We > might consider their views as well... > >My motivation for this change is to support a compiler I am developing as a personal project. My primary development platform is windows where I currently link against either visual studio's, or mingw's runtime. I envision an optional platform agnostic interpretation of the unwind/invoke that would do whatever is necessary to make the value I pass into the unwind instruction come out the invoke instruction with out me needing to concern my self with the implementation. I accept that in this case, for exceptions outside my language, my program will just terminate. I think this is a reasonable limitation, especially if my program was just going to terminate anyway once it got to the root of the call stack and didn't find a handler.> cheers, > --renato >thanks, -Nathan -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20100926/8050ccbb/attachment.html>