Using JIT code to code a program to call C++

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 {
  public:
    item();
    item( int );
    ~item();
    // ...
  };

  class item_iterator {
  public:
    virtual ~item_iterator();
    virtual bool next( item *result ) = 0;
  };

  class singleton_iterator : public item_iterator {
  public:
    singleton_iterator( item const &i );
    // ...
  };

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" {

    void thunk_item_M_new( item *addr ) {
      new( addr ) item;
    }

    void thunk_singleton_iterator_M_new( singleton_iterator *addr, item *i ) {
      new( addr ) singleton_iterator( *i );
    }

    bool thunk_iterator_M_next( item_iterator *that, item *result ) {
      return that->next( result );
    }

  } // extern "C"

The first problem is how to allocate an item from LLVM. I know how to create StructTypes and add fields to them, but I don't have to have to parallel the C++ class layout -- that's tedious and error-prone.

The idea I got was simply to add a char[sizeof(T)] as the only field to a StructType for a C++ class type:

  StructType *const llvm_item_type = StructType::create( llvm_ctx, "item" );
  vector<Type*> llvm_struct_types;
  llvm_struct_types.push_back( ArrayType::get( IntegerType::get( llvm_ctx, 8 ), sizeof( item ) ) );
  llvm_item_type->setBody( llvm_struct_types, false );
  PointerType *const llvm_item_ptr_type = PointerType::getUnqual( llvm_item_type );

I would think that, because it's a StructType, the alignment would be correct and the sizeof(item) would get the size right. Will that work? Is there a better way?

The second problem is that, unlike the C++ class hierarchy, there's no inheritance relationship between StructTypes. If I create a Function that takes an llvm_iterator_type but try to build a Function object using an llvm_singleton_iterator_type, the LLVM verifyModule() function complains at me:

Call parameter type does not match function signature!

So then I thought I'd simply use void* everywhere:

  Type *const llvm_void_type = Type::getVoidTy( llvm_ctx );
  PointerType *const llvm_void_ptr_type = PointerType::getUnqual( llvm_void_type );

but verifyModule() still complains at me because, apparently, there's no automatic casting to void* types in LLVM. How can I solve this problem?

- Paul

"Paul J. Lucas" <paul@lucasmail.org> writes:

[snip]

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:

Yes, this is the only sane way unless you are willing to replicate the
ABI of your chosen C++ implementation onto your system. Let's forget
about dealing with more than one C++ ABI :wink:

[snip]

I would think that, because it's a StructType, the alignment would be
correct and the sizeof(item) would get the size right. Will that
work?

Yes. I do the same on my compiler. Please note that this trick is only
necessary if you pretend to put C++ objects on your stack. If all C++
objects your JITted code creates are on the heap, you can simply use
`new' instead of placement `new' in the C function that creates the C++
object and deal with the returned pointer..

Is there a better way?

It all depends on the charateristics of your language.

[snip]

but verifyModule() still complains at me because, apparently, there's
no automatic casting to void* types in LLVM. How can I solve this
problem?

You must do the cast yourself, or simply use void* for all pointers for
all your C++ objects and deal with its real type on the upper part of
the compiler.

As said above, it all depends on the details of your language.

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

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

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

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:

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.

-bw

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?

- Paul

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

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

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:

You're missing the point.

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.

You need to look at how the equivalent code in C++ looks in LLVM IR. The "do something with x & y" part will be an 'invoke' that lands on at landing pad, where you will then call the d'tor to your allocated object.

-bw

You need to look at how the equivalent code in C++ looks in LLVM IR. The "do something with x & y" part will be an 'invoke' that lands on at landing pad, where you will then call the d'tor to your allocated object.

OK, after a lot of tinkering (and looking at the ExceptionDemo.cpp file), I have something that works using invoke and landingpad.

The code in ExceptionDemo.cpp seems more involved than I (think I) need it to be in that I don't care what exception is thrown or whether it's "foreign" (again, I think). I just want to implement try/finally semantics and propagate whatever exception was thrown back up the call stack.

I've written a function to add what I think is all the necessary code for constructing/destructing an object:

