为什么这段代码比同等的C ++ / Clang生成更多的程序集?

时间:2017-08-08 07:40:22

标签: optimization rust llvm

我编写了一个简单的C ++函数来检查编译器优化:

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

之后我检查了Rust中的等价物:

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

我使用godbolt检查汇编程序输出。

C ++代码的结果(由clang -O3标志编译)如下:

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

Rust等效的结果要长得多:

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的优化输出?

4 个答案:

答案 0 :(得分:38)

使用编译器标志-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 ++版本更长?

    Rust版本正好有三个指令:

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

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

  • 为什么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 ++版本的输出。

    你可以"撤消" Godbolt通过passing -C debuginfo=0 to the compiler做了什么。

  • 为什么-O代替--release

    Godbolt直接使用rustc代替cargo--release标志是cargo的标志。要在rustc上启用优化,您需要传递-O-C opt-level=3(或0到3之间的任何其他级别)。

答案 1 :(得分:8)

使用godbolt中的http://steamcommunity.com/my/badges进行编译:

-C opt-level=3

看起来与C ++版本相当。有关详细说明,请参阅Lukas Kalbertodt's answer

注意:我必须使函数example::f1: push rbp mov rbp, rsp xor dil, 1 or dil, sil mov eax, edi pop rbp ret 停止编译器将其优化为空,因为它未被使用。

答案 2 :(得分:4)

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

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

答案 3 :(得分:2)

它没有(实际差异比问题中显示的要小得多)。我很惊讶没有人检查过C ++输出:

godbolt C++ x64 clang 4.0, no compiler options

godbolt Rust 1.18, no compiler options