在 ISA 设计的硬件/机器代码级别,MIPS 没有subi
/subiu
指令。 这是有道理的,因为 MIPS 没有 FLAGS 寄存器。
没有进位标志可以记录添加负数或减去正数之间的差异,就像在许多其他架构(x86、ARM 和许多其他较少 RISCy 架构)上一样。因此,花费额外的操作码(以及解码它的晶体管)是没有意义的。
添加否定立即数不会改变/的有符号溢出检测。当您将两个具有相同符号的数字相加并且结果具有相反的符号时,您会得到有符号溢出。或者在减法时,如果 x 和 y 有相反的符号,并且有相反的符号,那就是减法溢出(你想要捕获的地方。) 是直接的,所以将它实现为使' 的溢出检测工作。addi
subi
z = x - y
z
x
subi
y
z = x + y_negated
addi
当然,通常您(或 C 编译器)只会使用addiu
/subiu
因为您不想陷入陷阱,并且宁愿将环绕作为签名溢出行为,除非您使用-fsanitize=undefined-behavior
或其他东西进行编译。
在 asm 源代码级别,为方便起见,它可以实现为伪指令,正如您在 NIOS II 手册中的引用所示。(或者甚至作为一个宏,在不支持伪指令的汇编器上。)
subi
硬件/保存指令的唯一时间subiu
是您想要加32768
/ 减-32678
。(注意 NIOS II 手册指出subi
支持-32767 .. 32768
范围内的立即数,与正常有符号的 16 位 2 的补码相反-32768 .. 32767
)
2 的补码中最负数是异常的,它的负数需要额外的位才能正确表示。即-(0x8000)
溢出到0x8000
16 位立即数。任何subi
作为伪指令实现的体面的汇编程序都应该对此发出警告,或者警告不要对addi
/使用带符号的 16 位范围之外的立即数addiu
。
addiu
将其立即数符号扩展为 32 位,与 . 相同addi
。“未签名”是用词不当。 为什么我们要使用 addiu 而不是 addi?. 对于 2 的补码机器,有符号和无符号加法是相同的二进制运算。命名类型匹配 C 有符号溢出是未定义的行为,但请注意未定义不需要故障。将addi
其视为溢出检查的签名加法,并且仅在您特别需要时才使用它。
有趣的事实:ori
其他布尔值对它们的立即数进行零扩展,因此可以使用或li $t0, val
扩展为仅一条指令。val = -32768 .. 65535
addiu $t0, $zero, signed_int16
ori $t0, $zero, uint16