Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I bind an existing method to a LLVM Function* and use it from JIT-compiled code?

Tags:

I'm toying around with the LLVM C++ API. I'd like to JIT compile code and run it.

However, I need to call a C++ method from said JIT-compiled code. Normally, LLVM treats method calls as function calls with the object pointer passed as the first argument, so calling shouldn't be a problem. The real problem is to get that function into LLVM.

As far as I can see, it's possible to use external linkage for functions and get it by its name. Problem is, since it's a C++ method, its name is going to be mangled, so I don't think it's a good idea to go that way.

Making the FunctionType object is easy enough. But from there, how can I inform LLVM of my method and get a Function object for it?

like image 306
zneak Avatar asked Jun 23 '10 18:06

zneak


People also ask

Is Llvm a JIT?

The LLVM JIT compiler is function-based because it is able to compile a single function at a time. This defines the granularity at which the compiler works, which is an important decision of a JIT system.

What is LLjit?

LLJIT is a suitable replacement for MCJIT in most cases (note: some more advanced features, e.g. JITEventListeners are not supported yet). The LLLazyJIT extends LLJIT and adds a CompileOnDemandLayer to enable lazy compilation of LLVM IR.


1 Answers

The dudes from the LLVM mailing list were helpful enough to provide a better solution. They didn't say how to get the pointer from the method to the function, but I've already figured out this part so it's okay.

EDIT A clean way to do this is simply to wrap your method into a function:

int Foo_Bar(Foo* foo) {     return foo->bar(); } 

Then use Foo_Bar's address instead of trying to get Foo::bar's. Use llvm::ExecutionEngine::addGlobalMapping to add the mapping as shown below.

As usual, the simplest solution has some interesting benefits. For instance, it works with virtual functions without a hiccup. (But it's so much less entertaining. The rest of the answer is kept for historical purposes, mainly because I had a lot of fun poking at the internals of my C++ runtime. Also note that it's non-portable.)


You'll need something along these lines to figure the address of a method (be warned, that's a dirty hack that probably will only be compatible with the Itanium ABI):

template<typename T> const void* void_cast(const T& object) {     union Retyper     {         const T object;         void* pointer;         Retyper(T obj) : object(obj) { }     };      return Retyper(object).pointer; }  template<typename T, typename M> const void* getMethodPointer(const T* object, M method) // will work for virtual methods {     union MethodEntry     {         intptr_t offset;         void* function;     };      const MethodEntry* entry = static_cast<const MethodEntry*>(void_cast(&method));      if (entry->offset % sizeof(intptr_t) == 0) // looks like that's how the runtime guesses virtual from static         return getMethodPointer(method);      const void* const* const vtable = *reinterpret_cast<const void* const* const* const>(object);     return vtable[(entry->offset - 1) / sizeof(void*)]; }  template<typename M> const void* getMethodPointer(M method) // will only work with non-virtual methods {     union MethodEntry     {         intptr_t offset;         void* function;     };      return static_cast<const MethodEntry*>(void_cast(&method))->function; } 

Then use llvm::ExecutionEngine::addGlobalMapping to map a function to the address you've gotten. To call it, pass it your object as the first parameter, and the rest as usual. Here's a quick example.

class Foo {     void Bar();     virtual void Baz(); };  class FooFoo : public Foo {     virtual void Baz(); };  Foo* foo = new FooFoo;  const void* barMethodPointer = getMethodPointer(&Foo::Bar); const void* bazMethodPointer = getMethodPointer(foo, &Foo::Baz); // will get FooFoo::Baz  llvm::ExecutionEngine* engine = llvm::EngineBuilder(module).Create();  llvm::Function* bar = llvm::Function::Create(/* function type */, Function::ExternalLinkage, "foo", module); llvm::Function* baz = llvm::Function::Create(/* function type */, Function::ExternalLinkage, "baz", module); engine->addGlobalMapping(bar, const_cast<void*>(barMethodPointer)); // LLVM always takes non-const pointers engine->addGlobalMapping(baz, const_cast<void*>(bazMethodPointer)); 
like image 173
zneak Avatar answered Feb 08 '23 00:02

zneak