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.
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
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'. –
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