为什么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后,它将使用movdqa加载/存储功能复制所有4个初始化程序)
为什么GCC不能像Clang那样只对最后96个元素进行lea edi, [esp+16]
memset和with rep stosd
? 这是错过的优化,还是以这种方式更有效?(C实际上调用memset
而不是内联rep stos
)
编者注:该问题最初具有未优化的编译器输出,其工作方式相同,但at处的低效率代码-O0
无法证明任何事情。但事实证明,即使在时,GCC也错过了此优化-O3
。
将指针传递a
给非内联函数将是迫使编译器实现的另一种方式a[]
,但是在32位代码中会导致汇编的大量混乱。(堆栈args会导致push,而push与存储中的数据混合到堆栈中以初始化数组。)
使用volatile a[100]{1,2,3,4}
获取GCC来创建然后复制该数组,这是很疯狂的。通常,volatile
这有助于查看编译器如何初始化局部变量或将其布置在堆栈上。
我无法读取该程序集,但是在哪里显示该数组完全用零填充?
—
smac89
另一个有趣的事实:对于更多已初始化的项目,gcc和clang都还原为从
—
小丑
.rodata
... 复制整个数组。
您禁用了优化;除非您验证相同的事情
—
彼得·科德斯
-O3
(确实如此),否则低效的代码就不足为奇了。 godbolt.org/z/rh_TNF
您还想知道什么?这是一项错过的优化,请使用
—
彼得·科德斯
missed-optimization
关键字在GCC的bugzilla中进行报告。
a[0] = 0;
然后a[0] = 1;
。