【问题标题】:What is the purpose of SUB Instruction?SUB指令的目的是什么?
【发布时间】:2019-06-01 10:24:21
【问题描述】:

我正在自学汇编,我有一个困惑想要解决。

据我了解,x86 计算机使用相同的电路进行加法和减法。对于减法,负数被转换为其 2 补码,然后加法电路可用于执行减法。例如:方程4 - 2 转换为4 + (-2)

那么如果减法可以使用 2 补码来完成,那么 SUB 指令的目的是什么?

【问题讨论】:

  • 在一个周期内完成。
  • @OliverCharlesworth 你能详细解释一下吗?
  • 一个更好的问题是:为什么我们需要添加指令?可以使用 AND/OR 和移位完成添加。
  • 一个更好的问题:我们为什么需要程序员? :-)
  • @Mysticial 另一个问题:为什么需要按位指令?你可以只用 add 或 sub 来完成所有这些,虽然有点长

标签: assembly x86


【解决方案1】:

在 2 的补码世界中,整数的取反可以通过取 1 的补码(所有位反转)并加 1 来获得。例如,在 8 位世界中:

A:   0x00000002    ; my number
~A:  0xFFFFFFFD    ; 1's complement of my number
-A:  0xFFFFFFFE    ; 2's complement of my number (negative A)

要减去A-B,当然可以加上负数A+(-B)

NOT  B              ; invert each bit in the 8-bit value, B
ADD  B, 1           ; add 1, giving the 2's complement negated B
ADD  A, B

当然,我必须先修改B(否定它)才能添加它。如果我想让B 保持原样怎么办?

PUSH B              ; save B
NOT  B              ; invert each bit in the 8-bit value, B
INC  A              ; add 1, giving the 2's complement negated B
ADD  A, B
POP  B              ; restore B

或者

NOT  B              ; invert each bit in the 8-bit value, B
INC  A              ; add 1, giving the 2's complement negated B
ADD  A, B
NOT  B              ; restore B

这样就行了。但是有一个SUB 指令不是更容易吗?

SUB  A, B

如果您正在编写汇编语言来进行大量算术运算,您更喜欢哪种方法?而且,在第一种情况下,我使用了INC A 指令。我可以在没有INC 的情况下逃脱,而只需使用ADD A, 1。但是,在许多微处理器上,ADD A, 1 要求我从指令内存中获取更多内容来执行,以便获得即时的1 值。因此,提供了INC,因为这样的操作很常见。

当微处理器设计人员确定要使用的指令集时,他们会考虑最常用的操作类型。减法很常见,所以SUB 指令非常方便。因此,它几乎存在于您能找到的任何指令集中。指令集中还有其他指令存在的原因不太明显。例如,x86 具有 XLAT 指令,以及所有“字符串”指令,LODSSTOS 等。当我可以使用 MOV 和 @987654342 完成所有这些工作时,它们为什么存在@, ETC?因为有人认为这些操作足够普遍,值得拥有一条指令。

因此,SUB 指令背后的目的,与 CPU 实现的许多其他指令一样,是提供一种更快(执行时间)和更简单的方式来执行最常在软件中执行的操作,同时兼顾以下事实:可以执行多少指令的实际限制。

【讨论】:

  • 但我认为汇编器会自动创建负操作数的 2 补码。就像在指令 ADD 1 中一样,编译器在汇编文件时 -2 不会将 -2 转换为其 2 的补码。
  • 您并不总是立即使用。这就是为什么在许多架构中,寄存器之间有子,但没有立即的子
  • @curious_kid 非常适用于常量,但不适用于其值在汇编时未知的寄存器或内存位置。
  • 好吧,这是有道理的,所以 SUB 指令实际上可以转换为多个微码指令,就像上面提到的答案一样?
  • @curious_kid 不,在 ALU 中(或之前)在 1 个操作数之前有一个 NOT 门,通过利用进位线,我们实际上可以在 1 个操作中加或减另一个 add/子选择
