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

jas*_*mar 15 optimization rust llvm-codegen

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

bool f1(bool a, bool b) {
    return !a || (a && b);
}
Run Code Online (Sandbox Code Playgroud)

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

fn f1(a: bool, b: bool) -> bool {
    !a || (a && b)
}
Run Code Online (Sandbox Code Playgroud)

我用godbolt检查汇编器输出.

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

f1(bool, bool):                                # @f1(bool, bool)
    xor     dil, 1
    or      dil, sil
    mov     eax, edi
    ret
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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

我故意不使用任何库来保持输出清洁.请注意两者clangrustc使用LLVM作为后端.什么解释了这个巨大的输出差异 如果它只是禁用 - 优化 - 切换问题,我怎样才能看到优化的输出rustc

Luk*_*odt 40

使用编译器标志进行编译-O(并添加pub),我得到此输出(链接到Godbolt):

push    rbp
mov     rbp, rsp
xor     dil, 1
or      dil, sil
mov     eax, edi
pop     rbp
ret
Run Code Online (Sandbox Code Playgroud)

一些东西:

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

    Rust版本正好是三个指令:

    push    rbp
    mov     rbp, rsp
    [...]
    pop     rbp
    
    Run Code Online (Sandbox Code Playgroud)

    这些是管理所谓的帧指针或指针(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
    
    Run Code Online (Sandbox Code Playgroud)

    正是您的C++版本的输出.

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

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

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


Scr*_*og1 8

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

example::f1:
  push rbp
  mov rbp, rsp
  xor dil, 1
  or dil, sil
  mov eax, edi
  pop rbp
  ret
Run Code Online (Sandbox Code Playgroud)

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

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