25

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?

zneak
  • 134,922
  • 42
  • 253
  • 328

3 Answers3

16

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));
zneak
  • 134,922
  • 42
  • 253
  • 328
  • Would you mind to show us how you resolved the issue in the end. I'm struggling with the same problem. – FFox Jul 16 '10 at 09:53
  • @FFox: sure. I've edited the answer to provide a helpful example. – zneak Jul 16 '10 at 15:10
  • @zneak, in the function type you pass to `llvm::Function::Create` what type did you use for the first argument (the object pointer)? did you use void*? – lurscher May 05 '12 at 14:15
  • @lurscher, no, because that would be a real hassle to work with. The pattern is to make a specialization of the class `StructType` with `T` being your struct and implement the `get` method there. You can then use `llvm::StructType::get(context)` to get the type, and getting the pointer type to it is then trivial. See [here](http://pastebin.com/JbzdPDZt) for an example implementation. – zneak May 05 '12 at 18:13
  • Do you know a way to do this with the more modern `orc` Jit api? ([question here](https://stackoverflow.com/questions/69742580/how-to-resolve-symbols-that-reside-in-the-current-session-when-constructing-a-o)) – CiaranWelsh Oct 27 '21 at 21:53
8

One way is a C wrapper around the desired method, i.e.

extern "C" {
  void wrapped_foo(bar *b, int arg1, int arg2) {
    b->foo(arg1, arg2);
  }
}

The extern "C" bit makes the function use C calling conventions and prevents any name mangling. See http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html#faq-32.6 for details on C/C++ interop including extern "C"

You should also probably be able to get the address of the function in your C++ code and then store that address in a global known to LLVM.

Geoff Reedy
  • 34,891
  • 3
  • 56
  • 79
5

Huh, using the non-standard dladdr and a ridiculously convoluted and unsafe way to cast method pointers to void pointers, there seems to be a way to obtain the name of a method from its pointer.

This is certainly more dangerous than firearms. Don't do this at home (or at work, for that matter).

C++ forbids to cast method pointers to void* (which is required by dladdr to work) even with the almighty C cast, but you can cheat that.

#include <string>
#include <dlfcn.h>

template<typename T>
static void* voidify(T method)
{
    asm ("movq %rdi, %rax"); // should work on x86_64 ABI compliant platforms
}

template<typename T>
const char* getMethodName(T method)
{
    Dl_info info;
    if (dladdr(voidify(method), &info))
        return info.dli_sname;
    return "";
}

From there:

int main()
{
    std::cout << getMethodName(&Foo::bar) << std::endl;
    // prints something like "_ZN3Foo3barEv"
}

...aaaand you should be able to use that symbol name with LLVM. But it won't work with virtual methods (another good reason to not use it).

EDIT Hacking much, much deeper into how virtual method pointers are handled, I've put together a more elaborate function that works for them, too. Only the most courageous of you should follow this link.

zneak
  • 134,922
  • 42
  • 253
  • 328
  • @Paul Nathan: it did emit a few warnings, though ("control reaches end of non-void function"). I've _beautified_ it to integrate better with C++. – zneak Jun 23 '10 at 21:14