[LLVMdev] Catching C++ exceptions, cleaning up, rethrowing

Bill Wendling wendling at apple.com
Sun Apr 8 22:40:42 PDT 2012


On Apr 8, 2012, at 8:47 AM, Paul J. Lucas wrote:

> On Apr 8, 2012, at 4:20 AM, Bill Wendling wrote:
> 
>> On Apr 4, 2012, at 9:32 PM, Paul J. Lucas wrote:
>> 
>>> This all seems to work just fine.  I can throw a C++ exception either in a C++ object's constructor or in an ordinary member function and the stack unwinds correctly (the object's destructors are called) and the exception is propagated back up the C++ code that called the JIT'd code.
>>> 
>>> So is this the right way to do this?  Am I missing anything?
>> 
>> This looks like roughly what I would expect the code to be. I'd have to see the LLVM IR it generated to be sure. But it looks okay to me. (And if it's working for you, all the better. ;-) )
> 
> A snippet of the IR code is:
> 
The code is good, except for one comment (see below).

>> entry:
>>  %add_it = alloca %add_iterator, align 8
>>  %y_it = alloca %singleton_iterator, align 8
>>  %x_it = alloca %singleton_iterator, align 8
>>  %y = alloca %item, align 8
>>  %x = alloca %item, align 8
>>  %exception_caught_flag = alloca i8, align 1
>>  store i8 0, i8* %exception_caught_flag, align 1
>>  %caught_result_storage = alloca %0, align 8
>>  store %0 zeroinitializer, %0* %caught_result_storage, align 8
>>  %0 = bitcast %item* %x to void*
>>  invoke void @thunk_item_M_new_i(void* %0, i32 1)
>>          to label %normal unwind label %unwind
>> 
>> exit:                                             ; preds = %unwind, %dtor
>>  %1 = phi %0 [ %5, %unwind ], [ %6, %dtor ]
>>  %2 = phi i8 [ 1, %unwind ], [ %7, %dtor ]
>>  %3 = icmp eq i8 %2, 0
>>  br i1 %3, label %return, label %unwind_resume
>> 
>> return:                                           ; preds = %exit
>>  ret void
>> 
>> unwind_resume:                                    ; preds = %exit
>>  resume %0 %1
>> 
>> normal:                                           ; preds = %entry
>>  %4 = bitcast %item* %y to void*
>>  invoke void @thunk_item_M_new_i(void* %4, i32 2)
>>          to label %normal1 unwind label %unwind2
>> 
>> unwind:                                           ; preds = %entry
>>  %5 = landingpad %0 personality i32 (...)* @__gxx_personality_v0
>>          cleanup
>>  store %0 %5, %0* %caught_result_storage, align 8

What gets returned by the landingpad instruction (%0 here) is normally a structure. LLVM doesn't typically treat aggregates as first-class citizens. In particular, you shouldn't store the whole structure to memory like you do to %5. You can use 'extractvalue' to get the different elements of the structure. If you need to reconstitute the structure (for the 'resume' instruction), you can use the 'insertvalue' instruction. The code it produces is more verbose, but after optimizations, code-gen typically doesn't produce a lot of code for these sequences.

>>  store i8 1, i8* %exception_caught_flag, align 1
>>  br label %exit
>> 
>> dtor:                                             ; preds = %unwind2, %dtor3
>>  %6 = phi %0 [ %9, %unwind2 ], [ %10, %dtor3 ]
>>  %7 = phi i8 [ 1, %unwind2 ], [ %11, %dtor3 ]
>>  call void @thunk_item_M_delete(void* %0)
>>  br label %exit
>> 
>> normal1:                                          ; preds = %normal
>>  %8 = bitcast %singleton_iterator* %x_it to void*
>>  invoke void @thunk_singleton_iterator_M_new(void* %8, void* %0)
>>          to label %normal4 unwind label %unwind5
>> 
>> ; ...
> 
> I generate the "exit", "return", and "unwind_resume" blocks first followed by a sequence of "normal", "unwind", and "dtor" blocks.  A dtorN block calls dtorN-1 unless N=0 in which case it calls exit instead.
> 
> Now I think my last part of the puzzle is the personality function.  Running on a *nix system (Mac OS X), my code has:
> 
>> Function* getPersonalityFunction( Module *m ) {
>>  if ( Function *const f = m->getFunction( "__gxx_personality_v0" ) )
>>    return f;
>>  return Function::Create(
>>    FunctionType::get( Type::getInt32Ty( m->getContext() ), true ),
>>    Function::ExternalLinkage,
>>    "__gxx_personality_v0",
>>    m
>>  );
>> }
> 
> for the personality function and it works.  But I assume that works only for a *nix/g++ system.  My code also needs to work when compiled on Windows using Visual Studio and I'm guessing that it needs a different personality function.  Where do I get that?
> 
That's not something I'm familiar with. You may be able to use the same personality function, but I know that Windows also uses SEH for its exception handling scheme. This is not supported by LLVM.

My suggestion is to use clang to compile a program with exceptions on your Windows box and then use the personality function that it generates.

> The code in ExceptionDemo.cpp doesn't say whether it's cross-platform.  Is it?  Would I instead need to copy/past all that code for the personality function?
> 
The code in ExceptionDemo.cpp is creating and using its own personality function. In that way, it's cross-platform (though I haven't personally tried it on Windows). I would say that you don't need most of it. The personality function is supplied to you via the unwinding runtime.

-bw




More information about the llvm-dev mailing list