我正在将一些汇编代码转换为C代码,但是在以下代码上遇到了麻烦:
mov $0x51eb851f,%edx
mov %ecx,%eax
imul %edx
sar $0x5,%edx
mov %edx,%edi
mov %ecx,%eax
sar $0x1f,%eax
sub %eax,%edi
imul $0x64,%edi,%eax
sub %eax,%ecx
%ecx
存储我们的参数,它是一个int类型(我们可以称其为x)。我知道前三个步骤实际上是在执行x / 100
。但是我在以下步骤中感到困惑。
一些帮助,将不胜感激。
x86的gcc 9.3.1 gcc -O3 -S -m32
转换以下C代码:
int foo(int x)
{
return x%100;
}
进入以下程序集:
foo:
movl 4(%esp), %ecx
movl $1374389535, %edx
movl %ecx, %eax
imull %edx
movl %edx, %eax
movl %ecx, %edx
sarl $5, %eax
sarl $31, %edx
subl %edx, %eax
imull $100, %eax, %eax
subl %eax, %ecx
movl %ecx, %eax
ret
实际上是相同的,除了一些小的重新排序和对整个功能符合x86 ABI的要求。
是的,前三个步骤在做[[x / 100 –或多或少地乘以1/100-but除法直到sub %eax, %edi
之后才完成。] >所以,要回答关于前三个步骤的问题,下面是带注释的代码片段:
mov $0x51eb851f,%edx # magic multiplier for signed divide by 100
mov %ecx,%eax # %ecx = x
imul %edx # first step of division signed x / 100
sar $0x5,%edx # q = %edx:%eax / 2^(32+5)
mov %edx,%edi # %edi = q (so far)
mov %ecx,%eax
sar $0x1f,%eax # %eax = (x < 0) ? -1 : 0
sub %eax,%edi # %edi = x / 100 -- finally
imul $0x64,%edi,%eax # %eax = q * 100
sub %eax,%ecx # %ecx = x - ((x / 100) * 100)
注意:
2 ^(32 + n)
放大(对于32位除法)。在这种情况下,n = 5。乘法的总结果为%edx:%eax
,而丢弃%eax
的结果除以2 ^ 32。 sar $05, %edx
除以2 ^ n-由于这是有符号除法,因此需要进行算术移位。%edx
并不是商。如果被除数为-ve(并且除数为+ ve),则需要加1
以获得商。因此,如果x <0
sar $0x1f, %eax
则为-1,否则为0。 sub %eax, %edi
完成除法。此步骤同样可以通过shr $0x1f, %eax
和add %eax, %edi
实现。或add %eax, %eax
和adc $0, %edi
。或cmp $0x80000000, %ecx
和sbb $-1, %edi
-这是我的最爱,但是不幸的是,保存mov %ecx, %eax
如今并没有节省任何费用,无论如何,cmp $0x80000000, %ecx
是一条很长的指令:-(q
%edi
-如果将其留在%edx
中,在imul $0x64,%edx,%eax
之后仍会存在。