64位数字的Mips减法

Mips subtraction of 64 bit numbers

我被要求在 Mips 中实现 64 位数字的减法,其中数字在 2 的补码 中给出,lower/upper 32 位存储在不同的寄存器。 我的想法:我们先减去较低的部分,然后再减去较高的部分。如果第一个数字的较低部分编号小于第二个数字的较低部分编号,则会出现“有问题”的情况,即考虑一个我们只有 4 位寄存器的小例子

 0110 0011 
-0010 1000 

所以首先是下部

 0000 0011    0000 0011
-0000 1000 = +1111 1000 = 1111 1011

然后是上半部分

 0110 0000    0110 0000
-0010 0000 = +1110 0000 = 0100 0000

然后将两部分相加

 0100 0000
+1111 1011 = 0011 1011

我的 Mips 实现看起来像这样(为简单起见,寄存器 1-8):

// A-B
lw  , 0() // upper part of A
lw  , 4() // lower part of A
lw  , 8() // upper part of B
lw  , 12() // lower part of B

subu , , 
stlu , ,  // if lower part of A < lower part of B we need to add 1111 0000 i.e. 
                // subtract 1 from upper part result
subu , , 
subu , , 

// Now the upper part of the result is stored in $ and the lower part in 

我的想法正确吗?

比较 GCC 和 clang 在 32 位 MIPS 上减去 uint64_t 所做的工作:https://godbolt.org/z/eGY3aWoKq

是的,sltu 从低半部分生成借位输出,加上 3x subu 是正确的指令组合。做低半减法,减去高半和借位。

手动将其转换为加法会比较慢,因此最简单的考虑方法可能是仅从高半部分减去借位,否则分别进行高半部分和低半部分的减法。


在带有 carry/borrow 标志的 ISA 上(如 x86 或 ARM https://godbolt.org/z/sfG5PzsPW),你会做

     subs    r0, r0, r2      # sub low half and set flags
     sbc     r1, r1, r3      # sub-with-carry does high half including borrow

ARM 和 x86 将它们的进位标志设置为彼此相反以进行减法(!借或借),但两者都设计为按进位传播顺序从低到高链接,因此只有 2 条指令。

# clang -O3 -m32 -mregparm=3   first uint64_t passed in EDX:EAX, second in mem
     sub     eax, dword ptr [esp + 4]
     sbb     edx, dword ptr [esp + 8]    # x86 sub-with-borrow

由于 MIPS 没有 FLAGS,您必须手动处理进位/借位传播,但这就是您所做的全部。

当您需要处理借入 时,对于更宽的整数会变得更加不便,因为模拟 sbb 包括进位 output 需要在减去整数操作数后减去借位 后检查环绕。就像模拟 adc