在一些 LLVM 教程中,我看到将 C 函数绑定到基于 LLVM 的自定义语言相当容易。LLVM 给程序员一个指向函数的指针,然后可以将其与 LLVM 生成的代码混合在一起。
使用 C++ 库执行此操作的最佳方法是什么。假设我有一个相当复杂的库,如 Qt 或 Boost,我想绑定到我的自定义语言。我是否需要创建一个存根库(如 Python 或 Lua 需要),或者 LLVM 是否提供某种外部函数接口 (FFI)?
在我的 LLVM 代码中,我extern "C"
为此创建了包装函数,并将 LLVM 函数声明插入到模块中以便调用它们。然后,让 LLVM 了解函数的一个好方法是不要让它使用dlopen
并在执行的二进制文件中搜索函数名(这很麻烦,因为函数名需要在.dynsym
节中,而且也很慢),但要手动进行映射,使用ExecutionEngine::addGlobalMapping。
只需通过转换为llvm::Function*
C++ 中给出的该声明和函数的地址,并将这两个东西传递给 LLVM。然后,执行您的东西的 JIT 将知道在哪里可以找到该函数。&functionname
void*
例如,如果你想包装QString
你可以创建几个函数来创建、销毁和调用这样一个对象的函数
extern "C" void createQString(void *p, char const*v) {
new (p) QString(v); // placement-new
}
extern "C" int32_t countQString(void *p) {
QString *q = static_cast<QString*>(p);
return q->count();
}
extern "C" void destroyQString(void *p) {
QString *q = static_cast<QString*>(p);
q->~QString();
}
并创建适当的声明和映射。然后你可以使用call
这些函数,传递一个适当对齐和调整大小的内存区域QString
(可能是alloca
'ed),并i8*
指向 C 字符串数据进行初始化。
如果您将一些 C++ 代码和一些另一种语言的代码编译为 LLVM 位码,那么应该完全有可能将它们链接在一起并让一个调用另一个......理论上。
在实践中,您将需要胶水代码在不同语言的类型之间进行转换(例如,除非您使用 CPython,否则在 C++ 中没有与 Python 字符串等效的字符串,因此要void reverse(std::string s)
使用 a 进行调用,str
您需要进行转换 - 更糟糕的是,整个对象模型非常不同)。Qt 特别有很多魔力,在编译后可能需要更多的努力才能暴露出来。此外,可能还有其他我不知道的潜在问题。
即使这样有效,使用起来也可能非常难看。尽管 Python 的描述符非常方便,但 PyQt中仍然存在get*
和set*
函数 - 并且在 PyQt 上付出了很多努力,它们不仅仅是创建了一些存根。