Jos*_*ica 7 c assembly gcc x86-64 clang
考虑这个函数:
unsigned long f(unsigned long x) {
return x / 7;
}
Run Code Online (Sandbox Code Playgroud)
使用-O3,Clang将除法变成乘法,正如预期的那样:
f: # @f
movabs rcx, 2635249153387078803
mov rax, rdi
mul rcx
sub rdi, rdx
shr rdi
lea rax, [rdi + rdx]
shr rax, 2
ret
Run Code Online (Sandbox Code Playgroud)
除了rdx在 Clang 使用rcx. 但他们似乎都在做额外的举动。为什么不是这个呢?
f:
movabs rax, 2635249153387078803
mul rdi
sub rdi, rdx
shr rdi
lea rax, [rdi + rdx]
shr rax, 2
ret
Run Code Online (Sandbox Code Playgroud)
特别是,他们都将分子放在 中rax,但是通过将幻数放在那里,您根本不必移动分子。如果这实际上更好,我很惊讶 GCC 和 Clang 都没有这样做,因为这感觉很明显。他们的方式实际上比我的方式快吗?
这看起来很像 gcc 和 clang 都错过了优化;额外的动作没有任何好处。
如果尚未报告,GCC 和 LLVM 都接受错过优化的错误报告:https://bugs.llvm.org/和https://gcc.gnu.org/bugzilla/。对于 GCC 甚至有一个错误标签“missed-optimization”。
不幸的是,浪费的mov指令并不罕见,特别是在查看微小函数时,其中输入/输出寄存器是由调用约定确定的,而不是由寄存器分配器决定的。有时,确实仍然会在循环中发生,例如每次迭代都会做一些额外的工作,因此所有内容都位于循环后运行一次的代码的正确位置。/捂脸。
零延迟mov(mov-elimination)有助于降低此类错过优化的成本(以及mov无法避免的情况),但它仍然需要前端微指令,因此情况要糟糕得多。(除非偶然它有助于稍后调整某些内容,但如果这是原因,那么 anop也一样好)。
而且它会占用 ROB 中的空间,从而减少了乱序执行程序在缓存未命中或其他停顿之后可以看到的时间。 mov从来都不是真正免费的,只是消除了执行单元和延迟部分 - x86 的 MOV 真的可以“免费”吗?为什么我根本无法重现这个?
我对编译器内部结构的总体猜测:
可能 gcc/clang 的内部机制需要了解这种除法模式是可交换的,并且可以在其他寄存器中获取输入值并将常量放入 RAX 中。
在循环中,他们希望常量位于其他寄存器中,以便可以重用它,但希望编译器仍然可以在有用的情况下弄清楚这一点。
| 归档时间: |
|
| 查看次数: |
177 次 |
| 最近记录: |