ChatGPT解决这个技术问题 Extra ChatGPT

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

从 Pentium Pro(P6 微架构)开始,英特尔重新设计了它的微处理器,并在旧的 CISC 指令下使用了内部 RISC 内核。自 Pentium Pro 以来,所有 CISC 指令都被分成更小的部分(微指令),然后由 RISC 内核执行。

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

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

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

看看我看到的新的英特尔“Core i”系列,它们只扩展了 CISC 指令集,添加了 AVX、SSE4 等。

请注意,在某些 x86 CPU 中,the internal RISC instruction set is exposed

j
jalf

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

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

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

它不是一种稳定的格式。英特尔可以在 CPU 型号之间进行更改,以最适合特定架构。这使他们能够最大限度地提高效率,如果他们不得不为内部使用和外部使用确定一个固定、稳定的指令格式,那么这种优势就会丧失。

这样做没有任何收获。对于当今庞大而复杂的 CPU,解码器只是 CPU 中相对较小的一部分。必须解码 x86 指令会使这变得更加复杂,但 CPU 的其余部分不受影响,所以总的来说,几乎没有什么收获,特别是因为 x86 前端仍然必须在那里,才能执行“遗留”代码.因此,您甚至不会保存 x86 前端当前使用的晶体管。

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


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

真正的答案很简单。

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

如果您的 CPU 以与内存相同的速度运行,或者至少如果它们都以相当相似的速度运行,那么这种副作用并没有多大意义。

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

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

您可以争辩说缓存可以加速 RISC CPU。但是对于 CISC cpus 也可以这样说。

与 RISC 和缓存相比,使用 CISC 和缓存可以获得更大的速度提升,因为相同大小的缓存对 CISC 提供的高密度代码的影响更大。

另一个副作用是 RISC 在编译器实现上更难。更容易为 CISC cpus 优化编译器。等等

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

确实如此,ARM 具有更高的代码密度模式,称为 Thumb。


此外,内部 RISC 内核减少了 CISC CPU 上的晶体管数量。您可以使用微码来执行它们,而不是硬连接每条 CISC 指令。这导致为不同的 CISC 指令重用 RISC 微码指令,从而使用更少的芯片面积。
英特尔notes,通常一条指令被解码为多个 μOps。但是在很多情况下,背靠背的多个指令会融合到单个 μOp 中。他们给出的一个例子是比较后跟分支,它被融合成一个单一的μOp。
M
Mike Thomsen

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

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

您基本上要求的是英特尔割腕以换取开发人员的温暖绒毛。破坏 x86 不符合他们的利益。任何让更多开发人员不必选择以 x86 为目标的东西都会破坏 x86。这反过来又会破坏它们。


是的,当英特尔试图这样做(安腾)时,市场只是耸了耸肩。
应该注意的是,安腾失败的原因有很多,不仅仅是因为它是一个新架构。例如,将 CPU 调度卸载到从未真正实现其目标的编译器。如果 Itanium 的速度比 x86 CPU 快 10 倍或 100 倍,它就会像热蛋糕一样畅销。但它并没有更快。
P
Peter Cordes

通过 C3 处理器允许这样的事情,在通过 MSR 启用它并执行未记录的 0F 3F 指令来激活 https://en.wikipedia.org/wiki/Alternate_Instruction_Set 后,它不会强制执行通常的操作特权(环 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(备用指令集)使用类似 RISC 的固定宽度 32 位指令;因此我们已经知道并非所有可能的微指令都可以编码为 RISC 指令。机器将 x86 指令(如 6 字节 add eax, 0x12345678(带有 32 位立即数))解码为单个 uop。但是 32 位指令字没有空间容纳 32 位常量操作码和目标寄存器。所以它是一个替代的类似 RISC 的 ISA,它仅限于后端可以执行的事情的子集,并且他们的 RISC 解码器可以从 32 位指令解码。

(相关:Could a processor be made that supports multiple ISAs? (ex: ARM + x86) 讨论了这样做的一些挑战,而不仅仅是一个噱头,比如拥有一个具有实际性能预期的完整 ARM 模式,以及 ARM 需要的所有寻址模式和指令。)

uops 不如真正的 ARM 或 PowerPC

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

(除非那些笨重的语义是用多个微指令处理的,在这种情况下,您可以只使用一个有用的微指令。例如,对于带有原始微指令的 shl reg, cl,您可以省去当班次计数为 { 时不修改 FLAGS 的不便要求2},这就是为什么 shl reg,cl 在 Intel SnB 系列上是 3 uops,因此使用原始 uops 会很棒。没有原始 uops,您需要 BMI2 shlx 进行单 uop 班次(根本不接触 FLAGS ).)

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

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

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

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

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

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

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

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

TL;DR 总结:

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

前端与后端的内部 uop 格式

另请参阅 Micro fusion and addressing modes,了解前端与后端 uop 格式在 Intel CPU 上可以表示的差异的一种情况。

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

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

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

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

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

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

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

如果我们可以在没有问题/重命名阶段瓶颈的情况下将微指令输入后端(现代英特尔管道中最窄的点,例如 Skylake 上的 4 宽与 4 ALU + 2 负载 + 1 存储端口),那可能会很整洁后端)。但是如果你这样做了,我认为你不能静态地安排代码来避免寄存器重用和踩到如果缓存未命中使负载长时间停止加载仍然需要的结果。

