Assembly 为什么g++;x64/i86,无需优化,以这种方式编译模数

Assembly 为什么g++;x64/i86,无需优化,以这种方式编译模数,assembly,g++,64-bit,x86-64,Assembly,G++,64 Bit,X86 64,以下代码是由g++5.4.0编译的x64。左边是编译后的输出。右边的东西是我期望的样子。当然,右边的stuf在语法上可能不正确。该代码基本上应该执行以下操作: 如果(i%3==0) 做事 400512将把u带到if块后的空间 mov -0x4(%rbp),%ecx mov -0x4(%rbp), %eax mov $0x55555556,%edx idvq $0x3 mov %ecx,%eax

以下代码是由g++5.4.0编译的x64。左边是编译后的输出。右边的东西是我期望的样子。当然,右边的stuf在语法上可能不正确。该代码基本上应该执行以下操作:

如果(i%3==0) 做事

400512将把u带到if块后的空间

mov    -0x4(%rbp),%ecx              mov -0x4(%rbp), %eax
mov    $0x55555556,%edx             idvq $0x3
mov    %ecx,%eax                    cmp %edx, 0x0
imul   %edx                         jnz 400512 <main+0x3c>
mov    %ecx,%eax
sar    $0x1f,%eax
sub    %eax,%edx
mov    %edx,%eax
mov    %eax,%edx
add    %edx,%edx
add    %eax,%edx
mov    %ecx,%eax
sub    %edx,%eax
test   %eax,%eax
jne    400512 <main+0x3c>
mov-0x4(%rbp),%ecx mov-0x4(%rbp),%eax
mov$0x555556,%edx idvq$0x3
mov%ecx,%eax cmp%edx,0x0
imul%edx jnz 400512
mov%ecx,%eax
sar$0x1f,%eax
子%eax,%edx
移动%edx,%eax
mov%eax,%edx
添加%edx,%edx
添加%eax,%edx
mov%ecx,%eax
低于%edx,%eax
测试%eax,%eax
jne 400512

对于那些比我聪明得多的人,我的问题是:为什么g++有这么多计算模数的东西,有人能给我解释一下它在做什么。

出于好奇,Ross Ridge的注释修改为
unsigned int
生成了更短的代码:

bar(unsigned int):
    mov     eax, edi
    mov     edx, -1431655765
    mul     edx
    shr     edx
    lea     eax, [rdx+rdx*2]
    cmp     edi, eax
    je      .L4
    rep ret
.L4:
    jmp     foo()

但是回到
int
版本。。。它很整洁。。。对于一个编译器,但它仍然感觉有些

等等,它是否对3进行了正确的除法,然后将其相乘,然后检查是否计算了相等的数字?六羟甲基三聚氰胺六甲醚。。。但是我们只需要
mod 3==0
测试,对吗

那么这也应该做到:

bar(int):
    mov     eax, edi
    mov     edx, 1431655766
    imul    edx
; don't +1 of div result for negative edi
;    mov     eax, edi  
;    sar     eax, 31
;    sub     edx, eax
; instead do +3 for all results
    lea     eax, [rdx+rdx*2+3]
    sub     eax, edi   ; eax = 0, 1, 2 or 3 (!)
    jpe     .L4        ; so parity-even condition will resolve it (!)
    rep ret
.L4:
    jmp     foo()

不幸的是,这只是“mod 3”优化的特例(我不确定这是否真的改善了任何东西)但我还是忍不住要“高尔夫”一下。

出于好奇,罗斯·里奇为
unsigned int
修改的评论产生了更短的代码:

bar(unsigned int):
    mov     eax, edi
    mov     edx, -1431655765
    mul     edx
    shr     edx
    lea     eax, [rdx+rdx*2]
    cmp     edi, eax
    je      .L4
    rep ret
.L4:
    jmp     foo()

但是回到
int
版本。。。它很整洁。。。对于一个编译器,但它仍然感觉有些

等等,它是否对3进行了正确的除法,然后将其相乘,然后检查是否计算了相等的数字?六羟甲基三聚氰胺六甲醚。。。但是我们只需要
mod 3==0
测试,对吗

那么这也应该做到:

bar(int):
    mov     eax, edi
    mov     edx, 1431655766
    imul    edx
; don't +1 of div result for negative edi
;    mov     eax, edi  
;    sar     eax, 31
;    sub     edx, eax
; instead do +3 for all results
    lea     eax, [rdx+rdx*2+3]
    sub     eax, edi   ; eax = 0, 1, 2 or 3 (!)
    jpe     .L4        ; so parity-even condition will resolve it (!)
    rep ret
.L4:
    jmp     foo()


不幸的是,这只是“mod 3”优化的特例(我不确定这是否真的改善了任何东西)但我还是忍不住要“高尔夫”一下。

我可能已经找到了答案,risc操作大约需要1/2个周期。看起来idvq可能需要10个左右,但仍在调查它是针对速度而不是尺寸优化的;)分裂是缓慢的。参考ah-thx,我当然会看一看,当然你可以使用计数器,得到更好的代码;)gcc at
-O0
仍然使用其模块化乘法逆生成器。位于
-O0
的其他一些编译器将发出朴素的
idiv
(没错,有一个语法错误:idiv没有立即形式。但有相同的区别)。请参阅:gcc
-O0
并不意味着没有优化。64位IDIV非常慢。请看,我可能已经找到了risc操作大约需要1/2个周期的答案。看起来idvq可能需要10个左右的周期,仍然在研究它是针对速度而不是大小进行优化的;)分裂是缓慢的。参考ah-thx,我当然会看一看,当然你可以使用计数器,得到更好的代码;)gcc at
-O0
仍然使用其模块化乘法逆生成器。位于
-O0
的其他一些编译器将发出朴素的
idiv
(没错,有一个语法错误:idiv没有立即形式。但有相同的区别)。请参阅:gcc
-O0
并不意味着没有优化。64位IDIV非常慢。请看,您的第一个代码片段仍然适用于
int
。使用
unsigned int
参数,它将
imul
替换为
mul
,并省略
sub
。也许你复制并粘贴了错误的超负荷?@CodyGray是的,谢谢你,我在编辑这篇文章的过程中有点困惑,混淆了两条不同的思路。。。现在应该有意义了。现在我想知道,为什么我要麻烦
sub
cmp
也会更新PF,对吗?是的,
cmp
会以与
sub
完全相同的方式更新标志。但无论如何,您在这里并没有执行
cmp
,因此将
sub
替换为
cmp
并不能提高性能。唯一的优势是如果需要保留目标寄存器。(实际上,这里不能使用
cmp
,因为需要将结果放在
eax
中才能返回!)您的第一个代码段仍然是
int
。使用
unsigned int
参数,它将
imul
替换为
mul
,并省略
sub
。也许你复制并粘贴了错误的超负荷?@CodyGray是的,谢谢你,我在编辑这篇文章的过程中有点困惑,混淆了两条不同的思路。。。现在应该有意义了。现在我想知道,为什么我要麻烦
sub
cmp
也会更新PF,对吗?是的,
cmp
会以与
sub
完全相同的方式更新标志。但无论如何,您在这里并没有执行
cmp
,因此将
sub
替换为
cmp
并不能提高性能。唯一的优势是如果需要保留目标寄存器。(实际上,您不能在这里使用
cmp
,因为您需要将结果放在
eax
中才能返回它!)