GEP 分段错误 LLVM C++ API

GEP segmentation fault 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. 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

    This class does not own the underlying data, it is expected to be used in situations where the data resides in some other buffer, whose lifetime extends past that of the ArrayRef.

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

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

请注意,如果在您的 LLVM 构建中启用了断言,第二个问题应该会导致断言失败 "Invalid GetElementPtrInst indices for type!",虽然没有完全告诉您您需要知道的一切(比如以何种方式索引无效),确实比 "segmentation fault" 更能为您指明正确的方向。因此,如果您没有收到该消息,请确保您已启用断言,以便下次您 运行 遇到问题时可以从断言消息中受益。