【问题标题】:What is the "relationship" between addi and subi?addi 和 subi 之间的“关系”是什么?
【发布时间】:2012-08-12 11:27:54
【问题描述】:

我应该回答这个问题。经过一些研究,它说 add 和 sub 具有相同的操作码,并且仅在功能领域有所不同。这是答案还是别的什么?

更新

可在 Nios II CPU 手册中找到:

subi
subtract immediate
Operation: rB ← rA – σ (IMMED)
Assembler Syntax: subi rB, rA, IMMED
Example: subi r8, r8, 4
Description: Sign-extends the immediate value IMMED to 32 bits, subtracts it from the value of rA and then
            stores the result in rB.
Usage: The maximum allowed value of IMMED is 32768. The minimum allowed value is
–32767.
Pseudo-instruction:
© March 2009
subi is implemented as addi rB, rA, -IMMED

【问题讨论】:

    标签: assembly cpu mips nios


    【解决方案1】:

    在 ISA 设计的硬件/机器代码级别,MIPS 没有subi / subiu 指令。 这是有道理的,因为 MIPS 没有 FLAGS 寄存器。

    没有进位标志可以记录加负数或减正数之间的差异,就像在许多其他架构(x86、ARM 和许多其他较少 RISCy 架构)上一样。因此花费额外的操作码(以及解码它的晶体管)是没有意义的。

    添加否定立即数不会改变addi / subi签名溢出检测。当您将两个具有相同符号的数字相加并且结果具有相反的符号时,您会得到有符号溢出。或者当减去z = x - y时,如果x和y有相反的符号,而zx有相反的符号,那就是减法溢出(你想让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) 在 16 位立即数中溢出到 0x8000。任何将subi 实现为伪指令的体面的汇编程序都应该对此发出警告,或者警告在addi / addiu 使用带符号的16 位范围之外的立即数。


    addiu 将其立即数符号扩展为 32 位,与 addi 相同。 “未签名”是用词不当。 Why would we use addiu instead of addi?。对于 2 的补码机器,有符号和无符号加法是相同的二进制运算。命名类型匹配 C 有符号溢出是未定义的行为,但请注意,未定义不需要错误。将addi 视为经过溢出检查的签名加法,并且仅在您特别需要时使用它。

    有趣的事实:ori 和其他布尔值对它们的立即数进行零扩展,因此 li $t0, val 可以使用 addiu $t0, $zero, signed_int16ori $t0, $zero, uint16 扩展为仅针对 val = -32768 .. 65535 的一条指令。

    【讨论】:

      【解决方案2】:

      同意以上观点。说明一些环境。提出我在这件事上看到的小真理。发展我自己的(阴谋)理论。

      关键字在倒数第二行(第三行,包括版权)中-它是伪指令。这一直是一个常见的课堂问题,所以值得说清楚。一些仿真器(看着你的火星)将其视为真正的指令,而其他仿真器(qtspim - 耻辱!)甚至不会费心编译它。

      虽然我从来没有找到一个很好的例子,但据说将一个转换为另一个太简单了,所以它是多余的。他们试图遵循“简单的设计原则”。对我来说,这有点崩溃,因为已经有很多伪指令代表对其他命令的简单修改。分支伪指令分解为 2 个命令,这将需要 2 个甚至 3 个命令。所以,这一款也不例外。那么像“li”这样的东西呢?我的意思是,来吧,那只是补充。没有其他的。如果他们加上这一点,那就没有任何借口了。老实说,为了简单和对称,我认为添加它会更容易。但是,每个人最喜欢的 MIPS 考试问题会发生什么?

      这就是没有 SUBI 的真正原因。让陈旧的 ASM 程序员与本科生搞混。

      【讨论】:

      • 在硬件/机器代码级别,这是因为 MIPS 没有 FLAGS 寄存器。因此,没有进位标志可以记录加负数或减正数之间的差异,就像在 x86 上一样。因此,花费额外的操作码(以及解码它的晶体管)是没有意义的。在 asm 源代码级别,是的,显然它可以实现为方便的伪指令,但与 addiu 相比,它从不需要额外的指令(除非你想减去 -32768 而只能添加 32767)。
      • 你到底在声称什么是阴谋?缺硬件subi?或者某些汇编程序中缺少subi 伪指令?我刚试过 MARS,它把subi $t1, $t2, 1234 组装成两条指令:首先在$at ($1) 中生成常量,然后是sub 指令(这当然是一条真正的指令)。所以 MARS 是愚蠢的,并且不只是否定 addi 的立即数。但它只在启用“扩展伪指令”的情况下执行此操作,而不是大多数汇编程序支持的基本伪指令。这就是为什么它会发出如此低效的代码。
      【解决方案3】:

      我不知道 MIPS 甚至 正确的 subi 指令(尽管某些环境可能会为它实现宏)。

      由于您要减去 立即数 值,您只需将它的否定提供给 addi 指令:

      addi $r1, $r2, -42     ; equivalent to subi $r1, $r2, 42
      

      立即操作数是一个二进制补码值,这意味着它完全可以是一个负数,而二进制补码的工作方式意味着您可以以 unsigned 方式添加一个负数并且给出与减法相同的结果(因为你环绕)。

      例如,16 位二进制补码中的-42 是无符号值65494。当您在 65536 处添加 5065494 时,您最终会得到:

           50
      + 65494 (ie, -42)
        -----
        65544 (overflow, so
      - 65536  we wrap at 64K)
        -----
            8 (identical to "50 - 42")
      

      【讨论】:

      • 感谢您的正确回答。我还发现我的环境(Nios II)实际上有 subi 的参考,我已经更新了问题,您可以根据手册查看说明。
      • 更有可能一些汇编程序将其作为伪指令而不是源代码级宏。 MIPS 汇编器通常有很多伪指令;甚至还有一个寄存器通常被保留为临时供伪指令使用。 (例如比较和分支)。问题更新中的手动输入将其描述为伪指令。
      猜你喜欢
      • 2021-01-04
      • 2012-01-30
      • 2023-03-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-13
      • 2016-10-03
      • 1970-01-01
      相关资源
      最近更新 更多