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
我被要求在 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