zoecarver
zoecarver

Reputation: 6403

GEP segmentation fault LLVM C++ API

I am sure that this is really simple but, I have been trying to figure it out for more than an hour and I cannot figure it out.

The following code gives me a segmentation fault:

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

but this does not

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

Value of value

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

Debugging

When I debug it using lldb I get:

* 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

Question

Why am I getting a segmentation fault and how do I fix it?

How to reproduce the problem?

The following code reproduces the problem:

#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.

Upvotes: 1

Views: 1273

Answers (1)

sepp2k
sepp2k

Reputation: 370387

There are two problems with your code:

  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);
    }
    

    From the 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.

    In other words returning an ArrayRef to a local variable is illegal in the same way that returning a pointer to a local variable would be. Internally the ArrayRef just stores out's data pointer and as soon as out goes out of scope (i.e. at the end of PrefixZero), the data is freed and the ArrayRef now contains a pointer to freed memory.

  2. When using getelementptr on a struct, the index that represent the member access (i.e. the second index in your case) must be a constant. If you think about it, it would be impossible to typecheck the instruction otherwise (keeping in mind that usually the members of a struct don't all have the same type). Plus calculating the pointer offset for a given non-constant index would basically have to generate an entire lookup table and it would be counter-intuitive for a pointer-arithmetic instruction to generate that much code. You can think of GEP on a struct as equivalent to the_struct.member_name in C and you can't replace member_name with a variable there either.

Note that, if assertions are enabled in your build of LLVM, the second issue should cause an assertion failure "Invalid GetElementPtrInst indices for type!", which, while not quite telling you everything you need to know (like in what way the indices are invalid), does point you in the right direction a lot more than just "segmentation fault" would. So if you didn't get that message, make sure you have assertions enabled, so you can benefit from the assertion messages the next time you run into problems.

Upvotes: 2

Related Questions