所以我们几乎需要将微指令输入问题/重命名阶段,可能只是绕过解码,而不是微指令缓存或 IDQ。然后我们得到正常的 OoO exec 和健全的危险检测。寄存器分配表仅设计用于将 16 + 几个整数寄存器重命名为 168 项整数 PRF。我们不能指望硬件将更大的逻辑寄存器集重命名为相同数量的物理寄存器。这将需要更大的 RAT。


g
geo

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

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

此外,英特尔确切地知道这种承诺的重要性,因为他们曾经尝试过不同的方式。你知道有多少人使用安腾 CPU?!?

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


我不同意英特尔处理器对开发人员不友好的暗示。多年来对 PowerPC 和 x86 进行了编程,我开始相信 CISC 对程序员更友好。 (我现在在英特尔工作,但我在被录用之前就已经决定了这个问题。)
@Jeff 这根本不是我的本意!问题是,为什么英特尔没有开放 RISC 指令集让开发人员可以使用它。我没有说 x86 对开发人员不友好。我所说的是,诸如此类的决定并不是由开发人员决定的,而是严格意义上的商业决定。
C
CoolOppo

直到最近,英特尔一直是领导者很长一段时间。他们没有理由改变他们的架构,因为他们可以通过更好的内部优化每年做出的迭代改变让他们保持领先。那,AMD——他们在台式机和服务器 CPU 领域唯一的真正竞争对手——也使用 x86。因此,从本质上讲,该领域仅有的两家公司中的任何一家都必须在每年优化 x86 代码方面击败对方。

创建一个新的架构和指令集来配合它对公司来说是一个很大的风险,因为他们正在放弃在 x86 优化竞赛中的立足点,投入人才来创建一个需要 Microsoft 和/或广泛支持的新架构。或Linux,以保持轻微的兼容性。与微软合作以在 Windows 操作系统中进行二进制翻译(必要)可能被视为信任活动,除非两家制造商同意签署并共同努力创建一个标准架构,微软可以将其翻译层翻译成该架构。

Apple 最近刚刚发布了他们的新 M1 芯片,它实际上只是 ARM,但这些芯片本质上是 RISC,而你在汇编中编写的是在 CPU 上运行的东西。这需要 Apple 和制造商之间的密切合作,他们的公司一直做得很好(这有其优点和缺点)。他们能够对软件和硬件进行如此严格控制的一件事是,他们可以创建他们想要运行的特定硬件所需的精确转换层。

我的预测是 AMD 和 Intel 将在不久的将来推出仅限 RISC 的 CPU,因为毫无疑问,Apple 将继续改进“M”系列芯片,在编译器/软件方面,以使他们的芯片在需要时具有所需的确切代码。这种方法显然更好,但就像我之前说的:英特尔和 AMD 步调一致,无法采取行动。现在他们的手是被迫的。

至于他们为什么隐藏内部 RISC 架构的主要问题?我认为这个问题有点“离题”。这不像他们是故意“隐藏”你的......这意味着让你远离它的意图。您无权访问的真正原因是他们需要做更多的工作才能允许您在同一个核心上使用两种架构。您需要两个管道,其中代码可以作为数据进入。你同步时钟吗?它们可以相互操作吗?如果它们是隔离的,你会失去一个 x86 核心并获得一个 RISC 核心吗?或者同一个核心可以同时运行吗?那么潜在的安全漏洞呢……我们可以让 RISC 代码干扰 x86 代码,从而扰乱内部优化器吗?我可以继续说下去,但我想你明白我的意思:很难有两种架构可用于编程。

这让我们只有一个选择:我们必须选择我们要支持的架构。正如我在前面几段中解释的那样,有很多原因表明他们不能只提供 RISC 处理器。因此,我们的技术霸主赋予了我们 x86。


K
KOLANICH

为什么他们不允许我们编译程序以便绕过 CISC 指令并直接使用 RISC 内核?

除了前面的答案,另一个原因是市场细分。一些指令被认为是在微代码中而不是在硬件中实现的,因此允许任何人执行任意微操作可能会破坏具有“新”性能更高 CISC 指令的新 CPU 的销售。


我不认为这是有道理的。 RISC 可以使用微码,尤其是当我们谈论只是将 RISC 解码器添加到 x86 前端时。
那还是错的。 AES 新指令(以及即将推出的 SHA 指令)以及其他类似 PCLMULQDQ 的东西都有专用硬件。在 Haswell 上,AESENC 解码为单个 uop (agner.org/optimize),因此它绝对不是微编码的。 (解码器只需要激活微码 ROM 序列器 for instructions that decode to more than 4 uops。)
您是对的,一些新指令确实只是以 x86 指令不可用的方式使用现有功能。一个很好的例子是 BMI2 SHLX,它允许您在不将计数放入 CL 的情况下进行可变计数移位,并且不会产生处理糟糕的 x86 标志语义所需的额外微指令(如果移位计数为零,则标志不会被修改,所以 SHL r/m32, cl 具有对 FLAGS 的输入依赖性,并在 Skylake 上解码为 3 uop。不过,根据 Agner Fog 的测试,它在 Core2/Nehalem 上只有 1 uop。)
谢谢您的意见。