以前的 ISA 为了缩短代码长度而显著扩展了指令和指令格式的数量,比如添加了一些只有两个(而不是三个)操作数的指令,减小立即数域,等等。ARM 和 MIPS 为了能缩小代码,重新设计了两遍指令集,ARM 设计出了 ARM Thumb 和 Thumb 2,MIPS 先后设计出了 MIPS16 和 microMIPS。这些新的 ISA 为处理器和编译器增加了负担,同时也增加了汇编语言程序员的认知负担。

RV32C 采用了一种新颖的方法:每条短指令必须和一条标准的 32 位 RISC-V 指令一一对应。此外,16 位指令只对汇编器和链接器可见,并且是否以短指令取代对应的宽指令由它们决定。编译器编写者和汇编语言程序员可以幸福地忽略 RV32C 指令及其格式,他们能感知到的则是最后的程序大小小于大多数其它 ISA 的程序。图 7.1 是 RV32C 扩展指令集的图形化表示。

为了能在一系列的程序上得到良好的代码压缩效果,RISC-V 架构师精心挑选了 RVC 扩展中的指令。同时,基于以下的三点观察,架构师们成功地将指令压缩到了 16 位。

  • 第一,对十个常用寄存器(a0-a5,s0-s1,sp 以及 ra)访问的频率远超过其他寄存器;
  • 第二,许多指令的写入目标是它的源操作数之一;
  • 第三,立即数往往很小,而且有些指令比较喜欢某些特定的立即数。

因此,许多 RV32C 指令只能访问那些常用寄存器;一些指令隐式写入源操作数的位置;几乎所有的立即数都被缩短了,load 和 store 操作只使用操作数整数倍尺寸的无符号数偏移量。

有什么不同之处?RV32C 中没有字节或半字指令,因为其他指令对代码长度的影响更大。 第 9 页图 1.5 中 Thumb-2 相对于 RV32C,在代码长度上更有优势,这是由于 Load and Store Multiple 对于过程(函数、子例程)进入和退出时可以节省不少代码。为了保证能和 RV32G 中的指令一一对应,RV32C 中没有包括它们。而 RV32G 为了降低高端处理器的实 现复杂性而省略了这些指令。由于 Thumb-2 是独立于 ARM-32 的 ISA,但是处理器可以在 两个 ISA 间切换。为了支持两套 ISA,硬件必须有两个指令解码器,一个用于 ARM-32, 一个用于 Thumb-2。RV32GC 是一个单独的 ISA,因此 RISC-V 处理器只需要一个解码器。

为什么有些架构师不考虑 RV32C?

超标量处理器在一个时钟周期内同时取几条指令,因此译码阶段可能成为超标量处理器的瓶颈。

macrofusion 是另一个例子,其中指令解码器把 RISC-V 指令组合为更加强大的指令 来执行(参见第一章)。在这种情况下,16 位 RV32C 指令和 32 位 RV32I 指令混杂在一起增 加了解码的复杂度,从而使得高性能处理器中在一个时钟周期内完成解码变得更难。

[! note] RV32C 真的是独一无二的吗? RV32 指令在 RV32IC 中无法区分。

Thumb-2 实际上是一个单独的 ISA,包含 16 位指令和 ARMv7 中大多数(但不是全部)的指令。例如,在 Thumb-2 中有 Compare and Branch on Zero,而 ARMv7 中没有,而对于 Reverse Subtarct with Carry 则正好相反。 microMIPS 也不是 MIPS32 的超集。例如,microMIPS 计算分支偏移量的时候乘以 2,但在 MIPS32 中则为 4。RISC-V 中总是乘以 2。