Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get the value of a member of a structure in LLVM?

Tags:

c++

llvm

llvm-ir

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

like image 586
caa515 Avatar asked Nov 23 '16 17:11

caa515


1 Answers

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)
like image 74
Sriram Murali Avatar answered Sep 20 '22 05:09

Sriram Murali