计算机组织中的分支指令

17 Mar 2025 | 6 分钟阅读

分支是计算机程序中的一条指令,它可以使计算机开始执行不同的指令序列,从而偏离其按顺序执行指令的默认行为。分支也可能指由于执行分支指令而切换到不同指令序列的动作。分支指令用于实现程序循环和条件判断中的控制流(即,仅当满足某些条件时才执行特定的指令序列)。

分支指令可以是无条件分支,它总是导致分支;也可以是条件分支,它根据某些条件可能会也可能不会导致分支。此外,它还取决于如何指定新指令序列的地址(目标地址)。

分支指令通常被分类为直接、间接相对。这意味着指令包含目标地址,指定目标地址在哪里找到(例如,寄存器或内存位置),或指定当前地址和目标地址之间的差值。分支指令通过以下四种方式之一计算目标地址:

  • 目标地址是常量与分支指令本身地址之和。
  • 目标地址是作为指令的操作数给出的绝对地址。
  • 目标地址是链接寄存器中找到的地址。
  • 目标地址是计数寄存器中找到的地址。

使用前两种方法,可以在分支之前足够远地计算目标地址,以便预取目标路径上的指令。

使用第三种和第四种方法,只要链接寄存器或计数寄存器在分支指令之前足够远地加载,就可以预取分支路径上的指令。

分支指令的类型

计算机组织中有三种类型的分支指令:

Branch Instruction in Computer Organization

1. 跳转指令

跳转指令根据指定的标志将程序序列转移到操作数中给出的内存地址。跳转指令进一步分为两类:无条件跳转指令和条件跳转指令。

  • 无条件跳转指令:将程序序列转移到指定的内存地址。
  • 条件跳转指令:仅当满足条件时,才将程序序列转移到指定的内存地址。

2. 调用指令

调用指令将程序序列转移到操作数中给出的内存地址。在转移之前,CALL之后的下一条指令的地址被推入堆栈。调用指令也有两种类型:无条件调用指令和条件调用指令。

  • 无条件调用指令:它将程序序列转移到操作数中给出的内存地址。
  • 条件调用指令:仅当满足条件时,指令才会执行。

3. 返回指令

返回指令将程序序列从子程序转移到调用程序。返回指令有两种类型:无条件跳转指令和条件跳转指令。

  • 无条件返回指令:程序序列无条件地从子程序转移到调用程序。
  • 条件返回指令:仅当满足条件时,程序序列才无条件地从子程序转移到调用程序。

分支指令的实现

从机制上讲,分支指令可以改变 CPU 的程序计数器。程序计数器存储要执行的下一条指令的内存地址。因此,分支指令会导致 CPU 从不同的内存单元序列开始获取指令。机器级分支指令有时被称为跳转指令

  1. 机器级跳转指令通常有无条件和条件形式,其中后者可能根据某个条件而被采取不采取。通常,对于单向跳转有不同的形式,称为跳转,而子程序调用称为调用,它会自动将原始地址作为返回地址保存在堆栈上,从而允许从代码中的多个位置调用单个子程序。
    • 当分支被采取时,CPU 的程序计数器被设置为跳转指令的参数。因此,下一条指令将成为内存中该地址处的指令。因此,控制流发生了改变。
    • 当分支不被采取时,CPU 的程序计数器保持不变。因此,执行的下一条指令是分支指令之后的指令。因此,控制流保持不变。
  2. 分支一词可以指高级语言编写的程序,也可以指用机器码或汇编语言编写的程序。
    • 在高级编程语言中,分支通常采用各种形式的条件语句,这些语句封装了在满足条件时将执行的指令序列。
    • 无条件分支指令,如 GOTO,用于无条件地“跳转”到(开始执行)不同的指令序列。
  3. 在具有标志寄存器的 CPU 中,较早的指令在标志寄存器中设置一个条件。较早的指令可能是算术或逻辑指令。它通常接近分支,但并非一定是分支指令正前方的指令。
    • 然后,存储的条件用于分支,例如“如果溢出标志设置则跳转”
    • 此临时信息通常存储在标志寄存器中,但也可能位于其他位置。
    • 对于较慢、简单的计算机,标志寄存器设计很简单。对于快速计算机,标志寄存器可能会成为速度瓶颈,因为可以并行操作的指令需要按特定顺序设置标志位。
  4. 还有一些机器或特定指令,其中条件可以由跳转指令本身检查,例如“分支 <标签> 如果寄存器 X 为负”。在简单的计算机设计中,比较分支执行更多的算术运算,并且比标志寄存器分支消耗更多能量。
    • 计算机设计中的比较分支比标志寄存器分支运行得更快,因为比较分支可以以更高的并行性访问寄存器,使用与计算相同的 CPU 机制。
  5. 一些早期和简单的 CPU 架构(仍在微控制器中发现)可能不实现条件跳转,而是实现条件“跳过下一条指令”的操作。条件跳转或调用因此被实现为条件性地跳过无条件跳转或调用指令。

分支指令的处理

分支指令可以通过多种方式进行处理,以减少其对指令执行速率的负面影响。

Branch Instruction in Computer Organization

1. 分支延迟槽

处理器在确定当前指令是否为分支指令之前就会获取下一条指令。当当前指令执行完成并且需要进行分支时,处理器必须丢弃剩余指令并从分支目标处获取新的分支指令。分支指令后面的位置称为分支延迟槽。根据执行分支指令所需的时间,可能有一个或多个分支延迟槽。

一种称为延迟分支的技术可以最小化由条件分支指令引起的惩罚。延迟槽中的指令总是被获取。因此,我们希望安排它们无论分支是采取还是不采取都能完全执行。目标是将有用的指令放置在这些槽中。如果无法将有用的指令放置在延迟槽中,则必须用 NOP 指令填充这些槽。

2. 分支预测

分支预测利用统计数据并使用结果来优化代码。程序员会编译程序的测试版本并使用测试数据运行它。

  • 测试代码计算了分支被采取的次数。
  • 然后,编译器使用测试代码中的统计数据来优化发布代码的分支。优化将安排最快的分支方向(采取或不采取)始终是发生频率最高的过程流路径。
  • 为了实现这一点,CPU 必须设计成具有可预测的分支时序。一些 CPU 具有设计有“分支提示”的指令集,因此编译器可以告知 CPU 每个分支将如何被采取。

软件分支预测的问题在于它需要复杂的软件开发过程。

3. 无分支代码

一些逻辑可以在没有分支或分支较少的情况下编写。通常可以使用按位运算、条件移动或其他谓词而不是分支。无分支代码对于加密至关重要,因为可以防止时序攻击。

4. 硬件分支预测器

为了运行任何软件,硬件分支预测器将统计数据转移到了电子元件中。分支预测器是处理器的一部分,用于猜测条件分支的结果。然后,处理器的逻辑通过开始执行预期的指令流来“赌博”这个猜测。

一个简单的硬件分支预测方案是假设所有向后分支(到较小的程序计数器)都被采取(因为它们是循环的一部分),而所有向前分支(到较大的程序计数器)都不被采取(因为它们离开了循环)。

通过在模拟中针对各种测试程序运行,可以开发和统计验证更好的分支预测器。好的预测器通常会计算分支先前执行的结果。