GEP分段错误LLVM C ++ API

时间:2018-06-14 19:43:08

标签: llvm llvm-ir llvm-c++-api

我确信这很简单但是,我一直试图弄清楚它超过一个小时,我无法弄明白。

以下代码给出了分段错误:

Value *newArray = mBuilder.CreateGEP(alloca, value); // alloca is a `StructType`

但这不是

Value *newArray = mBuilder.CreateGEP(alloca, ConstantInt::get(mContext, APInt(32, 0)));

value

的值
%bar1 = load double, double* %bar
%3 = fptoui double %bar1 to i32

调试

当我使用lldb调试它时,我得到:

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
    frame #0: 0x00000001000b9e6e a.out`llvm::PointerType::get(llvm::Type*, unsigned int) + 20
a.out`llvm::PointerType::get:
->  0x1000b9e6e <+20>: movq   (%rdi), %rax

问题

为什么我会出现分段错误,如何解决?

如何重现问题?

以下代码重现了该问题:

#include <vector>

#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/IR/Value.h"
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/APInt.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"

using namespace llvm;

static LLVMContext mContext;
static IRBuilder<> mBuilder(mContext);
static std::unique_ptr<Module> mModule = make_unique<Module>("example", mContext);
static Module *M = mModule.get();

static Type *dType = Type::getDoubleTy(mContext);
static Type *i32 = IntegerType::get(mContext, 32);

// helper functions
static AllocaInst *entryCreateBlockAllocaType(Function *func, std::string name, Type* type) {
  IRBuilder<> tmpBuilder(&func->getEntryBlock(), func->getEntryBlock().begin());
  return tmpBuilder.CreateAlloca(type, nullptr, name);
}

static ArrayRef<Value *> PrefixZero (Value *index) {
  std::vector<Value *> out;
  out.push_back(ConstantInt::get(mContext, APInt(32, 0)));
  out.push_back(index);
  return ArrayRef<Value *>(out);
}

static AllocaInst *createVariable () {
  auto *func = mBuilder.GetInsertBlock()->getParent();
  auto *initValue = ConstantInt::get(mContext, APInt(32, 0));

  auto *alloca = entryCreateBlockAllocaType(func, "var", initValue->getType());
  mBuilder.CreateStore(initValue, alloca);
  return alloca; 
}

static std::vector<Type *> elementTypes (3, dType);
static AllocaInst *createStruct () {
  auto *func = mBuilder.GetInsertBlock()->getParent();

  auto *mStructType = StructType::get(mContext, elementTypes);
  return entryCreateBlockAllocaType(func, "str", mStructType);
}

int main () {
  // create a main function
  auto *FT = FunctionType::get(i32, std::vector<Type *>(), false);
  auto *f = Function::Create(FT, Function::ExternalLinkage, "main", M);

  // set insert point for out below code
  auto *bb = BasicBlock::Create(mContext, "entry", f);
  mBuilder.SetInsertPoint(bb);

  // Create a variable
  auto *variable = createVariable();
  // create a struct
  auto *mStruct = createStruct();

  // Create a GEP with the loaded index
  auto *loadedVar = mBuilder.CreateLoad(variable, "loaded_index");

  // This is where the problem is.
  // If `PrefixZero` is changed to `ConstantInt::get(mContext, APInt(32, 0))` this works
  auto *elementPtr = mBuilder.CreateGEP(mStruct, PrefixZero(loadedVar)); 

  mBuilder.CreateRet(ConstantInt::get(mContext, APInt(32, 0))); 
  f->print(errs()); // print out the function

  return 1;
}

The code can also be checked out here.

1 个答案:

答案 0 :(得分:1)

您的代码存在两个问题:

  1. static ArrayRef<Value *> PrefixZero (Value *index) {
      std::vector<Value *> out;
      out.push_back(ConstantInt::get(mContext, APInt(32, 0)));
      out.push_back(index);
      return ArrayRef<Value *>(out);
    }
    

    来自documentation of ArrayRef

      

    此类不拥有底层数据,预计会在数据驻留在某些其他缓冲区的情况下使用,其缓冲区的生命周期超出ArrayRef的范围。

    换句话说,将ArrayRef返回到局部变量是非法的,就像返回指向局部变量的指针一样。在内部,ArrayRef仅存储out data指针,并且只要out超出范围(即PrefixZero的末尾) ,data被释放,ArrayRef现在包含一个指向释放内存的指针。

  2. 在结构上使用getelementptr时,表示成员访问权的索引(即您的第二个索引)必须是常量。如果你考虑一下,就不可能对指令进行类比检查(请记住,通常结构的成员都不具有相同的类型)。另外,计算给定非常量索引的指针偏移量基本上必须生成整个查找表,并且指针算术指令生成那么多代码将是违反直觉的。您可以将结构上的GEP视为与C中的the_struct.member_name等效,并且您无法用变量替换member_name

  3. 请注意,如果在构建LLVM时启用了断言,则第二个问题应该导致断言失败&#34;类型为!&#34;的GetElementPtrInst索引无效,这虽然不能完全告诉您需要的所有内容知道(比如索引无效的方式),确实指出了正确的方向,而不仅仅是&#34;分段错误&#34;将。因此,如果您没有收到该消息,请确保已启用断言,以便下次遇到问题时可以从断言消息中受益。