【问题标题】:LLVM struct array iterationLLVM 结构数组迭代
【发布时间】:2026-02-17 20:25:01
【问题描述】:

使用 LLVM 编译此代码时:

struct bar {
    int int1;
    int int2;
    char char1;
    char char2;
    char char3;
};


struct foo {
    struct bar array[16];
};


int func(struct foo *f, int num) {

    for(int i = 0; i < num; i++){
        f->array[i].int1 = 1;
        f->array[i].int2 = 2;
        f->array[i].char1 = 'a';
        f->array[i].char2 = 'b';        
        f->array[i].char3 = 'c';        
    }
    return num;
}

由于某种原因,编译器决定以一种奇怪的方式遍历这个数组。 首先,它在结构的中间或末尾选择一个看似任意的点, 然后用相对于任意点的立即数存储适当的值。

我发现任意点是从这个 IR 代码中选择的:

  %scevgep = getelementptr %struct.foo* %f, i32 0, i32 0, i32 0, i32 4

其中 4 是 char3 的偏移量。

在此示例中,int1、int2、char1、char2 的存储将具有负立即数,char3 将具有立即数 0。

似乎通过 struct bar 的不同排列,您会得到不同的偏移量,但总是在结构内部或末尾。

例如将 struct bar 更改为:

struct bar {
    char char1;
    char char2;
    char char3;
    int int1;
    int int2;
};

将产生以下 IR 行:

  %scevgep = getelementptr %struct.foo* %f, i32 0, i32 0, i32 0, i32 3

这意味着 char1、char2 和 char 3 的存储将具有负立即数,int1 将具有立即数 0,而 int2 将具有正立即数。

为什么它会相对于结构体基础以外的点进行迭代?

【问题讨论】:

  • scev 代表标量演化。我不知道为什么会发生这种优化,但你可以从那里开始。
  • 你能发布你的构建选项和整个 llvm ir 吗?至少对于商店部分?

标签: c llvm llvm-clang llvm-ir


【解决方案1】:

Clang 的最新版本不会产生您描述的 getelementptr 指令。它使用正常的索引。它所做的最奇怪的事情是生成一个循环体展开两次的版本:

target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

%struct.foo = type { [16 x %struct.bar] }
%struct.bar = type { i32, i32, i8, i8, i8 }

