我正在使用LLVM C++ API.我想JIT编译代码并运行它.
但是,我需要从所述JIT编译的代码中调用C++方法.通常,LLVM将方法调用视为函数调用,并将对象指针作为第一个参数传递,因此调用不应该是一个问题.真正的问题是将该功能转换为LLVM.
据我所知,可以使用外部链接功能,并通过其名称获取它.问题是,因为它是一个C++方法,它的名字会被破坏,所以我不认为这样做是个好主意.
使FunctionType对象很容易.但是从那里,我如何通知LLVM我的方法并Function为它获取一个对象?
zne*_*eak 15
来自LLVM邮件列表的人很有帮助,可以提供更好的解决方案.他们没有说如何从方法到函数获取指针,但我已经想出了这一部分,所以没关系.
编辑干净的方法是将方法包装到函数中:
int Foo_Bar(Foo* foo)
{
return foo->bar();
}
Run Code Online (Sandbox Code Playgroud)
然后使用Foo_Bar的地址而不是试图得到Foo::bar的.使用llvm::ExecutionEngine::addGlobalMapping添加映射,如下图所示.
像往常一样,最简单的解决方案有一些有趣的好处.例如,它可以在没有打嗝的情况下使用虚拟功能.(但它的娱乐性却低得多.其余的答案都是为了历史目的而保留的,主要是因为我在C++运行时的内部发挥了很多乐趣.还要注意它是不可移植的.)
你需要沿着这些方向的东西来计算方法的地址(要注意,这是一个可能只与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;
}
Run Code Online (Sandbox Code Playgroud)
然后使用llvm::ExecutionEngine::addGlobalMapping将函数映射到您已获得的地址.要调用它,将您的对象作为第一个参数传递,其余的像往常一样.这是一个简单的例子.
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));
Run Code Online (Sandbox Code Playgroud)
一种方法是围绕所需方法的C包装器,即
extern "C" {
void wrapped_foo(bar *b, int arg1, int arg2) {
b->foo(arg1, arg2);
}
}
Run Code Online (Sandbox Code Playgroud)
该extern "C"位使函数使用C调用约定并防止任何名称修改.有关C/C++互操作的详细信息,请参阅http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html#faq-32.6extern "C"
您还应该能够在C++代码中获取函数的地址,然后将该地址存储在LLVM已知的全局中.
呵呵,使用非标准dladdr且极其复杂且不安全的方式将方法指针强制转换为 void 指针,似乎有一种方法可以从方法的指针获取方法的名称。
这肯定比枪支更危险。不要在家里(或在工作中)这样做。
dladdr即使使用全能的 C 强制转换,C++ 也禁止将方法指针强制转换为 void*(这是工作所必需的),但您可以欺骗这一点。
#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 "";
}
Run Code Online (Sandbox Code Playgroud)
从那里:
int main()
{
std::cout << getMethodName(&Foo::bar) << std::endl;
// prints something like "_ZN3Foo3barEv"
}
Run Code Online (Sandbox Code Playgroud)
...aaa并且您应该能够在 LLVM 中使用该符号名称。但它不适用于虚拟方法(不使用它的另一个很好的理由)。
编辑更深入地了解虚拟方法指针的处理方式,我已经组合了一个更复杂的函数,也适用于它们。只有最勇敢的人才应该点击此链接。