2017-03-15 7 views
2

コンパイラの実装に関するLLVMチュートリアルに従っていますが、オブジェクトコードを発行しようとすると、私のコードはsegfaultsになります。オブジェクトコードを発行しようとすると、なぜLLVMがsegfaultするのですか?

ここに、関数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 

残念ながら、(アーチLinux上pacmanを使用してインストール)私のLLVMのインストールは行番号デバッグ情報を持っていないようですので、私はllvm::FPPassManager::runOnFunctionの実行中に問題が発生している場所を正確に伝えることができません。

私は何をしようとしているのか、コンセプトかインプリメンテーションのどちらかに間違いがありますか?

答えて

2

すべてのLLVM基本ブロックは終了する必要があります(例:http://llvm.org/docs/doxygen/html/classllvm_1_1BasicBlock.html#detailsを参照)。あなたのケースでは、生成されたIRは、次のようになります。あなたのC++コードで

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

を、あなたはllvm::verifyFunctionを呼び出す前にbuilder.CreateRetVoid()を追加する必要があります。

また、LLVMがエラーを出力するストリームを示す2番目のパラメータを渡していないため、llvm::verifyFunctionは目に見えないエラーを出力しません。 stderrに出力する代わりにこれを試してみてください:

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を参照してください)。

最後に、正しく生成されたIRの外観を調べるためにCコードをコンパイルするときにClangによって生成されたIRを検査することをお勧めします。

// test.c 
void func(void) {} 

をそして次のようにコンパイルして、ビュー:たとえば、次のように簡単なCファイルを作成することができます

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

はそれを解決しました。ありがとう!私は関数が 'void'だったからといって' return'ステートメントが必要ではないと仮定してはなりませんでした。うわー。他のヒントも参考になります。 –

関連する問題