首页 > 解决方案 > 使用 llvm-13 构建(OrcV2)Jit 编译器时如何解析驻留在当前会话中的符号?

问题描述

编辑

我基本上是在尝试这样做但是使用 llvm 的orcJit api (llvm-13)


我有一个使用 llvm (13) 的 JIT 代码库。我在该库中有一些函数,我想将它们提供给 JIT,而不用在 LLVM IR 中编写它们。

这是一些代码:


#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 << "\n";

    // 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) << "\n";

到目前为止,一切都很好。我无法解决的是如何以可缓存的方式add2使该功能可用。我已经成功地按照此处的说明在当前会话中启用地址的硬编码,如下所示:

    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");
    }

但是指令明确建议不要使用这种策略,因为以这种方式解析的符号是不可缓存的。但是,使用说明建议的方式解析符号:

JD.addGenerator(DynamicLibrarySearchGenerator::Load("/path/to/lib"
                                                    DL.getGlobalPrefix()));

不可能,因为没有/path/to/lib. 处理这种情况的正常方法是什么?

标签: c++llvmsymbolsjitllvm-orc

解决方案


您需要向链接器添加-rdynamic-Wl, -export-dynamic标记。

-E --export-动态

创建动态链接的可执行文件时,将所有符号添加到动态符号表中。动态符号表是在运行时从动态对象可见的符号集。如果您不使用此选项,动态符号表通常将仅包含链接中提到的某个动态对象引用的那些符号。如果您使用 dlopen 加载需要引用程序定义的符号的动态对象,而不是其他一些动态对象,那么在链接程序本身时可能需要使用此选项。


推荐阅读