2017-08-08 72 views
14

我写了一个简单的C++功能,以检查的编译器优化:这段代码为什么会产生比等效的C++/Clang更多的程序集?

bool f1(bool a, bool b) { 
    return !a || (a && b); 
} 

之后,我在签锈相当于:

fn f1(a: bool, b: bool) -> bool { 
    !a || (a && b) 
} 

我以前godbolt检查汇编输出。

C++代码(通过与-O3标志铛编译)的结果如下:

f1(bool, bool):        # @f1(bool, bool) 
    xor  dil, 1 
    or  dil, sil 
    mov  eax, edi 
    ret 

和防锈等效的结果为更长:

example::f1: 
    push rbp 
    mov rbp, rsp 
    mov al, sil 
    mov cl, dil 
    mov dl, cl 
    xor dl, -1 
    test dl, 1 
    mov byte ptr [rbp - 3], al 
    mov byte ptr [rbp - 4], cl 
    jne .LBB0_1 
    jmp .LBB0_3 
.LBB0_1: 
    mov byte ptr [rbp - 2], 1 
    jmp .LBB0_4 
.LBB0_2: 
    mov byte ptr [rbp - 2], 0 
    jmp .LBB0_4 
.LBB0_3: 
    mov al, byte ptr [rbp - 4] 
    test al, 1 
    jne .LBB0_7 
    jmp .LBB0_6 
.LBB0_4: 
    mov al, byte ptr [rbp - 2] 
    and al, 1 
    movzx eax, al 
    pop rbp 
    ret 
.LBB0_5: 
    mov byte ptr [rbp - 1], 1 
    jmp .LBB0_8 
.LBB0_6: 
    mov byte ptr [rbp - 1], 0 
    jmp .LBB0_8 
.LBB0_7: 
    mov al, byte ptr [rbp - 3] 
    test al, 1 
    jne .LBB0_5 
    jmp .LBB0_6 
.LBB0_8: 
    test byte ptr [rbp - 1], 1 
    jne .LBB0_1 
    jmp .LBB0_2 

我也试图与-O选项,但输出为空(删除未使用的功能)。

我故意不使用任何库来保持输出清洁。请注意,clangrustc都使用LLVM作为后端。什么解释这种巨大的产出差异?如果它只是禁用 - 优化开关问题,我怎么能看到来自rustc的优化输出?

+10

你用'-O'(在释放模式)编译? – Boiethios

+0

@Boiethios是的,但它删除了整个代码(未使用)。使函数'酒吧extern'工作:) – jaskmar

回答

36

与编译器标志-Oand with an added pub)编译,我得到这个输出(Link to Godbolt):

push rbp 
mov  rbp, rsp 
xor  dil, 1 
or  dil, sil 
mov  eax, edi 
pop  rbp 
ret 

有几件事情:

  • 为什么比C更长++版本?

    锈病版本是正好三个指令更长:

    push rbp 
    mov  rbp, rsp 
    [...] 
    pop  rbp 
    

    这些管理所谓的帧指针或指针(rbp)的指令。这主要是为了获得很好的堆栈跟踪。如果您通过-fno-omit-frame-pointer,you get the same result禁用它的C++版本。请注意,这使用g++而不是clang++,因为我是haven't found a comparable option for the clang compiler

  • Rust为什么不省略帧指针?

    其实,它的确如此。但Godbolt向编译器添加了一个选项来保留帧指针。你可以阅读更多关于为什么这样做here。如果您有rustc -O --crate-type=lib foo.rs --emit asm -C "llvm-args=-x86-asm-syntax=intel"本地编译代码,你会得到这样的输出:

    f1: 
        xor dil, 1 
        or dil, sil 
        mov eax, edi 
        ret 
    

    这是正是你的C++版本的输出。

    您可以通过passing -C debuginfo=0 to the compiler“撤销”Godbolt的功能。

  • 为什么-O而不是--release

    Godbolt直接使用rustc而不是cargo--release标志是cargo的标志。要启用rustc上的优化,您需要通过-O-C opt-level=3(或0和3之间的任何其他级别)。

+0

请强调一下这个事实,功能签名。没有这个,Godbolt会产生空的产出。 – jaskmar

+1

@jaskmar这里有一个[一个专门的问题](https://stackoverflow.com/questions/45563133/why-doesnt-the-godbolt-compiler-explorer-show-any-output-for-my-function-when -C)。 –

+0

我的意思是这篇文章的未来读者的信息,以防止混淆。谢谢! – jaskmar

7

在godbolt与-C opt-level=3编译给出:

example::f1: 
    push rbp 
    mov rbp, rsp 
    xor dil, 1 
    or dil, sil 
    mov eax, edi 
    pop rbp 
    ret 

它看起来堪比C++版本。有关更多说明,请参阅Lukas Kalbertodt's answer

注:我必须使函数pub extern停止编译器优化它为空,因为它是未使用的。

+1

哦,所以'-C'是你如何通过'选择级别'。我尝试了'-O',但是完全删除了'f1'。 –

+1

看起来好像还有一个没有意义的堆栈框架 – 6502

+1

简单的'-O'而不是'opt-level'也可以,但是只有当你使用函数'pub extern' :) – jaskmar

3

要获得相同的asm代码,您需要禁用调试信息 - 这将删除帧指针推送。

-C opt-level=3 -C debuginfo=0https://godbolt.org/g/vdhB2f

相关问题