MIPS I 如何在不停止的情况下处理先前 ALU 指令的分支?

How does MIPS I handle branching on the previous ALU instruction without stalling?

        addiu   ,,5
        bltz    ,$L5
        nop
        ...
$L5:

这在没有停顿的情况下如何安全,经典的 MIPS 甚至不能做到,除了缓存未命中? (MIPS 最初代表没有互锁流水线阶段的微处理器,并且有一个加载延迟槽而不是互锁。)

原始 MIPS I 是一个经典的 5 阶段 RISC IF ID EX MEM WB 设计,通过在 ID 阶段尽早检查分支条件(更正:这是错误,请阅读此答案;不要被基于此错误前提的问题中的其余细节所误导)。这就是为什么它仅限于 equal/not-equal,或像 lt 或 ge 零这样的符号位检查,而不是 lt 在需要通过加法器进行进位传播的两个寄存器之间。

这是否意味着分支需要比 ALU 指令早一个周期输入准备好? bltz 在与 [= 相同的周期内进入 ID 阶段13=]进入EX.

MIPS I(又名 R2000)使用 bypass forwarding 从 EX 输出到 EX 输入,因此普通整数 ALU 指令(如 addu/xor 链)具有单-周期延迟,并且可以 运行 连续周期。


MIPS 代表“没有 Interlocked Pipeline Stages 的微处理器”,因此它不会检测 RAW 危害;代码必须避免它们。 (因此第一代 MIPS 上的加载延迟槽,MIPS II 在这种情况下添加了互锁以停止,使首字母缩写词 :P 无效)。

但我从来没有看到任何关于提前多条指令计算分支条件以避免停顿的讨论。 (addiu/bltz 示例由 MIPS gcc5.4 -O3 -march=mips1 on Godbolt 发出,确实 尊重加载延迟槽,填充 nop 如果需要的话。)


它是否使用了某种技巧,例如 EX 在时钟下降沿读取输入,并且 ID 在上升沿之前不需要转发寄存器值? (随着 EX 尽早产生结果以使其起作用)

我想如果时钟速度的上限足够低以便缓存访问是单周期的,那将是有意义的。

Stalling or bubble in MIPS 声称加载结果上的 lw + a beq 需要 2 停顿周期,因为它无法转发。这对于实际的 MIPS I 是不准确的(除非 gcc 有问题)。不过,它确实提到了半个时钟周期,允许在同一整个周期内写入一个值,然后从寄存器文件中读取一个值。

你实际上是在问两个问题:

  1. 在 MIPS I 上安全吗?
  2. 如果是,怎么做?

Is that safe on MIPS I?

我看过 MIPS CPUs 的不同框图。他们中的大多数在 EX 甚至 MEM 阶段而不是 ID 阶段执行分支决策。

当然,当您的示例代码被执行时,这样的设计会有不同的反应。

如果没有您实际使用的 CPU 手册的 CPU 官方声明,您的问题无法得到肯定的回答。

(Paul Clayton 在 上的回答同意一个延迟槽确实完全隐藏了 MIPS R2000 上的分支延迟,但不是 MIPS R4000。所以这是真正的商业 MIPS CPUs 工作的很好的证据问题假设的方式,尽管存在可能不完全遵循 MIPS ISA 的各种实现。)

If so, how?

Doesn't this mean that branches need their input ready a cycle earlier than ALU instructions?

没有

关键是旁路转发逻辑。让我们看看下面的例子:

add  $A, $B, $C      ; Currently in MEM stage
or   $D, $E, $F      ; Currently in EX stage
bltz $G, someLabel   ; Currently in ID stage

(而 AB、... G 是 GPR 号码。)

EX 阶段的旁路转发逻辑(or 指令)包含一个按以下方式工作的多路复用器(伪代码):

if E = A
    take ALU input from EX/MEM shift register output
else
    take ALU input from ID/EX shift register output
end-if

正是这个多路复用器允许您在下一条 (or) 中使用某些指令 (add) 的结果。

当然也可以使用 3 路多路复用器对 ID 阶段执行相同的操作:

if G = D
    take branch decision input from ALU output
else if G = A
    take branch decision input from EX/MEM shift register output
else
    take branch decision input from register bank output
end-if

这样做,信号传播时间将增加 EX 阶段所需的时间。这意味着这将限制处理器的时钟频率。

但是,某些指令的结果已经可以在下一条指令的 ID 阶段使用,而不需要额外的时钟周期。

TL:DR: Classic MIPS I 在 EX 的前半周期检查分支条件,因此转发 它们并不特殊.

IF 只需要第二个半周期的地址,EX 就可以转发给它。

这些因素结合起来仅给出 1 个分支延迟周期(被 1 个延迟槽隐藏),对于依赖于先前 ALU 指令的分支没有问题。


在 MIPS I (R2000) 上 运行 sltu / beq 绝对安全。这被列为 bgeu 伪指令的扩展,例如,在真正的 MIPS 手册和书籍中,没有关于它在 MIPS R2000 或任何其他 MIPS 上不安全的警告。

GCC 在实践中使用类似的序列,即使 march=mips1 尊重加载延迟槽和真正 MIPS R2000 的其他特性。


MIPS 的 IF 直到时钟周期的第二个半周期才需要地址,允许 EX 足够快地产生它。

来自 Dominic Sweetman 的 See MIPS Run,(涵盖 MIPS I 到 MIPS IV),章节 1.5.1 指令约束

We’ll see later that efficient conditional branching means that the decision about whether to branch or not has to be squeezed into only half a pipeline stage; the architecture helps out by keeping the branch decision tests very simple. So conditional branches (in MIPS) test a single register for sign/zero or a pair of registers for equality.

他们的 图 1.3:流水线和分支延迟 显示了在 EX 的前半部分计算的分支条件,并在 IF 的第二半部分使用,总分支只有 1 个周期/流水线阶段 (ID)/指令的延迟。 IF 直到时钟周期的第二个半周期才真正开始。(并继续进入 ID。ID 的实际 decode/register-fetch 仅占用时钟周期的最后一部分。 )

这与我在问题中建议的结果相同(在 ID 末尾检查分支条件),除了它只需要 EX -> EX 转发到分支的结果先前的 ALU 指令。

也许我记错了或误解了我之前读到的关于半周期分支决策的内容。这个半周期的东西很可能正是我记得看到的。

进一步引用 参见 MIPS 运行 1.5.5 程序员可见的流水线效果

• Delayed branches: [first paragraph explains the branch-delay slot]

If nothing special was done by the hardware, the decision to branch or not, together with the branch target address, would emerge at the end of the ALU pipestage — in time to fetch the branch target instruction instead of the next instruction but two. But branches are important enough to justify special treatment, and you can see from Figure 1.3 [described above] that a special path is provided through the ALU to make the branch address available half a clock cycle early. Together with the odd half-clock-cycle shift of the instruction fetch stage, that means that the branch target can be fetched in time to become the next but one, so the hardware runs the branch instruction, then the branch delay slot instruction, and then the branch target — with no other delays.

... [don't waste your branch-delay slots]

... [many MIPS assemblers will reorder instructions for you if it's safe, to hide branch delay]

参见 MIPS 运行 有 John L. Hennessy 的前言, MIPS Technologies 等的创始人。这并不能证明他签署了书中的所有内容都是准确的,但它很好地证明了该书对 MIPS 如何管理此技巧的描述是准确的。

它很容易理解并且 100% 合理;我们已经知道数据缓存具有单周期获取延迟(在 EX 阶段的地址生成之后)。