2016-05-01 44 views
8

Durante la compilazione di questo codice con LLVM:LLVM struct matrice iterazione

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; 
} 

Per qualche motivo il compilatore decide di scorrere questo array in maniera strana. per prima cosa seleziona un punto apparentemente arbitrario nel mezzo o alla fine della struttura, e quindi memorizza i valori appropriati con immediati relativi al punto arbitrario.

ho scoperto che il punto arbitrario è scelto tra questo codice IR:

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

dove 4 è l'offset di char3.

In questo esempio il negozio per int1, int2, char1, char2 avrà immediates negativi, char3 avrà immediato 0.

Sembra che con diversi arrangiamenti di bar struct si ottiene diversi offset, ma sempre all'interno o al la fine della struttura.

Per esempio cambiando bar struct a:

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

Sarà risultato con la seguente riga di IR:

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

Il che significa che il negozio per char1, char2 e char 3 avrà immediates negativi, int1 avrà 0 immediato e int2 avrà un immediato positivo.

Perché itera in relazione a un punto diverso dalla base della struttura?

+0

Leggi http://llvm.org/docs/GetElementPtr.html –

+0

'scev' sta per evoluzione scalare. Non so in modo specifico perché avvenga l'ottimizzazione, ma potresti iniziare da lì. –

+1

Potresti postare le opzioni di compilazione e l'intero llvm ir? Almeno per la parte del negozio? –

risposta

1

Una recente build di Clang non produce l'istruzione getelementptr che si descrive. Usa l'indicizzazione normale. La cosa più strana che fa è quello di produrre una versione con il corpo del ciclo srotolato due volte:

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 
} 

Se si aggiorna la tua domanda con passaggi per riprodurre il IR che avete visto, sono felice di spiegare il motivo per cui ha prodotto LLVM , ma non voglio indovinare in base al nome dell'istruzione.