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

Bill Wendling wendling at apple.com
Thu Mar 22 00:28:58 PDT 2012


On Mar 20, 2012, at 7:38 PM, Paul J. Lucas wrote:

> To recap, on Mar 14, 2012, I wrote:
> 
>> My project has a C++ library that I want to allow the user to use via some programming language to be JIT'd to call functions in said library.  For the sake of simplicity, assume the library has classes like:
>> 
>> 	class item_iterator {
>> 	public:
>> 	  virtual ~item_iterator();
>> 	  virtual bool next( item *result ) = 0;
>> 	};
>> 
>> I'm aware that LLVM doesn't know anything about C++ and that one way to call C++ functions is to wrap them in C thunks:
>> 
>> 	extern "C" bool thunk_iterator_M_next( void *v_that, void *v_result ) {
>> 	  item_iterator *const that = static_cast<item_iterator*>( v_that );
>> 	  item *const result = static_cast<item*>( v_result );
>> 	  return that->next( result );
>> 	}
>> 
>> 	extern "C" void thunk_iterator_M_delete( void *v_that ) {
>> 	  item_iterator *const that = static_cast<item_iterator*>( v_that );
>> 	  that->~item_iterator();
>> 	}
> 
> Thanks to a previous answer, I now have everything working.  My next problem is to deal with exceptions that may be thrown from the C++ functions that are called via the thunks, e.g., what if that->next() throws an exception?  I need to be able to catch it, call a clean-up function, and rethrow the exception so the code calling the JIT'd code can deal with the exception.
> 
> I've read the docs on LLVM exceptions, but I don't see any examples.  A little help?
> 
I don't think this has anything to do with LLVM's IR-level exception system. It sounds to me like you just need a way to handle C++ exceptions inside of the C++ code and then rethrow so that the JIT's caller can do its thing. (Right?)

You could move the C++ code into a C++ function that catches all exceptions. The C functions you provide would call the small bit of C++ code that would then execute the "real" functionality. You would have to wrap/unwrap the variables, of course. (There are examples of wrapping/unwrapping of variables in LLVM's source tree.) That way you will get to use C++'s exception handling system instead of creating your own, which is a huge massive undertaking full of pitfalls. When you rethrow the exception, it will propagate past the C function to the code calling the JIT'ed code.

> One thought might be to try to handle all the C++ exception code in the thunks.  The JIT'd code would create/maintain a simple array-of-structs like:
> 
> 	struct dtor_pair {
> 	  void (*dtor_fn)(void*);
> 	  void *that;
> 	};
> 	dtor_pair dtor_pairs[10];
> 
> that contain a pointer to the thunk for a destructor and a pointer to the object to be destructed.
> 
> As the JIT'd code creates objects on the stack, it populates the dtor_pairs array.  This array could then be passed to every thunk:
> 
> 	extern "C" bool thunk_iterator_M_next( void *v_that, void *v_result,
> 	                                       dtor_pairs *dtors ) {
> 	  try {
> 	    item_iterator *const that = static_cast<item_iterator*>( v_that );
> 	    item *const result = static_cast<item*>( v_result );
> 	    return that->next( result );
> 	  }
> 	  catch ( ... ) {
> 	    run_dtors( dtors );
> 	    throw;
> 	  }
> 	}
> 
> where run_dtors() would run through the array backwards calling the destructor functions in reverse order of construction.
> 
> Would this work?  If so, then I wouldn't have to mess with handing C++ exceptions from LLVM.  But is there a better "LLVM way" to do what I want?
> 
What you're doing is recreating what the personality function and DWARF unwinding library do.

-bw





More information about the llvm-dev mailing list