这个问题是在为16位自制CPU编写C编译器的背景下提出的。
我有12位操作数用于ALU指令(如ADD、SUB等)。
我可以从16个寄存器中发出三个操作数,或者从64个寄存器中发出两个操作数。
例如:
SUB A <- B - C (registers r0-r15)
vs
SUB A <- A - B (registers r0-r63)
对于C编译器及其作者来说,有三个操作数指令的16个寄存器比有两个操作数指令的64个寄存器更有用吗?
发布于 2016-05-17 15:44:40
使用非破坏性3操作数指令的16寄存器可能更好。
但是,您还应该考虑使用这些指令位执行其他有趣的操作。对于自制软件,您可能不关心为将来的扩展保留任何内容,也不想添加大量额外的操作码(就像PPC那样)。
ARM采用了一种有趣的方法,让每个指令有一个操作数通过桶形移位器,因此每条指令都是免费的“移位和任何”指令。即使在“拇指”模式下也支持这一点,其中最常见的指令只有16位。(ARM模式有传统的RISC 32位固定指令大小。它将其中的4个比特用于对每条指令进行预测执行。)
我记得我看到了一项关于在理论架构中将寄存器数量增加一倍的额外收益的研究,比如SPECint之类的。8->16可能是5%或10%,16->32只是一对夫妇,32->64甚至更小。
因此,16个整数寄存器在大多数情况下都是“足够”的,除非您经常使用int32_t
,因为每个这样的值都需要两个16位寄存器。x86-64只有16个GP寄存器,而且大多数函数都可以将它们的状态保持在相当舒适的寄存器中。即使在进行函数调用的循环中,ABI中也有足够的调用保留寄存器,因此泄漏/重新加载通常不必发生在循环中。
3-操作数指令在代码大小和指令计数方面的增加将大于避免偶尔发生溢出/重新装载的情况。gcc的输出必须时刻保持mov
,并使用lea
作为一个非破坏性的加/移。
如果您想优化您的CPU软件流水线以隐藏内存加载延迟(它比完全无序的执行更简单。),更多的寄存器是很好的,尤其是。如果您没有注册重命名。但是,我不知道静态指令调度有多好的编译器。这不再是一个热门话题,因为所有高性能CPU都是无序的.(OTOH,很多人们实际使用的软件都运行在智能手机的有序ARM CPU上。)我没有经验试图让编译器对顺序CPU进行优化,所以IDK依赖于它是多么的可行。
如果您的CPU非常简单,在负载处于正常运行状态时,它不能做任何其他事情,那么这可能并不重要。(这真的是手摇,因为我对简单设计的实用性还不太了解。即使是“简单”的顺序现代CPU也是流水线的。)
64寄存器正在进入“太多”的领域,在那里保存/恢复它们需要大量代码。内存的数量可能仍然可以忽略不计,但由于无法循环寄存器,因此需要64个指令。
如果您正在从头开始设计ISA,将查看 Agner Fog的CRISC提案 和由此产生的讨论。您的目标非常不同(高性能/功率预算64位CPU与简单16位CPU),因此您的ISAs当然会有很大的不同。然而,讨论可能会让你想到你没有考虑过的事情,或者你想尝试的想法。
发布于 2016-05-17 15:17:19
关于寄存器的数量,一般来说,当只有16个通用寄存器可用时,大多数C都可以编译成高效的机器代码(比如AMD64)。然而,有几个寄存器专门用于函数参数和一些标记为易失性可能是有益的-这意味着它们可以在任何函数中使用,但可能被任何被调用的函数破坏。增加到32个寄存器可能是有益的,但如果你有64个普通16位CPU的通用寄存器,我怀疑会有很大的改善。无论如何,您必须将在C函数中使用的大多数寄存器的原始内容保存到堆栈中。限制一个函数同时只使用7个寄存器(而不是37个)对C编译器来说仍然更有效(堆栈),即使有更多的寄存器可用。
这在很大程度上取决于您将要使用的C 呼叫约定。哪些寄存器将用于将值从调用方传递给被调用方,哪些寄存器被认为是易失性的,从堆栈推到/弹出的成本是多少,等等。通过使用寄存器窗口来管理您的寄存器和跨函数调用的堆栈使用情况,您可能会赢得更多。例如,Sun Sparc有一个完全为“本地”寄存器的寄存器窗口,与调用方共享的8个寄存器和将与任何被调用函数共享的8个寄存器。(此外,还可以处理8个全球登记册。)这样您就不用担心推到堆栈了,每次函数调用总是有一个16个寄存器的单独推送来改变执行指针,每次返回都会有一个16个寄存器弹出。英特尔ia64有一些类似的东西,但具有可配置的寄存器窗口大小。
但是,当保存中间结果非常重要(需要经常保存A)时,SUB C,A,B
只比SUB A,B
有一点优势,注册副本的简单寄存器非常昂贵。在大多数情况下,这似乎不太可能。
你会使用单独的浮点或固定点寄存器吗?
https://stackoverflow.com/questions/37274571
复制相似问题