2016-05-01 27 views
8

虽然与LLVM编译的代码: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

似乎与结构bar的不同安排,你会得到不同的偏移,但总是内部或在结构的结尾。

例如改变结构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和焦炭3将具有负立即数, int1将立即为0,并且int2将具有正的立即数。

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

+0

阅读http://llvm.org/docs/GetElementPtr.html –

+0

'scev'代表标量进化。我不知道为什么会发生这种优化,但你可以从那里开始。 –

+1

你可以发布你的编译选项和整个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生产出来,但我不想根据指令的名称进行猜测。