AllocaInst* construct_obj( char const *obj_name, Type *obj_type,
                           Function *ctor, vector<Value*> &ctor_args,
                           Function *dtor ) {

  // Allocate object's storage and insert 'this' ptr into ctor args.
  AllocaInst *const obj_ptr = createEntryBlockAlloca( fn, obj_name, obj_type );
  Value *const void_obj_ptr = builder.CreateBitCast( obj_ptr, void_ptr_type );
  ctor_args.insert( ctor_args.begin(), void_obj_ptr );
  
  // Call object's constructor.
  BasicBlock *const normal_blk = BasicBlock::Create( ctx, "normal", fn );
  BasicBlock *const unwind_blk = BasicBlock::Create( ctx, "unwind", fn );
  builder.CreateInvoke( ctor, normal_blk, unwind_blk, ctor_args );
  
  // Call object's destructor.
  BasicBlock *const dtor_blk = BasicBlock::Create( ctx, "dtor", fn );
  builder.SetInsertPoint( dtor_blk );
  builder.CreateCall( dtor, void_obj_ptr );
  builder.CreateBr( unwind_blks_.back() );
  
  // Catch exception, if any.
  builder.SetInsertPoint( unwind_blk );
  LandingPadInst *const lpad = builder.CreateLandingPad(
    caught_result_type_, personality_fn_, 0
  );
  lpad->setCleanup( true );
  builder.CreateStore( lpad, caught_result_storage_ );
  builder.CreateStore( exception_thrown_state_, exception_caught_flag_ );
  builder.CreateBr( unwind_blks_.back() );

  unwind_blks_.push_back( dtor_blk );

  builder.SetInsertPoint( normal_blk );
  return obj_ptr;
}

The unwind_blks_ is a vector<BasicBlock*> that contains the blocks of code that should be executed in reverse-iterator order as the function is unwinding (either due to an exception being thrown or simply normally). The value of unwind_blks_[0] is the exit block that's created by:

  builder.SetInsertPoint( exit_blk_ );
  builder.CreateCondBr(
    // If we caught an exception ...
    builder.CreateICmpNE(
      builder.CreateLoad( exception_caught_flag_ ),
      exception_not_thrown_state_
    ),
    // ... then branch to the unwind-resume block
    unwind_resume_blk,
    // ... else branch to the return block
    return_blk
  );

The rest of the unwind_blks_ are the dtor_blk's that are created to call the object's destructors.

The unwind_resume_blk referenced in the exit_blk is created by:

  BasicBlock *const unwind_resume_blk = BasicBlock::Create( ctx, "unwind_resume", fn );
  builder.SetInsertPoint( unwind_resume_blk );
  builder.CreateResume( builder.CreateLoad( caught_result_storage_ );

and the return_blk is simply a CreateRetVoid().

This all seems to work just fine. I can throw a C++ exception either in a C++ object's constructor or in an ordinary member function and the stack unwinds correctly (the object's destructors are called) and the exception is propagated back up the C++ code that called the JIT'd code.

So is this the right way to do this? Am I missing anything?

- Paul

[...]

This all seems to work just fine. I can throw a C++ exception either in a C++ object's constructor or in an ordinary member function and the stack unwinds correctly (the object's destructors are called) and the exception is propagated back up the C++ code that called the JIT'd code.

So is this the right way to do this? Am I missing anything?

Hi Paul,

This looks like roughly what I would expect the code to be. I'd have to see the LLVM IR it generated to be sure. But it looks okay to me. (And if it's working for you, all the better. :wink: )

-bw

This all seems to work just fine. I can throw a C++ exception either in a C++ object's constructor or in an ordinary member function and the stack unwinds correctly (the object's destructors are called) and the exception is propagated back up the C++ code that called the JIT'd code.

So is this the right way to do this? Am I missing anything?

This looks like roughly what I would expect the code to be. I'd have to see the LLVM IR it generated to be sure. But it looks okay to me. (And if it's working for you, all the better. :wink: )

A snippet of the IR code is:

entry:
  %add_it = alloca %add_iterator, align 8
  %y_it = alloca %singleton_iterator, align 8
  %x_it = alloca %singleton_iterator, align 8
  %y = alloca %item, align 8
  %x = alloca %item, align 8
  %exception_caught_flag = alloca i8, align 1
  store i8 0, i8* %exception_caught_flag, align 1
  %caught_result_storage = alloca %0, align 8
  store %0 zeroinitializer, %0* %caught_result_storage, align 8
  %0 = bitcast %item* %x to void*
  invoke void @thunk_item_M_new_i(void* %0, i32 1)
          to label %normal unwind label %unwind

