如何正确访问 LLVM 数组中的非常量索引?

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

我一直在尝试处理 LLVM 中的数组,但我无法访问非常量索引处的元素。常量索引工作正常。当我 运行 我的程序时,它立即退出。在我的具体示例中,我尝试使用 for 循环将 0 到 5 的元素设置为它们各自的值。这是 LLVM 的输出(禁用优化):

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
}

发生数组访问的最重要的块是正文。这是代码的样子,以帮助解决应该发生的事情:

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

打印是一个经过 100% 测试并且可以正常工作的外部函数。 分配前的打印语句似乎也不 运行。如果有人发现任何错误,那就太好了! PS:在我的语言中,所有数字都是双精度数,所以 i 也是双精度数,这就是我将其转换为整数的原因。

编辑:我使用 clang 和 c++ 编译了相同的代码,它给出了以下 llvm 输出:

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
}

看起来和我拥有的几乎一模一样。我不明白,为什么这行不通...

Edit2:我已将数组的基本类型更改为 char - i8,然后所有这些都有效。我很困惑。我想知道这是否与对齐有关?在 clang-llvm 上是 16,在我这里是 8

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

这看起来 ill-typed。 %test 的类型是 [100 x double]**,而不是 [100 x double]*alloca T 会给你一个 T* 类型的指针),并且在任何情况下,存储一个双精度你应该有一个 double*.

类型的指针

作为一条建议:如果您正在使用 API,您应该在完成创建模块后调用 verifyModule。当您的代码是 ill-formed 时它会告诉您(如果您将代码生成为文本然后使用 llc 进行编译,它应该会告诉您相同的信息)并且允许您捕获这样的问题马上。