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

时间:2017-03-01 13:34:39

标签: c gcc assembly

假设我有以下C代码:     #include

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(根据我的实验效果很好)?

1 个答案:

答案 0 :(得分:4)

您可能没有启用优化。

如果没有优化,编译器将生成这样的代码。例如,将数据分配给寄存器,但是在堆栈上。这意味着当您对变量进行操作时,它们将首先被转移到寄存器然后进行操作。

因为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的事实。顺便说一下,它还意识到它无法在x使用-4(%ebp)的存储空间。最后它可能知道printf做了什么(因为你包含了stdio.h)所以可以分析格式字符串并在编译时进行转换,将printf语句替换为直接写{的调用{1}}标准化。