【解决方案2】:

TL;DR

因为它是最常见的操作之一,所以有专门的指令有助于提高速度。设计师知道如何让它变得更快,不需要像你想象的那样单独否定


从 David A. Patterson 和 John L. Hennessy 的著名计算机体系结构书籍Computer Organization and Design, Fourth Edition: The Hardware/Software Interface 和 David Money Harris、Sarah L. Harris 的 Digital Design and Computer Architecture 我们知道 MIPS 设计原则如下

  1. 设计原则 1: 简洁有利于规律性。
  2. 设计原则 2: 快速完成常见案例。
  3. 设计原则 3: 越小越快。
  4. 设计原则 4: 好的设计需要好的妥协。

这些在其他架构中也是正确的。在 x86 和许多其他(主要是较旧的)架构中,由于向后兼容性,有些无法实现,但要点适用。

由于 1st 和 3rd 原则,我们需要使指令集尽可能紧凑并且不要创建如果我们可以使用其他指令来执行新指令。但是根据原则 2 和 4,我们需要尽可能快地进行常用操作

事实上,大多数指令都是多余的,因为我们可以将其作为图灵完备的指令集,仅使用 one instruction。 x86 本身不是 OISC 架构,但仍然可以只用一个 movadd/sub 来做任何事情,因为它们是 proved to be Turing-complete。甚至还有一个编译器可以将有效的 C 代码编译成只有 MOV 的程序(或只有 XOR、SUB、ADD、XADD、ADC、SBB、AND/OR、PUSH/POP、 1 位移位,或 CMPXCHG/XCHG)命名为 movfuscator

因此,使用addsub,我们可以轻松获得移位、按位运算和乘法/除法。然而,这些基本操作可能需要非常长的一系列指令来模拟,用户不会对此感到满意。

这就是制造商不断向新微架构添加新指令的原因,因为新的需求会使事情在大量投入使用之前变得不常见。例如,当 3D 应用成为新趋势时,他们决定为矢量和 3D 运算添加SIMD instructions,矩阵运算也很常见。然后,当安全要求的提高使加密变得更加普遍时,AES instructions 被引入以提升加密应用程序。但这还不够,因为密码学和许多其他应用程序使用大量多精度算术,英特尔添加了MULX/ADOX/ADCX instructions 以使其更快。现在您会看到加速 AI 操作的指令开始进入架构


回到主要问题,减法非常常见,因此值得单独说明。没有它,您将不得不negate 一个操作数,然后是add,这至少会花费两倍的时间和指令空间。 sub a, b 优于 neg b; add a, b

但是减法并不一定像您想象的那样因为否定而变慢,因为设计人员使用巧妙的技巧让加法器在相同数量的时钟内同时执行 addsub只有 muxer 和 NOT 门以及新输入 Binvert 以便有条件地反转第二个输入,如下所示

Computer Architecture - Full Adder

它的工作原理是在二进制补码-b = ~b + 1,所以a - b = a + ~b + 1。这意味着我们只需要将进位设置为 1(或对进位取反以借入)并反转第二个输入。

这种ALU在我一开始提到的书中也有提到。不幸的是,由于许可问题,我无法引用它,但我在教授的另一本书中找到了它。帕特森和教授。轩尼诗:

Computer Organization and Design RISC-V Edition: The Hardware Software Interface

如您所见,通过另一个非常简单的修改,它们现在可以使用单个 ALU 执行 6 种不同的操作:add、sub、slt、and、or、nor

CSE 675.02: Introduction to Computer Architecture

您可以在 ALU 设计课程中找到更多信息,或者在 Google 上使用关键字 Binvert/Bnegate 找到更多信息

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-12
    • 2011-02-17
    • 2012-10-05
    • 2020-11-12
    • 2021-08-29
    相关资源
    最近更新 更多