首页 > 解决方案 > 关于否定 mips 中的符号整数?

问题描述

我正在考虑如何否定 mips32 中的有符号整数。我的直觉是使用 2 的补码的定义,例如:(假设$s0是要否定的数字)

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

然后我意识到它可以这样做:

sub $t0, $zero, $s0

所以......有什么区别?哪个更快?IIRC sub 将尝试检测溢出,但这会使速度变慢吗?最后,有没有其他方法可以做到这一点?

标签: assemblymipscpu-architecturemicro-optimizationmips32

解决方案


subu $t0, $zero, $s0是最好的方法,也是编译器所做的。

在任何给定的 MIPS 实现中,大多数简单的 ALU 指令(add/sub/and/nor)具有相同的性能。用 1 条简单指令而不是 2 条简单指令完成相同的工作是代码大小、延迟和吞吐量的胜利。

更少的指令并不总是更好,但是作为经典的 RISC ISA 的 MIPS 除了 mult / div / rem 之外没有很多“慢”指令。


sub而不是subu会在 上引发异常-INT_MIN,您避免addiu在 nor/add 版本中使用该异常。除非您特别希望有符号溢出引发异常,否则您应该始终使用和指令的u版本。C 编译器始终使用该版本。(在 C 中,有符号溢出是未定义的行为。这意味着它允许出错,但不需要出错,而且通常没有人想要这样。编译器希望能够优化和引入创建临时值的转换,这些临时值在 C 抽象中不存在机器,所以他们在这样做时必须避免出错。)subaddu

在 Godbolt 编译器资源管理器中, MIPS gcc5.4 -O3 编译

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

进入

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

完全符合我们的预期。询问编译器通常是找到在 asm 中做事的有效方法的好方法。


IIRC sub 将尝试检测溢出,但这会使速度变慢吗?

不。据我所知,在无异常情况下,sub与 具有相同的性能。subu

CPU 针对常见情况进行了大量优化。在普通代码中很少发生异常,因此异常需要相当多的周期是可以的。因此,CPU 内核只需要在任何错误结果被写回寄存器文件或存储到缓存/内存之前检测到异常。在任何 MIPS 管道上,执行和回写之间至少有几个管道阶段。

在有符号溢出的情况下,ALU 可以在与结果相同的周期内产生溢出信号。(带有被大多数指令更新的“标志”寄存器的 ISA 作为指令正常操作的一部分add一直这样做:如果软件想要在 x86 或 ARM 上的有符号溢出上做一些特殊的事情,他们会使用条件分支在溢出标志上(x86 上的 OF,ARM 上的 V)。MIPS 的特殊之处在于,除了对有符号溢出进行异常处理之外,它很难做任何事情。)


推荐阅读