Which is more useful at an assembly level, 64 registers or three operand instructions?

时间:2016-05-17 11:16:51

标签: c assembly compiler-construction

This question is in the context of writing a C compiler for a 16 bit homebrew CPU.

I have 12 bits of operand for ALU instructions (such as ADD, SUB, AND, etc.).

I could give instructions three operands from 16 registers or two operands from 64 registers.

e.g.

SUB A <- B - C  (registers r0-r15)

vs

SUB A <- A - B  (registers r0-r63)

Are sixteen registers, with three-operand instructions, more useful than 64 registers with two-operand instructions, to C compilers and their authors?

2 个答案:

答案 0 :(得分:4)

16个带有非破坏性3操作数指令的寄存器可能更好。

但是,您还应该考虑使用这些指令位做一些有趣的事情。对于自制软件,您可能不关心为将来的扩展保留任何内容,并且不希望添加大量额外的操作码(like PPC does)

ARM采用了一种有趣的方法,即每条指令都有一个操作数通过the barrel shifter,因此每条指令都是免费的“shift-and-whatever”指令。即使在“拇指”模式下也支持此功能,其中最常见的指令仅为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以进行软件流水线操作以隐藏内存加载延迟(which is simpler than full out-of-order execution),那么更多寄存器是很棒的,尤其是。如果你没有注册重命名。但是,我不确定static instruction scheduling的编译器有多好。这不再是一个热门话题,因为所有高性能CPU都是无序的。 (OTOH,人们实际使用的许多软件都在智能手机中的有序ARM CPU上运行。)我没有尝试让编译器针对有序CPU进行优化的经验,因此IDK依赖于它的可行性是多么可行这一点。

如果您的CPU非常简单,当负载在飞行中时它无法执行任何其他操作,这可能无关紧要。 (这真的是手工波浪,因为我不太了解简单设计的实用性。即使是“简单”的现代CPU也是流水线的。)

64个寄存器进入“太多”区域,保存/恢复它们需要大量代码。内存量可能仍然可以忽略不计,但由于您无法遍历寄存器,因此需要64条指令。

如果您是从头开始设计ISA,会查看Agner Fog's CRISC proposal以及由此产生的讨论。你的目标是非常不同的(高性能/功耗预算64位CPU与简单的16位),所以你的ISA当然会有很大差异。然而,讨论可能会让你想到你没有考虑过的事情,或想要尝试的想法。

答案 1 :(得分:2)

关于寄存器的数量,一般来说,我认为当只有16个通用寄存器可用时,大多数C可以编译为高效的机器代码(如AMD64)。但是,让一些寄存器专用于函数参数并且一些寄存器标记为volatile可能是有益的 - 这意味着它们可以在任何函数内部使用,但可能被任何被调用的函数破坏。增加到32个寄存器可能是有益的,但是如果你有一个普通的16位CPU的64个通用寄存器,我怀疑会有很多改进。无论如何,您必须将要在C函数中使用的大多数寄存器的原始内容保存到堆栈中。将函数限制为仅同时使用7个寄存器(而不是37个)可能仍然对C编译器更有效(堆栈),即使有更多可用的寄存器也是如此。

很大程度上取决于您将使用的C calling convention。哪些寄存器用于将值从调用者传递给被调用者,哪些寄存器被认为是易失性的,从栈中推送/弹出的成本是多少等等。使用Register Window可以获得更多在函数调用中管理寄存器和堆栈使用。例如,Sun Sparc的注册窗口完全是8,本地&#34;寄存器,8个与调用者共享的寄存器和8个将与任何被调用函数共享的寄存器。 (此外,还可以解决8个全局寄存器。)这样你不必担心推送到堆栈,每次函数调用总会有16个寄存器同时改变执行指针和每次退货都有一个16个寄存器。 Intel ia64具有类似的功能,但具有可配置的寄存器窗口大小。

但是,SUB C,A,B仅比SUB A,B略有优势,因为保留中间结果非常重要(A需要经常保留),而注册副本的简单寄存器相当昂贵。在大多数情况下,这似乎不太可能。

您是否会使用单独的浮点或定点寄存器?