为什么英特尔在其处理器中隐藏内部RISC内核?

Goo*_*ofy 85 x86 intel cpu-architecture

从Pentium Pro(P6微体系结构)开始,英特尔重新设计了它的微处理器,并在旧的CISC指令下使用了内部RISC内核.由于Pentium Pro所有CISC指令都分为较小的部分(uops),然后由RISC内核执行.

一开始我很清楚英特尔决定隐藏新的内部架构并强迫程序员使用"CISC shell".由于这一决定,英特尔可以在不破坏兼容性的情况下完全重新设计微处理器架构,这是合理的.

但是我不明白一件事,为什么英特尔仍然保留了多年内隐藏的内部RISC指令集?为什么他们不让程序员使用RISC指令,比如使用旧的x86 CISC指令集?

如果英特尔长期保持向后兼容性(我们仍然在64位模式旁边有虚拟8086模式),为什么它们不允许我们编译程序以便它们绕过CISC指令并直接使用RISC核心?这将开启自然的方式来慢慢放弃x86指令集,现在已弃用(这是英特尔决定在内部使用RISC核心的主要原因,对吧?).

看看新的英特尔'酷睿i'系列,我看到,他们只扩展了CISC指令集,增加了AVX,SSE4等.

jal*_*alf 87

不,x86指令集肯定不会被弃用.它一如既往地受欢迎.英特尔在内部使用一组类似RISC的微指令的原因是因为它们可以更有效地处理.

所以x86 CPU的工作原理是在前端有一个非常重型的解码器,它接受x86指令,并将它们转换为后端可以处理的优化内部格式.

至于将这种格式暴露给"外部"程序,有两点:

  • 它不是一个稳定的格式.英特尔可以在CPU型号之间进行更改,以最好地适应特定架构.这使他们能够最大限度地提高效率,如果他们不得不采用固定,稳定的指令格式供内部使用和外部使用,这种优势就会丧失.
  • 这样做没有什么可以获得的.对于今天庞大而复杂的CPU,解码器只占CPU的一小部分.必须解码x86指令会使其变得更加复杂,但CPU的其余部分不会受到影响,因此总体来说,只能获得很少的内容,尤其是因为x86前端仍然必须存在,以便执行"遗留"代码.因此,您甚至不会保存当前在x86前端使用的晶体管.

