Reputation: 109
I'm asked to implement subtraction for 64 bit numbers in Mips, whereby the numbers are given in 2's complement, and the lower/upper 32 bits are stored in different registers. My thoughts: We first subtract the lower parts and then the upper ones. A "problematic" situation occurs if the lower part number of the first number is smaller than that of the second number, i.e. considering a small example where we only have 4 bit registers
0110 0011
-0010 1000
so first the lower part
0000 0011 0000 0011
-0000 1000 = +1111 1000 = 1111 1011
and then the upper part
0110 0000 0110 0000
-0010 0000 = +1110 0000 = 0100 0000
and then adding the two parts together
0100 0000
+1111 1011 = 0011 1011
My Mips implementation would then look something like this (registers 1-8 for simplicity):
// A-B
lw $1, 0($8) // upper part of A
lw $2, 4($8) // lower part of A
lw $3, 8($8) // upper part of B
lw $4, 12($8) // lower part of B
subu $5, $2, $4
stlu $6, $2, $4 // if lower part of A < lower part of B we need to add 1111 0000 i.e.
// subtract 1 from upper part result
subu $7, $1, $3
subu $7, $7, $6
// Now the upper part of the result is stored in $7$ and the lower part in $5
Does my idea seem correct?
Upvotes: 0
Views: 1027
Reputation: 363999
Compare what GCC and clang do for subtracting uint64_t
on 32-bit MIPS: https://godbolt.org/z/eGY3aWoKq.
Yes, an sltu
to generate the borrow output from the low half, plus 3x subu is the right mix of instructions. Do the low half subtraction, and subtract the high half and the borrow.
Manually transforming it into addition would be slower, so it's probably easiest to think about this in terms of just subtracting the borrow from the high half, and otherwise doing the high and low half subtractions separately.
On an ISA with a carry/borrow flag (like x86 or ARM https://godbolt.org/z/sfG5PzsPW), you'd do
subs r0, r0, r2 # sub low half and set flags
sbc r1, r1, r3 # sub-with-carry does high half including borrow
ARM and x86 set their carry flag opposite from each other for subtraction (!borrow or borrow), but both are designed to chain from low to high in carry-propagation order so it's just 2 instructions.
# 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
Since MIPS doesn't have FLAGS, you have to manually handle carry / borrow propagation, but that's all you're doing.
It gets more inconvenient for wider integers when you need to handle borrow in and out, because emulating sbb
including the carry output requires checking for wraparound after subtracting the borrow and after subtracting the integer operands. Just like for emulating adc
Upvotes: 1