C++ 编译器在eax上来回生成mov

C++ 编译器在eax上来回生成mov,c++,gcc,assembly,x86-64,micro-optimization,C++,Gcc,Assembly,X86 64,Micro Optimization,为什么在idiv之后前后移动eax gcc也有类似的行为,所以这似乎是有意的 具有-O3-march=native的gcc符合规范要求 test1(int, int): # @test1(int, int) cmp edi, esi jl .LBB0_1 mov eax, esi ret .LBB0_1: mov eax, ed

为什么在
idiv
之后前后移动
eax

gcc也有类似的行为,所以这似乎是有意的

具有
-O3-march=native
的gcc符合规范要求

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

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

如果没有
\u内置的\u expect
,clang会生成:

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
虽然寄存器分配在这里仍然很奇怪,但至少有意义:如果执行分支,则
ecx
b
的值将作为返回值转移到
eax
。如果未进行除法,除法结果(在
eax
中)必须传输到
ecx
中,以便与其他情况下的结果位于同一寄存器中


可能是
\uuuu内置的expect
使编译器相信在特殊情况下,分支在编译过程的后期执行,孤立了
.LBB1\u2
标签,并导致它最终从程序集中消失。

idiv esi
是32位操作数大小,所以EAX已经是零扩展来填充RAX。因此,复制到ESI或R8D并返回对EAX中的值没有影响。(而且调用约定不需要对64位进行零扩展或符号扩展;32位类型在32位寄存器中返回,可能的垃圾在32位上限中。)


这看起来纯粹是一个遗漏的优化。(也没有微体系结构性能原因说明这是一件好事。)

我不认为这是有意的。似乎
expect
混淆了两个编译器。@geza我发现两个编译器不太可能有相同的问题。如果删除expect,您可以看到额外移动的来源。有趣的是,如果分支不在代码中,编译器不会发出任何移动。查看我的答案,了解编译器是如何得到这个想法的。有趣的是,可能在某一点上,这些指令之间有一个基本的块边界。Clang的输出将注释放在不是分支目标的基本块边界上,如
#%bb.2:
,但是如果这个假设是正确的,那么必须在这个遗漏的优化被确定之后才能确定。我们在mov指令之间看不到
%bb
注释,因此至少在最终asm输出时,clang知道这不是bb边界。@PeterCordes如果关闭标签筛选,孤立标签实际上就在那里。添加
volatile int i=0
返回b之前
避免了gcc和clang都错过的优化(代价是在红色区域中建立一个存储,所以您实际上不希望这样)。但它不会改变整体功能布局。
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
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