我正在尝试编写一个程序来 JIT 一些代码。 JITTed 代码需要回调正在运行的应用程序以获得运行时支持,并且在实现函数时找不到运行时支持符号。
我尝试遵循万花筒教程。我需要在运行时从一些 IR 生成的代码中调用函数。例如,我想从一些 llvm IR 调用这个函数。
extern "C" void* llvmNewVector() {
return new vector<int>();
}
根据万花筒教程,它应该在应用程序的运行时声明为 extern“C”。在 LLVM IR 中,我创建了一个函数原型,并且正确生成了 IR(检查我正在执行的函数后没有错误)。
在我看来,需要做更多的事情才能将此函数链接到 jitted 代码,但 Kaleidscope 教程似乎没有这样做。
我的问题是,由于外部符号未解析,因此即时代码无法实现。
以下代码打印“made it here”,但没有进一步说明。
cerr << "made it here." << endl;
auto Sym = ExitOnErr(TheJIT->lookup(name));
NativeCodePtr FP = (NativeCodePtr)Sym.getAddress();
assert(FP && "Failed to find function ");
cerr << "returning jitted function " << name << endl;
return FP;
我确信我做错了什么或遗漏了某些步骤,但我一直无法找到它。
我得到的输出是:
made it here.
JIT session error: Symbols not found: { llvmNewVector }
Failed to materialize symbols: { my_test }
代码是使用 LLVM-9 编译的,带有以下标志:
clang++ -I. -g -I../include/ -std=c++11 -fexceptions -fvisibility=hidden -fno-rtti -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -MT main.o -MD -MP -MF .deps/main.Tpo -c -o main.o main.cpp
使用以下链接进行链接:
llvm-config --libs
我遇到了同样的问题,可以通过以下方式解决:
教程中的以下代码行的目标是解析主机进程中的符号,但似乎不起作用。
ES.getMainJITDylib().setGenerator(
cantFail(DynamicLibrarySearchGenerator::GetForCurrentProcess(DL)));
因此,我手动注册了我想要链接的符号,如下所示:
SymbolMap M;
// Register every symbol that can be accessed from the JIT'ed code.
M[Mangle("llvmNewVector")] = JITEvaluatedSymbol(
pointerToJITTargetAddress(&llvmNewVector), JITSymbolFlags());
}
cantFail(ES.getMainJITDylib().define(absoluteSymbols(M)));
我在上面提到的教程中的两行代码之后添加了此代码。
为 clang 添加
-Xlinker --export-dynamic
选项怎么样?
我在教程中遇到了类似的问题。 在我的环境(Ubuntu 20.04)中,
sin
和cos
可以解析,但printd
或putchard
(Kaleidscope处理器源代码中定义的函数)不能解析。
编译后,可以在程序的dynamic符号表中看到函数名吗?
objdump -T program | grep llvmNewVector
如果 objdump 中没有
-T
选项(例如 Mac),则情况可能并非如此。
就我而言, printd
和 putchard
都不会出现在动态符号表中(但出现在符号表中)。
要将这些函数名称添加到动态符号表中,需要为 clang 传递
-Xlinker --export-dynamic
选项(实际上,该选项传递给 ld),例如(这是教程中的一个),
clang++ -Xlinker --export-dynamic -g toy.cpp `llvm-config --ldflags --system-libs --libs all` -O3 -o toy
编译后,函数名称出现在动态符号表中,并且教程的示例运行良好。
这取决于您使用的 llvm 版本。 LLVM 10 有 LLJIT 类,它通过以下方式为我工作
auto J = ExitOnErr(LLJITBuilder().create());
auto M = createDemoModule();
auto &dl = J->getDataLayout();
MangleAndInterner Mangle(J->getExecutionSession(), dl);
auto &jd = J->getMainJITDylib();
auto s = absoluteSymbols({{ Mangle("printd"), JITEvaluatedSymbol(pointerToJITTargetAddress(&printd), JITSymbolFlags::Exported)}});
jd.define(s);
printd 函数定义在同一个文件中
extern "C" int32_t printd() {
std::cout << "calling " << __FUNCTION__ << "...\n";
return 11;
}
对于使用 LLVM-16 的任何人,这里有一个解决方案:
int main() {
...
TheJIT = ExitOnError(llvm::orc::KaleidoscopeJIT::Create());
auto &jd = TheJIT->getMainJITDylib();
auto mangle = llvm::orc::MangleAndInterner(jd.getExecutionSession(), TheJIT->getDataLayout());
auto s = [](llvm::orc::MangleAndInterner interner) {
llvm::orc::SymbolMap symbolMap;
symbolMap[interner("putchard")] = {
llvm::pointerToJITTargetAddress(&putchard),
llvm::JITSymbolFlags(),
};
symbolMap[interner("printd")] = {
llvm::pointerToJITTargetAddress(&printd),
llvm::JITSymbolFlags(),
};
return llvm::orc::absoluteSymbols(symbolMap);
}(mangle);
ExitOnError(jd.define(s));
...
MainLoop();
}