JON*_*234 4 optimization x86 assembly sar disassembly
我已经反汇编了编译器生成的代码,并且看到它生成了以下指令序列:
mov eax, edx
shr eax, 1Fh
add eax, edx
sar eax, 1
Run Code Online (Sandbox Code Playgroud)
该代码的目的是什么?
我知道
sar eax, 1
Run Code Online (Sandbox Code Playgroud)
除以2,但是做什么
shr eax, 1Fh
Run Code Online (Sandbox Code Playgroud)
做?这是否意味着EAX如果左位为0或1,则为0或1?
这对我来说看起来很奇怪!有人可以解释吗?
对您的问题的快速解答(是什么)是shr eax, 1Fh,它可以隔离的最高位eax。如果将十六进制转换1Fh为十进制,可能会更容易理解31。现在,您看到将eax右移31。由于它eax是一个32位值,因此将其右移31将隔离最高位,从而eax根据原始值将包含0或1。 31位(假设我们从0开始对位进行编号)。
这是隔离符号位的常见技巧。在二进制补码机上将值解释为有符号整数时,最高位是符号位。如果值为负,则设置为(== 1),否则为(== 0)。当然,如果将值解释为无符号整数,则最高位只是用于存储其值的另一位,因此最高位具有任意值。
逐行进行反汇编,这是代码的作用:
Run Code Online (Sandbox Code Playgroud)mov eax, edx
显然,输入在中EDX。该指令将值从复制EDX到EAX。这允许后续代码在EAX不丢失原始值(in EDX)的情况下操纵in的值。
Run Code Online (Sandbox Code Playgroud)shr eax, 1Fh
EAX向右移动31位,从而隔离最高位。假设输入值是一个有符号整数,则这将是符号位。EAX现在,如果原始值为负数,则将包含1,否则为0。
Run Code Online (Sandbox Code Playgroud)add eax, edx
在中将原始值(EDX)添加到我们的临时值中EAX。如果原始值为负,则将其加1。否则,它将加0。
Run Code Online (Sandbox Code Playgroud)sar eax, 1
EAX向右移动1位。这里的区别是,这是一个算术右移,而SHR一个逻辑右移。逻辑移位用0填充新暴露的位。算术移位将最高位(符号位)复制到新暴露的位。
综上所述,这是将带符号的整数值除以2以确保负值正确取整的标准习惯用法。
当您将无符号值除以2时,只需要简单的移位即可。从而:
unsigned Foo(unsigned value)
{
return (value / 2);
}
Run Code Online (Sandbox Code Playgroud)
等效于:
shr eax, 1
Run Code Online (Sandbox Code Playgroud)
但是,当除以有符号的值时,必须处理符号位。您可以sar eax, 1用来实现有符号整数除以2,但这将导致结果值四舍五入为负无穷大。注意,这与DIV/ IDIV指令的行为不同,后者总是四舍五入。如果要模拟舍入为零的行为,则需要一些特殊的处理,这正是您所执行的代码所要做的。实际上,当您编译以下函数时,GCC,Clang,MSVC以及可能所有其他编译器都将精确地生成此代码:
int Foo(int value)
{
return (value / 2);
}
Run Code Online (Sandbox Code Playgroud)
这是一个很老的把戏。迈克尔·阿布拉什(Michael Abrash)在 1990年左右出版的《汇编语言禅》中对此进行了讨论。(这是他的在线著作的相关部分。)在此之前,汇编语言专家肯定是常识。