int test1(int a, int b) {
if (__builtin_expect(a < b, 0))
return a / b;
return b;
}
由clang用-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具有类似的行为,因此这似乎是有意的。
带有-O3 -march=native
的gcc已将代码编译为
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
答案 0 :(得分:2)
这不是解决难题的完整方法,但应提供一些线索。
在没有file = open(testfile,'r')
的情况下,铛会生成:
__builtin_expect
尽管这里的寄存器分配仍然很奇怪,但这至少是有道理的:如果采用分支,则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
的值将作为返回值传输到ecx
。如果不采用,则除法结果(在eax
中)必须转移到eax
中,以与其他情况相同。
可能是ecx
使编译器处于特殊情况,这种情况是分支在编译过程中较晚进行,从而使__builtin_expect
标签变成孤立的并导致其最终不在汇编中
答案 1 :(得分:1)
idiv esi
是32位操作数大小,因此EAX已经零扩展以填充RAX。因此,复制并复制到ESI或R8D对EAX中的值没有影响。 (而且调用约定无论如何都不需要对64位进行零扩展或符号扩展;在32位寄存器中返回32位类型,而在高32位中可能存在垃圾。)
这纯粹是错过的优化。 (没有微体系结构性能的原因,这也不是一件好事。)