About negate a sign-integer in mips?

瘦欲@ 提交于 2021-02-08 06:57:22

问题


I'm thinking about how to negate a signed-integer in mips32. My intuition is using definition of 2's complement like: (suppose $s0 is the number to be negated)

nor $t0, $s0, $s0   ; 1's complement
addiu $t0, $t0, 1   ; 2's = 1's + 1

then I realized that it can be done like:

sub $t0, $zero, $s0

so... what's the difference? Which is faster? IIRC sub will try to detect overflow, but would this make is slower? Finally, is there any other way to do so?


回答1:


subu $t0, $zero, $s0 is the best way, and is what compilers do.

On any given implementation of MIPS, most simple ALU instructions (add/sub/and/nor) have identical performance. Getting the same work done in 1 simple instruction instead of 2 simple instructions is a win for code-size, latency, and throughput.

Fewer instructions isn't always better, but MIPS being a classic RISC ISA doesn't have many "slow" instructions other than mult / div / rem.


sub instead of subu would raise an exception on -INT_MIN, which you avoid using addiu in the nor/add version. You should always use the u version of sub and add instructions unless you specifically want signed overflow to raise an exception. C compilers always use the u version. (In C, signed overflow is undefined behaviour. That means it's allowed to fault, but not required to fault, and generally nobody wants that. Compilers want to be able to optimize and introduce transformations that create temporary values that never exist in the C abstract machine, so they must avoid faulting when doing that.)

On the Godbolt compiler explorer, MIPS gcc5.4 -O3 compiles

int neg(int x) { return -x; }

into

neg(int):
    j       $31
    subu    $2,$0,$4        # in the branch delay slot

exactly like we'd expect. Asking a compiler is usually a good way to find efficient ways to do things in asm.


IIRC sub will try to detect overflow, but would this make is slower?

No. In the no-exception case, sub has the same performance as subu, as far as I know.

CPUs are heavily optimized for the common case. Taking an exception happens so rarely in normal code that it's fine for exceptions to take quite a lot of cycles. So the CPU core just has to detect an exception before any bad result is written back to the register file, or stored to cache/memory. There are at least a couple pipeline stages between Execute and Write-Back on any MIPS pipeline.

In the case of signed overflow, the ALU can produce the overflow signal in the same cycle as the result. (ISAs with a "flags" register that's updated by most instructions do this all the time as part of the normal operation of an add instruction: if software wants to do something special on signed overflow on x86 or ARM, they'd use a conditional branch on the overflow flag (OF on x86, V on ARM). MIPS is special in that it's difficult to do anything other than take an exception on signed overflow.)



来源:https://stackoverflow.com/questions/53568440/about-negate-a-sign-integer-in-mips

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!