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

Bill Wendling wendling at apple.com
Fri Mar 23 16:46:31 PDT 2012


On Mar 23, 2012, at 4:29 PM, Paul J. Lucas wrote:

> On Mar 23, 2012, at 3:25 PM, Bill Wendling wrote:
> 
>> Let's take your example. You will have code that looks like this:
>> 
>> extern "C" void thunk_item_M_delete( void *v_that ) {
>>   item *that = 0;
>>   try {
>>     that = static_cast<item*>( v_that );
>>     that->~item();
>>   } catch (...) {
>>     that->~item();
>>   }
>> }
> 
> No I wouldn't since destructors should never throw exceptions:

You're missing the point.

>> Now the point I was making is that all of the locally scoped variables will have their d'tors called automatically by the unwinding mechanism. So something like this:
>> 
>> void foo() {
>>   try {
>>     Bar b;
>>     mux(b);  // may throw an exception.
>>   } catch (...) {
>>     // The d'tor for 'b' is called before the code is is executed.
>>   }
>> }
> 
> Except I don't have code like that.  Again, the object is *not* constructed by a normal function, but a JIT'd function.  Since LLVM knows nothing about C++ or destructors, I certainly have to call the destructor manually, e.g., obj->~Class();, for the case where no exception is thrown.  My JIT'd code looks like:
> 
> %item = type { [16 x i8] }
> %singleton_iterator = type { [24 x i8] }
> %add_iterator = type { [24 x i8] }
> 
> define void @program(void* %result) {
> entry:
>  ; alloc raw space for x on the stack
>  %x = alloca %item, align 8
> 
>  ; alloc raw space for y on the stack
>  %y = alloca %item, align 8
> 
>  ; call item::item(1) constructor for x as new(%0) item(1);
>  %0 = bitcast %item* %x to void*
>  call void @thunk_item_M_new_i(void* %0, i32 1)
> 
>  ; call item::item(2) constructor for y as new(%1) item(2);
>  %1 = bitcast %item* %y to void*
>  call void @thunk_item_M_new_i(void* %1, i32 2)
> 
>  ; Do something with x & y via another thunk_ function that MIGHT throw an exception.
>  ; If an exception is thrown, the code below will never be reached.
> 
>  ; call destructor for x as %1->!item();
>  call void @thunk_item_M_delete(void* %1)
> 
>  ; call destructor for y as %1->!item();
>  call void @thunk_item_M_delete(void* %0)
>  ret void
> }
> 
> Clearly, I *must* call the destructors manually for the case where no exception is thrown (again, because LLVM knows nothing about C++ or destructors).  If I do nothing else and an exception is thrown during the use of x & y, then control flow will never reach my JIT'd code that calls the destructors manually.  I've tested this.  It behaves as I describe it.
> 
> So, to ensure the destructors are called, I have to catch the exception, call the destructors, and rethrow.  I can't see any other way despite what you've been saying.  If I'm still wrong, I'd really appreciate your continued patience in explaining where.
> 
You need to look at how the equivalent code in C++ looks in LLVM IR. The "do something with x & y" part will be an 'invoke' that lands on at landing pad, where you will then call the d'tor to your allocated object.

-bw




More information about the llvm-dev mailing list