11

我正在阅读《RISC-V 读者:开放架构图集》一书。为了解释 ISA(指令集架构)与特定实现(即微架构)的隔离,作者写道:

架构师的诱惑是在 ISA 中包含指令,这些指令有助于在特定时间提高一个实现的性能或成本,但会给不同的或未来的实现带来负担。

据我了解,它指出在设计 ISA 时,ISA 应该避免暴露实现它的特定微架构的细节。


记住上面的引用:当谈到程序计数器时,在 RISC-V ISA 上,程序计数器 ( pc) 指向当前正在执行的指令。另一方面,在 x86 ISA 上,程序计数器 ( eip) 不包含当前正在执行的指令的地址,而是包含当前指令之后的指令的地址。

x86 程序计数器是从微架构中抽象出来的吗?

4

2 回答 2

7

我将用 MIPS 而不是 x86 来回答这个问题,因为 (1) MIPS 和 x86 在这方面有相似之处,并且因为 (2) RISC V 是由 Patterson 等人开发的,经过数十年的 MIPS 经验. 我觉得他们书中的这些陈述在这个比较中得到了最好的理解,因为 x86 和 MIPS 都对相对于指令结尾的分支偏移进行编码(MIPS 中的 pc+4)。

在 MIPS 和 x86 中,PC 相对寻址模式仅在早期 ISA 版本的分支中发现。后来的版本增加了 PC 相对地址计算(例如 MIPSauipc或 x86-64 的 LEA 或加载/存储的 RIP 相对寻址模式)。这些都是相互一致的:偏移量是相对于(过去的)指令结尾(即下一条指令开始)编码的——而正如你所注意到的,在 RISC V 中,编码的分支偏移量(和 auipc , etc..) 是相对于指令的开始。

这样做的价值在于它从某些数据路径中移除了一个加法器,有时这些数据路径中的一个可能位于关键路径上,因此对于某些实现而言,数据路径的这种微小缩短意味着更高的时钟速率。

(当然,RISC V 仍然需要为 pc-next 生成指令 + 4 和调用指令的返回地址,但这在关键路径上要少得多。请注意,在下图中,都没有显示 pc+4 的捕获作为退货地址。)


让我们比较一下硬件框图:

MIPS 数据路径(简化)                                                MIPS 数据路径(简化)


RISC V 数据路径(简化)                                                RISC V 数据路径(简化)

您可以在 RISC V 数据路径图中看到标记为 #5 的线(红色,就在控制椭圆的上方)绕过了加法器(#4,它将 4 加到 pc 中以用于 pc-next)。


图表的归属


为什么 x86 / MIPS 在其初始版本中做出不同的选择?

当然,我不能肯定地说。在我看来,有一个选择要做,而且对于最早的实现来说根本不重要,所以他们可能甚至没有意识到潜在的问题。无论如何,几乎每条指令都需要计算下一条指令,所以这似乎是合乎逻辑的选择。

充其量,他们可能已经节省了一些电线,因为其他指令(例如调用)确实需要 pc-next 并且不一定需要 pc+0 。

对先前处理器的检查可能表明这正是当时的做法,因此这可能更多是对现有方法的继承,而不是设计选择。

8086 没有流水线化(指令预取缓冲区除外),可变长度解码在开始执行之前已经找到指令的结尾。

经过多年的事后看来,这个数据路径问题现在在 RISC V 中得到解决。

我怀疑他们对此做出了同样程度的有意识的决定,例如,对于分支延迟时隙 (MIPS)。


根据评论中的讨论,8086 可能没有任何推送指令起始地址的异常。与后来的 x86 模型不同,除法异常将指令地址推到 div/idiv 之后。而在 8086 中,interrupt-resume after cs rep movsb(或其他字符串指令)推送最后一个前缀的地址,而不是包括多个前缀的整个指令。此“错误”记录在Intel 的 8086 手册(扫描的 PDF)中。所以很有可能8086真的没有记录指令的起始地址或长度,只记录了开始执行前解码完成的地址。这至少由 286 修复,也许是 186,但适用于所有 8086 / 8088 CPU。

