c - 为什么 asm 由 gcc mov 生成两次?

标签 c gcc assembly

假设我有以下 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 语句,在这里它可以使用它知道 x11 的事实并将其直接传递给 printf 。顺便说一句,它还意识到它无法使用 -4(%ebp) 处的 x 的存储。最后它可能知道 printf 做了什么(因为你包含了 stdio.h)所以可以分析格式字符串并在编译时进行转换以替换 printf 语句到直接将 14 写入标准输出的调用。

关于c - 为什么 asm 由 gcc mov 生成两次?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42533198/

相关文章:

c - __attribute__((constructor)) 它如何改变入口点?

c - Eclipse C/C++ (CDT) 添加 -l 选项(链接数学模块)gcc -lm

assembly - 从 64 位地址加载到其他寄存器而不是 rax

c - 在此片段中,内存分配有帮助吗?我认为它被 *tmpl 覆盖了?

C 程序 - 我无法在后台运行 shell 脚本

ubuntu - 如何为 ubuntu 16.04 安装 gcc 7.3?

winapi - 学习32位汇编

java - 调用字节码类方法,java

c - 在 C 语言中,带括号和不带括号的循环处理方式不同吗?

c - linux fcntl 文件锁定超时