Reputation: 6403
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
%bar1 = load double, double* %bar
%3 = fptoui double %bar1 to i32
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
Why am I getting a segmentation fault and how do I fix it?
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
Reputation: 370387
There are two problems with your code:
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.
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