如何从ifstream中加载LLVM位码文件?

5

我想在运行时加载一个LLVM模块,该模块定义在一个名为.bc的文件中,但我遇到了一些问题。

感兴趣的位码是从hello.cpp生成的:

// hello.cpp
// build with:
// clang-3.4 -c -emit-llvm hello.cpp -o hello.bc
#include <iostream>

void hello()
{
  std::cout << "Hello, world!" << std::endl;
}

当下面的程序尝试在运行时加载它时,在llvm::BitstreamCursor::Read()内部崩溃:
// main.cpp
// build with:
// g++ main.cpp `llvm-config-3.4 --cppflags --ldflags --libs` -ldl -lpthread -lcurses
#include <llvm/IR/Module.h>
#include <llvm/IRReader/IRReader.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/Support/SourceMgr.h>
#include <llvm/Support/MemoryBuffer.h>
#include <llvm/Support/raw_ostream.h>
#include <fstream>
#include <iostream>

llvm::Module *load_module(std::ifstream &stream)
{
  if(!stream)
  {
    std::cerr << "error after open stream" << std::endl;
    return 0;
  }

  // load bitcode
  std::string ir((std::istreambuf_iterator<char>(stream)), (std::istreambuf_iterator<char>()));

  // parse it
  using namespace llvm;
  LLVMContext context;
  SMDiagnostic error;
  Module *module = ParseIR(MemoryBuffer::getMemBuffer(StringRef(ir.c_str())), error, context);

  if(!module)
  {
    std::string what;
    llvm::raw_string_ostream os(what);
    error.print("error after ParseIR()", os);
    std::cerr << what;
  } // end if

  return module;
}

int main()
{
  std::ifstream stream("hello.bc", std::ios_base::binary);
  llvm::Module *m = load_module(stream);
  if(m)
  {
    m->dump();
  }

  return 0;
}

我正在使用评论中提到的命令行构建LLVM v3.4。

你有什么想法,我做错了什么吗?

2个回答

6
有两个问题:
  1. LLVMContext的生命周期需要超过Module的生命周期。否则,Module将引用一个不存在的LLVMContext
  2. 指向IR的StringRef应该从包含IR的std::string构建,而不是从零终止的字符串构建。否则,ParseIR将无法正确找到IR的结尾。
这是经过修正的load_module版本:
llvm::Module *load_module(std::ifstream &stream, llvm::LLVMContext &context)
{
  if(!stream)
  {
    std::cerr << "error after open stream" << std::endl;
    return 0;
  }

  // load bitcode
  std::string ir((std::istreambuf_iterator<char>(stream)), (std::istreambuf_iterator<char>()));

  // parse it
  using namespace llvm;
  SMDiagnostic error;
  Module *module = ParseIR(MemoryBuffer::getMemBuffer(StringRef(ir)), error, context);

  if(!module)
  {
    std::string what;
    llvm::raw_string_ostream os(what);
    error.print("error after ParseIR()", os);
    std::cerr << what;
  } // end if

  return module;
}

5
一种可能的解决方案是将ParseIR()调用替换为ParseIRFile("hello.bc", error, getGlobalContext())
这样做还可以使代码更简单,因为您可以跳过手动加载位代码的部分。
以下内容足以获取模块的转储(应添加一些错误处理):
#include <llvm/IR/Module.h>
#include <llvm/IRReader/IRReader.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/Support/SourceMgr.h>

using namespace llvm;
int main()
{
  LLVMContext context;
  SMDiagnostic error;
  Module *m = ParseIRFile("hello.bc", error, context);
  if(m)
  {
    m->dump();
  } 

  return 0;
}

谢谢。不幸的是,我的代码结构需要从流中获取数据。 - Jared Hoberock
好的。我刚开始探索LLVM,还不知道该怎么做。 - vPraetor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接