本节以MIPS、PA-RISC、PowerPC、SPARC v9和LoongArch为例,比较不同RISC指令系统的指令格式、寻址模式和指令功能,以加深对RISC的了解。
2.5.1 指令格式比较
五种RISC指令集的指令格式如图2.8所示。在寄存器类指令中,操作码都由操作码(OP)和辅助操作码(OPX)组成,操作数都包括两个源操作数(RS)和一个目标操作数(RD);立即数类指令都由操作码、源操作数、目标操作数和立即数(Const)组成,立即数的位数各有不同;跳转类指令大同小异,PA-RISC与其他四种差别较大。总的来说,五种RISC指令集的指令编码主要组成元素基本相同,只是在具体摆放位置上存在差别。
图 2.8: 五种RISC指令集的指令编码格式
2.5.2 寻址方式比较
五种指令集的寻址方式如表2.6所示。MIPS、SPARC和LoongArch只支持四种常用的寻址方式,PowerPC和PA-RISC支持的寻址方式较多。
表 2.6: 五种指令集的寻址方式比较
寻址方式 |
MIPS |
PowerPC |
PA-RISC |
SPARC |
LoongArch |
寄存器寻址 |
Y |
Y |
Y |
Y |
Y |
立即数寻址 |
Y |
Y |
Y |
Y |
Y |
偏移量寻址 |
Y |
Y |
Y |
Y |
Y |
变址寻址 |
Y(仅浮点) |
Y |
Y |
Y |
Y |
比例变址寻址 |
Y |
||||
自增/自减+偏移量寻址 |
Y |
Y |
|||
自增/自减+变址寻址 |
Y |
Y |
注:表2.6中Y表示支持该寻址方式。
2.5.3 公共指令功能
RISC指令集都有一些公共指令,如load-store、算术运算、逻辑运算和控制流指令。不同指令集在比较和转移指令上区别较大。
1)load-store指令。load指令将内存中的数据取入通用寄存器,store指令将通用寄存器中的数据存至内存中。表2.7给出了LoongArch指令集的load-store指令实例。当从内存中取回的数据位宽小于通用寄存器位宽时,后缀没有U的指令进行有符号扩展,即用取回数据的最高位(符号位)填充目标寄存器的高位,否则进行无符号扩展,即用数0填充目标寄存器的高位。
表 2.7: LoongArch指令集的load-store指令
指令 |
指令功能 |
LD.B |
取字节 |
LD.BU |
取字节,无符号扩展 |
LD.H |
取半字 |
LD.HU |
取半字,无符号扩展 |
LD.W |
取字 |
LD.WU |
取字,无符号扩展 |
LD.D |
取双字 |
ST.B |
存字节 |
ST.H |
存半字 |
ST.W |
存字 |
ST.D |
存双字 |
2)ALU指令。ALU指令都是寄存器型的,常见的ALU指令包括加、减、乘、除、与、或、异或、移位和比较等。表2.8为LoongArch指令集的ALU指令实例。其中带有“.W”后缀的指令操作的数据位宽为32位(字),带有“.D”后缀的指令操作的数据位宽为64位(双字)。
表 2.8: LoongArch指令集的ALU指令
指令 |
指令功能 |
ADD.W |
字加 |
ADDI.W |
字加立即数 |
SUB.W |
字减 |
ADD.D |
双字加 |
ADDI.D |
双字加立即数 |
SUB.D |
双字减 |
SLT |
有符号数比较小于置1 |
SLTI |
有符号数立即数比较小于置1 |
SLTU |
无符号数比较小于置1 |
SLTUI |
无符号数立即数比较小于置1 |
AND |
与 |
OR |
或 |
XOR |
异或 |
NOR |
或非 |
ANDI |
与立即数 |
ORI |
或立即数 |
XORI |
异或立即数 |
LU12I.W |
加载20位立即数到高位 |
SLL.W |
字逻辑左移变量位 |
SRL.W |
字逻辑右移变量位 |
SRA.W |
字算术右移变量位 |
SLLI.W |
字逻辑左移常量位 |
SRLI.W |
字逻辑右移常量位 |
SRAI.W |
字算术右移常量位 |
SLL.D |
双字逻辑左移变量位 |
SRL.D |
双字逻辑右移变量位 |
SRA.D |
双字算术右移变量位 |
SLLI.D |
双字逻辑左移常量位 |
SRLI.D |
双字逻辑右移常量位 |
SRAI.D |
双字算术右移常量位 |
MUL.W |
字乘取低半部分 |
MULH.W |
有符号字乘取高半部分 |
MULH.WU |
无符号字乘取高半部分 |
MUL.D |
双字乘取低半部分 |
MULH.D |
有符号双字乘取高半部分 |
MULH.DU |
无符号双字乘取高半部分 |
DIV.W |
有符号字除取商 |
DIV.WU |
无符号字除取商 |
MOD.W |
有符号字除取余 |
MOD.WU |
无符号字除取余 |
DIV.D |
有符号双字除取商 |
DIV.DU |
无符号双字除取商 |
MOD.D |
有符号双字除取余 |
MOD.DU |
无符号双字除取余 |
3)控制流指令。控制流指令分为绝对转移指令和相对转移指令。相对转移的目标地址是当前的PC值加上指令中的偏移量立即数;绝对转移的目标地址由寄存器或指令中的立即数给出。表2.9为LoongArch指令集中控制流指令的实例。
表 2.9: LoongArch指令集的控制流指令
指令 |
指令功能 |
JIRL |
相对寄存器偏移跳转并链接 |
B |
无条件相对转移 |
BL |
无条件相对转移并链接 |
BEQ |
等于时相对转移 |
BNE |
不等时相对转移 |
BLT |
有符号比较小于时相对转移 |
BGE |
有符号比较大于等于时相对转移 |
BLTU |
无符号比较小于时相对转移 |
BGEU |
无符号比较大于等于时相对转移 |
BEQZ |
等于0相对转移 |
BNEZ |
不等于0时相对转移 |
在条件转移指令中,转移条件的确定有两种方式:判断条件码和比较寄存器的值。SPARC采用条件码的方式,整数运算指令置条件码,条件转移指令使用条件码进行判断。MIPS和LoongArch的定点转移指令使用寄存器比较的方式进行条件判断,而浮点转移指令使用条件码。PowerPC中包含一个条件寄存器,条件转移指令指定条件寄存器中的特定位作为跳转条件。PA-RISC有多种选择,通常通过比较两个寄存器的值来决定是否跳转。
RISC指令集中很多条件转移采用了转移延迟槽(Delay Slot)技术,程序中条件转移指令的后一条指令为转移延迟槽指令。在早期的静态流水线中,条件转移指令在译码时,后一条指令即进入取指流水级。为避免流水线效率的浪费,有些指令集规定转移延迟槽指令无论是否跳转都要执行。MIPS、SPARC和PA-RISC都实现了延迟槽,但对延迟槽指令是否一定执行有不同的规定。对于当今常用的动态流水线和多发射技术而言,延迟槽技术则没有使用的必要,反而成为指令流水线实现时需要特殊考虑的负担。Alpha、PowerPC和LoongArch均没有采用转移延迟槽技术。
2.5.4 不同指令系统的特色
除了上述公共功能外,不同的RISC指令集经过多年的发展形成了各自的特色,下面举例介绍其各自的主要特色。
1)MIPS部分指令特色。前面介绍过访存地址的对齐问题,当确实需要使用不对齐数据时,采用对齐访存指令就需要复杂的地址计算、移位和拼接等操作,这会给大量使用不对齐访存的程序带来明显的代价。MIPS指令集实现了不对齐访存指令LWL/LWR。LWL指令读取访存地址所在的字并将访存地址到该字中最低位的字节拼接到目标寄存器的高位,LWR指令读取访存地址所在的字并将访存地址到该字中最高位的字节拼接到目标寄存器的低位。上述字中的最低位和最高位字节会根据系统采用的尾端而变化,不同尾端下,LWL和LWR的作用相反。例如,要加载地址1至4的内容到R1寄存器,不同尾端的指令和效果如图2.9所示。
图 2.9: 不同尾端下的LWL/LWR指令效果
LWL和LWR指令设计巧妙,兼顾了使用的便利性和硬件实现的简单性,是MIPS指令集中比较有特色的指令。
2)SPARC部分指令特色。SPARC指令系统有很多特色,这里挑选寄存器窗口进行介绍。在SPARC指令系统中,一组寄存器(SPARC v9中规定为8~31号寄存器)可用于构成窗口,窗口可有多个,0~7号寄存器作为全局寄存器。寄存器窗口的好处在于函数调用时可不用保存现场,只需切换寄存器组。
3)PA-RISC部分指令特色。PA-RISC指令集最大的特色就是Nullification指令,除了条件转移指令,其他指令也可以根据执行结果确定下一条指令是否执行。例如ADDBF(add and branch if false)指令在完成加法后,检查加法结果是否满足条件,如果不满足就进行转移。一些简单的条件判断可以用Nullification指令实现。
4)PowerPC部分指令特色。在RISC结构中,PowerPC的寻址方式、指令格式和转移指令都是最多的,甚至支持十进制运算,因此又被称为“RISC中的CISC”。表2.10给出了分别用PowerPC指令和Alpha指令实现的简单程序示例。实现同样的循环程序,PowerPC只需要6条指令,Alpha则需要10条指令,原因就在于PowerPC的指令功能较强。例如其中的LFU(load with update)和STFU(store with update)指令,除了访存外还能自动修改基址寄存器的值;FMADD可以在一条指令中完成乘法和加法;转移指令BC可同时完成计数值减1和条件转移。
表 2.10: PowerPC和Alpha汇编对比
源代码:for(k=0;k<512;k++) x[k]=r*x[k]+t*y[k]; |
|
PowerPC代码 |
Alpha代码 |
r3+8指向x |
r1指向x |
LOOP: |
LOOP: |
5)LoongArch部分指令特色。LoongArch指令集的一个特色是其二进制翻译扩展1。LoongArch的二进制翻译扩展提供了百余条指令和一些系统资源来支持软件实现高效的二进制翻译。例如,把X86指令翻译为RISC类的指令集有个影响翻译效率的因素:eflags标志位处理。因为X86指令集中,一个运算指令除了产生运算结果,还会同时产生是否进位、是否溢出等多>个标志位。完全模拟这样的一条指令的语义一般需要30条以上常规RISC指令。LoongArch提供了一系列专门指令用于产生和使用相应的标志位,在保持RISC指令风格的同时消除了这个瓶颈。目前业界最先进的二进制翻译系统可以实现80%左右的翻译运行效率,LoongArch致力于通过深度的软硬件协同进一步提升效率,实现多个主流指令集到龙芯指令集几乎无损的翻译,最终达到“消灭指令集”或者说软件定义指令集的目的。

