Creating a global variable for a struct array

Hello,

I would like to create a global variable for the following struct array, h1

dhash* h1 = new dhash[10];

typedef struct dhash{
char* filenm;
dlist* llist;
}dhash;

typedef struct dlist{
int soffst;
int eoffst;
uint8_t* dptr;
}dlist;

I also need to allocate space for:

  1. the field llist in struct dhash which is a pointer to another struct dlist and
  2. the field dptr in struct dlist

Is there an example that I can refer to for doing this ?

I tried to create a GlobalVariable using ConstantStruct::get(StructType*, ArrayRef<Constant *>). I’m not sure how to get the second argument of type ArrayRef<Constant *> from the above variable h1.

Thanks,
Chaitra

dhash* h1 = new dhash[10];

Note that this "new" expression needs a runtime call to actually
allocate the memory on the heap. In LLVM terms this is A
GlobalVariable with type PointerType::get(StructTy, ...), initialized
to null, together with a global init function (runs when the program
starts) that makes the call to new and stores it to that global. My
guess is that's not what you intended

I also need to allocate space for:
1) the field llist in struct dhash which is a pointer to another struct dlist and
2) the field dptr in struct dlist

Is there an example that I can refer to for doing this ?

I think you should start by writing exactly what you want in C++, and
then (when you're happy with the assembly output) using Clang's "-S
-emit-llvm" options to see what LLVM IR that produces.

That will clear up the question of how things should be allocated,
which isn't quite obvious from the questions you're asking, and make
translating the IR into LLVM API calls more straightforward.

I tried to create a GlobalVariable using ConstantStruct::get(StructType*, ArrayRef<Constant *>). I'm not sure how to get the second argument of type ArrayRef<Constant *> from the above variable h1.

Hopefully it'll be clearer when you have the real IR you want (in
textual form) in front of you.

Cheers.

Tim.

Let me clarify my question.

I have a struct array h1 as follows:
dhash h1[10];

I want to get a Constant* to variable h1. It looks like I can use ConstantStruct::get(StructType*, ArrayRef<Constant *>) to do this.

My question is how to get the second argument of type ArrayRef<Constant *> from the above variable h1.

Thanks,
Chaitra

Let me clarify my question.

Some C or C++ code showing what you want to do in LLVM would really be
most helpful there.

I have a struct array h1 as follows:
dhash h1[10];

I want to get a Constant* to variable h1.

Do you mean you want to create a GlobalVariable "h1" with a Constant*
initializer? If so, we need to know what you want to initialize it to.
Writing IR for

    dhash h1[10] = {0};

would be very different from

    dlist static_lst = {1, 5, NULL};
    dhash h1[10] = {{"myfile.txt", &static_list}, 0 };

It looks like I can use ConstantStruct::get(StructType*, ArrayRef<Constant *>) to do this.

It would be involved in the second example above, but because h1 is an
array you'd need a whole ContantArray of them.

My question is how to get the second argument of type ArrayRef<Constant *> from the above variable h1.

In the second example I gave above you'd write something like

    // First create a global variable to hold the filename string.
    Constant *FileNameInit = ConstantDataArray::getString("myfile.txt");
    Constant *FileName = new GlobalVariable(Module,
FileNameInit->getType(), true, GlobalValue::PrivateLinkage,
FileNameInit, ".str");
    FileName = ConstantExpr::getBitCast(FileName, Int8PtrTy);

    // Look up the previously created static_list variable (code to
produce it omitted for brevity).
    GlobalVariable *StaticList = Module->getNamedValue("static_list");

    // Create the ConstantStruct that will initialize the first
element of the array.
    Constant *FirstInitArr[] = { FileName, StaticList };
    ConstantStruct *FirstInit = ConstantStruct::get(DHashTy, FirstInitArr);

    // Create an all-zero struct for the rest of the array.
    Constant *OtherInits = ConstantAggregateZero::get(DHashTy);

    // Create the global variable.
    Type *H1Ty = ArrayType::get(DHashTy, 10);
    Constant *H1InitArr[] = {FirstInit, OtherInits, OtherInits,
OtherInits, OtherInits, OtherInits, OtherInits, OtherInits,
OtherInits, OtherInits};
    Constant *H1Init = ConstantArray::get(H1Ty, H1InitArr);
    GlobalVariable *H1 = new GlobalVariable(Module, H1Ty, false,
GlobalVariable::ExternalLinkage, H1Init, "h1");

