Assembly 哪个2';如果只需要结果的低位,则可以使用s补码整数运算,而无需将输入中的高位置零?
在汇编编程中,很常见的一种情况是,需要从寄存器的低位计算某些东西,而这些低位不能保证将其他位归零。在像C这样的高级语言中,您只需将输入转换为较小的大小,然后让编译器决定是否需要将每个输入的高位分别归零,或者是否可以在事后切掉结果的高位 这在x86-64(又名AMD64)中尤其常见,原因有多种1,其中一些存在于其他ISA中 我将使用64位x86作为示例,但目的是询问/讨论一般的无符号二进制算法,因为。(注意C和C++不保证两个互补,而有符号的溢出是未定义的行为。)< /P>Assembly 哪个2';如果只需要结果的低位,则可以使用s补码整数运算,而无需将输入中的高位置零?,assembly,binary,x86,integer,twos-complement,Assembly,Binary,X86,Integer,Twos Complement,在汇编编程中,很常见的一种情况是,需要从寄存器的低位计算某些东西,而这些低位不能保证将其他位归零。在像C这样的高级语言中,您只需将输入转换为较小的大小,然后让编译器决定是否需要将每个输入的高位分别归零,或者是否可以在事后切掉结果的高位 这在x86-64(又名AMD64)中尤其常见,原因有多种1,其中一些存在于其他ISA中 我将使用64位x86作为示例,但目的是询问/讨论一般的无符号二进制算法,因为。(注意C和C++不保证两个互补,而有符号的溢出是未定义的行为。)< /P> 作为一个例子,考虑一个
作为一个例子,考虑一个简单的函数,可以编译到<代码> LEA<代码>指令2中。(在x86-64 SysV(Linux)3中,前两个函数参数位于
rdi
和rsi
中,在rax
中返回的int
是32位类型。)
gcc知道,即使是负符号整数的加法,也只能从右向左进行,因此输入的高位不会影响进入eax
的内容。因此,leaeax,[rdi+rsi*4+3]
还有哪些操作具有不依赖于输入高位的结果低位特性?
为什么它会起作用
脚注 1为什么经常出现这种情况: x86-64具有可变长度指令,其中额外的前缀字节会更改操作数大小(从32更改为64或16),因此在以相同速度执行的指令中通常可以保存字节。在写入寄存器的低8b或16b时(或稍后读取完整寄存器(Intel pre IvB)时暂停),它也有错误的依赖项(AMD/P4/Silvermont):出于历史原因。几乎所有的算术和逻辑都可以在通用寄存器的低位8、16或32位以及全64位上使用。整数向量指令也是相当非正交的,有些操作对于某些元素大小不可用 此外,与x86-32不同,ABI在寄存器中传递函数参数,对于窄类型,高位不需要为零 2 LEA:与其他指令一样,的默认操作数大小为32位,但默认地址大小为64位。操作数大小前缀字节(
0x66
或REX.W
)可以使输出操作数大小为16或64位。地址大小前缀字节(0x67
)可以将地址大小减少到32位(64位模式)或16位(32位模式)。因此,在64位模式下,leaeax[edx+esi]
比leaeax[rdx+rsi]
多占用一个字节
可以执行lea-rax、[edx+esi]
,但地址仍然只计算32位(进位不设置rax
的32位)。使用leaeax、[rdx+rsi]
可以获得相同的结果,即缩短两个字节。因此,地址大小前缀在LEA
中永远不会有用,Agner Fog优秀的objconv反汇编程序反汇编输出中的注释警告了这一点
3 x86 ABI:
调用方不必将用于按值传递或返回较小类型的64位寄存器的上部归零(或符号扩展)。想要将返回值用作数组索引的调用方必须对其进行签名扩展(使用movzx-rax、eax
,或eax指令的特例cdqe
(不要与cdq
混淆,后者将eax
扩展为edx:eax
,例如设置idiv
)
这意味着返回unsigned int
的函数可以在rax
中以64位临时形式计算其返回值,并且不需要rax
的mov eax、eax
。这种设计决策在大多数情况下都很有效:调用方通常不需要任何额外的指令来忽略rax
上半部分中的未定义位
4 C和C++
C和C++特别不需要两个补二进制符号整数(除)。因此,对于完全可移植的C,这些技巧只对
无符号类型有用。显然,对于有符号操作,例如,在符号/幅度表示中设置符号位意味着减去其他位,而不是相加。我还没有研究过补语的逻辑
然而,这是因为实际上没有人关心其他任何事情。许多使用二的补码的东西也应该使用一的补码,因为符号位仍然不会改变其他位的解释:它只是有一个值-(2N-1)(而不是2N)。符号/幅值表示不具有此属性:根据符号位的不同,每个位的位置值为正或负
还请注意,C编译器可以假设签名溢出从未发生,因为它是未定义的行为。比如说。这使得在可与高位垃圾一起使用的C..范围的操作中检测有符号溢出非常不方便:
- 位逻辑
- 左移(包括
[reg1+reg2*scale+disp]
中的*scale
)
- 加法/减法(因此
LEA
指令:不需要地址大小前缀。如果需要,只需使用所需的操作数大小进行截断即可。)
- 乘法的下半部分。e、 g.16b x 16b->16b可以用32b x 32b->32b完成。您可以使用32位的imul r32、r/m32、imm32,然后只读取结果的低位16。(但是,如果使用
m32
版本,请小心使用更宽的内存引用。)
正如Intel的insn ref手册所指出的,2和3操作数形式
; int intfunc(int a, int b) { return a + b*4 + 3; }
intfunc:
lea eax, [edi + esi*4 + 3] ; the obvious choice, but gcc can do better
ret
0x801F
-0x9123
-------
0xeefc
*Warning*: This diagram is probably slightly bogus.
ABCD A has a place value of -2^3 = -8
* abcd a has a place value of -2^3 = -8
------
RRRRrrrr
AAAAABCD * d sign-extended partial products
+ AAAABCD * c
+ AAABCD * b
- AABCD * a (a * A = +2^6, since the negatives cancel)
----------
D*d
^
C*d+D*c