为何GCC不对临时变量赋值全局的内联汇编"=g"输出做内存优化?
问题解释:GCC内联汇编为何未生成最优代码
先整理你的测试场景与编译结果,再逐一拆解背后的原因:
测试场景1(使用"g"约束)
原代码
int global; static inline int return3() { int ret; __asm__("mov %0, 3" : "=g" (ret)); return ret; } void f(int x) { global = return3(); }
GCC 12.2编译输出
f: mov eax, 3 mov DWORD PTR global, eax ret global: .zero 4
为何没生成mov DWORD PTR global, 3的最优代码?
核心原因是GCC对__asm__块的黑箱处理逻辑:
- GCC不会解析汇编指令的具体语义,它只根据约束规则为操作数分配存储位置,把
__asm__块当作无法理解的原子操作。 - 对于
mov %0, 3,GCC仅知道要把3存入%0对应的位置(由"=g"约束决定可以是寄存器、内存或立即数),但它不知道这个操作数的具体值是3,也无法将后续global = ret的赋值逻辑与汇编指令合并优化。 - 再加上x86平台的函数返回约定:int类型的返回值默认通过
eax寄存器传递,所以return ret会触发将ret的值移入eax的操作,最终就出现了先把3写入eax、再从eax写入global的两步操作。
测试场景2(使用"m"约束)
编译输出
f: sub esp, 16 mov DWORD PTR [esp+12], 3 mov eax, DWORD PTR [esp+12] mov DWORD PTR global, eax add esp, 16 ret global: .zero 4
为何出现冗余的栈操作与寄存器拷贝?
"=m"约束强制要求操作数必须是内存位置,因此GCC为ret分配了栈上的存储空间([esp+12])。- 汇编指令把3写入栈地址后,
return ret需要遵循x86返回约定,将栈中的值加载到eax寄存器。 - 最后
global = ret又把eax的值写入全局变量,于是就出现了栈存储→寄存器读取→全局变量写入的三次冗余拷贝。
如何生成最优代码?
如果想要直接得到mov global, 3的汇编,可以直接在汇编块中操作全局变量,或者让GCC直接将约束绑定到global:
int global; void f(int x) { // 方式1:直接在汇编中操作全局变量 __asm__("mov DWORD PTR global, 3" : :); }
或者更安全的方式(让GCC处理符号地址):
int global; void f(int x) { // 方式2:将约束直接绑定到global __asm__("mov %0, 3" : "=m"(global)); }
这两种写法都会生成你期望的最优汇编代码。
内容的提问来源于stack exchange,提问作者DarkAtom
相关产品推荐
相关产品推荐

