PUSH/POP指令被认为是RISC还是CISC?

11

我在一次面试中被问到PUSHPOP指令是RISC还是CISC指令。我回答说它们是RISC指令,但他们告诉我这些指令实际上是CISC指令。我提出ARM(一个常见的RISC实现)有这些指令,但他们指出ARM已经不再是纯粹的RISC,而是混合结构。

我在网上找不到任何明确的证据支持其中一种观点。 PUSHPOP指令是否真的被认为是CISC架构的标志,或者它们会出现在RISC系统上?为什么?


我了解过,他们说Arm已经混合了,不再是纯粹的RISC架构了。 - user1998844
3
ARM一直支持使用单条指令执行push或pop操作,但不是通过专门的PUSH/POP指令实现的。而是通过自动后增和前减寻址方式来完成。 - Ross Ridge
1
在x86 / x86-64上,push和pop被大量优化。如此优化,以至于它们特殊处理对于连续推送的堆栈指针依赖性 - 第二个推送不会等待第一个推送更新堆栈指针。Push只是向(通常)预减的堆栈指针寄存器进行存储。具有特定寻址模式的存储是否特别是CISC或RISC?这是可以争论的。 - doug65536
1
@doug65536:对“堆栈引擎”的更清晰解释,它通过跟踪在乱序核心中活动的RSP值的偏移量来使push/pop操作高效:https://dev59.com/vVoV5IYBdhLWcg3wJ78b。幸运的是,由于专利共享协议,AMD和英特尔都拥有非常相似的堆栈引擎。 - Peter Cordes
1
@Jonas 这个问题不是关于特定架构的,而且 [tag:risc] 标签实际上是适用的。(你可以争论它是否超出了 Stack Overflow 的范围,因为它涉及到 CPU 架构分类学(即有关命名的哲学辩论),而不是编程...)话虽如此,在元社区没有同意将其删除之前,请不要从适用的问题中删除它。 - Peter Cordes
2个回答

11
RISC是“精简指令集”的缩写(通常为LOAD reg,STORE register,ADD register,CMP register,Branch conditional和其他几个指令)。
这意味着复杂的指令往往不能实现比简单指令序列更有用的效果,尤其是如果用于实现这些复杂指令的额外逻辑反而被用来使简单的RISC指令运行更快,则更是如此。
PUSH和POP基本上是STORE / LOAD indirect和ADD常量到寄存器的简单组合。因此,如果一个专用的寄存器用于堆栈指针,那么PUSH和POP可以很容易地模拟,并且快速的流水线机器可能会执行PUSH和POP与相应的RISC指令一样快。因此,大多数人认为PUSH和POP是CISC指令;它们并没有给你带来很多好处。
如果考虑CALL(== PUSH PC + JMP)和RET(POP PC),情况会变得更加有趣。在适当的RISC架构上,这些也很容易模拟。但是,POP PC会产生一个流水线泡沫,因为处理器很难预测新PC将在哪里,因此无法进行预取。由于内存在时间上“远离”,这可能是具有许多子例程调用的代码中的主要性能抑制因素。
在这里,人们希望采用CISC。你真正想要的是一种方法来预测返回PC。许多现代CPU通过在硬件中保留“阴影调用堆栈”来实现这一点。每个CALL将PC推入内存堆栈,并且推入阴影堆栈;每个RET从内存堆栈弹出一个PC值,但使用阴影堆栈的顶部条目来预测指令流的流程,该条目基本上具有零时间访问(当然,还会弹出阴影堆栈)。这样指令流就不会被中断,因此CISC机器在性能上胜出。
(人们想知道是否有许多寄存器的RISC机器,编译叶函数调用始终使用寄存器存储返回PC,可能与阴影堆栈一样有效。Sun Sparc在其寄存器窗口中有点这样做。)