这不是一个完美的安排,但成本相当小,而且它比设计CPU以支持两个完全不同的指令集更好.(在这种情况下,他们可能最终会发明内部使用的第三组微操作,只是因为那些可以自由调整以最适合CPU的内部架构)

  • 好点。RISC 是一个很好的核心架构,其中 GOOD 意味着运行速度快并且可以正确实现,而具有 CISC 架构历史的 x86 ISA 只是现在,具有悠久历史和丰富的二进制软件可用的指令集布局,以及高效的存储和处理。它不是 CISC 外壳,而是行业事实上的标准 ISA。 (2认同)
  • @Warren:在最后一部分,我实际上并不这么认为.一个*精心设计的*CISC指令集在存储方面更有效,是的,但是从我看过的几个测试中,"平均"x86指令就像4.3字节宽,比*更多*d通常采用RISC架构.x86失去了很多存储效率,因为它多年来一直是如此随意地设计和扩展.但正如你所说,它的主要优势在于历史和大量现有的二进制代码. (2认同)
  • 我没有说它是“精心设计的CISC”,只是“巨大的历史”。好的部分是RISC芯片设计部分。 (2认同)
  • @jalf - 从检查实际二进制文件开始,x86中的指令大小平均每个大约3个字节.当然有更长的指令,但较小的指令往往在实际使用中占主导地位. (2认同)
  • 平均指令长度不是代码密度的一个很好的衡量标准:典型代码中最常见的 x86 指令类型是加载和存储(只是将数据移动到可以处理的地方,然后返回内存,RISC 处理器和大约 1/2 的 CISC 有很多寄存器所以不需要做这么多。还有一条指令可以做多少(arm指令可以做大约3件事)。 (2认同)

Jor*_*ldo 18

真正的答案很简单.

实施RISC处理器背后的主要因素是降低复杂性和提高速度.RISC的缺点是指令密度降低,这意味着以RISC格式表示的相同代码需要比等效CISC代码更多的指令.

如果你的CPU以与内存相同的速度运行,或者至少如果它们以相当相似的速度运行,那么这种副作用并不意味着什么.

目前,与CPU速度相比的内存速度显示出时钟的巨大差异.当前CPU有时比主存储器快五倍或更多.

这种技术状态有利于更密集的代码,这是CISC提供的.

您可以认为缓存可以加速RISC CPU.但关于CISC cpus也是如此.

通过使用CISC和缓存而不是RISC和缓存,您可以获得更大的速度提升,因为相同大小的缓存对CISC提供的高密度代码具有更大的影响.

另一个副作用是RISC在编译器实现上更难.它更容易优化CISC cpu的编译器.等等

英特尔知道他们在做什么.

这是如此真实,以至于ARM具有更高的代码密度模式,称为Thumb.

  • 此外,内部 RISC 内核减少了 CISC CPU 上的晶体管数量。您可以使用微码来执行它们,而不是硬连线每个 CISC 指令。这导致对不同的 CISC 指令重复使用 RISC 微码指令,从而使用更少的芯片面积。 (3认同)

Mik*_*sen 15

如果英特尔长期保持向后兼容性(我们仍然在64位模式旁边有虚拟8086模式),为什么它们不允许我们编译程序以便它们绕过CISC指令并直接使用RISC核心?这将开启自然的方式来慢慢放弃x86指令集,现在已弃用(这是英特尔决定在内部使用RISC核心的主要原因,对吧?).

你需要看看这个的商业角度.英特尔实际上试图摆脱x86,但它是为公司带来金蛋的鹅.XScale和Itanium从未达到其核心x86业务所取得的成功水平.

你基本上要问的是,英特尔要削减手腕以换取开发人员的温暖模糊.破坏x86不符合他们的利益.任何使更多开发人员不必选择以x86为目标的东西都会破坏x86.反过来,这会破坏它们.

  • 是的,当英特尔试图这样做(安腾)时,市场只是耸耸肩. (4认同)
  • 应该指出的是,安腾的失败有多种因素,而不仅仅是因为它是一种新的架构。例如,将 CPU 调度卸载给编译器,但实际上从未实现其目标。如果安腾的速度比 x86 CPU 快 10 倍或 100 倍,那么它的销量一定会很热。但速度并没有更快。 (2认同)

Pet*_*des 7

在通过 MSR 启用它并执行未记录的0F 3F指令以激活https://en.wikipedia.org/wiki/Alternate_Instruction_Set之后,通过 C3 处理器确实允许这样的事情,它不强制执行通常的特权(环 0)与特权(环 0)对比。非特权(环 3)保护。(不幸的是,Via Samuel II 附带了 MSR 设置以允许此默认设置。他们没有记录它,因此操作系统不知道他们应该关闭该功能。其他 Via CPU 默认为禁用。)

参见 Christopher Domas 在 DEF CON 26:
GOD MODE UNLOCKED Hardware Backdoors in redacted x86 中的演讲。
他还为该 AIS(备用指令集)开发了一个汇编程序:
https : //github.com/xoreaxeaxeax/rosenbridge,以及用于激活它(或关闭漏洞!)的工具

运行后0F 3F(跳转到 EAX),AIS 指令在 4 字节 RISC 指令前用 3 字节前缀编码。(与现有的 x86 指令编码没有区别,例如它接管 LEA 和 Bound,但您可以通过其他方式混合 Via RISC 和 x86 指令。)

AIS(Alternate Instruction Set)使用类似RISC的固定宽度32位指令;因此我们已经知道并非所有可能的 uops 都可以编码为 RISC 指令。该机器将 x86 指令(如 6 字节add eax, 0x12345678(具有 32 位立即数))解码为单个 uop。但是 32 位指令字没有空间容纳 32 位常量操作码和目标寄存器。因此,它是一种替代的 RISC 式 ISA,仅限于后端可以执行的部分内容,并且它们的 RISC 解码器可以从 32 位指令进行解码。

(相关:是否可以制造支持多个 ISA 的处理器?(例如:ARM + x86)讨论了做这件事的一些挑战,而不仅仅是一个噱头,比如拥有一个具有实际性能预期的完整 ARM 模式,以及所有寻址模式和ARM 需要的指令。)


uops 不会像实际的 ARM 或 PowerPC 那样好

@jalf 的回答涵盖了大部分原因,但它没有提到一个有趣的细节: 内部类似 RISC 的内核并非旨在运行类似于 ARM/PPC/MIPS 的指令集。x86 税不仅在耗电的解码器中支付,而且在某种程度上贯穿整个内核。即它不仅仅是 x86 指令编码;每条指令都有奇怪的语义。

(除非那些笨重的语义是用多个 uops 处理的,在这种情况下,您可以只使用一个有用的 uop。例如,shl reg, cl对于原始 uops,您可以省略不方便的要求,即在移位计数为 时不修改 FLAGS 0,这就是为什么shl reg,cl在 Intel SnB 系列上是 3 uops,因此使用原始 uops 会很棒。没有原始 uops,您需要 BMI2shlx进行单uop转换(根本不接触 FLAGS)。

让我们假设英特尔确实创建了一种操作模式,其中指令流不是 x86,指令更直接地映射到 uops。让我们还假设每个 CPU 模型都有自己的 ISA 用于这种模式,所以他们仍然可以在他们喜欢的时候自由地改变内部结构,并用最少量的晶体管暴露它们以进行这种替代格式的指令解码。

大概您仍然只有相同数量的寄存器,映射到 x86 架构状态,因此 x86 操作系统可以在上下文切换时保存/恢复它,而无需使用特定于 CPU 的指令集。但是如果我们抛开这个实际限制,是的,我们可以有更多的寄存器,因为我们可以使用通常为微码1保留的隐藏临时寄存器。


如果我们只有备用解码器,而没有更改后期流水线阶段(执行单元),那么这个 ISA 仍然会有很多 x86 怪癖。 它不会是一个非常好的 RISC 架构。没有一条指令会非常复杂,但 x86 的其他一些疯狂之处仍然存在。

例如: int->FP 转换如cvtsi2sd xmm0, eax合并到 XMM 寄存器的低元素,因此对旧寄存器值有(假)依赖性。即使是 AVX 版本也只需要一个单独的 arg 来将寄存器合并到其中,而不是将零扩展到 XMM/YMM 寄存器中。这当然不是你通常想要的,所以 GCC 通常会做一些额外的pxor xmm0, xmm0事情来打破对以前使用 XMM0 的任何东西的依赖。同样sqrtss xmm1, xmm2合并成xmm1。

同样,没有人想要这个(或者在极少数情况下,他们可以模仿它),但是 SSE1 是在 Pentium III 时代设计的,当时英特尔的 CPU 将 XMM 寄存器处理为两个 64 位的一半。零扩展到完整的 XMM 寄存器会在该内核中的每个标量浮点指令上花费额外的 uop,但压缩浮点 SIMD 指令每个已经是 2 uop。但这是非常短视的;不久 P4 就有了全宽 XMM 寄存器。(虽然当他们在放弃 P4 后回到 P6 内核时,Pentium-M 和 Core(不是 Core2)仍然拥有半宽 XMM 硬件。)不过,英特尔对 P-III 的短期收益对编译器来说是持续的长期痛苦,以及未来的 CPU 必须运行带有额外指令或可能的错误依赖项的代码。

如果您要为 RISC ISA 制作一个全新的解码器,您可以让它挑选部分 x86 指令作为 RISC 指令公开。这在一定程度上减轻了核心的 x86 专业化。


指令编码可能不是固定大小的,因为单个 uops 可以容纳大量数据。如果所有 insn 的大小相同,那么数据就会多得多。单个微融合 uop 可以添加一个 32 位立即数和一个内存操作数,该操作数使用具有 2 个寄存器和 32 位位移的寻址模式。(在 SnB 及更高版本中,只有单寄存器寻址模式可以与 ALU 操作进行微融合)。

uops 非常大,与固定宽度的 ARM 指令不太相似。固定宽度的 32 位指令集一次只能加载 16 位立即数,因此加载 32 位地址需要加载立即数低半/加载高立即数对。x86 不必这样做,这有助于它不会很糟糕,因为只有 15 个 GP 寄存器限制了在寄存器中保持常量的能力。(15 对 7 个寄存器有很大帮助,但再次加倍到 31 的帮助要小得多,我认为发现了一些模拟。RSP 通常不是通用的,所以它更像是 15 个 GP 寄存器和一个堆栈。)


TL; DR 总结:

无论如何,这个答案归结为“x86 指令集可能是对必须能够快速运行 x86 指令的 CPU 进行编程的最佳方式”,但希望能阐明原因。


前端与后端的内部 uop 格式

另请参阅微融合和寻址模式,了解前端与后端 uop 格式在 Intel CPU 上可以表示的差异的一种情况。

脚注 1:有一些“隐藏”寄存器可用作微码的临时寄存器。这些寄存器像 x86 架构寄存器一样被重命名,因此多 uop 指令可以乱序执行。

例如,xchg eax, ecx在英特尔 CPU 上解码为 3 个 uops(为什么?),我们最好的猜测是这些是类似 MOV 的 uops tmp = eax; ecx=eax ; eax=tmp;。按照这个顺序,因为我测量了 ~1 个周期的 dst->src 方向的延迟,而另一种方式则是 2。这些移动mov指令不像常规指令;他们似乎不是零延迟移动消除的候选人。

另请参阅http://blog.stuffedcow.net/2013/05/measuring-rob-capacity/中提到尝试通过实验测量 PRF 大小,并且必须考虑用于保持架构状态的物理寄存器,包括隐藏寄存器。

在解码器之后的前端,但在将寄存器重命名到物理寄存器文件的发布/重命名阶段之前,内部 uop 格式使用类似于 x86 reg 编号的寄存器编号,但有空间来寻址这些隐藏的寄存器。

乱序核心(ROB 和 RS)内部的 uop 格式有些不同,也就是后端(在发布/重命名阶段之后)。每个 int/FP 物理寄存器文件在 Haswell 中有 168 个条目,因此 uop 中的每个寄存器字段都需要足够宽以处理这么多。

由于重命名器位于硬件中,因此我们最好使用它,而不是将静态调度指令直接提供给后端。因此,我们将开始使用与 x86 架构寄存器 + 微代码临时寄存器一样大的一组寄存器,仅此而已。

后端设计为与避免 WAW / WAR 危险的前端重命名器一起使用,因此即使我们想要,我们也不能像有序 CPU 一样使用它。它没有联锁来检测这些依赖关系;这是由问题/重命名处理的。

如果我们可以在没有问题/重命名阶段瓶颈的情况下将 uops 馈送到后端(现代英特尔管道中的最窄点,例如 Skylake 上的 4 宽与 4 个 ALU + 2 个负载 + 1 个存储端口)后端)。但是如果你这样做了,我认为你不能静态调度代码来避免寄存器重用和踩踏在缓存未命中导致加载长时间停滞时仍然需要的结果。

所以我们几乎需要将 uops 提供给 issue/rename 阶段,可能只是绕过解码,而不是 uop 缓存或 IDQ。然后我们得到正常的 OoO exec,并带有理智的危险检测。寄存器分配表仅设计用于将 16 + 几个整数寄存器重命名为 168 条目整数 PRF。我们不能指望硬件将更多的逻辑寄存器重命名为相同数量的物理寄存器;这将需要更大的 RAT。


geo*_*geo 6

答案很简单。英特尔不是为开发人员开发 CPU !他们正在为做出购买决定的人开发它们,顺便说一句,这是世界上每家公司所做的!

英特尔很久以前就承诺(当然,在合理范围内)他们的 CPU 将保持向后兼容。人们想知道,当他们购买一台新的基于 Intel 的计算机时,他们当前的所有软件都将与在旧计算机上的运行完全相同。(虽然,希望更快!)

此外,英特尔清楚知道这一承诺的重要性,因为他们曾经试图走不同的道路。知道有多少人使用安腾 CPU?!?

您可能不喜欢它,但继续使用 x86 的一个决定使英特尔成为世界上最知名的企业名称之一!

  • 我不同意英特尔处理器对开发人员不友好的暗示。在对 PowerPC 和 x86 进行编程多年后,我开始相信 CISC 对程序员更加友好。(我现在在 Intel 工作,但我在被录用之前就决定了这个问题。) (2认同)