So I've created a structure type with this:
llvm::StructType* llvm_struct = llvm::StructType::create(llvm_context, struct_name);
std::vector<llvm::Type*> members;
for(size_t j = 0; j != struct_data.members.size(); j++){
llvm::Type* member_type = /*get member type*/;
members.push_back(member_type);
}
llvm_struct->setBody(members)
and I'm wondering how to access the members within the struct.
I've tried using getelementptr with no luck so far:
llvm::Value* member_index = llvm::ConstantInt::get(llvm_context, llvm::APInt(32, /*structure member index*/, true));
llvm::Value* indices[2] = {llvm::ConstantInt::get(member_index->getType(), 0), member_index};
llvm::Value* data = /*expression value*/;
return irbuilder.CreateInBoundsGEP(data, llvm::ArrayRef<llvm::Value*>(indices, 2), "membtmp");
Thanks for any feedback!
EDIT:
Okay so the type of llvm::Value* data
is a %a_struct
which was loaded from a pointer on the stack. It seems from the documentation that irbuilder.CreateInBoundsGEP(llvm::Value*, llvm::ArrayRef<llvm::Value*>, llvm::Twine)
requires the first argument to be a pointer to the structure and not the value of the structure itself.
When copying the value of the structure into a variable on the stack, this error is thrown: Expression: getOperand(0)->getType() == cast<PointerType>(getOperand(1)->getType())->getElementType() && "Ptr must be a pointer to Val type!"
. The pointer pasted to irbuidler.CreateInBoundsGEP(...)
when this error was thrown was an llvm::AllocaInst*
which was newly allocated on the stack and contained the value of llvm::Value* data
(type of %a_struct
) copied into it.
The IR generated right before the call to irbuilder.CreateInBoundsGEP(...)
with the value copied to a variable on the stack:
define i32 @main() {
entry:
%calltmp = call %a_struct @new_a_struct()
%a_var = alloca %a_struct
store %a_struct %calltmp, %a_struct* %a_var
%a_var1 = load %a_struct, %a_struct* %a_var
%memballoctmp = alloca %a_struct
store %a_struct %a_var1, %a_struct* %memballoctmp
}
Besides there should be a better way to access the members of %a_var
without duplicating it (while still supporting expressions like a_struct_var1.member + a_struct_var2.member
in the language).
It takes some time to grasp the concept, but it is well worth investing time on it. Look at the documentation for getelementpointer from llvm language reference. It explains how the member access works.
struct RT {
char A;
int B[10][20];
char C;
};
struct ST {
int X;
double Y;
struct RT Z;
};
int *foo(struct ST *s) {
return &s[1].Z.B[5][13];
}
Reading the member B[5][13] of the struct can be done either directly:
%arrayidx = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 1, i32 2, i32 1, i64 5, i64 13
or indirectly:
%t1 = getelementptr %struct.ST, %struct.ST* %s, i32 1
%t2 = getelementptr %struct.ST, %struct.ST* %t1, i32 0, i32 2
%t3 = getelementptr %struct.RT, %struct.RT* %t2, i32 0, i32 1
%t4 = getelementptr [10 x [20 x i32]], [10 x [20 x i32]]* %t3, i32 0, i32 5
%t5 = getelementptr [20 x i32], [20 x i32]* %t4, i32 0, i32 13
I will show the direct way. Let us first create the struct
StructType* createStruct(Module &M)
{
Type* intTy = Type::getInt32Ty(M.getContext());
Type* charTy = Type::getInt8Ty(M.getContext());
Type* doubleTy = Type::getDoubleTy(M.getContext());
auto* _B = ArrayType::get(intTy, 20);
auto* B = ArrayType::get(_B, 10);
auto* RT = StructType::create("struct.RT", charTy, B, charTy);
auto* ST = StructType::create("struct.ST", intTy, doubleTy, RT);
RT->dump();
ST->dump();
return ST;
}
Now we can use gep to access the struct, but first we need a vector of Values* to store the indices to access to a particular gep address
template <size_t N>
std::vector<Value*> getIndex(Module &M, int (&dims)[N])
{
std::vector<Value*> idx;
for (auto i : dims)
{
idx.push_back(ConstantInt::get(M.getContext(), APInt(32, i, true)));
}
for (auto i : idx)
{
i->dump();
}
return idx;
}
void doGEP(Module &M)
{
auto* structInst = createStruct(M);
auto* structGlobVar = new GlobalVariable(M, structInst, true, GlobalVariable::ExternalLinkage, UndefValue::get(structInst), "_structGV", nullptr, GlobalVariable::ThreadLocalMode::NotThreadLocal, 0, true);
structGlobVar->dump();
int dims[] = {1, 2, 1, 5, 13};
std::vector<Value*> indx = getIndex(M, dims);
auto* gepInst = builder.CreateGEP(structGlobVar, indx);
gepInst->dump();
}
will generate output:
%struct.RT = type { i8, [10 x [20 x i32]], i8 }
%struct.ST = type { i32, double, %struct.RT }
i32 1
i32 2
i32 1
i32 5
i32 13
@_structGV = externally_initialized constant %struct.ST undef
i32* getelementptr (%struct.ST, %struct.ST* @_structGV, i32 1, i32 2, i32 1, i32 5, i32 13)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With