2013-03-07 15 views
9

Durante la compilazione di un progetto più ampio con clang sono incappato in un bug di irritante.Errore di ottimizzazione LLVM o comportamento non definito?

Si consideri il seguente piccolo esempio:

unsigned long int * * fee(); 

void foo(unsigned long int q) 
{ 
    unsigned long int i,j,k,e; 
    unsigned long int pows[7]; 
    unsigned long int * * table; 

    e = 0; 
    for (i = 1; i <= 256; i *= q) 
    pows[e++] = i; 
    pows[e--] = i; 

    table = fee(); // need to set table to something unknown 
        // here, otherwise the compiler optimises 
        // parts of the loops below away 
        // (and no bug occurs) 

    for (i = 0; i < q; i++) 
    for (j = 0; j < e; j++) 
     ((unsigned char*)(*table) + 5)[i*e + j] = 0; // bug here 
} 

Al meglio della mia conoscenza di questo codice non viola lo standard C in qualsiasi modo, anche se l'ultima riga sembra scomodo (nel progetto attuale, il codice come questo appare a causa dell'uso eccessivo delle macro del preprocessore).

compilazione questo con clangore (versione 3.1 o superiore) a livello di ottimizzazione -O1 o superiore risultati nel codice di scrittura nella posizione sbagliata nella memoria.

Le parti cruciali del file di assiemi prodotto da clang/LLVM come segue: (Questa è la sintassi di GAS, quindi a quelli di voi che sono abituati a Intel: Attenzione!)

[...] 
    callq _fee 
    leaq 6(%rbx), %r8   ## at this point, %rbx == e-1 
    xorl %edx, %edx 
LBB0_4: 
    [...] 
    movq %r8, %rsi 
    imulq %rdx, %rsi 
    incq %rdx 
LBB0_6: 
    movq (%rax), %rcx   ## %rax == fee() 
    movb $0, (%rcx,%rsi) 
    incq %rsi 
    [conditional jumps back to LBB0_6 resp. LBB0_4] 
    [...] 

In altre parole, le istruzioni fanno

(*table)[i*(e+5) + j] = 0; 

invece dell'ultima riga sopra riportata. La scelta di + 5 è arbitraria, sommando (o sottraendo) altri interi si ottiene lo stesso comportamento. Quindi - questo è un bug nell'ottimizzazione di LLVM o c'è un comportamento indefinito in corso qui?

Modifica: Nota anche che l'errore scompare se lascio il cast (unsigned char*) nell'ultima riga. In generale, il bug sembra essere abbastanza sensibile a qualsiasi cambiamento.

+1

non possono vedere moltiplicazione per 5 nel codice assembler sopra (ma poi io sono più abituati a assembler ARM che Intel, se si tratta di Intel :-)), ma, l'ultima riga del codice C si traduce in '* ((unsigned char *) (* table) + 5 + i * e + j) ', quindi ... sei sicuro di mettere queste parentesi attorno a" e + 5 "nella tua interpretazione dell'output dell'assembler correttamente? – user2116939

+0

Sì, sono abbastanza sicuro. Questa è la sintassi di GAS, non Intel, in modo che il 'movq% r8,% rsi' e' imulq% RDX,% rsi' significa che '% rsi' terrà' (% rbx + 6) *% RDX = (e + 5) *% rdx'. –

+0

Sì, ora posso vedere questo. Sembra un bug ottimizzatore poiché il codice è abbastanza kosher anche se un po 'strano (ma le macro possono generare output strani). – user2116939

risposta

5

Sono abbastanza sicuro che questo è un bug di ottimizzatore. Ripropone in LLVM-2.7 e LLVM-3.1, le uniche versioni a cui ho accesso.

ho postato a bug al LLVM Bugzilla.

Il bug è dimostrata da questo SSCCE:

#include <stdio.h> 

unsigned long int * table; 

void foo(unsigned long int q) 
{ 
    unsigned long int i,j,e; 

    e = 0; 
    for (i = 1; i <= 256; i *= q) 
    e++; 
    e--; 

    for (i = 0; i < q; i++) 
    for (j = 0; j < e; j++) 
     ((unsigned char*)(table) + 13)[i*e + j] = 0; // bug here 
} 

int main() { 
    unsigned long int v[8]; 
    int i; 
    memset(v, 1, sizeof(v)); 

    table = v; 
    foo(2); 

    for(i=0; i<sizeof(v); i++) { 
     printf("%d", ((unsigned char*)v)[i]); 
    } 
    puts(""); 
    return 0; 
} 

Dovrebbe stampare

1111111111111000000000000000011111111111111111111111111111111111 

sotto GCC e "clang -O0". L'uscita errata osservata con LLVM è

0000000011111111111110000000011111111111111111111111111111111111 

Grazie per aver notato questo!

Problemi correlati