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

Paul J. Lucas paul at lucasmail.org
Fri Mar 23 16:29:26 PDT 2012


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:

	http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.9

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

- Paul





More information about the llvm-dev mailing list