Bee*_*ope 12 c++ optimization performance x86-64 clang
考虑以下几乎叶函数:
int almost_leaf(int* x) {
if (__builtin_expect(*x >= 0, true)) {
return *x;
}
return x_was_negative() + 1;
}
Run Code Online (Sandbox Code Playgroud)
它几乎是叶子,因为它不是严格意义上的叶子函数(它可能调用x_was_negativeis x 为负数,但__builtin_expect提示编译器return *x通常采用分支,这不涉及任何调用。
clang-16 像这样编译它:
almost_leaf(int*): # @almost_leaf(int*)
push rax
mov eax, dword ptr [rdi]
test eax, eax
js .LBB0_1
pop rcx
ret
.LBB0_1:
call x_was_negative()
inc eax
pop rcx
ret
Run Code Online (Sandbox Code Playgroud)
快速(预期)路径上的and (直到第一个的部分)在这里push是完全不必要的:堆栈未使用,并且不会进行需要“由于 ABI”而对齐的堆栈的调用。popret
最好将堆栈对齐到x_was_negative()调用的慢速路径上,就像 gcc 那样:
almost_leaf(int*):
mov eax, DWORD PTR [rdi]
test eax, eax
js .L8
ret
.L8:
sub rsp, 8
call x_was_negative()
add rsp, 8
inc eax
ret
Run Code Online (Sandbox Code Playgroud)
可以说服 clang 有效地编译这个几乎是叶子的函数吗?
请注意,clang可以在不对齐堆栈的情况下编译几乎叶函数:例如, ifx是 anint而不是int*它可以工作,并且 ifx_was_negative可以编译为 tailcall 它也可以工作(但很简单,因为在这种情况下根本不需要对齐)。
Jér*_*ard 10
此优化现在在 Clang (trunk) 的开发版本上执行,因此应该在下一个版本(当然是 Clang 17.0)中可用。这可以在Godbolt上看到。这是新生成的代码:
almost_leaf(int*):
mov eax, dword ptr [rdi]
test eax, eax
js .LBB0_1
ret
.LBB0_1:
push rax
call x_was_negative()@PLT
inc eax
add rsp, 8
ret
Run Code Online (Sandbox Code Playgroud)
正如我们所看到的,快速路径是相同的。
请注意,对于两个版本的 Clang,初始未优化的 LLVM-IR 大致相同,但最后的低级优化步骤会导致此代码的结果略有不同。更具体地说,对所执行的 LLVM-IR 优化步骤的深入分析表明,Clang 16 在“Prologue/Epilogue Insertion & Frame Finalization”优化步骤(又名“prologepilog”)期间错过了优化。-O1即使在Clang 的开发版本中也进行了这种优化。这个优化步骤可以在Godbolt上看到。
| 归档时间: |
|
| 查看次数: |
179 次 |
| 最近记录: |