在使用llvm-13构建(OrcV2)Jit编译器时,如何解析当前会话中的符号?
编辑
我基本上是在尝试this,但使用llvm的orc
Jit API(llvm-13)
我有一个库,其中包含一些使用llvm(13)的代码。我想让JIT使用该库中的一些函数,而不必用LLVMIR编写它们。
以下是一些代码:
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/ExecutionEngine/JITSymbol.h"
#include "llvm/ExecutionEngine/Orc/CompileUtils.h"
#include "llvm/ExecutionEngine/Orc/Core.h"
#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
#include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
#include "llvm/ExecutionEngine/Orc/IRTransformLayer.h"
#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"
#include "llvm/ExecutionEngine/Orc/Mangling.h"
#include "llvm/ExecutionEngine/Orc/LLJIT.h"
#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Support/TargetSelect.h"
using namespace llvm;
using namespace llvm::orc;
// this is just a demo module that creates a function that adds 1 to an int
ThreadSafeModule makeSimpleModule() {
auto Context = std::make_unique<LLVMContext>();
auto M = std::make_unique<Module>("test", *Context);
// Create the add1 function entry and insert this entry into module M. The
// function will have a return type of "int" and take an argument of "int".
Function *Add1F =
Function::Create(FunctionType::get(Type::getInt32Ty(*Context),
{Type::getInt32Ty(*Context)}, false),
Function::ExternalLinkage, "add1", M.get());
// Add a basic block to the function. As before, it automatically inserts
// because of the last argument.
BasicBlock *BB = BasicBlock::Create(*Context, "EntryBlock", Add1F);
// Create a basic block builder with default parameters. The builder will
// automatically append instructions to the basic block `BB'.
IRBuilder<> builder(BB);
// Get pointers to the constant `1'.
Value *One = builder.getInt32(1);
// Get pointers to the integer argument of the add1 function...
assert(Add1F->arg_begin() != Add1F->arg_end()); // Make sure there's an arg
Argument *ArgX = &*Add1F->arg_begin(); // Get the arg
ArgX->setName("AnArg"); // Give it a nice symbolic name for fun.
// Create the add instruction, inserting it into the end of BB.
Value *Add = builder.CreateAdd(One, ArgX);
// Create the return instruction and add it to the basic block
builder.CreateRet(Add);
return {std::move(M), std::move(Context)};
}
// this represents a function in my library that I want to make available to the JIT.
namespace mylibsubnamespace {
extern "C" {
int add2(int a) {
return a + 2;
}
}
}
int main(int argc, const char *argv[]) {
// do some JIT initialization
llvm::InitLLVM X(argc, argv);
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
llvm::InitializeNativeTargetAsmParser();
// Create an LLJIT instance.
auto J = LLJITBuilder().create();
// this code seems to enable symbol resolution for when the missing symbol is
// in the standard C library (and presumably included).
// This is what allows the "cos" function below to work (comment it out and we get a seg fault)
auto DLSG = llvm::orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(
(*J)->getDataLayout().getGlobalPrefix());
if (!DLSG) {
llvm::logAllUnhandledErrors(
std::move(DLSG.takeError()),
llvm::errs(),
"DynamicLibrarySearchGenerator not built successfully"
);
}
(*J)->getMainJITDylib().addGenerator(std::move(*DLSG));
auto M = makeSimpleModule();
(*J)->addIRModule(std::move(M));
// Look up the JIT'd function, cast it to a function pointer, then call it.
// This function is written in LLVM IR directly.
auto Add1Sym = (*J)->lookup("add1");
int (*Add1)(int) = (int (*)(int)) Add1Sym->getAddress();
int Result = Add1(42);
outs() << "add1(42) = " << Result << "
";
// Look up the JIT'd function, cast it to a function pointer, then call it.
// This function is defined in the standard C library. Its symbol is resolved
// by DynamicLibrarySearchGenerator above
auto CosSym = (*J)->lookup("cos");
double (*Cos)(double) = (double (*)(double)) CosSym->getAddress();
outs() << "Cos(50) = " << Cos(50) << "
";
到目前为止一切顺利。我还没有弄清楚的是如何以可缓存的方式使add2
函数可用。我已经成功地按照here的说明在当前会话中启用了地址的硬编码,如下所示:
auto symbolStringPool = (*J)->getExecutionSession().getExecutorProcessControl().getSymbolStringPool();
orc::SymbolStringPtr symbPtr = symbolStringPool->intern("add2");
// JITTargetAddress is uint64 typedefd
llvm::JITSymbolFlags flg;
llvm::JITEvaluatedSymbol symb((std::int64_t) &mylibsubnamespace::add2, flg);
if (llvm::Error err = (*J)->getMainJITDylib().define(
llvm::orc::absoluteSymbols({{symbPtr, symb}}))) {
llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "Could not add symbol add2");
}
但instructions明确建议不要使用此策略,因为以这种方式解析的符号不可缓存。但是,使用类似说明建议的方式来解析符号:
JD.addGenerator(DynamicLibrarySearchGenerator::Load("/path/to/lib"
DL.getGlobalPrefix()));
是不可能的,因为没有/path/to/lib
。处理此类情况的正常方法是什么?
解决方案
您需要的是向链接器添加-rdynamic
或-Wl, -export-dynamic
标志。
-E--导出-动态
在创建动态链接的可执行文件时,将所有符号添加到动态符号表。动态符号表是在运行时从动态对象可见的符号集。如果不使用此选项,动态符号表通常只包含链接中提到的某些动态对象引用的符号。如果使用dlopen加载需要引用程序定义的符号的动态对象,而不是某个其他动态对象,则在链接程序本身时可能需要使用此选项。
相关文章