为什么我的LLVM JIT实现是segfaulting?

时间:2017-08-20 06:09:07

标签: c++ segmentation-fault llvm jit llvm-c++-api

我正在尝试使用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老手能找到我的错误。谢谢,伙计们!

1 个答案:

答案 0 :(得分:4)

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拥有的对象(ok),然后从中移动。这会将托管对象的所有权从module转移出去。

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