[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