tags:

views:

92

answers:

3

Hey guys,

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 jitted 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. 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?

+1  A: 

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
I thought there could be a more straightforward way.
zneak
+1  A: 

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
Voidify, eh. Nice.
Paul Nathan
@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
+1  A: 

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.

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
Would you mind to show us how you resolved the issue in the end. I'm struggling with the same problem.
FFox
@FFox: sure. I've edited the answer to provide a helpful example.
zneak