Чому gcc заповнює весь масив нулями замість лише 96 цілих чисел? Ненульові ініціалізатори знаходяться на початку масиву.
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 і gcc9.2 обидва роблять asm таким ( провідник компілятора Godbolt ).
# 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
(з увімкненою SSE вона копіює всі 4 ініціалізатори з завантаженням / зберіганням movdqa)
Чому GCC не робить lea edi, [esp+16]
і не запам'ятовує (з rep stosd
) лише останні 96 елементів, як це робить Кланг? Це пропущена оптимізація, чи це якось ефективніше зробити так? (Clang насправді дзвонить memset
замість inlining rep stos
)
Примітка редактора: запитання спочатку мало неоптимізований вихід компілятора, який працював так само, але неефективний код -O0
не підтверджує нічого. Але виявляється, що цю оптимізацію GCC пропускає навіть при -O3
.
Перехід вказівника на a
не вбудовану функцію був би іншим способом змусити компілятор здійснитися a[]
, але в 32-бітовому коді, що призводить до значного захаращення ASM. (Аргументи стека призводять до натискань, які змішуються зі сховищами до стеку, щоб запустити масив.)
Використання volatile a[100]{1,2,3,4}
отримує GCC для створення, а потім копіювання масиву, який є божевільним. Зазвичай volatile
це добре для того, щоб подивитися, як компілятори вставляють локальні змінні або викладають їх у стек.
.rodata
... Я не можу повірити, що копіювання 400 байтів швидше, ніж нулювання та встановлення 8 елементів.
-O3
(що це робиться). godbolt.org/z/rh_TNF
missed-optimization
ключовим словом.
a[0] = 0;
і потімa[0] = 1;
.