define i32 @func(%struct.foo* nocapture %f, i32 %num) {
entry:
  %cmp25 = icmp sgt i32 %num, 0
  br i1 %cmp25, label %for.body.preheader, label %for.cond.cleanup

for.body.preheader:                               ; preds = %entry
  %xtraiter = and i32 %num, 1
  %0 = icmp eq i32 %num, 1
  br i1 %0, label %for.cond.cleanup.loopexit.unr-lcssa, label %for.body.preheader.new

for.body.preheader.new:                           ; preds = %for.body.preheader
  %unroll_iter = sub i32 %num, %xtraiter
  br label %for.body

for.cond.cleanup.loopexit.unr-lcssa.loopexit:     ; preds = %for.body
  %indvars.iv.next.1.lcssa = phi i64 [ %indvars.iv.next.1, %for.body ]
  br label %for.cond.cleanup.loopexit.unr-lcssa

for.cond.cleanup.loopexit.unr-lcssa:              ; preds = %for.cond.cleanup.loopexit.unr-lcssa.loopexit, %for.body.preheader
  %indvars.iv.unr = phi i64 [ 0, %for.body.preheader ], [ %indvars.iv.next.1.lcssa, %for.cond.cleanup.loopexit.unr-lcssa.loopexit ]
  %lcmp.mod = icmp eq i32 %xtraiter, 0
  br i1 %lcmp.mod, label %for.cond.cleanup.loopexit, label %for.body.epil.preheader

for.body.epil.preheader:                          ; preds = %for.cond.cleanup.loopexit.unr-lcssa
  br label %for.body.epil

for.body.epil:                                    ; preds = %for.body.epil.preheader
  %int1.epil = getelementptr inbounds %struct.foo, %struct.foo* %f, i64 0, i32 0, i64 %indvars.iv.unr, i32 0
  store i32 1, i32* %int1.epil, align 4, !tbaa !1
  %int2.epil = getelementptr inbounds %struct.foo, %struct.foo* %f, i64 0, i32 0, i64 %indvars.iv.unr, i32 1
  store i32 2, i32* %int2.epil, align 4, !tbaa !6
  %char1.epil = getelementptr inbounds %struct.foo, %struct.foo* %f, i64 0, i32 0, i64 %indvars.iv.unr, i32 2
  store i8 97, i8* %char1.epil, align 4, !tbaa !7
  %char2.epil = getelementptr inbounds %struct.foo, %struct.foo* %f, i64 0, i32 0, i64 %indvars.iv.unr, i32 3
  store i8 98, i8* %char2.epil, align 1, !tbaa !8
  %char3.epil = getelementptr inbounds %struct.foo, %struct.foo* %f, i64 0, i32 0, i64 %indvars.iv.unr, i32 4
  store i8 99, i8* %char3.epil, align 2, !tbaa !9
  br label %for.cond.cleanup.loopexit.epilog-lcssa

for.cond.cleanup.loopexit.epilog-lcssa:           ; preds = %for.body.epil
  br label %for.cond.cleanup.loopexit

for.cond.cleanup.loopexit:                        ; preds = %for.cond.cleanup.loopexit.unr-lcssa, %for.cond.cleanup.loopexit.epilog-lcssa
  br label %for.cond.cleanup

for.cond.cleanup:                                 ; preds = %for.cond.cleanup.loopexit, %entry
  ret i32 %num

for.body:                                         ; preds = %for.body, %for.body.preheader.new
  %indvars.iv = phi i64 [ 0, %for.body.preheader.new ], [ %indvars.iv.next.1, %for.body ]
  %niter = phi i32 [ %unroll_iter, %for.body.preheader.new ], [ %niter.nsub.1, %for.body ]
  %int1 = getelementptr inbounds %struct.foo, %struct.foo* %f, i64 0, i32 0, i64 %indvars.iv, i32 0
  store i32 1, i32* %int1, align 4, !tbaa !1
  %int2 = getelementptr inbounds %struct.foo, %struct.foo* %f, i64 0, i32 0, i64 %indvars.iv, i32 1
  store i32 2, i32* %int2, align 4, !tbaa !6
  %char1 = getelementptr inbounds %struct.foo, %struct.foo* %f, i64 0, i32 0, i64 %indvars.iv, i32 2
  store i8 97, i8* %char1, align 4, !tbaa !7
  %char2 = getelementptr inbounds %struct.foo, %struct.foo* %f, i64 0, i32 0, i64 %indvars.iv, i32 3
  store i8 98, i8* %char2, align 1, !tbaa !8
  %char3 = getelementptr inbounds %struct.foo, %struct.foo* %f, i64 0, i32 0, i64 %indvars.iv, i32 4
  store i8 99, i8* %char3, align 2, !tbaa !9
  %indvars.iv.next = or i64 %indvars.iv, 1
  %int1.1 = getelementptr inbounds %struct.foo, %struct.foo* %f, i64 0, i32 0, i64 %indvars.iv.next, i32 0
  store i32 1, i32* %int1.1, align 4, !tbaa !1
  %int2.1 = getelementptr inbounds %struct.foo, %struct.foo* %f, i64 0, i32 0, i64 %indvars.iv.next, i32 1
  store i32 2, i32* %int2.1, align 4, !tbaa !6
  %char1.1 = getelementptr inbounds %struct.foo, %struct.foo* %f, i64 0, i32 0, i64 %indvars.iv.next, i32 2
  store i8 97, i8* %char1.1, align 4, !tbaa !7
  %char2.1 = getelementptr inbounds %struct.foo, %struct.foo* %f, i64 0, i32 0, i64 %indvars.iv.next, i32 3
  store i8 98, i8* %char2.1, align 1, !tbaa !8
  %char3.1 = getelementptr inbounds %struct.foo, %struct.foo* %f, i64 0, i32 0, i64 %indvars.iv.next, i32 4
  store i8 99, i8* %char3.1, align 2, !tbaa !9
  %indvars.iv.next.1 = add nsw i64 %indvars.iv, 2
  %niter.nsub.1 = add i32 %niter, -2
  %niter.ncmp.1 = icmp eq i32 %niter.nsub.1, 0
  br i1 %niter.ncmp.1, label %for.cond.cleanup.loopexit.unr-lcssa.loopexit, label %for.body
}

如果您使用重现您看到的 IR 的步骤更新您的问题,我很乐意解释 LLVM 产生它的原因,但我不想根据指令的名称来猜测。

【讨论】: