[LLVMdev] Darwin vs exceptions
Dale Johannesen
dalej at apple.com
Mon Dec 10 11:04:22 PST 2007
On Dec 9, 2007, at 1:01 PM, Duncan Sands wrote:
> Hi Dale,
>
>> #include <cstdio>
>> class A {
>> public:
>> A() {}
>> ~A() {}
>> };
>> void f() {
>> A a;
>> throw 5.0;
>> }
>> main() {
>> try {
>> f();
>> } catch(...) { printf("caught\n"); }
>> }
>
> this example indeed shows the problem. Let me explain to see if we
> agree on what
> the problem is. Suppose we don't artificially add catch-alls to
> selectors. Then
> the above example compiles to:
>
> define void @_Z1fv() {
> ...
> invoke void @__cxa_throw( something ) noreturn
> to label %somewhere unwind label %lpad
> ...
> lpad:
> %eh_ptr = tail call i8* @llvm.eh.exception( )
> %eh_select8 = tail call i32 (i8*, i8*, ...)*
> @llvm.eh.selector.i32( i8* %eh_ptr, i8* bitcast (i32 (...)*
> @__gxx_personality_v0 to i8*))
I wasn't advocating this; agree it is wrong.
> tail call i32 (...)* @_Unwind_Resume( i8* %eh_ptr )
> unreachable
> ...
> }
>
> define i32 @main() {
> entry:
> invoke void @_Z1fv( )
> to label %somewhere2 unwind label %lpad2
> ...
> lpad2: ; preds = %entry
> %eh_ptr = tail call i8* @llvm.eh.exception( )
> %eh_select14 = tail call i32 (i8*, i8*, ...)*
> @llvm.eh.selector.i32( i8* %eh_ptr, i8* bitcast (i32 (...)*
> @__gxx_personality_v0 to i8*), i8* null )
> print_a_message_and_exit
> ...
> }
>
> And this works fine: main calls _Z1fv which throws an exception.
> Execution branches to lpad where
> (empty) cleanup code is run, then unwinding is resumed. The
> unwinder unwinds into main, and branches
> to lpad2 (because the selector has a catch-all, the null) which
> prints a message and exits.
>
> If the inliner is run, then we get:
>
> define i32 @main() {
> ...
> invoke void @__cxa_throw( something ) noreturn
> to label %somewhere unwind label %lpad.i
> ...
> lpad.i:
> %eh_ptr.i = tail call i8* @llvm.eh.exception( ) ;
> <i8*> [#uses=2]
> %eh_select8.i = tail call i32 (i8*, i8*, ...)*
> @llvm.eh.selector.i32( i8* %eh_ptr.i, i8* bitcast (i32 (...)*
> @__gxx_personality_v0 to i8*))
> invoke i32 (...)* @_Unwind_Resume( i8* %eh_ptr.i )
> to label %somewhere2 unwind label %lpad2
> ...
> lpad2: ; preds = %lpad.i
> %eh_ptr = tail call i8* @llvm.eh.exception( ) ;
> <i8*> [#uses=2]
> %eh_select14 = tail call i32 (i8*, i8*, ...)*
> @llvm.eh.selector.i32( i8* %eh_ptr, i8* bitcast (i32 (...)*
> @__gxx_personality_v0 to i8*), i8* null )
> print_a_message_and_exit
> ...
> }
>
> This is perfectly correct given LLVM invoke semantics.
> Unfortunately the unwinder doesn't
> know about those :) When run, the exception is thrown but the
> unwinder doesn't branch
> to lpad.i because the selector doesn't state that that (or any)
> exception should be caught. Thus
> the program is terminated.
OK.
> If you force a "cleanup" by changing the selector call to:
> %eh_select8.i = tail call i32 (i8*, i8*, ...)*
> @llvm.eh.selector.i32( i8* %eh_ptr.i, i8* bitcast (i32 (...)*
> @__gxx_personality_v0 to i8*), i32 0)
> then it doesn't work either: the unwinder observes that there is
> only a cleanup, and
> using some special logic (bogus in this case) deduces that the
> exception will be rethrown
> after running the cleanup code (and thus the program terminated), so
> doesn't bother running
> the cleanup code and directly terminates the program (apparently
> terminating programs quickly
> was important to whoever wrote the unwinder, I don't know why; the
> Ada unwinder doesn't do
> this for example :) ).
OTOH, claiming that everything has a cleanup seems to me a correct
description of what the IR code does: control reenters the throwing
function to execute a possibly null cleanup, then resumes. The
trouble is you can't simply copy that IR while inlining and expect
things to still work, because the operation of Unwind_Resume depends
on what stack frame it's in. I don't agree that the inlined version
is correct IR. The 'invoke semantics' you're talking about are
inextricably intertwined with _Unwind_Resume's semantics.
> However if you add a catch-all to the selector instead:
> %eh_select8.i = tail call i32 (i8*, i8*, ...)*
> @llvm.eh.selector.i32( i8* %eh_ptr.i, i8* bitcast (i32 (...)*
> @__gxx_personality_v0 to i8*), i8* null)
> then the unwinder does branch to lpad.i. Then the _Unwind_Resume
> call causes a branch to
> lpad2 and all works perfectly.
>
> This is why I forcably push a catch-all at the end of each selector
> call: because then if an
> exception unwinds through an invoke, control always branches to the
> landing pad, which is what
> LLVM invoke semantics require and the inliner has exploited.
>
> Unfortunately it seems this breaks the Darwin unwinder.
Yes.
> It is true that you can imagine a solution in which the inliner
> knows about selector calls
> and shuffles them around. I will think about this. That said,
> invoke is defined to have
> certain semantics, and I don't much like the idea of trying to do an
> end-run around them
> and around optimizers that exploit them...
I don't see much choice. I guess I'll look at getting the inliner to
do what I think it should do. (I'll make it Darwin-specific at
first, but it should work on Linux, and let me point out that you'll
get more efficient code this way.)
I guess an easy but undesirable fallback position is to tell the
inliner not to inline anything that invokes Unwind_Resume.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20071210/1bed530c/attachment.html>
More information about the llvm-dev
mailing list