exit: ; preds = %unwind, %dtor
  %1 = phi %0 [ %5, %unwind ], [ %6, %dtor ]
  %2 = phi i8 [ 1, %unwind ], [ %7, %dtor ]
  %3 = icmp eq i8 %2, 0
  br i1 %3, label %return, label %unwind_resume

return: ; preds = %exit
  ret void

unwind_resume: ; preds = %exit
  resume %0 %1

normal: ; preds = %entry
  %4 = bitcast %item* %y to void*
  invoke void @thunk_item_M_new_i(void* %4, i32 2)
          to label %normal1 unwind label %unwind2

unwind: ; preds = %entry
  %5 = landingpad %0 personality i32 (...)* @__gxx_personality_v0
          cleanup
  store %0 %5, %0* %caught_result_storage, align 8
  store i8 1, i8* %exception_caught_flag, align 1
  br label %exit

dtor: ; preds = %unwind2, %dtor3
  %6 = phi %0 [ %9, %unwind2 ], [ %10, %dtor3 ]
  %7 = phi i8 [ 1, %unwind2 ], [ %11, %dtor3 ]
  call void @thunk_item_M_delete(void* %0)
  br label %exit

normal1: ; preds = %normal
  %8 = bitcast %singleton_iterator* %x_it to void*
  invoke void @thunk_singleton_iterator_M_new(void* %8, void* %0)
          to label %normal4 unwind label %unwind5

; ...

I generate the "exit", "return", and "unwind_resume" blocks first followed by a sequence of "normal", "unwind", and "dtor" blocks. A dtorN block calls dtorN-1 unless N=0 in which case it calls exit instead.

Now I think my last part of the puzzle is the personality function. Running on a *nix system (Mac OS X), my code has:

Function* getPersonalityFunction( Module *m ) {
  if ( Function *const f = m->getFunction( "__gxx_personality_v0" ) )
    return f;
  return Function::Create(
    FunctionType::get( Type::getInt32Ty( m->getContext() ), true ),
    Function::ExternalLinkage,
    "__gxx_personality_v0",
    m
  );
}

for the personality function and it works. But I assume that works only for a *nix/g++ system. My code also needs to work when compiled on Windows using Visual Studio and I'm guessing that it needs a different personality function. Where do I get that?

The code in ExceptionDemo.cpp doesn't say whether it's cross-platform. Is it? Would I instead need to copy/past all that code for the personality function?

- Paul

This all seems to work just fine. I can throw a C++ exception either in a C++ object's constructor or in an ordinary member function and the stack unwinds correctly (the object's destructors are called) and the exception is propagated back up the C++ code that called the JIT'd code.

So is this the right way to do this? Am I missing anything?

This looks like roughly what I would expect the code to be. I'd have to see the LLVM IR it generated to be sure. But it looks okay to me. (And if it's working for you, all the better. :wink: )

A snippet of the IR code is:

The code is good, except for one comment (see below).

entry:
%add_it = alloca %add_iterator, align 8
%y_it = alloca %singleton_iterator, align 8
%x_it = alloca %singleton_iterator, align 8
%y = alloca %item, align 8
%x = alloca %item, align 8
%exception_caught_flag = alloca i8, align 1
store i8 0, i8* %exception_caught_flag, align 1
%caught_result_storage = alloca %0, align 8
store %0 zeroinitializer, %0* %caught_result_storage, align 8
%0 = bitcast %item* %x to void*
invoke void @thunk_item_M_new_i(void* %0, i32 1)
         to label %normal unwind label %unwind

exit: ; preds = %unwind, %dtor
%1 = phi %0 [ %5, %unwind ], [ %6, %dtor ]
%2 = phi i8 [ 1, %unwind ], [ %7, %dtor ]
%3 = icmp eq i8 %2, 0
br i1 %3, label %return, label %unwind_resume

return: ; preds = %exit
ret void

unwind_resume: ; preds = %exit
resume %0 %1

normal: ; preds = %entry
%4 = bitcast %item* %y to void*
invoke void @thunk_item_M_new_i(void* %4, i32 2)
         to label %normal1 unwind label %unwind2

unwind: ; preds = %entry
%5 = landingpad %0 personality i32 (...)* @__gxx_personality_v0
         cleanup
store %0 %5, %0* %caught_result_storage, align 8

