William Faulker:「过去永远不会死,它甚至还没有过去。」
随着使用固定长度控制字来操纵乱序执行数据路径的现代 x86 处理器的出现,RISC 和 CISC 之间的混淆变得越来越严重。「RISC 和 CISC 正在融合」是一个在根本上就存在缺陷的观点,可以追溯到 1992 年 i486 的发布。其根源在于人们对指令集架构和物理处理器实现细节之间的差异普遍无知。
显然到目前为止,「RISC」和「CISC」这两种缩写术语掩盖了一个事实,即两种设计理念都不仅仅处理指令集的简单性或复杂性...... 从 RISC 和 CISC 的发展史以及两种方法试图解决的问题看,这两个术语都很荒谬…… 关于「RISC 与 CISC」的辩论早已结束,现在必须要进行一个更细致入微、更有趣的讨论,即基于硬件和软件、ISA 和实现等方面进行讨论。
英特尔的 x86 在「引擎的外表」下并没有 RISC 引擎。它们通过依赖于将 x86 指令映射到机器操作或复杂指令的机器操作序列的解码 / 执行的方案来实现 x86 指令集架构,然后这些操作通过微架构找到自己的方式,遵守有关数据依赖的各种规则,最终确定时序。
完成这个过程的「微操作」有 100 多比特,携带各种复杂特异的信息,不能由编译器直接生成,且不一定是单周期。但最重要的是,它们只是一种微架构技巧,而 RISC/CISC 是关于指令集架构的。微操作的想法不是受 RISC 启发的、「类 RISC」的,或者说与 RISC 完全无关。而是我们的设计团队找到了一种方法,打破了非常复杂的指令集的复杂性,也摆脱了竞争型微处理器中存在的限制。
我们开发了一个松散地基于 29000 指令集的微型 ISA。一些额外的控制字段将微指令的大小扩展到 59 位。其中一些简化并加速了超标量控制逻辑,其他的用于提供特定于 x86 的功能,这些功能对于性能非常重要,因此不能用微指令序列来合成。但是这些微指令仍然遵循基本的 RISC 原则:简单的寄存器到寄存器操作,对寄存器指定符和其他字段进行固定位置编码,并且每个操作不多于一个内存引用。因此我们称它们为 RISC 操作,或简称为 ROPs。这种简单、通用的特性为实现更复杂的 x86 操作提供了灵活性,从而有利于保持执行逻辑相对简单。
RISC 微架构最关键的一点是 x86 指令集的复杂性止于解码器,并且在很大程度上对乱序执行内核是透明的。这种方法只需要很少的额外控制复杂度,而不需要乱序的 RISC 执行来实现乱序的 x86 执行。任务切换的 ROP 序列看起来并不比简单指令串的 ROP 序列复杂。执行内核的复杂性被有效地从架构的复杂性中分离出来,而不是复合起来的。
ISA 并非无关紧要。x86 ISA 非常复杂,因为长期以来人们一直在进行小的更改和补丁,以向 ISA 中添加更多功能,而 ISA 确实已没有空间容纳此类新功能。 复杂的 x86 ISA 使解码成为瓶颈。x86 指令的长度在 1 到 15 个字节之间,计算长度非常复杂。在开始解码下一条指令之前需要知道指令的长度。如果您想每个时钟周期解码 4 或 6 条指令,这肯定是个问题!英特尔和 AMD 现在都在不断增加微操作缓存来克服这个瓶颈。而 ARM 有固定大小的指令,所以这个瓶颈不存在,也不需要微操作缓存。
x86 的另一个问题是它需要很长的管道来处理复杂性。分支误预测惩罚等于 pipeline 的长度。因此,他们正在添加越来越复杂的分支预测机制,其中包含大型分支历史信息表和分支目标缓冲区。当然,所有这些都需要更多的芯片空间和更多的功耗。 尽管有这些负担,x86 ISA 还是相当成功的。这是因为它可以为每条指令做更多的工作。