开启-O3优化时GCC/clang为何保留未用数组?如何优化bar生成foo同款汇编?
问题
编写分支无代码时发现,bar函数通过创建数组实现无分支逻辑,但开启-O3优化后,GCC或clang仍会生成数组相关的汇编指令;而foo函数则生成了更高效的无分支汇编(使用cmovg指令)。想保留bar这种数组写法,如何修改代码,让它生成与foo完全一致的汇编?
原代码
int foo(int x, int y) { if (x > 0) { return x; } else { return y; } } int bar(int x, int y) { int a[2] = {y, x}; return a[x > 0]; }
原汇编输出
foo(int, int): mov eax, esi test edi, edi cmovg eax, edi ret bar(int, int): mov dword ptr [rsp - 8], esi mov dword ptr [rsp - 4], edi xor eax, eax test edi, edi setg al mov eax, dword ptr [rsp + 4*rax - 8] ret
解决方案
原bar函数的数组会被编译器分配到栈上并存储值,导致无法优化成cmov指令。要保留数组写法同时触发高效的无分支优化,核心是让编译器意识到无需实际存储数组元素,直接通过索引选择原有变量即可。
方法1:使用指针数组
把数组改成指向输入变量的指针数组,编译器会直接通过索引取对应变量的地址并取值,不会在栈上额外存储数据:
int bar(int x, int y) { const int* a[2] = {&y, &x}; return *a[x > 0]; }
方法2:编译期常量数组配合条件选择
如果更倾向于值语义,也可以用编译期数组定义索引规则,再通过索引直接选择变量:
int bar(int x, int y) { static const int select[2] = {0, 1}; return select[x > 0] ? x : y; }
优化后汇编
两种修改方式在-O3优化下,GCC和clang都会生成与foo完全一致的汇编:
bar(int, int): mov eax, esi test edi, edi cmovg eax, edi ret
内容的提问来源于stack exchange,提问作者weineng
相关产品推荐
相关产品推荐