What gets returned by the landingpad instruction (%0 here) is normally a structure. LLVM doesn't typically treat aggregates as first-class citizens. In particular, you shouldn't store the whole structure to memory like you do to %5. You can use 'extractvalue' to get the different elements of the structure. If you need to reconstitute the structure (for the 'resume' instruction), you can use the 'insertvalue' instruction. The code it produces is more verbose, but after optimizations, code-gen typically doesn't produce a lot of code for these sequences.

store i8 1, i8* %exception_caught_flag, align 1
br label %exit

dtor: ; preds = %unwind2, %dtor3
%6 = phi %0 [ %9, %unwind2 ], [ %10, %dtor3 ]
%7 = phi i8 [ 1, %unwind2 ], [ %11, %dtor3 ]
call void @thunk_item_M_delete(void* %0)
br label %exit

normal1: ; preds = %normal
%8 = bitcast %singleton_iterator* %x_it to void*
invoke void @thunk_singleton_iterator_M_new(void* %8, void* %0)
         to label %normal4 unwind label %unwind5

; ...

I generate the "exit", "return", and "unwind_resume" blocks first followed by a sequence of "normal", "unwind", and "dtor" blocks. A dtorN block calls dtorN-1 unless N=0 in which case it calls exit instead.

Now I think my last part of the puzzle is the personality function. Running on a *nix system (Mac OS X), my code has:

Function* getPersonalityFunction( Module *m ) {
if ( Function *const f = m->getFunction( "__gxx_personality_v0" ) )
   return f;
return Function::Create(
   FunctionType::get( Type::getInt32Ty( m->getContext() ), true ),
   Function::ExternalLinkage,
   "__gxx_personality_v0",
   m
);
}

for the personality function and it works. But I assume that works only for a *nix/g++ system. My code also needs to work when compiled on Windows using Visual Studio and I'm guessing that it needs a different personality function. Where do I get that?

That's not something I'm familiar with. You may be able to use the same personality function, but I know that Windows also uses SEH for its exception handling scheme. This is not supported by LLVM.

My suggestion is to use clang to compile a program with exceptions on your Windows box and then use the personality function that it generates.

The code in ExceptionDemo.cpp doesn't say whether it's cross-platform. Is it? Would I instead need to copy/past all that code for the personality function?

The code in ExceptionDemo.cpp is creating and using its own personality function. In that way, it's cross-platform (though I haven't personally tried it on Windows). I would say that you don't need most of it. The personality function is supplied to you via the unwinding runtime.

-bw

What gets returned by the landingpad instruction (%0 here) is normally a structure. LLVM doesn't typically treat aggregates as first-class citizens. In particular, you shouldn't store the whole structure to memory like you do to %5. You can use 'extractvalue' to get the different elements of the structure. If you need to reconstitute the structure (for the 'resume' instruction), you can use the 'insertvalue' instruction. The code it produces is more verbose, but after optimizations, code-gen typically doesn't produce a lot of code for these sequences.

I copied that bit of code from ExceptionDemo.cpp (lines 1270-1290):

  llvm::LandingPadInst *caughtResult =
    builder.CreateLandingPad(ourCaughtResultType,
                             personality,
                             numExceptionsToCatch,
                             "landingPad");
  // ...

  builder.CreateStore(caughtResult, caughtResultStorage);

Then on line 1255:

  builder.CreateResume(builder.CreateLoad(caughtResultStorage));

Are you saying that code is wrong too?

- Paul

Hi,

What gets returned by the landingpad instruction (%0 here) is normally a structure. LLVM doesn't typically treat aggregates as first-class citizens.

registers of aggregate type are legal and work. For example the dragonegg
frontend represents complex numbers as registers with type { float, float },
and it works fine. However not many advanced optimizations are performed on
them.

  In particular, you shouldn't store the whole structure to memory like you do to %5.

It is fine to store them to memory and load them out again. The dragonegg
frontend does this all the time for complex numbers.

  You can use 'extractvalue' to get the different elements of the structure. If you need to reconstitute the structure (for the 'resume' instruction), you can use the 'insertvalue' instruction. The code it produces is more verbose, but after optimizations, code-gen typically doesn't produce a lot of code for these sequences.

Indeed using extractvalue and insertvalue work fine too. But it's not
obligatory.

Ciao, Duncan.

Assuming I do that and it works, wouldn't IR code generated on Windows then be incompatible with IR-code-compiled-to-machine-code on *nix (because it would use the wrong personality function)?

