2017-03-15 105 views
2

我想跟着LLVM教程编译器实现,但我的代码段错误,当我尝试发出对象代码。为什么当我尝试发出目标代码时,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(); 

} 

这是我正在编译的命令。我正在使用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` 

这里是输出。

define void @func() { 
entry: 
} 

Running pass manager. 
Segmentation fault (core dumped) 

翻译到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 

不幸的是,我的LLVM安装似乎不具有行号调试信息(使用pacman在Arch Linux的安装),所以我不能告诉正是在llvm::FPPassManager::runOnFunction的执行问题发生。

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

回答

2

所有LLVM基本块必须被终止(例如参见http://llvm.org/docs/doxygen/html/classllvm_1_1BasicBlock.html#details)。在你的情况下,产生的IR应该是这样的:

define void @func() { 
entry: 
    ret void 
} 

在你的C++代码,你需要添加builder.CreateRetVoid()你打电话之前llvm::verifyFunction

另外,llvm::verifyFunction不会显示输出错误,因为您尚未传递第二个参数,指示LLVM应输出错误的流。试试这个,而不是输出到标准错误:

llvm::verifyFunction(*func, &llvm::errs()) 

您还应该检查的llvm::verifyFunction返回值。 A 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) {} 

然后编译并图如下:

clang -S -emit-llvm test.c 
cat test.ll 
+0

解决它。谢谢!我不应该认为仅仅因为函数是'void'就不需要'return'语句。哎呦。其他技巧也有帮助。 –

相关问题