16

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

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

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

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

我使用Godbolt检查汇编器输出。

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

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

4 回答 4

41

使用编译器标志-O并添加pub)编译,我得到这个输出(链接到 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
    

    这些是管理所谓的帧指针或指针 ( rbp) 的指令。这主要是获得良好的堆栈跟踪所必需的。如果您通过 为 C++ 版本禁用它-fno-omit-frame-pointer您将得到相同的结果。请注意,这使用g++而不是clang++因为我还没有找到可比的 clang 编译器选项

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

    事实上,确实如此。但是 Godbolt 向编译器添加了一个选项来保留帧指针。你可以在这里阅读更多关于为什么这样做的信息。如果你在本地编译你的代码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++ 版本的输出。

    您可以通过传递-C debuginfo=0给编译器来“撤消” Godbolt 所做的事情。

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

    Godboltrustc直接使用而不是cargo. 该--release标志是一个标志cargo。要启用对 的优化rustc,您需要传递-O-C opt-level=3(或 0 到 3 之间的任何其他级别)。

于 2017-08-08T07:50:55.970 回答
8

-C opt-level=3在 Godbolt 中编译给出:

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

它看起来与 C++ 版本相当。有关更多解释,请参阅Lukas Kalbertodt 的回答

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

于 2017-08-08T07:47:53.437 回答
4

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

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

于 2017-08-08T08:05:59.610 回答
2

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

godbolt C++ x64 clang 4.0,没有编译器选项

godbolt Rust 1.18,没有编译器选项

于 2017-08-08T10:38:49.217 回答