Doing all the grunt work of emitting landingpads, resumes, having to get the right personality function, etc., seems a lot more work that my original proposal of catching C++ exceptions in my C thunk functions and calling a linked-list of destructors to run myself. This also has the benefit of not having to deal with platform-specific personality functions.

I realize I'm a newb and there may be something subtle I'm not getting, but I have to ask: why is doing the work in IR "better"?

- Paul

But I assume [the personality function] works only for a *nix/g++ system. My code also needs to work when compiled on Windows using Visual Studio and I'm guessing that it needs a different personality function. Where do I get that?

That's not something I'm familiar with. You may be able to use the same personality function, but I know that Windows also uses SEH for its exception handling scheme. This is not supported by LLVM.

My suggestion is to use clang to compile a program with exceptions on your Windows box and then use the personality function that it generates.

Assuming I do that and it works, wouldn't IR code generated on Windows then be incompatible with IR-code-compiled-to-machine-code on *nix (because it would use the wrong personality function)?

IR is not portable.

But I assume [the personality function] works only for a *nix/g++ system. My code also needs to work when compiled on Windows using Visual Studio and I'm guessing that it needs a different personality function. Where do I get that?

That's not something I'm familiar with. You may be able to use the same personality function, but I know that Windows also uses SEH for its exception handling scheme. This is not supported by LLVM.

My suggestion is to use clang to compile a program with exceptions on your Windows box and then use the personality function that it generates.

Assuming I do that and it works, wouldn't IR code generated on Windows then be incompatible with IR-code-compiled-to-machine-code on *nix (because it would use the wrong personality function)?

I suggested this only so that you can then copy what personality function declaration it uses. :slight_smile:

Doing all the grunt work of emitting landingpads, resumes, having to get the right personality function, etc., seems a lot more work that my original proposal of catching C++ exceptions in my C thunk functions and calling a linked-list of destructors to run myself. This also has the benefit of not having to deal with platform-specific personality functions.

I realize I'm a newb and there may be something subtle I'm not getting, but I have to ask: why is doing the work in IR "better"?

What you seem to be proposing is code that's already handled via the personality function and the unwind library. I'm trying to save you from duplicating that effort. But I also can't stop you from going along with your scheme.

Keep in mind that catching a C++ exception is a non-trivial matter. You would have to ensure that it has the same semantics as the existing DWARF implementation. E.g., running cleanups on all of the functions up the stack from where it was thrown to where it was caught (a two-phase process). And that's just the tip of the iceberg.

It *sounds* like I'm asking you to do a lot, but that's the nature of IR. You should eventually find that it isn't a lot of work to implement. :slight_smile:

-bw

My suggestion is to use clang to compile a program with exceptions on your Windows box and then use the personality function that it generates.

Assuming I do that and it works, wouldn't IR code generated on Windows then be incompatible with IR-code-compiled-to-machine-code on *nix (because it would use the wrong personality function)?

I suggested this only so that you can then copy what personality function declaration it uses. :slight_smile:

OK, I've finally had time to do this. (Other, more pressing things kept coming up.) I built llvm+clang using MinGW32 and compiled a simple C++ program that uses throw/try/catch. The landingpad instruction it generates is (for example):

%14 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*)

So this code uses the same personality function as a Unix system... which seems wrong. Shouldn't it be different for Windows?

Doing all the grunt work of emitting landingpads, resumes, having to get the right personality function, etc., seems a lot more work that my original proposal of catching C++ exceptions in my C thunk functions and calling a linked-list of destructors to run myself. This also has the benefit of not having to deal with platform-specific personality functions.

I realize I'm a newb and there may be something subtle I'm not getting, but I have to ask: why is doing the work in IR "better"?

[ snip ]

Keep in mind that catching a C++ exception is a non-trivial matter. You would have to ensure that it has the same semantics as the existing DWARF implementation. E.g., running cleanups on all of the functions up the stack from where it was thrown to where it was caught (a two-phase process). And that's just the tip of the iceberg.

But I'd have my C thunk code catching the exception and it would do the right thing (or rather the C++ compiler would emit the right code for me). *I* wouldn't have to do anything special. The C thunk code would call the destructors explicitly and simply re-throw. I assume that a C++ exception can pass through IR-generated code unmolested -- yes?

- Paul