Perché gcc riempie l'intero array di zeri invece che solo dei restanti 96 numeri interi? Gli inizializzatori diversi da zero sono tutti all'inizio dell'array.
void *sink;
void bar() {
int a[100]{1,2,3,4};
sink = a; // a escapes the function
asm("":::"memory"); // and compiler memory barrier
// forces the compiler to materialize a[] in memory instead of optimizing away
}
MinGW8.1 e gcc9.2 fanno entrambi asm in questo modo ( Godbolt compiler explorer ).
# gcc9.2 -O3 -m32 -mno-sse
bar():
push edi # save call-preserved EDI which rep stos uses
xor eax, eax # eax=0
mov ecx, 100 # repeat-count = 100
sub esp, 400 # reserve 400 bytes on the stack
mov edi, esp # dst for rep stos
mov DWORD PTR sink, esp # sink = a
rep stosd # memset(a, 0, 400)
mov DWORD PTR [esp], 1 # then store the non-zero initializers
mov DWORD PTR [esp+4], 2 # over the zeroed part of the array
mov DWORD PTR [esp+8], 3
mov DWORD PTR [esp+12], 4
# memory barrier empty asm statement is here.
add esp, 400 # cleanup the stack
pop edi # and restore caller's EDI
ret
(con SSE abilitato avrebbe copiato tutti e 4 gli inizializzatori con carico / archivio movdqa)
Perché GCC non fa lea edi, [esp+16]
e memorizza (con rep stosd
) solo gli ultimi 96 elementi, come fa Clang? È un'ottimizzazione mancata o è in qualche modo più efficiente farlo in questo modo? (Clang in realtà chiama memset
invece di inline rep stos
)
Nota del redattore: la domanda originariamente aveva un output del compilatore non ottimizzato che funzionava allo stesso modo, ma il codice inefficiente su -O0
non dimostra nulla. Ma si scopre che questa ottimizzazione manca anche a GCC -O3
.
Passare un puntatore a a
una funzione non inline sarebbe un altro modo per forzare il compilatore a materializzarsi a[]
, ma nel codice a 32 bit che porta a un ingombro significativo dell'asm. (Stack args si traduce in push, che viene mischiato con i negozi nello stack per avviare l'array.)
Utilizzando si volatile a[100]{1,2,3,4}
ottiene GCC per creare e quindi copiare l'array, il che è folle. Normalmente volatile
è utile osservare come i compilatori inizializzano le variabili locali o le dispongono nello stack.
.rodata
... Non posso credere che copiare 400 byte sia più veloce dell'azzeramento e impostazione di 8 elementi.
-O3
(cosa che fa). godbolt.org/z/rh_TNF
missed-optimization
parola chiave.
a[0] = 0;
e poia[0] = 1;
.