符合者在EAX上来回产生一个移动
complier generating a mov back and forth on eax
int test1(int a, int b) {
if (__builtin_expect(a < b, 0))
return a / b;
return b;
}
由-O3 -march=native
到
test1(int, int): # @test1(int, int)
cmp edi, esi
jl .LBB0_1
mov eax, esi
ret
.LBB0_1:
mov eax, edi
cdq
idiv esi
mov esi, eax
mov eax, esi # moving eax back and forth
ret
为什么eax
在idiv
之后来回移动?
GCC具有相似的行为,因此似乎是打算的。
gcc with -O3 -march=native
将代码符合到
test1(int, int):
mov r8d, esi
cmp edi, esi
jl .L4
mov eax, r8d
ret
.L4:
mov eax, edi
cdq
idiv esi
mov r8d, eax
mov eax, r8d #back and forth mov
ret
Godbolt
这不是拼图的完整解决方案,而应提供一些线索。
没有 __builtin_expect
,clang生成:
test2(int, int): # @test2(int, int)
mov ecx, esi
cmp edi, esi
jge .LBB1_2
mov eax, edi
cdq
idiv ecx
mov ecx, eax
.LBB1_2:
mov eax, ecx
ret
虽然在这里寄存器分配仍然很奇怪,但这至少是有道理的:如果采用分支,则将b
中的CC_6值传输到eax
作为返回值。如果不采取任何措施,则必须将划分的结果(在eax
中)转移到ecx
,以与其他情况相同的寄存器。
可能是__builtin_expect
说服编译器对特殊情况下分支在编译蛋白过程后期进行,孤立.LBB1_2
标签并最终导致组装中缺少该分支的情况。
idiv esi
是32位操作数尺寸,因此EAX已经将零扩展以填充RAX。因此,复制到ESI或R8D和背部对EAX中的值没有影响。(无论如何,调用约定不需要零扩展或签名扩展到64位;在32位寄存器中返回32位类型,上部32中可能垃圾。)
这看起来纯粹是错过的优化。(也没有微体系绩效的原因,这也是一件好事。)
相关文章:
- 如果需要转换,我可以在读取参数的同时将其移动到另一个参数吗?
- 如何知道是否移动一个物体
- 当在SFML C 中移动一个圆圈时,球后面会有口吃的束缚小故障
- 从另一个函数的"if loop"中调用/移动一个函数 - Qt,C++
- 我应该移动一个不再使用的值吗?
- 移动一个结构数组C++
- 在OpenGL中移动一个简单的形状(形状在数据结构中)
- 如果我移动一个值进行注册和编辑,它将有所作为
- 在保龄球比赛中,一旦按下一个键,就移动一个球
- 想要移动一个2d对象而不是另一个
- 关于移动一个const对象
- 在现代opengl中移动一个形状/对象
- 移动一个矩阵
- 沿着二维网格移动一个值
- 为什么编译器需要一个复制构造函数,需要并移动一个并且不使用它们中的任何一个?
- 如何从数组中删除一个元素并将所有元素向上移动一个位置
- 我有一个字符数组,我想把它的二进制位向右移动一个
- 在c++ 11中从std::deque中移动一个元素
- 如何在c++中操纵/移动一个网格中的对象
- 如何在不移动整个场景的情况下移动一个2d对象?