which would produce LLVM IR looking like this:

     @.str = private unnamed_addr constant [11 x i8] c"myfile.txt\00"
     @h1 = global [10 x %dhash] [%dhash { i8* bitcast([11 x i8]* @.str
to i8*), %dlist* @static_list}, %dhash zeroinitializer, %dhash
zeroinitializer, ...}

I think the critical point that might be missing is that if you have a
pointer you want to initialize to something other than NULL (in this
case the filename and the dlist pointer), you're going to need a
separate global variable that provides real storage for it. You can't
create a ConstantStruct and use that directly because a ConstantStruct
has %struct type, but your field is %struct*.

Or I might have gone off at a wild tangent that has nothing to do with
what you're asking.

Cheers.

Tim.

Thank you very much. The code to initialize h1 to non-zero values was what I was looking for.

It’s almost working except for a type mismatch wrt dlist* llist field of h1.
dlist static_lst[10] = { {1, 5, NULL}, … };

dhash h1[10] = {{“myfile.txt”, static_lst}, … };

Along the lines of the code you had sent, I created a GlobalVariable* llist of type [10 x %struct.dlist]* for the field dlist* llist in h1. But this results in an LLVM ERROR: Type mismatch in constant table!.

How do I fix this error ?

The type you pass to GlobalVariable's constructor for that variable
should be "[10 x %struct.dlist]" because that's what you want storage
for. Then the GlobalVariable itself will be a Constant of type "[10 x
%struct.dlist]*".

Since that error is coming from the bitcode reader, I suspect you
might have built a version of LLVM without assertions enabled (fixed
with -DLLVM_ENABLE_ASSERTIONS=ON in CMake, or a debug build) so the
crash you'd normally get when actually constructing the constants is
deferred. Hopefully if you turn assertions on it'll point to the exact
line the error is happening on and be much easier to fix.

Cheers.

Tim.

The type you pass to GlobalVariable’s constructor for that variable
should be “[10 x %struct.dlist]” because that’s what you want storage
for. Then the GlobalVariable itself will be a Constant of type “[10 x
%struct.dlist]*”.

Yes, I verified that this is the case.

I enabled assertions and the error seems to occur while creating GlobalVariable for both struct dhash and struct dlist.

Here is a simpler version of the code causing error:

//struct dhash
typedef struct dhash{
char* filenm;
int n;
}dhash;

//create global variable to hold filename string
Constant *filenmInit = ConstantDataArray::getString(M.getContext(), “myfile.txt”);
Constant *filenmConst = new GlobalVariable(M, filenmInit->getType(), false,
GlobalVariable::ExternalLinkage, filenmInit, “.filenm”);

//create global variable for int
Constant* intInit = ConstantInt::get(Type::getInt32Ty(M.getContext()), 10);
Constant* intConst = new GlobalVariable(M, intInit->getType(), false,
GlobalVariable::ExternalLinkage, intInit, “.int”);

//create ConstantStruct for each hashtable entry
Constant *dhashInit[] = {filenmConst, intConst};
Constant *dhashConst = ConstantStruct::get(dhashTy, dhashInit);

Constant* htabInitArr[2];
htabInitArr[0] = dhashConst;
htabInitArr[1] = dhashConst;

ArrayType *htabTy = ArrayType::get(dhashTy, 2);
Constant *htabInit = ConstantArray::get(htabTy, makeArrayRef(htabInitArr,2));
GlobalVariable *htabConst = new GlobalVariable(M, htabInit->getType(), false,
GlobalVariable::ExternalLinkage, htabInit, “htab”);

Here is the error message:

opt: /home/chaitra/llvm-5-src/lib/IR/Constants.cpp:879: llvm::ConstantAggregate::ConstantAggregate(llvm::CompositeType*, llvm::Value::ValueTy, llvm::ArrayRefllvm::Constant*): Assertion `V[I]->getType() == T->getTypeAtIndex(I) && “Initializer for composite element doesn’t match!”’ failed.
#0 0x0000000001e558fa (opt+0x1e558fa)
#1 0x0000000001e5366e (opt+0x1e5366e)
#2 0x0000000001e537e2 (opt+0x1e537e2)
#3 0x00007f8a98a55390 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x11390)
#4 0x00007f8a977c7438 gsignal /build/glibc-e6zv40/glibc-2.23/signal/…/sysdeps/unix/sysv/linux/raise.c:54:0
#5 0x00007f8a977c903a abort /build/glibc-e6zv40/glibc-2.23/stdlib/abort.c:91:0
#6 0x00007f8a977bfbe7 __assert_fail_base /build/glibc-e6zv40/glibc-2.23/assert/assert.c:92:0
#7 0x00007f8a977bfc92 (/lib/x86_64-linux-gnu/libc.so.6+0x2dc92)
#8 0x000000000189c982 (opt+0x189c982)
#9 0x000000000189cc7c (opt+0x189cc7c)
#10 0x00000000018a93e0 (opt+0x18a93e0)
#11 0x00000000018a95f3 (opt+0x18a95f3)
#12 0x00007f8a9758e59a iospecns::storage_bitcode::runOnModule(llvm::Module&) (/home/chaitra/Desktop/iospec_test/test/build/proj/libstorage_bitcode.so+0x459a)
#13 0x00000000019473ad (opt+0x19473ad)
#14 0x00000000006db61c (opt+0x6db61c)
#15 0x00007f8a977b2840 __libc_start_main /build/glibc-e6zv40/glibc-2.23/csu/…/csu/libc-start.c:325:0
#16 0x0000000000747519 (opt+0x747519)
Stack dump:
0. Program arguments: opt -load /home/chaitra/Desktop/iospec_test/test/build/proj/libstorage_bitcode.so -storage_bitcode

  1. Running pass ‘storage_bitcode’ on module ‘’.
    Aborted (core dumped)

Thanks,
Chaitra

filenmConst's LLVM type is "[12 x i8]*" (or similar if I've
miscounted), but I assume the slot in the struct is i8*. So you need
either a bitcast or a getelementptr constant expression to convert it.

Cheers.

Tim.

The error still persists despite adding the following cast:

filenmConst = ConstantExpr::getBitCast(filenmConst, Type::getInt8PtrTy(M.getContext()));

That’s why I had omitted this line from code earlier.

Thanks,
Chaitra

filenmConst’s LLVM type is "[12 x i8]" (or similar if I’ve
miscounted), but I assume the slot in the struct is i8
. So you need
either a bitcast or a getelementptr constant expression to convert it.

The error still persists despite adding the following cast:
filenmConst = ConstantExpr::getBitCast(filenmConst, Type::getInt8PtrTy(M.getContext()));

That’s why I had omitted this line from code earlier.

Also, the same type mismatch error occurs when I try to create a GlobalVariable for a struct with 2 int fields as follows:

//struct dhash
typedef struct dhash{
int m;
int n;
}dhash;

//create global variable for int

Constant* intInit1 = ConstantInt::get(Type::getInt32Ty(M.getContext()), 11);
Constant* intConst1 = new GlobalVariable(M, intInit1->getType(), false,
GlobalVariable::ExternalLinkage, intInit1, “.int1”);

//create global variable for int
Constant* intInit = ConstantInt::get(Type::getInt32Ty(M.getContext()), 10);
Constant* intConst = new GlobalVariable(M, intInit->getType(), false,
GlobalVariable::ExternalLinkage, intInit, “.int”);

//create ConstantStruct for each hashtable entry
Constant *dhashInit[] = {intConst1, intConst};//{filenmConst, intConst};
Constant *dhashConst = ConstantStruct::get(dhashTy, dhashInit);

Constant* htabInitArr[2];
htabInitArr[0] = dhashConst;
htabInitArr[1] = dhashConst;

ArrayType *htabTy = ArrayType::get(dhashTy, 2);
Constant *htabInit = ConstantArray::get(htabTy, makeArrayRef(htabInitArr,2));
GlobalVariable *htabConst = new GlobalVariable(M, htabInit->getType(), false,
GlobalVariable::ExternalLinkage, htabInit, “htab”);

struct dhash in LLVM IR is as follows:

%struct.dhash = type { i32, i32 }

So, I’m not sure why the error occurs ?

The type mismatch is because the struct field type is i32 and GlobalVariable type is i32*. The issue is resolved after using proper casts as you pointed out. Thanks.

-Chaitra