Paul J. Lucas
2012-Mar-21 02:38 UTC
[LLVMdev] Catching C++ exceptions, cleaning up, rethrowing
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? 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? - Paul
Paul J. Lucas
2012-Mar-22 18:40 UTC
[LLVMdev] Catching C++ exceptions, cleaning up, rethrowing
On Mar 22, 2012, at 12:28 AM, Bill Wendling wrote:> On Mar 20, 2012, at 7:38 PM, Paul J. Lucas wrote: > >> 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?)Right. The call sequence is: my_lib(1) -> JIT_code -> C_thunk -> my_lib(2) The JIT code creates Functions that create C++ objects on their stacks (by using alloca instructions then calling a C thunk that calls the C++ object's constructor via placement new). If an exception is thrown in my_lib(2), then somewhere between there and when the stack unwinds to my_lib(1), the C++ objects that were created on the stack must have their destructors called (also via C thunks). Hence, some code somewhere between my_lib(1) and C_thunk has to catch all exceptions, call the destructors, and rethrow the exceptions.> 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.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: 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; } } - Paul
Apparently Analagous Threads
- [LLVMdev] Catching C++ exceptions, cleaning up, rethrowing
- [LLVMdev] Catching C++ exceptions, cleaning up, rethrowing
- [LLVMdev] Catching C++ exceptions, cleaning up, rethrowing
- [LLVMdev] Using JIT code to code a program to call C++
- [LLVMdev] Catching C++ exceptions, cleaning up, rethrowing