MIPS 从一开始就有虚拟内存,所以它确实需要能够记录错误指令的地址,以便在异常返回后重新运行。此外,软件 TLB 未命中处理还需要重新运行错误指令。但是异常很慢并且无论如何都会刷新管道,并且直到获取之后才检测到,所以无论如何可能都需要一些计算。

于 2019-07-23T21:33:56.680 回答
6

据我了解,它指出在设计 ISA 时,ISA 应该避免暴露实现它的特定微架构的细节。

如果您对理想 ISA 的衡量标准是简单性,那么我可能会同意您的看法。但在某些情况下,通过 ISA 公开微架构的某些特性以提高性能可能是有益的,并且有一些方法可以使这样做的负担可以忽略不计。例如,考虑 x86 中的软件预取指令。这些指令的行为在架构上被定义为依赖于微架构。英特尔甚至可以在未来设计一个微架构,让这些指令表现为无操作,而不会违反 x86 规范。唯一的负担是定义这些指令的功能1. 但是,如果预取指令在架构上被定义为将 64 字节对齐的数据预取到 L3 高速缓存中,并且没有 CPUID 位允许对该指令的可选支持,那么这可能确实会使支持这样的指令成为未来的重大负担.

x86 程序计数器是从微架构中抽象出来的吗?

在@InstructionPointer 对其进行编辑之前,您在这个问题中提到了x86 的“第一个实现”,即8086。这是一个具有两个管道阶段的简单处理器:获取和执行。其中一个架构寄存器是IP,它被定义为包含下一条指令的 16 位偏移量(从代码段基址开始)。因此,每条指令的架构价值IP等于偏移量加上指令的大小。这在 8086 中是如何实现的?实际上没有存储该IP值的物理寄存器。有一个物理指令指针寄存器,但它指向接下来要取入指令队列的 16 位,最多可容纳 6 个字节(参见:https ://patents.google.com/patent/US4449184A/en)。如果正在执行的当前指令是控制传输指令,则目标地址是根据指令的相对偏移量、物理中的当前值IP和指令队列中的有效字节数即时计算的. 例如,如果相对偏移量为 15,物理IP地址为 100,指令队列包含 4 个有效字节,则目标偏移量为:100 - 4 + 15 = 111。物理地址可以通过将 20-位代码段地址。显然,建筑IP不会暴露任何这些微架构细节。在现代英特尔处理器中,可能有许多指令在运行,因此每条指令都需要携带足够的信息来重建其地址或下一条指令的地址。

如果 x86 架构IP被定义为指向当前指令而不是下一条指令呢?这将如何影响 8086 的设计?好吧,控制转移指令的相对偏移量是相对于当前指令的偏移量,而不是下一条指令的偏移量。在前面的示例中,我们必须从 111 中减去当前指令的长度才能得到目标偏移量。因此可能需要额外的硬件来跟踪当前指令的大小并将其包含在计算中。但在这样的 ISA 中,我们可以将所有控制传输指令定义为具有统一长度2(其他指令仍然可以是可变长度的),这消除了大部分开销。我想不出一个现实的例子,以一种方式定义程序计数器明显优于另一种方式。但是,它可能会影响 ISA 的设计。


脚注:

(1) 解码器可能仍然必须能够识别预取指令有效并发出相应的微指令。然而,这种负担不是定义微架构相关指令的结果,而是定义新指令的结果,而与这些指令的功能无关。

(2) 或者,当前指令的长度可以存储在一个微型寄存器中。IIRC,8086中的最大指令长度为6字节,因此最多需要3位来存储任何指令的长度。即使对于 8086 天,此开销也非常小。

于 2019-07-24T18:16:01.300 回答