2017-07-18 453 views
-1

我打打闹闹,发现以下为什么使用leal而不是incq?

#include <stdio.h> 

void f(int& x){ 
    x+=1; 
} 

int main(){ 
    int a = 12; 
    f(a); 
    printf("%d\n",a); 
} 

g++ (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4g++ main.cpp -S翻译产生这个组件(只显示相关部分)

_Z1fRi: 
    pushq %rbp 
    movq %rsp, %rbp 
    movq %rdi, -8(%rbp) 
    movq -8(%rbp), %rax 
    movl (%rax), %eax 
    leal 1(%rax), %edx 
    movq -8(%rbp), %rax 
    movl %edx, (%rax) 
    popq %rbp 
    ret 
main: 
    pushq %rbp 
    movq %rsp, %rbp 
    subq $16, %rsp 
    movl $12, -4(%rbp) 
    leaq -4(%rbp), %rax 
    movq %rax, %rdi 
    call _Z1fRi 
    movl -4(%rbp), %eax 
    movl %eax, %esi 
    movl $.LC0, %edi 
    movl $0, %eax 
    call printf 
    movl $0, %eax 
    leave 
    ret 

问:为什么编译器选择使用leal而不是incq?或者我错过了什么?

+2

请看优化代码。如果有差异,请更新您的帖子。 –

+0

在这种情况下,它如何使用'incq'? 'incq'是一元的。编译器需要'rdx = rax + 1'。也许真正的问题是:为什么它选择包含两个寄存器,其中一个就足够了(使用'incq')?这是优化的代码?显然不是。 – AnT

+1

'lea'也是有用的,因为它不会影响任何标志寄存器值,这对于x86 [-64]代码非常有用,在那里有很多指令。这只是一个更好的范例。 inc还介绍了其他问题,例如部分标志寄存器摊位。真正的问题是*为什么*你会在这里使用'inc'而不是'lea'? –

回答

4

您编译没有优化。在建立“调试”模式时,GCC不会做出任何努力来选择特别合适的指令;它只是集中在尽可能快地生成代码(和着眼于使调试easier- 例如,设置在源代码行断点的能力)。

当我能够通过将-O2开关的优化,我得到:

_Z1fRi: 
    addl $1, (%rdi) 
    ret 

随着通用的调教下,addl是首选,因为some Intel processors (specifically Pentium 4, but also possibly Knight's Landing) have a false flags dependency

随着-march=k8incl来代替。

有时a use-case for leal in optimized code,虽然,那就是当你想增加一个寄存器的值,并将结果在不同寄存器。通过这种方式使用leal将允许您保存寄存器的原始值,而不需要额外的指令movl。的leal超过incl/addl另一个优点是,leal不影响标志,其可以是在指令调度是有用的。

+0

有趣的事实:'-O0'代码是可怕的目的,因为它想要让你*修改*变量与调试器,而记忆停在任何断点,仍然有程序执行书面。这就是为什么*在语句(或源代码行?)之后,* everything *总是会溢出到内存中,并在之后重新加载。不幸的是,调试信息格式无法指定变量当前是否在寄存器中。 –

相关问题