【发布时间】:2015-06-08 22:32:12
【问题描述】:
我想用 Intel I64 Assembler 做一些长整数数学运算(128 位),并且需要创建一个 2 的补码。假设我的正值在 RDX:RAX 中。
2 的补码是通过“翻转位并加 1”来完成的。所以最天真的实现是(4条指令和14字节代码):
NOT RAX
NOT RDX
ADD RAX,1 ; Can't use INC, it doesn't set Carry
ADC RDX,0
当我在 RAX 上使用 NEG 指令而不是 NOT 时,它为我执行“+1”但进位错误,当 RAX 为零时 NEG RAX 清除进位,但在这种情况下我需要进位。因此,下一个最佳方法可能是(4 条指令和 11 字节代码):
NOT RDX
NEG RAX
CMC
ADC RDX,0 ; fixed, thanks lurker
还有 4 条指令。但是我可以减去 -1 而不是加 +1,因为 SBB 将进位位添加到减数中,所以当进位清除时我会加 +1。所以我的下一个最佳尝试是这个,有 3 条指令和 10 个字节的代码:
NOT RDX
NEG RAX
SBB RDX,-1
从我冗长的文字中可以看出,这并不容易理解。有没有更好、更容易理解的方法在汇编程序中进行级联 2 的补码?
【问题讨论】:
-
您似乎认为“更好”等于“更短的代码”,这不必像 x86-64 那样应用于乱序多标量处理器。我会说你的实现中最不稳定的是第一个,如果它们都需要相同的时间来执行,我不会感到惊讶。
-
顺便说一句:您考虑过使用 XMM 寄存器吗?它们足够宽,可以容纳 128 位数字,并且(我还没有检查过)它们可能有整数指令来处理整数
-
@mcleod_ideafix 他们没有,所以你仍然面临手动携带携带的问题。
-
Thanx@lurker,我修好了。是的,我考虑过 XMM 寄存器。它们适用于不能选择进位传播的整数向量。因此,它们可以让您在未检测到的溢出或整数饱和之间进行选择。不适合我的目的。
-
查看 Agner Fog 的指令表,令人惊讶的是,
CMC在现代微架构上具有低延迟、高吞吐量,因此第二个版本可能具有竞争力。另一方面,考虑依赖链: 1:ADC通过标志依赖于NOT/ADD,依赖于NOT。 2:ADC依赖于NOT/CMC依赖于NEG。 3:SBB依赖于NOT/NEG。我想说你在上一个版本中找到了一个非常聪明的方法。
标签: assembly x86-64 micro-optimization twos-complement