当我尝试发出目标代码时,为什么LLVM会出现错误?

Sam*_*lli 4 c++ llvm segmentation-fault llvm-ir

我正在尝试跟随关于编译器实现的LLVM教程,但是当我尝试发出目标代码时,我的代码会出现段错误.

这是一个尝试编译函数的最小示例func.为了简单起见,这func是一个什么都不做的功能.

#include <iostream>
#include <llvm/ADT/Optional.h>
#include <llvm/IR/BasicBlock.h>
#include <llvm/IR/DerivedTypes.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Type.h>
#include <llvm/IR/Verifier.h>
#include <llvm/Support/CodeGen.h>
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/Host.h>
#include <llvm/Support/TargetRegistry.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Support/raw_ostream.h>
#include <llvm/Target/TargetMachine.h>
#include <llvm/Target/TargetOptions.h>
#include <stdexcept>
#include <string>
#include <system_error>
#include <vector>

int main() {

    llvm::LLVMContext context;
    llvm::IRBuilder<> builder(context);
    llvm::Module      module("module", context);

    llvm::Function* const func = llvm::Function::Create(
        llvm::FunctionType::get(llvm::Type::getVoidTy(context),
                                std::vector<llvm::Type*>(), false),
        llvm::Function::ExternalLinkage, "func", &module
    );

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

    llvm::verifyFunction(*func);

    func->dump();

    llvm::InitializeAllTargetInfos();
    llvm::InitializeAllTargets();
    llvm::InitializeAllTargetMCs();
    llvm::InitializeAllAsmParsers();
    llvm::InitializeAllAsmPrinters();

    const std::string triple = llvm::sys::getDefaultTargetTriple();

    std::string message;
    const llvm::Target* const target = llvm::TargetRegistry::lookupTarget(
        triple, message
    );
    if (!target) throw std::runtime_error("Couldn't find target.");

    llvm::TargetMachine* const machine = target->createTargetMachine(
        triple, "generic", "", llvm::TargetOptions(),
        llvm::Optional<llvm::Reloc::Model>()
    );

    module.setDataLayout(machine->createDataLayout());
    module.setTargetTriple(triple);

    std::error_code code;
    llvm::raw_fd_ostream obj_file("func.o", code, llvm::sys::fs::F_None);
    if (code) throw std::runtime_error("Couldn't open object file.");

    llvm::legacy::PassManager manager;
    if (
        machine->addPassesToEmitFile(manager, obj_file,
                                     llvm::TargetMachine::CGFT_ObjectFile)
    ) throw std::runtime_error("Adding passes failed.");

    std::cout << "Running pass manager." << std::endl;
    manager.run(module);
    std::cout << "Ran pass manager." << std::endl;

    obj_file.flush();

}
Run Code Online (Sandbox Code Playgroud)

这是我正在编译的命令.我正在使用GCC版本6.3.1和LLVM版本3.9.1.

g++ src/main.cc -o bin/test -std=c++1z -Wall -Wextra             \
    -Wno-unused-function -Wno-unused-value -Wno-unused-parameter \
    -Werror -ggdb -O0 `llvm-config --system-libs --libs core`
Run Code Online (Sandbox Code Playgroud)

这是输出.

define void @func() {
entry:
}

Running pass manager.
Segmentation fault (core dumped)
Run Code Online (Sandbox Code Playgroud)

对IR的转换成功 - 至少对我来说转储看起来是正确的 - 但是在调用时发生了段错误llvm::legacy::PassManager::run.

我尝试使用GDB逐步完成代码.这是段错误时刻的回溯.

#0  0x00007ffff56ce72f in ?? () from /usr/lib/libLLVM-3.9.so
#1  0x00007ffff56477c2 in llvm::FPPassManager::runOnFunction(llvm::Function&) () from /usr/lib/libLLVM-3.9.so
#2  0x00007ffff5647b4b in llvm::FPPassManager::runOnModule(llvm::Module&) () from /usr/lib/libLLVM-3.9.so
#3  0x00007ffff5647e74 in llvm::legacy::PassManagerImpl::run(llvm::Module&) () from /usr/lib/libLLVM-3.9.so
#4  0x0000000000403ab6 in main () at src/main.cc:76
Run Code Online (Sandbox Code Playgroud)

不幸的是,我的LLVM安装(pacman在Arch Linux上安装)似乎没有行号调试信息,所以我无法确切地知道llvm::FPPassManager::runOnFunction问题发生在哪里.

无论是在概念上还是在实施中,有什么明显的错误,我正在尝试做什么?

Ala*_*lan 6

必须终止所有LLVM基本块(参见http://llvm.org/docs/doxygen/html/classllvm_1_1BasicBlock.html#details).在您的情况下,生成的IR应如下所示:

define void @func() {
entry:
    ret void
}
Run Code Online (Sandbox Code Playgroud)

在您的C++代码中,您需要builder.CreateRetVoid()在调用之前添加llvm::verifyFunction.

此外,llvm::verifyFunction由于您尚未传递指示LLVM应输出错误的流的第二个参数,因此不会显着输出错误.尝试这样输出到stderr:

llvm::verifyFunction(*func, &llvm::errs())
Run Code Online (Sandbox Code Playgroud)

你还应该检查的返回值llvm::verifyFunction.一个true返回值指示错误.

请参阅:http://llvm.org/docs/doxygen/html/namespacellvm.html#a26389c546573f058ad8ecbdc5c1933cfhttp://llvm.org/docs/doxygen/html/raw__ostream_8h.html

您还应该考虑在调用生成目标文件之前验证整个模块llvm::verifyModule(theModule, theOsStream)(请参阅http://llvm.org/docs/doxygen/html/Verifier_8h.html).

最后,我建议在编译C代码时检查Clang生成的IR,以便检查正确生成的IR是什么样的.例如,您可以创建一个简单的C文件,如下所示:

// test.c
void func(void) {}
Run Code Online (Sandbox Code Playgroud)

然后编译并查看如下:

clang -S -emit-llvm test.c
cat test.ll
Run Code Online (Sandbox Code Playgroud)