编译器在eax上来回移动

Tyk*_*ker 5 c++ assembly gcc x86-64 micro-optimization

int test1(int a, int b) {
    if (__builtin_expect(a < b, 0))
        return a / b;
    return b;
}
Run Code Online (Sandbox Code Playgroud)

被铛编译-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
Run Code Online (Sandbox Code Playgroud)

为什么eax在之后来回移动idiv

gcc具有类似的行为,因此这似乎是有意的。

gcc -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
Run Code Online (Sandbox Code Playgroud)

哥德宝

fuz*_*fuz 2

这并不是这个难题的完整解决方案,但应该提供一些线索。

如果没有__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
Run Code Online (Sandbox Code Playgroud)

虽然这里的寄存器分配仍然很奇怪,但它至少是有意义的:如果采用分支,则bin的值ecx将被传输eax为返回值。如果不采用,则除法的结果(在 中eax)必须转移到ecx与其他情况相同的寄存器中。

可能是 a__builtin_expect说服编译器处理特殊情况,即在编译过程中较晚采取分支的情况,孤立标签.LBB1_2并导致它最终从程序集中消失。