这告诉我们的是,RISC与CISC之争过于简化了设计权衡。你需要的是简单,除非更多的复杂性实际上给你带来了好处。例如,使用硬件中的IEEE浮点运算比使用RISC指令模拟要快得多。

因此,大多数现代计算机不是明显的RISC或CISC。性能分析会做出选择。


2
ARM是这些权衡的一个很好的例子。它主要是RISC(load/store machine),但在代码大小值得时,会偏离RISC教条:使用一些带有寄存器位域的push/pop指令。(在ARM模式下,而不是thumb模式下,store-multiple / load-multiple可以使用任何寄存器作为基础。)还有一些不那么简单的寻址模式。例如,与MIPS相比,ARM要少得多的RISCy。 - Peter Cordes

2
“RISC”对不同人来说有不同的含义。我看过的定义包括:
  • 减少指令数量

  • 固定大小的指令(可能有很多)

  • 一个Load/Store体系结构(可能有很多可变大小的指令)

  • 不将指令转化为微操作并且没有“微码”的CPU(例如8086、8088、80186等,但不包括80486、Pentium等)

  • 以上任意2个或以上的组合

  • 为了降低开发成本而牺牲性能的任何东西

是否认为PUSH/POP指令属于RISC或CISC?

是的(取决于应用RISC或CISC定义时的具体情况,PUSH/POP指令可以被认为是RISC、CISC、两者都是,或两者都不是)。

大多数情况下,问题本身是一种虚假的二元分法;就像问“灰色是黑色还是白色?”一样。


我认为对于一个 RISC 来说,在现代实际的 RISC (其中一些并不太关注象牙塔式的 RISC 纯度)中最重要的方面实际上是单个指令的最大复杂度:它们可以由一个(流水线)执行单元完成,例如 FP 乘法或整数加法,但不包括像 x86 的 rep movsb 或 ARM 的 push {r0,r1,r4,lr} 这样的东西(带有要推送的寄存器的位图,使其几乎不可能在恒定时间内运行。ARM 不太像 RISC,这是它违反标准 RISC 纯度的例外之一;相关信息请参见 链接)。 - Peter Cordes
我曾见到将其称为Reduced Insn Set Complexity,这在我看来是更有用的首字母缩略词扩展。它意味着成为一种Load-Store ISA,并绝对不含任何内存目标RMW指令,因为这将意味着多个不同的执行单元。 - Peter Cordes
@PeterCordes:说实话,我认为“RISC”是基于这样一个想法的,即简单的CPU可以以更高的频率运行,并完成与复杂的CPU(在较低频率下)相同数量的工作;而且(在1990年代末期),每个人都发现增加时钟频率是一场巨大的灾难,并开始“创造性地重新定义”“RISC”的含义,试图挽救十年的营销炒作;结果导致“RISC” CPU比CISC最初更加复杂(因为“增加并行性”取代了“增加时钟频率”作为性能提升的手段)。 - Brendan
没错,早期的RISC理念通常更倾向于保持硬件简单,即使这会损害每个时钟周期的性能。我想我的定义归结为“流水线良好,特别是超标量/乱序执行”,或者因为早期的RISC机器实现了RISC-ness而获得了这种好处,从而允许高时钟频率(甚至在功耗墙之前,例如R10k的管道宽度)。这并不是太过牵强:在标量顺序流水线中,“良好的流水线”仍然是好的,包括固定宽度指令以便于获取/解码,然后现在允许有效的并行解码。 - Peter Cordes
哲学真正转变的地方只在细节上。当时,是的,保持硬件简单,这样它就可以更高速地运行,而不仅仅是在流水线中表现良好,以便用更少的操作完成更多的工作。例如,Andy Glew评论缺乏对TLB一致性的硬件支持,其中一个指令的uops内存目标adc会增加额外的uops成本:他说我加入P6时是RISC的支持者,我的态度是“让软件(微码)来做”,这是关于后悔这个选择的评论线程的一部分(引用在此)。 - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接