【发布时间】:2010-08-23 21:11:21
【问题描述】:
在一些 LLVM 教程中,我看到将 C 函数绑定到基于 LLVM 的自定义语言相当容易。 LLVM 给程序员一个指向函数的指针,然后可以将其与 LLVM 生成的代码混合在一起。
使用 C++ 库执行此操作的最佳方法是什么。假设我有一个相当复杂的库,如 Qt 或 Boost,我想绑定到我的自定义语言。我需要创建存根库(如 Python 或 Lua 需要),还是 LLVM 提供某种外部函数接口 (FFI)?
【问题讨论】:
在一些 LLVM 教程中,我看到将 C 函数绑定到基于 LLVM 的自定义语言相当容易。 LLVM 给程序员一个指向函数的指针,然后可以将其与 LLVM 生成的代码混合在一起。
使用 C++ 库执行此操作的最佳方法是什么。假设我有一个相当复杂的库,如 Qt 或 Boost,我想绑定到我的自定义语言。我需要创建存根库(如 Python 或 Lua 需要),还是 LLVM 提供某种外部函数接口 (FFI)?
【问题讨论】:
在我的 LLVM 代码中,我为此创建了 extern "C" 包装函数,并将 LLVM 函数声明插入到模块中以便调用它们。然后,让 LLVM 了解函数的一个好方法是不要让它使用 dlopen 并在执行的二进制文件中搜索函数名(这很麻烦,因为函数名需要在 @ 987654324@ 部分,也很慢),但要手动进行映射,使用ExecutionEngine::addGlobalMapping。
只需获取该声明的llvm::Function* 和&functionname 在C++ 中给出的函数地址,转换为void* 并将这两件事传递给LLVM。然后,执行您的东西的 JIT 将知道在哪里可以找到该函数。
例如,如果您想包装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 字符串等效的字符串,因此您需要使用 str 调用 void reverse(std::string s)转换 - 更糟糕的是,整个对象模型非常不同)。 Qt 特别有很多魔力,在编译后可能需要更多的努力才能暴露出来。此外,可能还有其他我不知道的潜在问题。
即使这样可行,使用起来也可能非常难看。尽管 Python 有非常方便的描述符,但 PyQt 中仍然有 get* 和 set* 函数 - PyQt 付出了很多努力,它们不只是创建一些存根。
【讨论】: