Search code examples
c++llvmllvm-c++-api

LLVM: how to assign an array element?


I'm struggling to figure out how to assign an array element using the LLVM c++ API. consider this C code:

int main()
{
    int aa[68];
    aa[56] = 7;

    return 0;
}

using

clang -S -emit-llvm main.c

I get the following IR (attributes and other things are skipped for simplicity):

define dso_local i32 @main() #0 {
  %1 = alloca i32, align 4
  %2 = alloca [68 x i32], align 16
  store i32 0, i32* %1, align 4
  %3 = getelementptr inbounds [68 x i32], [68 x i32]* %2, i64 0, i64 56
  store i32 7, i32* %3, align 16
  ret i32 0
}

I already know how to create an inbounds GEP, but when storing a value (7) to the array the type is a pointer to i32.

my language is very similar to C, that's why I'm using C as an example (so far it's just C but with a different syntax). generated IR for my language is:

define i32 @main() {
  %0 = alloca [2 x i32], align 4
  %1 = getelementptr [2 x i32], [2 x i32]* %0, i32 1
  store i32 1, [2 x i32]* %1, align 4
  ret i32 0
}

how can I possibly turn [2 x i32]* into i32* when creating a store? this is how I create the store:

llvm::AllocaInst *stored = symbol_table[arr_name];
llvm::Value *result = ir_builder->CreateGEP(stored->getAllocatedType(), stored, idx_vals); 
// idx_vals contains the index

ir_builder->CreateStore(val, result); // here val is a value stored in a symbol table
                                      // and it's type is llvm::Value *

Solution

  • how can I possibly turn [2 x i32]* into i32* when creating a store?

    This is exactly what the "get element pointer" instruction does. You have a pointer to an object like a struct or an array, and you want a pointer to one element.

    %1 = getelementptr [2 x i32], [2 x i32]* %0, i32 1
    

    This isn't quite what you want. Picture a C string in memory, you don't have a [*number* x i8]* you just have an i8*. If you had a pointer to [*number* x i8]* you'd be stepping into that array and getting a pointer to one of its elements, but with a C string you have a pointer to a single i8 element and you step over it, advancing your pointer by sizeof the pointee.

    What your %1 is doing is stepping over one whole [2 x i32] and pointing to another full [2 x i32] after it. You don't want to step over at all, so your first index should be i32 0. Then you want to step into it and select the second i32 in your 2 x i32? Use i32 1 as your second index.

    %1 = getelementptr [2 x i32], [2 x i32]* %0, i32, 0, i32 1
    

    produces an i32*.

    See the LLVM GEP FAQ: https://www.llvm.org/docs/GetElementPtr.html