为什么我的 LLVM JIT 实现会出现段错误?

Why is my LLVM JIT implementation segfaulting?

我正在尝试使用 LLVM 实现一个简单的 JIT 编译器,遵循教程 (http://releases.llvm.org/4.0.1/docs/tutorial/BuildingAJIT1.html),但我 运行 遇到了段错误。我以最小(尽管仍然有点长)示例的形式重写了我的代码。该示例循环遍历从 0 到 9 的整数,并尝试为每个整数编译一个打印该整数的函数,将其添加到一个模块,执行该函数,然后从 JIT 中删除该模块。这是为了模拟一个交互式会话,用户在其中输入 print 0print 1 等命令

#include <array>
#include <cstdint>
#include <iostream>
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/ExecutionEngine/JITSymbol.h>
#include <llvm/ExecutionEngine/Orc/CompileUtils.h>
#include <llvm/ExecutionEngine/Orc/IRCompileLayer.h>
#include <llvm/ExecutionEngine/Orc/LambdaResolver.h>
#include <llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h>
#include <llvm/ExecutionEngine/SectionMemoryManager.h>
#include <llvm/ExecutionEngine/RuntimeDyld.h>
#include <llvm/IR/BasicBlock.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/DerivedTypes.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/GlobalValue.h>
#include <llvm/IR/GlobalVariable.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/IR/Mangler.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Type.h>
#include <llvm/IR/Value.h>
#include <llvm/IR/Verifier.h>
#include <llvm/Support/DynamicLibrary.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Support/raw_ostream.h>
#include <llvm/Target/TargetMachine.h>
#include <llvm/Transforms/Scalar.h>
#include <llvm/Transforms/Scalar/GVN.h>
#include <memory>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>

int main() {

    llvm::InitializeNativeTarget();
    llvm::InitializeNativeTargetAsmPrinter();
    llvm::InitializeNativeTargetAsmParser();

    auto machine = llvm::EngineBuilder().selectTarget();

    llvm::orc::ObjectLinkingLayer<> linking_layer;
    llvm::orc::IRCompileLayer<llvm::orc::ObjectLinkingLayer<>> compile_layer(
        linking_layer, llvm::orc::SimpleCompiler(*machine)
    );

    llvm::LLVMContext context;
    llvm::IRBuilder<> builder(context);
    auto layout = machine->createDataLayout();

    auto module  = std::make_unique<llvm::Module>("module", context);

    auto manager = std::make_unique<llvm::legacy::FunctionPassManager>(
        module.get()
    );
    for (
        auto p : {
            llvm::createInstructionCombiningPass(),
            llvm::createReassociatePass(), llvm::createGVNPass(),
            llvm::createCFGSimplificationPass()
        }
    ) manager->add(p);

    module->setDataLayout(layout);

    llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr);

    auto index = llvm::ConstantInt::get(context, llvm::APInt(8, 0));
    std::vector<llvm::Constant*> indices = {index, index};

    std::string func_name = "func";

    for (auto i = 0; i < 10; ++i) {

        auto format_str = new llvm::GlobalVariable(
            *module, llvm::ArrayType::get(llvm::Type::getInt8Ty(context), 4),
            true, llvm::GlobalValue::PrivateLinkage,
            llvm::ConstantDataArray::getString(context, "%i\n"), "format_str"
        );
        format_str->setAlignment(1);

        auto function = llvm::Function::Create(
            llvm::FunctionType::get(
                llvm::Type::getVoidTy(context), std::vector<llvm::Type*>{},
                false
            ), llvm::Function::ExternalLinkage, func_name, module.get()
        );

        builder.SetInsertPoint(
            llvm::BasicBlock::Create(context, "entry", function)
        );

        builder.CreateCall(
            module->getOrInsertFunction(
                "printf", llvm::FunctionType::get(
                    llvm::IntegerType::getInt32Ty(context),
                    llvm::PointerType::get(llvm::Type::getInt8Ty(context), 0),
                    true
                )
            ), std::vector<llvm::Value*>{
                llvm::ConstantExpr::getGetElementPtr(
                    nullptr, format_str, indices
                ), llvm::ConstantInt::get(context, llvm::APInt(32, i))
            }, "call"
        );

        builder.CreateRetVoid();

        std::string message;
        llvm::raw_string_ostream message_stream(message);
        if (llvm::verifyFunction(*function, &message_stream))
            throw std::runtime_error(message_stream.str());

        auto handle = compile_layer.addModuleSet(
            std::array<std::unique_ptr<llvm::Module>, 1>{std::move(module)},
            std::make_unique<llvm::SectionMemoryManager>(),
            llvm::orc::createLambdaResolver(
                [&](const std::string& name) {
                    auto symbol = compile_layer.findSymbol(name, false);
                    return symbol ? symbol : llvm::JITSymbol(nullptr);
                }, [](const std::string& name) {
                    auto address = llvm::RTDyldMemoryManager::
                        getSymbolAddressInProcess(name);
                    return address ? llvm::JITSymbol(
                        address, llvm::JITSymbolFlags::Exported
                    ) : llvm::JITSymbol(nullptr);
                }
            )
        );

        std::string mangled_name;
        llvm::raw_string_ostream mangled_name_stream(mangled_name);
        llvm::Mangler::getNameWithPrefix(
            mangled_name_stream, func_name, layout
        );

        (
            reinterpret_cast <void(*)()> (
                static_cast <intptr_t> (
                    compile_layer.findSymbol(
                        mangled_name_stream.str(), true
                    ).getAddress()
                )
            )
        )();

        compile_layer.removeModuleSet(handle);

    }

}

预期输出如下。

0
1
2
3
4
5
6
7
8
9

相反,我明白了。

0
Segmentation fault (core dumped)

根据 GDB,段错误发生在调用 llvm::GlobalVariable::GlobalVariable 期间。这是回溯。

#0  0x00007ffcdb8b6541 in llvm::GlobalVariable::GlobalVariable(llvm::Module&, llvm::Type*, bool, llvm::GlobalValue::LinkageTypes, llvm::Constant*, llvm::Twine const&, llvm::GlobalVariable*, llvm::GlobalValue::ThreadLocalMode, unsigned int, bool) () from /usr/lib/libLLVM-4.0.so
#1  0x000000010000698a in main () at main.cc:83

我正在使用 LLVM 4.0.1 版和 GCC 7.1.1 版并使用以下命令进行编译。

g++ -std=c++17 main.cc -o main -O0 -Wall -Wextra -Wno-unused-function \
    -Wno-unused-value -Wno-unused-parameter -Werror -ggdb             \
    `llvm-config --system-libs --libs core`

我希望一些 LLVM 老手能发现我的错误。谢谢,伙计们!

modulefor 循环之前初始化:

 auto module  = std::make_unique<llvm::Module>("module", context);

然后在 for 循环中:

for(...)
{
    auto format_str = new llvm::GlobalVariable(*module, ...);
                                               ^~~~~~~
    ...
    std::array<std::unique_ptr<llvm::Module>, 1>{std::move(module)},
                                                 ^~~~~~~~~~~~~~~~~
}

在第一次迭代中,您访问 module 拥有的对象(确定),然后从中移动。这会将托管对象的所有权从 module.

转移出去

在第二次迭代中,您访问了由 module 管理的对象 -> 崩溃(因为它不再有管理对象)