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

Bill Wendling wendling at apple.com
Fri Mar 23 15:25:51 PDT 2012


On Mar 23, 2012, at 6:27 AM, Paul J. Lucas wrote:

> On Mar 22, 2012, at 5:29 PM, Bill Wendling wrote:
> 
>> On Mar 22, 2012, at 11:40 AM, Paul J. Lucas <paul at lucasmail.org> wrote:
>> 
>>> Unfortunately, I'm not following.  How is having the code that catches all exceptions in a separate function different from what I proposed (putting the try/catch in the thunks)?  (Ideally, I want to minimize layers of function calls.)  Again for reference:
>> 
>> No reason. But if you have the 'try{}catch(...){}', then it should run the d'tors for you. There's no reason for you to have a "run_dtors" function there.
> 
> How does the C++ implementation "know" to run the d'tors for me since the C++ objects that were created on the stack were created by JIT'd code, first via alloca to allocate StructTypes of the right size (char[sizeof(T)]) then calling a thunk of the form:
> 
> 	extern "C" void thunk_item_M_new( void *addr ) {
> 	  new( addr ) item;
> 	}
> 
> where "addr" is the address returned by alloca?  To me, it would seem that if you're right, that I shouldn't need any try/catch at all.
> 
> When I put tracer print statements in my class's destructors, I can see that they are called only if I explicitly call them via JIT'd code that calls the relevant thunk, e.g.:
> 
> 	extern "C" void thunk_item_M_delete( void *v_that ) {
> 	  item *const that = static_cast<item*>( v_that );
> 	  that->~item();
> 	}
> 
> Given that I have to call the thunks to run the d'tors manually in the normal control-flow case, it would seem that I would also have to call them in the exception-thrown control-flow case -- right?
> 
In the cases above, you've allocated in some capacity. The d'tor of a pointer is...nothing. It doesn't do anything. You have to manually call "delete" on the pointer. E.g.:

  Bar *foo() {
    Bar *b = new Bar();
    return b;   // b goes out of scope, but b::~b isn't called.
  }

  void qux() {
    Bar *b = foo();
    // do stuff with b.
    delete b; // Now we can delete the stuff.
  }

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();
    }
  }

The unwinding mechanism "knows" how to call the stuff in the 'catch' clause because of the exception handling metadata that's produced for this function (which is beyond the scope of this thread). 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.
    }
  }

-bw




More information about the llvm-dev mailing list