编译器在eax上来回移动

时间:2019-03-14 15:46:55

标签: c++ gcc assembly x86-64 micro-optimization

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

godbolt

2 个答案:

答案 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位中可能存在垃圾。)

这纯粹是错过的优化。 (没有微体系结构性能的原因,这也不是一件好事。)