为什么这段代码生成的汇编比等效的 C++/Clang 多得多?



我写了一个简单的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的优化输出?

使用编译器标志-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(的说明。这主要是获得良好的堆栈跟踪所必需的。如果通过-fno-omit-frame-pointer为C++版本禁用它,则会得到相同的结果。请注意,这使用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

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

在 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的回答以获取更多解释。

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

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

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

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

Godbolt C++ x64 clang 4.0,无编译器选项

Godbolt Rust 1.18,没有编译器选项

相关内容

  • 没有找到相关文章

最新更新