[LLVMdev] Darwin vs exceptions

Duncan Sands baldrick at free.fr
Sun Dec 9 13:01:48 PST 2007


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*))
        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.  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 :) ).  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.

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...

Ciao,

Duncan.



More information about the llvm-dev mailing list