如果只需要结果的较低部分,可以使用哪一个2的补整数运算而不对输入中的高位进行零化?在汇编编程中,想要从寄存器的低位数计算某些东西是相当常见的,而这些寄存器不一定要对其他位进行零化。在高级语言(如C)中,您只需将输入转换为较小的大小,并让编译器决定是否需要将每个输入的上位分别为零,或者是否可以在事实发生后将结果的上位切分。这在x86-64(又名AMD 64)中尤为常见,原因有多种。1,其中一些存在于其他“国际审计准则”中。我将使用64位x86作为示例,但目的是询问/讨论2补体和一般的无符号二进制算法,因为所有现代CPU都使用它。..(注意C+和C+不能保证两个补语4,该签名溢出是未定义的行为。)例如,考虑一个可以编译为LEA指令2..(在x8664 SysV(Linux)中)ABI3,前两个函数args在rdi和rsi,随着回归rax. int是32位类型。); 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
retGCC知道,即使是负号整数的加法,也只能从右到左,所以输入的上位不会影响输入的内容。eax..因此,它保存指令字节并使用 lea eax, [rdi + rsi*4 + 3]还有哪些其他操作不依赖于输入的高比特而具有结果的低比特的属性?为什么会起作用?脚注1为什么这经常出现在x86-64x86-64有可变长度的指令,其中额外的前缀字节会改变操作数的大小(从32到64或16),所以在以相同速度执行的指令中,保存字节通常是可能的。它在写入寄存器的低8b或16b(或稍后读取完整寄存器时的失速(Intel预IVB)时也存在错误依赖关系(AMD/P4/Silvermont):出于历史原因,只写入32b子寄存器,其余64b寄存器为零。..几乎所有的算术和逻辑都可以用于一般用途寄存器的低8、16或32位,以及完整的64位。整数向量指令也是非正交的,有些操作对于某些元素大小是不可用的.此外,与x86-32不同,ABI在寄存器中传递函数args,对于窄类型,不要求上层位为零。2Lea:与其他指令一样,默认操作数为利娅是32位,但默认地址大小是64位。操作数大小的前缀字节(0x66或REX.W)可以使输出操作数大小为16或64位。地址大小的前缀字节(0x67)可以将地址大小减小到32位(64位模式)或16位(32位模式)。所以在64位模式下,lea eax, [edx+esi]比lea eax, [rdx+rsi].做这件事是可能的lea rax, [edx+esi],但地址仍仅用32位计算(进位不设置32位)rax)。你得到了相同的结果lea eax, [rdx+rsi],即缩短了两个字节。因此,地址大小前缀对LEA,正如AgnerFog出色的objconv反汇编程序的反汇编输出中的注释所警告的。3x86 ABI*来电者不必须为零(或符号扩展)64位寄存器的上部,用于传递或返回较小类型的值。希望将返回值用作数组索引的调用方必须对其进行签名扩展(使用movzx rax, eax,或特殊情况下的eax指令。cdqe..(不要混淆cdq,哪个符号-扩展eax进edx:eax为idiv.))这意味着返回一个函数。unsigned int中的64位临时值中计算其返回值。rax,而不需要mov eax, eax 使上位为零的rax..此设计决策在大多数情况下运行良好:调用方通常不需要任何额外的指令来忽略上半部分中未定义的位。rax.4C+和C+C+和C+不需要两个补码二进制有符号整数(除C+std::atomic类型). 一个人的补足和符号/大小也是允许的。所以充分便携C,这些技巧只适用于unsigned类型。显然,对于有符号操作,符号/震级表示中的集合符号位意味着其他位被减去,而不是添加,例如。我还没有完成一个人的补语的逻辑然而,钻头黑客那,那个只有两个人的补语是广布因为在实践中没有人关心其他的事情。与两个补码一起工作的许多东西也应该与一个补语一起工作,因为符号位仍然不能改变对其他位的解释:它只是值为-(2)N-1)(而不是2N)。符号/大小表示没有这个属性:每个位的位置值是正的还是负的,取决于符号位。还请注意,C编译器可以假定签名溢出从未发生过因为这是不确定的行为。因此,例如。编译器可以并且确实假定(x+1) < x总是假的..这使得在C中检测符号溢出非常不方便。注意,无符号环绕(进位)和符号溢出之间的区别.
1 回答
- 1 回答
- 0 关注
- 455 浏览
添加回答
举报
0/150
提交
取消