6 optimization x86-64 llvm rust
这个功能:
pub fn g(n: u64) -> u32 {
n.trailing_zeros()
}
Run Code Online (Sandbox Code Playgroud)
生成带有分支的程序集:
playground::g:
testq %rdi, %rdi
je .LBB0_1
bsfq %rdi, %rax
retq
.LBB0_1:
movl $64, %eax
retq
Run Code Online (Sandbox Code Playgroud)
这个替代功能:
playground::g:
testq %rdi, %rdi
je .LBB0_1
bsfq %rdi, %rax
retq
.LBB0_1:
movl $64, %eax
retq
Run Code Online (Sandbox Code Playgroud)
生成没有分支的程序集:
playground::g:
bsfq %rdi, %rcx
xorl %eax, %eax
cmpq $1, %rdi
sbbl %eax, %eax
orl %ecx, %eax
retq
Run Code Online (Sandbox Code Playgroud)
事实证明,仅当返回的常量为 64 时才会创建分支。返回0、 或u32::MAX或任何其他数字会生成无分支程序集。
为什么是这样?只是优化器的一个怪癖还是有原因?
我正在尝试创建高性能、无分支的代码。
使用 Rust 1.65 发布配置文件
trailing_zeros对应于cttzLLVM 内在.
该内在函数恰好在 x86-64 上编译为以下指令:
g: # @g
test rdi, rdi
je .LBB0_1
bsf rax, rdi
ret
.LBB0_1:
mov eax, 64
ret
Run Code Online (Sandbox Code Playgroud)
当输入值为 0 时,该内在函数的输出是整数的位宽。LLVM 能够识别冗余操作并将其删除,这就是为什么u64::BITSor 只是64在您的条件结果中产生与内在函数相同的机器代码。
看来使用任何其他数字都会导致编译器将内部分支识别为死代码,因此将其删除:
e: # @e
xor ecx, ecx
bsf rax, rdi
cmove eax, ecx
ret
Run Code Online (Sandbox Code Playgroud)
相反,会生成单个条件移动。我相信,当涉及某些内在函数时,输出的这种差异只是 LLVM x86-64 汇编器的一个怪癖。
您可以使用 clang 重现与 C 相同的差异。神箭
为此可能值得提出 LLVM 问题,但前提是无分支版本实际上更好。
| 归档时间: |
|
| 查看次数: |
150 次 |
| 最近记录: |