Por que o gcc preenche toda a matriz com zeros em vez de apenas os 96 números inteiros restantes? Os inicializadores diferentes de zero estão todos no início da matriz.
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
}
O MinGW8.1 e o gcc9.2 fazem o asm assim ( 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
(com o SSE ativado, ele copiaria todos os 4 inicializadores com o movdqa load / store)
Por que o GCC não faz lea edi, [esp+16]
e memset (com rep stosd
) apenas os últimos 96 elementos, como Clang faz? É uma otimização perdida ou é, de alguma forma, mais eficiente fazê-lo dessa maneira? (Clang realmente chama em memset
vez de inlining rep stos
)
Nota do editor: a pergunta originalmente tinha saída de compilador não otimizada que funcionava da mesma maneira, mas código ineficiente em -O0
não prova nada. Mas acontece que essa otimização é perdida pelo GCC mesmo em -O3
.
Passar um ponteiro para a
uma função não embutida seria outra maneira de forçar o compilador a se materializar a[]
, mas no código de 32 bits que leva a uma confusão significativa do asm. (Args da pilha resultam em pushes, que são misturados com os armazenamentos na pilha para iniciar a matriz.)
Usar volatile a[100]{1,2,3,4}
faz com que o GCC crie e copie a matriz, o que é insano. Normalmente volatile
é bom ver como os compiladores iniciam variáveis locais ou as colocam na pilha.
.rodata
... Não acredito que copiar 400 bytes seja mais rápido do que zerar e definir 8 itens.
-O3
(o que acontece). godbolt.org/z/rh_TNF
missed-optimization
palavra - chave.
a[0] = 0;
e depoisa[0] = 1;
.