假设我有以下 C 代码: #包括
int main()
{
int x = 11;
int y = x + 3;
printf("%d\n", x);
return 0;
}
然后我使用 gcc 将它编译成 asm,我得到这个(删除了一些标志):
main:
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
movl $11, -4(%rbp)
movl -4(%rbp), %eax
addl $3, %eax
movl %eax, -8(%rbp)
movl -4(%rbp), %eax
movl %eax, %esi
movl $.LC0, %edi
movl $0, %eax
call printf
movl $0, %eax
leave
ret
我的问题是为什么它是 movl -4(%rbp), %eax
后跟 movl %eax, %esi
,而不是简单的 movl -4(%rbp), %esi
(根据我的实验效果很好)?
最佳答案
您可能没有启用优化。
如果不进行优化,编译器将生成这样的代码。其一,它不将数据分配给寄存器,而是在堆栈上。这意味着当您对变量进行操作时,它们将首先传输到寄存器中,然后再进行操作。
鉴于 x
生命分配在 -4(%rbp)
中,这就是代码的显示效果,就好像您直接翻译它而不进行优化一样。首先,将 11
移动到 x
的存储空间。这意味着:
movl $11, -4(%rbp)
完成第一个语句。下一条语句是评估x+3
并放入y
的存储中(即-8(%rbp)
,这样就完成了不考虑之前生成的代码:
movl -4(%rbp), %eax
addl $3, %eax
movl %eax, -8(%rbp)
完成第二个语句。顺便说一句,它分为两部分:x+3
的计算和结果的存储。然后编译器继续为 printf
语句生成代码,同样不考虑前面的语句。
另一方面,如果您启用优化,编译器会执行许多智能且对人类显而易见的事情。一件事是它允许将变量分配给寄存器,或者至少跟踪可以找到变量值的位置。在这种情况下,编译器例如会在第二个语句中知道 x
不仅存储在 -4(%ebp)
中,它还会知道它存储在 $11
(是的,它现在是实际值)。然后它可以使用它向它添加 3
这意味着它知道结果是 14
(但它更聪明 - 它也看到你没有使用该变量,因此它会完全跳过该语句)。下一个语句是 printf
语句,在这里它可以使用它知道 x
是 11
的事实并将其直接传递给 printf
。顺便说一句,它还意识到它无法使用 -4(%ebp)
处的 x
的存储。最后它可能知道 printf
做了什么(因为你包含了 stdio.h
)所以可以分析格式字符串并在编译时进行转换以替换 printf
语句到直接将 14
写入标准输出的调用。
关于c - 为什么 asm 由 gcc mov 生成两次?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42533198/