Search code examples
arraysllvmllvm-irinstructions

How to correctly access a non-constant index in an LLVM array?


I've been trying to tackle arrays in LLVM, but I am not able to access elements at a non-constant index. Constant indexing works correctly. When I run my program, it just immediately exits. In my specific example, I am trying to use a for loop to set the elements for 0 to 5 to their respective value. This is what LLVM outputs (with optimizations disabled):

Read function definition:define double @main() {
entry:
  %i = alloca double, align 8
  %test = alloca [100 x double]*, align 8
  store double 0.000000e+00, [100 x double]* %test, align 8
  store double 0.000000e+00, double %i, align 8
  br label %loop

loop:                                             ; preds = %latch, %entry
  %i1 = phi double [ 0.000000e+00, %entry ], [ %nextvar, %latch ]
  %i2 = load double, double %i, align 8
  %cmptmp = fcmp ult double %i2, 5.000000e+00
  %booltmp = uitofp i1 %cmptmp to double
  %loopcond = fcmp one double %booltmp, 0.000000e+00
  br i1 %loopcond, label %body, label %afterloop

body:                                             ; preds = %loop
  %i3 = load double, double %i, align 8
  %casttmp = fptosi double %i3 to i32
  %reftmp = getelementptr [100 x double], [100 x double]* %test, i32 0, i32 %casttmp
  store double 5.000000e+00, double* %reftmp, align 8
  br label %latch

latch:                                            ; preds = %body
  %i4 = load double, double %i, align 8
  %nextvar = fadd double %i4, 1.000000e+00
  store double %nextvar, double %i, align 8
  br label %loop

afterloop:                                        ; preds = %loop
  %reftmp5 = getelementptr [100 x double], [100 x double]* %test, i32 0, i32 0
  %loadtmp = load double, double* %reftmp5, align 8
  %calltmp = call double @print(double %loadtmp)
  ret double %calltmp
}

The most important block in which the array access happens, is body. This is how the code looks, to help with what's supposed to happen:

fun main() {
    mut test: number[100]
    for(i = 0, i < 5) {
        test[i] = 5
    }
    print(test[0])
}

Print is an external function that is 100% tested and it works correctly. Print statements before the allocation don't seem to run either. If anyone notices anything wrong, it would be great! PS: in my language, all numbers are doubles, so i is a double too, that's why I'm casting it to an integer.

Edit: I compiled the same code using clang and c++, and it gave the following llvm output:

define dso_local noundef i32 @main() #0 !dbg !207 {
  %1 = alloca i32, align 4
  %2 = alloca [100 x double], align 16
  %3 = alloca double, align 8
  store i32 0, i32* %1, align 4
  call void @llvm.dbg.declare(metadata [100 x double]* %2, metadata !210, metadata !DIExpression()), !dbg !215
  call void @llvm.dbg.declare(metadata double* %3, metadata !216, metadata !DIExpression()), !dbg !218
  store double 0.000000e+00, double* %3, align 8, !dbg !218
  br label %4, !dbg !219

4:                                                ; preds = %13, %0
  %5 = load double, double* %3, align 8, !dbg !220
  %6 = fcmp olt double %5, 5.000000e+00, !dbg !222
  br i1 %6, label %7, label %16, !dbg !223

7:                                                ; preds = %4
  %8 = load double, double* %3, align 8, !dbg !224
  %9 = load double, double* %3, align 8, !dbg !226
  %10 = fptosi double %9 to i32, !dbg !226
  %11 = sext i32 %10 to i64, !dbg !227
  %12 = getelementptr inbounds [100 x double], [100 x double]* %2, i64 0, i64 %11, !dbg !227
  store double %8, double* %12, align 8, !dbg !228
  br label %13, !dbg !229

13:                                               ; preds = %7
  %14 = load double, double* %3, align 8, !dbg !230
  %15 = fadd double %14, 1.000000e+00, !dbg !230
  store double %15, double* %3, align 8, !dbg !230
  br label %4, !dbg !231, !llvm.loop !232

16:                                               ; preds = %4
  %17 = getelementptr inbounds [100 x double], [100 x double]* %2, i64 0, i64 2, !dbg !235
  %18 = load double, double* %17, align 16, !dbg !235
  %19 = call i32 (i8*, ...) @printf(i8* noundef getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i64 0, i64 0), double noundef %18), !dbg !236
  %20 = load i32, i32* %1, align 4, !dbg !237
  ret i32 %20, !dbg !237
}

Which looks almost identical to what I have. I don't understand, why wouldn't this work...

Edit2: I've changed the base type of the array to char - i8, and then all of this works. I'm confused. I wonder if it has to do with the alignment? On clang-llvm, it's 16, at me, it's 8


Solution

  • store double 0.000000e+00, [100 x double]* %test, align 8
    

    This looks ill-typed. %test has the type [100 x double]**, not [100 x double]* (alloca T will give you a pointer of type T*) and, in any case, to store a single double you should have a pointer of type double*.

    As a piece of advice: If you're using the API, you should be calling verifyModule on your module when you're finished creating it. It will tell you when your code is ill-formed (and if you're generating the code as text and then compiling with llc, it should tell you the same) and will allow you to catch problems like this right away.