配色: 字号:
RISC-V指令集体系结构手册:压缩指令集手册
2016-11-11 | 阅:  转:  |  分享 
  




RISC-V压缩指令集手册

版本1.9



警告!这个规范的初稿在成为标准之前,可能会被修改,因此基于此规范

初稿的实现,可能与未来的标准规范并不相符。



(翻译:要你命3000@EETOP翻译版本1.0)



AndrewWaterman,YunsupLee,DavidPatterson,KrsteAsanovi?

CSDivision,EECSDepartment,UniversityofCalifornia,Berkeley

{waterman|yunsup|pattrsn|krste}@eecs.berkeley.edu

2015年11月5日





该文档同时也是UCB/EECS-2015-209技术报告



Copyright?2010-2015,TheRegentsoftheUniversityofCalifornia.Allrightsreserved.

2



1.1介绍

本文档是从RISC-V用户级ISA规范节录出来的,用于描述RISC-V标准压缩指令集扩展

的当前初稿,标准压缩指令集扩展,被命名为“C”,通过对常用操作加入短的16位指令编

码,减少了静态和动态代码大小。这个“C”扩展可以添加到任何基本的ISA上(RV32、RV64、

RV128),我们使用术语RVC来指明这种情形。典型的,程序中大约50%~60%的RISC-V指令

可以被RVC指令代替,导致大约25%~30%代码大小的减少。

我们相信这个初稿将和最终的RV32C和RV64C设计相接近(看起来现在形成RV128C并

不成熟),但我们仍然需要一轮或者多轮的评价,因此定名为1.9版本。请将您的评价发送

到isa-dev@lists.riscv.org的邮件列表的isa-dev。



1.2概述

RVC使用一种简单的压缩方案,以便在下列情形时,提供更短的16位版本的32位RISC-V

指令:

?立即数或者地址偏移量较小时

?其中一个寄存器是零寄存器(x0)、ABI链接寄存器(x1)或者ABI栈寄存器(x2)

?目标寄存器和第一个源寄存器相同

?最常见情况下使用了8个寄存器



C扩展与其它所有标准扩展兼容。C扩展允许16位指令可以自由地和32位指令混合执

行,并运行32位指令可以在任何16位边界开始。(译者注:一般情况下,32位指令必须“天

然地”对齐到32位存储器地址边界上,否则会导致非对齐存储器访问异常。)

在原来的32位指令上面去掉32位对齐约束要求,可以大幅度提高代码密

度。

压缩指令编码在绝大多数情况下,在RV32C、RV64C和RV128C下都是一样的,但如表0.3

所示,少数操作码依据基本ISA的宽度有不同的用途。例如,更宽地址空间的RV64C和RV128C

变种,需要额外的操作码来完成压缩load和store64位整数值,而RV32C使用与单精度浮

点值一样的操作码来进行压缩load和store。类似的,RV128C需要额外的操作码来完成压缩

load和store128位整数值,而在RV32C和RV64C中,它们使用与双精度浮点值一样的操作

码来进行压缩load和store。如果要实现标准C扩展,必须提供相应的压缩浮点load和store

指令,而不管相关的标准浮点扩展(F和/或D扩展)是否实现。另外,RV32C包含一条压

缩跳转和链接指令,以压缩短范围的子过程调用,同样的操作码被用于RV64C和RV128C的

压缩ADDIW指令。

双精度load和store是静态和动态指令的重要部分,因此想到要把它们加

入到RV32C和RV64C编码中。

虽然对于当前支持的ABI编译出来的基准测试程序(benchmark)来说,

单精度load和store并不十分重要,但是对于那些在硬件上仅支持单精度浮点

Copyright?2010-2015,TheRegentsoftheUniversityofCalifornia.Allrightsreserved.

3



单元、ABI仅支持单精度浮点数的微控制器来说,在基准程序上,单精度load

和store的使用频度至少和双精度load和store相同。因此,想到在RV32C中

提供这些指令的压缩支持。

对于微控制器来说,短范围子过程调用在小的二进制代码中非常常见,因

此想到在RV32C中加入它们。

虽然对不同的基本寄存器宽度、不同的用途,重用操作码会增加一定的文

档复杂度,但是它对实现的影响非常小,即使是对同时支持多个基本ISA寄存

器宽度也是如此。压缩浮点load和store指令使用了与更宽整数load和store

相同的指令格式、相同的寄存器区分符。

RVC是在这样的约束下设计的,即每一条RVC指令被扩展成在基本ISA(RV32I/E、RV64I

或者RV128I)或者F、D标准扩展中的一条32位指令。采用这条约束,有如下一些好处:

?硬件设计可以在译码时简单地扩展RVC指令,简化了验证,并使得对现有微体系

结构的改动最小化。

?编译器可以不处理RVC扩展部分,留到汇编器和链接器来进行代码压缩,虽然一

个压缩敏感的编译器通常可以生成更好代码。

我们感到通过在C和IFD指令之间进行简单的一对一映射,得到的多种复

杂度减少,其远远超过通过增加一些仅仅支持C扩展的指令以获得稍微高一些

的编码密度而带来的收益,也远高于允许将多条IFD指令编码入一条C指令而

带来的收益。

值得重视的是,C扩展并不是作为一个单独的ISA而被设计的,意味着它需要与一个基

本ISA一块使用。

变长指令集已经被用来提高代码密度使用很长时间了。例如,在1950后

期研发的IBMStretch[2]使用了一个具有32位和64位指令的ISA,其中有些32

位指令是64位指令的压缩版本。Stretch也使用了这样一个概念,就是在一些

较短的指令格式中,限制了可寻址的寄存器集合;具有短分支指令仅能引用索

引寄存器中的一个。后来的IBM360体系结构[1]支持一种简单的变长指令编码,

支持16位、32位或者48位指令格式。

在1963年,CDC发布了Cray设计的CDC6600[3],它是RISC的前辈,引

入了一个大量寄存器的load-store体系结构,其指令长度15位和30位两种。

后来的Cray-1设计使用了一种非常相似的指令格式,它采用了16位和32位

指令长度。

自1980以来的早期RISCISA,都是选择性能而不是代码大小,这在工作站

环境中是情理之中的,但对嵌入式系统并不合理。因此,ARM和MIPS在后续

的ISA版本中,都通过在标准32位指令集之外提供16位指令集,来达到更小

的代码大小。压缩过后的RISCISA相对于它们的完全版本,可以减少大约

25%~30%的代码大小,生成的代码远远小于80x86生成的代码。这个结果令一

些人感到震惊,因为他们认为一个变长CISCISA产生的代码应当比仅提供16

位和32位格式的RISCISA产生的代码要小。

由于原来的RISCISA并没有为这些计划外的压缩指令留有足够的操作码空

间,因此它们被当做一个新的ISA进行开发。这意味着编译器需要为单独的压

Copyright?2010-2015,TheRegentsoftheUniversityofCalifornia.Allrightsreserved.

4



缩ISA使用不同的代码生成器。第一个压缩RISCISA扩展(就是ARMThumb

和MIPS16)仅仅使用了一种固定16位指令长度,这在静态代码大小上得到了

很好的减少,但是导致动态指令数目的增加,这也导致了与原始固定32位长

度指令相比,性能的下降。这导致了16位和32位指令长度混合的第二代压缩

RISCISA(就是ARMThumb2、microMIPS、PowerPCVLE)的研发,于是可以获

得与纯32位指令相似的性能,同时有巨大的代码大小减少。不幸的是,这些

不同代次的压缩ISA彼此之间、与原始为压缩ISA之间,并不兼容,导致了文

档、实现和软件工具上的巨大复杂性。

在通用64位ISA中,当前只有PowerPC和microMIPS支持压缩指令格式。

令人感到惊讶的是,考虑到静态代码大小和动态取指带宽是重要的指标,当前

移动平台上最为流行的64位ISA(ARMv8)并没有包含一个压缩指令格式。

虽然对于较大的系统来说,静态代码大小并不是一个主要关心的因素,但是在

服务器运行商业负载(通常具有一个大的指令工作集)的时候,取指带宽可能

会成为一个主要的瓶颈。

得益于25年以来的经验,RISC-V被设计成从外部支持压缩指令,为RVC

保留了足够的操作码空间,使得它可以在基本ISA之上(与其它的标准扩展一

起)作为一个简单的扩展加入。RVC的理念在于为嵌入式应用程序减小代码大

小,为所有应用程序提高性能和能耗效率,因为可获得更少的指令cache缺失

(译者注:指令长度缩小,可使得一个Cacheline保存更多的指令,或者意味

着在同样大小的指令Cache中容纳更多的指令)。Waterman指出RVC少取了

25%~30%的指令位,减少了20~25%的指令Cache缺失,或者等效于指令Cache

大小翻倍同样的性能[4]。

1.3压缩指令格式

表0.1:压缩16位RVC指令格式给出了8种压缩指令格式。CR、CI和CSS格式可以使

用任何的32个RVI寄存器,但是CIW、CL、CS和CB被限制只能使用所有32个寄存器中的

8个。表0.2:CIW、CL、CS和CB格式中rs1’、rs2’和rd’字段三位指向的寄存器给出了这些

常用的寄存器,对应于x8到x15。注意到有一个单独版本的load和store指令,将栈指针作

为基地址寄存器使用,这是因为保存到栈和从栈恢复太常见了,并且它们使用CI和CSS格

式,以便能够访问到所有32个数据寄存器。CIW格式为ADDI4SPN指令提供了一个8位的

立即数。

RISC-V的ABI已经做了修改,将常用寄存器映射到寄存器x8-x15。这通过

提供一个连续的、自然对齐的寄存器编号,将简化解压缩的译码器,并且与

RV32E子集基本规范相兼容,在那里只有16个整数寄存器。

压缩的、基于寄存器的浮点load和store指令也分别使用了CL和CS格式,将8个寄存

器映射到f8到f15。

标准RISC-V调用规范,将最常用的浮点寄存器映射到f8-f15,这将允许与

整数寄存器编号相同的寄存器解压缩译码。

Copyright?2010-2015,TheRegentsoftheUniversityofCalifornia.Allrightsreserved.

5



这些格式被设计成将两个源寄存器区分符放在所有指令中的同一个地方,因此可以去掉

目的寄存器字段。如果存在完整的5位目的寄存器区分符,它与32位RISC-V编码中的位置

是一样的。当立即数是符号扩展的时候,符号扩展总是从第12位开始。立即数字段被打乱

了,就如同在基本规范中一样,以便减少用于立即数的多路选择器数量(immediatemux)。

指令格式中的立即数字段是被打乱的而不是按顺序存放的,这是为了保证

在每一条指令中尽可能让尽量多的位处在相同的位置,这将简化实现。例如,

立即数的位17-10,总是来自指令的相同位位置。5个其他的立即数位(5、4、

3、2、1)只有两个指令位的来源,而4个其他的立即数位(9、7、6、2)有

3个指令位的来源,而1个(第8位)有四个来源。

对于许多RVC指令来说,不允许0立即数,而且x0不是一个有效的5位寄存器区分符。

这个限制,为其他需要较少操作数位的指令,释放了编码空间。



格式含义1514131211109876543210

CR寄存器funct4rd/rs1rs2op

CI立即数funct3immrd/rs1immop

CSS栈相关storefunct3immrs2op

CIW宽立即数funct3immrd’op

CLLoadfunct3immrs1’immrd’op

CSStorefunct3immrs1’immrd’op

CB分支funct3offsetrs1’offsetop

CJ跳转funct3jumptargetop

表0.1:压缩16位RVC指令格式



RVC寄存器编号000001010011100101110111

整数寄存器编号x8x9x10x11x12x13x14x15

整数寄存器ABI名字s0s1a0a1a2a3a4a5

浮点寄存器编号f8f9f10f11f12f13f14f15

浮点寄存器ABI名字fs0fs1fa0fa1fa2fa3fa4fa5

表0.2:CIW、CL、CS和CB格式中rs1’、rs2’和rd’字段三位指向的寄存器

1.4Load和store指令

为了增加16位指令能够访问的范围,使用零扩展立即数的数据传输指令,其数据值的

大小被放大了多倍:对字×4、对双字×8、对四字×16。

RVC提供了两种类型的load和store。一种使用ABI栈指针x2作为基址寄存器,并可

定位到任何数据寄存器。另外一种可以引用8个基址寄存器之一,并引用8个数据寄存器之

一。



Copyright?2010-2015,TheRegentsoftheUniversityofCalifornia.Allrightsreserved.

6





基于栈指针的load和store

1513121176210

funct3immrdimmop

31552



C.LWSP偏移量[5]dest≠0偏移量[4:2|7:6]C2

C.LDSP偏移量[5]dest≠0偏移量[4:3|8:6]C2

C.LQSP偏移量[5]dest≠0偏移量[4|9:6]C2

C.FLWSP偏移量[5]dest偏移量[4:2|7:6]C2

C.FLDSP偏移量[5]dest偏移量[4:3|8:6]C2



这些指令使用CI格式。

C.LWSP指令将一个32位数值从存储器读入寄存器rd中。其有效地址的计算是通过将

零扩展的偏移量×4,然后加上栈指针x2形成的。它被扩展为lwrd,offset[7:2](x2)指令。

C.LDSP是一条RV64C/RV128C仅有指令,它将一个64位数值从存储器读入寄存器rd中。

其有效地址的计算是通过将零扩展的偏移量×8,然后加上栈指针x2形成的。它被扩展为ld

rd,offset[8:3](x2)指令。

C.LQSP指令是一条RV128C仅有指令,它将一个128位数值从存储器读入寄存器rd中。

其有效地址的计算是通过将零扩展的偏移量×16,然后加上栈指针x2形成的。它被扩展为

lqrd,offset[9:4](x2)指令。

C.FLWSP是一条RV32FC仅有指令,它将一个单精度浮点数值从存储器读入浮点寄存器

rd中。其有效地址的计算是通过将零扩展的偏移量×4,然后加上栈指针x2形成的。它被

扩展为flwrd,offset[7:2](x2)指令。

C.FLDSP是一条RV32DC/RV64DC仅有指令,它将一个双精度浮点数值从存储器读入浮点

寄存器rd中。其有效地址的计算是通过将零扩展的偏移量×8,然后加上栈指针x2形成的。

它被扩展为fldrd,offset[8:3](x2)指令。



15131276210

funct3immrs2op

3652



C.SWSP偏移量[5:2|7:6]srcC2

C.SDSP偏移量[5:3|8:6]srcC2

C.SQSP偏移量[5:4|9:6]srcC2

C.FSWSP偏移量[5:2|7:6]srcC2

C.FSDSP偏移量[5:3|8:6]srcC2



这些指令使用CSS格式。

C.SWSP指令将寄存器rs2中的32位值保存到存储器中。其有效地址的计算是通过将零

扩展的偏移量×4,然后加上栈指针x2形成的。它被扩展为swrs2,offset[7:2](x2)指令。

Copyright?2010-2015,TheRegentsoftheUniversityofCalifornia.Allrightsreserved.

7



C.SDSP是一条RV64C/RV128C仅有指令,它将寄存器rs2中的64位值保存到存储器中。

其有效地址的计算是通过将零扩展的偏移量×8,然后加上栈指针x2形成的。它被扩展为

sdrs2,offset[8:3](x2)指令。

C.SQSP是一条RV128C仅有指令,它将寄存器rs2中的128位值保存到存储器中。其

有效地址的计算是通过将零扩展的偏移量×16,然后加上栈指针x2形成的。它被扩展为sq

rs2,offset[9:4](x2)指令。

C.FSWSP是一条RV32FC仅有指令,它将浮点寄存器rs2中的单精度浮点数值保存到存

储器中。其有效地址的计算是通过将零扩展的偏移量×4,然后加上栈指针x2形成的。它被

扩展为fswrs2,offset[7:2](x2)指令。

C.FSDSP是一条RV32DC/RV64DC仅有指令,它将浮点寄存器rs2中的双精度浮点数值

保存到存储器中。其有效地址的计算是通过将零扩展的偏移量×8,然后加上栈指针x2形成

的。它被扩展为fsdrs2,offset[8:3](x2)指令。



基于寄存器的Load和store

1513121097654210

funct3immrs1’immrd’op

333232



C.LW偏移量[5:3]基址偏移量[2|6]destC0

C.LD偏移量[5:3]基址偏移量[7:6]destC0

C.LQ偏移量[5|4|8]基址偏移量[7:6]destC0

C.FLW偏移量[5:3]基址偏移量[2|6]destC0

C.FLD偏移量[5:3]基址偏移量[7:6]destC0



这些指令使用CL格式。

C.LW指令将一个32位数值从存储器读入寄存器rd’中。其有效地址的计算是通过将零

扩展的偏移量×4,然后加上寄存器rs1’中的基址形成的。它被扩展为lwrd’,offset[6:2](rs1’)

指令。

C.LD是一条RV64C/RV128C仅有指令,它将一个64位数值从存储器读入寄存器rd’中。

其有效地址的计算是通过将零扩展的偏移量×8,然后加上寄存器rs1’中的基址形成的。它

被扩展为ldrd’,offset[7:3](rs1’)指令。

C.LQ是一条RV128C仅有指令,它将一个128位数值从存储器读入寄存器rd’中。其有

效地址的计算是通过将零扩展的偏移量×16,然后加上寄存器rs1’中的基址形成的。它被扩

展为lqrd’,offset[8:4](rs1’)指令。

C.FLW是一条RV32FC仅有指令,它将一个单精度浮点数值从存储器读入浮点寄存器rd’

中。其有效地址的计算是通过将零扩展的偏移量×4,然后加上寄存器rs1’中的基址形成的。

它被扩展为flwrd’,offset[6:2](rs1’)指令。

C.FLD是一条RV32DC/RV64DC仅有指令,它将一个双精度浮点数值从存储器读入浮点寄

存器rd’中。其有效地址的计算是通过将零扩展的偏移量×8,然后加上寄存器rs1’中的基址

形成的。它被扩展为fldrd’,offset[7:3](rs1’)指令。



Copyright?2010-2015,TheRegentsoftheUniversityofCalifornia.Allrightsreserved.

8





1513121097654210

funct3immrs1’immrs2’op

333232



C.SW偏移量[5:3]基址偏移量[2|6]srcC0

C.SD偏移量[5:3]基址偏移量[7:6]srcC0

C.SQ偏移量[5|4|8]基址偏移量[7:6]srcC0

C.FSW偏移量[5:3]基址偏移量[2|6]srcC0

C.FSD偏移量[5:3]基址偏移量[7:6]srcC0



这些指令使用CS格式。

C.SW指令将寄存器rs2’中的32位值保存到存储器中。其有效地址的计算是通过将零扩

展的偏移量×4,然后加上寄存器rs1’中的基址形成的。它被扩展为swrs2’,offset[6:2](rs1’)

指令。

C.SD是一条RV64C/RV128C仅有指令,它将寄存器rs2’中的64位值保存到存储器中。其

有效地址的计算是通过将零扩展的偏移量×8,然后加上寄存器rs1’中的基址形成的。它被

扩展为sdrs2’,offset[7:3](rs1’)指令。

C.SQ是一条RV128C仅有指令,它将寄存器rs2’中的128位值保存到存储器中。其有效

地址的计算是通过将零扩展的偏移量×16,然后加上寄存器rs1’中的基址形成的。它被扩展

为sqrs2’,offset[8:4](rs1’)指令。

C.FSW是一条RV32FC仅有指令,它将浮点寄存器rs2’中的单精度浮点数值保存到存储

器中。其有效地址的计算是通过将零扩展的偏移量×4,然后加上寄存器rs1’中的基址形成

的。它被扩展为fswrs2’,offset[6:2](rs1’)指令。

C.FSD是一条RV32DC/RV64DC仅有指令,它将浮点寄存器rs2’中的双精度浮点数值保

存到存储器中。其有效地址的计算是通过将零扩展的偏移量×8,然后加上寄存器rs1’中的

基址形成的。它被扩展为fsdrs2’,offset[7:3](rs1’)指令。

1.5控制转移指令

RVC提供了无条件跳转指令和条件分支指令。如同基本RVI指令一样,所有RVC控制转

移指令的偏移量都是2字节的倍数。



151312210

funct3immop

3112



C.J偏移量[11|4|9:8|10|6|7|3:1|5]C1

C.JAL偏移量[11|4|9:8|10|6|7|3:1|5]C1



这些指令使用CJ格式。

C.J指令执行一个无条件控制转移。偏移量被符号扩展后,与pc相加形成跳转目标地址。

C.J指令因此可以在±2KB范围内进行跳转。C.J指令被扩展为jalx0,offset[11:1]。

Copyright?2010-2015,TheRegentsoftheUniversityofCalifornia.Allrightsreserved.

9



C.JAL指令是一条RV32C仅有指令,它执行与C.J指令相同的操作,但是它还将在跳转

指令后的指令地址(pc+2)写入到链接寄存器x1中。C.JAL指令被扩展为jalx1,offset[11:1]。



15121176210

funct4rs1rs2op

4552



C.JRsrc≠00C2

C.JALRsrc≠00C2



这些指令使用CR格式。

C.JR(jumpregister)指令执行一个无条件控制转移到寄存器rs1的地址。C.JR指令被扩

展为jalrx0,rs1,0。

C.JALR(jumpandlinkregister)指令执行与C.JR指令相同的操作,但是它还将在跳转指

令后的指令地址(pc+2)写入到链接寄存器x1中。C.JALR指令被扩展为jalrx1,rs1,0。

严格来说,C.JALR指令并没有被精确地扩展为基本RVI指令,因为那个被

加到pc上以形成链接地址的值是2,而不是基本ISA中的4,但是同时支持偏

移量2个字节和4个字节,只对微体系结构产生微小的影响。

15131210976210

funct3immrs1’immop

33352



C.BEQZ偏移量[8|4:3]src偏移量[7:6|2:1|5]C1

C.BNEZ偏移量[8|4:3]src偏移量[7:6|2:1|5]C1



这些指令使用CB格式。

C.BEQZ指令执行条件控制转移。偏移量被符号扩展后,与pc相加形成跳转目标地址。

C.BEQZ指令因此可以在±256B范围内进行跳转。如果寄存器rs1’的值是0,则C.BEQZ指令

产生控制转移(takethebranch)。这条指令被扩展为beqrs1’,x0,offset[8:1]。

C.BNEZ指令定义相似,只是当寄存器rs1’的值是非0值,则指令产生控制转移(takethe

branch)。这条指令被扩展为bners1’,x0,offset[8:1]。

1.6整数计算指令

RVC提供了一些用于整数算术和常数生成的指令。



整数常数-生成指令

两条常数-生成指令都使用CI格式,并且可以以任何整数寄存器为目标。

Copyright?2010-2015,TheRegentsoftheUniversityofCalifornia.Allrightsreserved.

10





1513121176210

funct3immrdimm[4:0]op

31552



C.LI立即数[5]dest≠0立即数[4:0]C1

C.LUI非零立即数[17]dest≠{0,2}非零立即数[16:12]C1



C.LI指令将符号扩展的6位立即数imm,写入寄存器rd中。C.LI指令仅在rd≠x0时才

是有效的。C.LI指令被扩展为addird,x0,imm[5:0]。

C.LUI指令将非零的6位立即数写入到目标寄存器的17-12位,并将目标寄存器的低12

位清零,然后将第17位符号扩展到整个目标寄存器的高位部分。C.LUI寄存器仅在rd≠{x0,x2}

且立即数不等于0时才是有效的。C.LUI指令被扩展为luird,nzimm[17:12]。



整数寄存器-立即数指令

这些整数寄存器-立即数指令都使用CI格式,并在认为非x0整数寄存器和一个6位立即

数之间进行操作。立即数不能为0。

1513121176210

funct3imm[5]rd/rs1imm[4:0]op

31552



C.ADDI非零立即数[5]dest非零立即数[4:0]C1

C.ADDIW立即数[17]dest≠0立即数[4:0]C1

C.ADDI16SP非零立即数[9]2非零立即数[4|6|8:7|5]C1



C.ADDI指令将非零的、符号扩展的6位立即数加到寄存器rd的值上,将结果写入rd。

C.ADDI指令被扩展为addird,rd,nzimm[5:0]。

C.ADDIW指令是一条RV64C/RV128C仅有的指令,它执行相同的计算,但是生成一个32

位的结果,然后符号扩展结果到64位。C.ADDIW指令被扩展为addiwrd,rd,imm[5:0]。对

C.ADDIW指令而言,立即数可以是0,这对应于sext.wrd。

C.ADDI16SP指令的操作码与C.LUI指令相同,但是使用x2作为目标寄存器。C.ADDI16SP

指令将一个非零的、符号扩展的6位立即数加到栈指针寄存器(sp=x2)上,此处立即数被

放大16倍,其范围为(-512,496)。C.ADDI16SP指令用于在过程的头部和尾部对栈指针进行

调整。它被扩展为addix2,x2,nzimm[9:4]。

在标准RISC-V调用约定中,栈指针sp总是16字节对齐的。



Copyright?2010-2015,TheRegentsoftheUniversityofCalifornia.Allrightsreserved.

11





15131254210

funct3immrd’op

3832



C.ADDI4SPN非零立即数[5:4|9:6|2|3]destC0



C.ADDI4SPN指令是一条CIW格式的、RV32C/RV64C仅有的指令,它将一个零扩展的、

非零立即数,乘以4,然后加到栈指针x2上,并将结果写入rd’。这条指令用于产生指向分

配在栈中的变量的指针,它被扩展为addird’,x2,zimm[9:2]。



1513121176210

funct3shamt[5]rd/rs1shamt[4:0]op

31552



C.SLLI移位次数[5]dest≠0移位次数[4:0]C2



C.SLLI指令是一条CI格式的指令,它对寄存器rd中的数值进行逻辑左移操作,并将结

果写入rd。移位次数被编码到shamt字段,此处对RV32C,shamt[5]必须为0。对于

RV32C/RV64C,移位次数必须为非零值。对于RV128C,一个shamt为0,编码为移位64次。

C.SLLI指令被扩展为sllird,rd,shamt[5:0],除了对于RV128C且shamt=0,则被扩展为sllird,rd,

64。



1513121110976210

funct3shamt[5]funct2rd’/rs1shamt[4:0]op

312352



C.SRLI移位次数[5]C.SRLIdest移位次数[4:0]C1

C.SRAI移位次数[5]C.SRAIdest移位次数[4:0]C1



C.SRLI指令是一条CB格式的指令,它对寄存器rd’中的数值进行逻辑右移操作,并将结

果写入rd’。移位次数被编码到shamt字段,此处对RV32C,shamt[5]必须为0。对于

RV32C/RV64C,移位次数必须为非零值。对于RV128C,一个shamt为0,编码为移位64次。

而且对于RV128C,移位次数是符号扩展的,因此合法的移位次数是1-31、64、96-127。C.SRLI

指令被扩展为srlird’,rd’,shamt[5:0],除了对于RV128C且shamt=0,则被扩展为srlird’,rd’,64。

C.SRAI指令与C.SRLI指令相似,不过它执行一个算术右移操作。C.SRAI指令被扩展为srai

rd’,rd’,shamt[5:0]。

左移通常比右移更为常用,因为左移被频繁地用于对地址值进行放大操作。

因此右移被分配了较小的编码空间,并且处于一个其他立即数都是符号扩展的

编码区域中。对于RV128,我们决定使得6位移位次数值也是符号扩展的。除

了减少硬件译码复杂度之外,我们相信右移96-127次要比64-95更为有用,

这可以用于从128位地址指针的高部分提取标签(tag)。我们注意到RV128C

Copyright?2010-2015,TheRegentsoftheUniversityofCalifornia.Allrightsreserved.

12



并不像RV32C和RV64C那样已经确定下来,以允许对使用128位地址空间的

典型代码进行评估。

1513121110976210

funct3imm[5]funct2rd’/rs1imm[4:0]op

312352



C.ANDI立即数[5]C.ANDIdest立即数[4:0]C1



C.ANDI指令是一条CB格式的指令,它在寄存器rd’的值和一个符号扩展的6位立即数

之间进行按位AND运算,并将结果写入到rd’中。C.ANDI指令被扩展为andird’,rd’,imm[5:0]。



整数寄存器-寄存器指令



15121176210

funct4rd/rs1rs2op

4552



C.MVdest≠0src≠0C0

C.ADDdest≠0src≠0C0



这些指令使用CB格式。

C.MV指令将寄存器rs2的值复制到寄存器rd中。C.MV指令被扩展为addrd,x0,rs2

C.ADD指令将寄存器rd的值与寄存器rs2的值相加,并将结果写入到寄存器rd中。C.ADD

指令被扩展为addrd,rd,rs2。



151097654210

funct6rd’/rs1’functrs2’op

63232



C.ANDdestC.ANDsrcC1

C.ORdestC.ORsrcC1

C.XORdestC.XORsrcC1

C.SUBdestC.SUBsrcC1

C.ADDWdestC.ADDWsrcC1

C.SUBWdestC.SUBWsrcC1



这些指令使用CS格式。

C.AND指令在寄存器rd’和rs2’之间执行按位AND操作,并将结果写入寄存器rd’。C.AND

指令被扩展为andrd’,rd’rs2’。

C.OR指令在寄存器rd’和rs2’之间执行按位OR操作,并将结果写入寄存器rd’。C.OR指

Copyright?2010-2015,TheRegentsoftheUniversityofCalifornia.Allrightsreserved.

13



令被扩展为orrd’,rd’rs2’。

C.XOR指令在寄存器rd’和rs2’之间执行按位XOR操作,并将结果写入寄存器rd’。C.XOR

指令被扩展为xorrd’,rd’rs2’。

C.SUB指令将寄存器rd’的值减去rs2’的值,并将结果写入寄存器rd’。C.SUB指令被扩展

为subrd’,rd’rs2’。

C.ADDW是一条RV64C/RV128C仅有的指令,它将寄存器rd’的值加上rs2’的值,将结果

的低32位进行符号扩展,再写入rd’中。C.ADDW指令被扩展为addwrd’,rd’,rs2’。

C.SUBW是一条RV64C/RV128C仅有的指令,它将寄存器rd’的值减去rs2’的值,将结果

的低32位进行符号扩展,再写入rd’中。C.SUBW指令被扩展为subwrd’,rd’,rs2’。

这组的6条指令,每条指令并没有提供太多的好处,但是也没有占用很多

的编码空间,并且实现起来也直截了当,作为一组指令是在静态和动态压缩中

提供了一定的提高。



预定义非法指令

1513121176210

00000

31552



00000

一条所有位都是0的16位指令,被永久的保留为一条非法指令。

我们将全零指令保留为非法指令,以帮助捕获试图执行被零填充的或者不

存在的存储器空间,产生自陷。全零值不应当被任何非标准扩展重新定义。类

似的,我们保留全1指令(在RISC-V变长编码方式中,对应于非常长的指令)

为非法指令,以捕获在不存在存储器区域的另外一种常见值。(译者注:如果

处理器试图访问不存在的存储器空间地址,外部硬件常规做法是总是返回全0,

或者返回全1)



NOP指令

1513121176210

funct3imm[5]rd/rs1imm[4:0]op

31552



C.NOP000C1



Copyright?2010-2015,TheRegentsoftheUniversityofCalifornia.Allrightsreserved.

14



C.NOP指令是一条CI格式指令,它不改变任何用户可见状态,除了推进pc之外。C.NOP

指令被编码为c.addix0,0并且被扩展为addix0,x0,0。



断点指令



151211210

funct40op

4102



C.EBREAK0C0



调试器可以使用C.EBREAK指令,它将被扩展为ebreak指令,并导致控制被转移回到调

试环境。C.EBREAK指令的操作码与C.ADD指令的操作码相同,但是其rd和rs2都是0,因

此也可以使用CR格式。



1.7优化寄存器保存/恢复代码大小

在函数入口、出口处的寄存器保存/恢复代码占据了静态代码大小的很重要组成部分。

RVC中的基于栈指针的load和store指令可以有效地减少一半的保存恢复静态代码大小,同

时通过减少动态指令带宽,提高了执行性能。

标准RISC-V软件工具链提供了另外一种更进一步减少保存/恢复静态代码大小的方法,

这是以降低性能来交换的。与将寄存器保存/恢复代码嵌入到每个函数中不同,寄存器保存

代码被一条跳转并链接指令代替,它将调用一个子过程将寄存器复制到栈中,然后再返回函

数。寄存器恢复代码被一条跳转指令代替,它将跳转到一个子过程从栈中恢复寄存器,然后

再跳转到恢复的返回地址。

图0.1给出了当直接应用到SPECCPU2006基准测试程序的所有函数上时,这些子过程

对静态代码和动态指令数目的影响。平均来说,代码大小减少了4%,而动态指令数目增大

了3%。

当将-Os(减少代码大小)标志传递给gcc时,嵌入函数的保存/恢复代码

被替换成调用保存/恢复子过程。



在其他ISA中,另外一种减少保存/恢复代码大小的机制是load-multiple、

store-multiple指令。我们考虑过将它们加入到RISC-V中,但是注意到这些指

令由下面这些不足:

?这些指令导致复杂的处理器实现。

?对于虚拟存储器系统来说,一些数据访问可能处在物理存储器中,而

另外一些数据访问不在,这就需要一种新的机制,以重启已经部分执

Copyright?2010-2015,TheRegentsoftheUniversityofCalifornia.Allrightsreserved.

15



行了的指令。(译者注:比如LDMr2,r3指令,假设需要读取到r2数

据在存储器中,而r3不在。那么在处理异常时,需要重新启动LDM

指令的一部分,因为r2上次已经读取了)

?与其他的RVC指令不同,Loadmultiple和Storemultiple并没有对等的

IFD。

?与其他的RVC指令不同,编译器必须在生成指令和分配寄存器时,

特别注意这些指令,以便最大化它们被按序保存和恢复,因为它们将

来会被按序保存和恢复的。

?简单的微体系结构实现,将会限制在load和storemultiple指令周围,

如何调度其他指令,导致潜在的性能损失。

?理想的按顺序寄存器分配可能与为CIW、CL、CS和CB格式选择的特

别寄存器冲突。

虽然一些体系结构设计师可能会得出不同的结论,但是我们决定去掉load

和storemultiple支持,而是使用调用保存/恢复子过程的软件方法,来获得最

大限度地代码大小减少。



图0.1:压缩的函数入口、出口处的子过程,对静态代码大小和动态指令数目的影响。

1.8RVC指令集列表

表0.3给出了RVC主要操作码的映射表。对于指令长度超过16位的指令,其最低两位

都是1,包括那些处于基本ISA中的指令。一些指令仅在某些操作数时是有效的;当无效时,

它们要么被标记为RES,意味着这个操作码被保留给未来的标准扩展;要么被标记为NSE,

意味着这个操作码被保留给未来的非标准扩展;或者被标记为HINT,意味着这个操作码被

保留给未来的微体系结构提示(hint)。在提示没有效果的实现上,标记为HINT的指令必须

作为空操作指令执行。

Copyright?2010-2015,TheRegentsoftheUniversityofCalifornia.Allrightsreserved.

16





HINT指令被设计成支持未来增加微体系结构提示,这些提示可能影响性

能,但是不能影响体系结构状态。HINT编码已经被选定,因此简单的实现可

以忽略HINT编码,并将HINT指令作为常规指令执行,不改变体系结构状态。

例如,C.ADD指令的目标寄存器如果是x0,那么它是一条HINT指令,此处5

位的rs2字段编码了HINT的细节。然而,一个简单的实现可以简单地把HINT

当作一条目标是x0的加法指令执行,这时将被忽略(即NOP指令)。



表0.4-表0.6列出了RVC指令。



inst[15:13]000001010011100101110111

inst[1:0]

00ADDI4SPN

FLD

FLD

LQ

LW

FLW

LD

LD

Reserved

FSD

FSD

SQ

SW

FSW

SD

SD

RV32

RV64

RV128

01ADDI

JAL

ADDIW

ADDIW

LILUI/ADDI16SPMISC-ALUJBEQZBNEZ

RV32

RV64

RV128

10SLLI

FLDSP

FLDSP

LQ

LWSP

FLWSP

LDSP

LDSP

J[AL]R/MV/ADD

FSDSP

FSDSP

SQ

SWSP

FSWSP

SDSP

SDSP

RV32

RV64

RV128

11>16位

表0.3:RVC操作码映射表



1514131211109876543210

0000000非法指令

000nzimm[5:4|9:6|2|3]rd’00C.ADDI4SPN(RES,nzimm=0)

001imm[5:3]rs1’imm[7:6]rd’00C.FLD(RV32/64)

001imm[5:4|8]rs1’imm[7:6]rd’00C.LQ(RV128)

010imm[5:3]rs1’imm[2|6]rd’00C.LW

011imm[5:3]rs1’imm[2|6]rd’00C.FLW(RV32)

011imm[5:3]rs1’imm[7:6]rd’00C.LD(RV64/128)

10000Reserved

101imm[5:3]rs1’imm[7:6]rs2’00C.FSD(RV32/64)

101imm[5:4|8]rs1’imm[7:6]rs2’00C.SQ(RV128)

110imm[5:3]rs1’imm[2|6]rs2’00C.SW

111imm[5:3]rs1’imm[2|6]rs2’00C.FSW(RV32)

111imm[5:3]rs1’imm[7:6]rs2’00C.SD(RV64/128)

表0.4:RVC指令列表,00部分





Copyright?2010-2015,TheRegentsoftheUniversityofCalifornia.Allrightsreserved.

17





1514131211109876543210

00000001C.NOP

000nzimm[5]rs1/rd≠0nzimm[4:0]01C.ADDI(RES,nzimm=0;HINT,rd=0)

001offset[11|4|9:8|10|6|7|3:1|5]01C.JAL(RV32)

001imm[5]rs1/rd≠0imm[4:0]01C.ADDIW(RV64/128;RES,rd=0)

010imm[5]rs1/rd≠0imm[4:0]01C.LI(HINT,rd=0)

011nzimm[9]2nzimm[4|6|8:7|5]01C.ADDI16SP(RES,nzimm=0)

011nzimm[17]rs1/rd≠{0,2}nzimm[16:12]01C.LUI(RES,nzimm=0;HINT,rd=0)

100nzimm[5]00rs1’/rd’nzimm[4:0]01C.SRLI(RV32NSE,nzimm[5]=1)

100000rs1’/rd’001C.SRLI64(RV128;RV32/64HINT)

100nzimm[5]01rs1’/rd’nzimm[4:0]01C.SRAI(RV32NSE,nzimm[5]=1)

100001rs1’/rd’001C.SRAI64(RV128;RV32/64HINT)

100imm[5]10rs1’/rd’imm[4:0]01C.ANDI

100011rs1’/rd’00rs2’01C.SUB

100011rs1’/rd’01rs2’01C.XOR

100011rs1’/rd’10rs2’01C.OR

100011rs1’/rd’11rs2’01C.AND

100111rs1’/rd’00rs2’01C.SUBW(RV64/128;RV32RES)

100111rs1’/rd’01rs2’01C.ADDW(RV64/128;RV32RES)

100111——10rs2’01Reserved

100111——11rs2’01Reserved

101offset[11|4|9:8|10|6|7|3:1|5]01C.J

110offset[8:4|3]rs1’offset[7:6|2:1|5]01C.BEQZ

111offset[8:4|3]rs1’offset[7:6|2:1|5]01C.BNEZ

表0.5:RVC指令列表,01部分



Copyright?2010-2015,TheRegentsoftheUniversityofCalifornia.Allrightsreserved.

18



1514131211109876543210

000nzimm[5]rd≠0nzimm[4:0]10C.SLLI(RV32NSE,nzimm[5]=1)

0000rd≠0010C.SLLI64(RV128;RV32/64HINT)

001imm[5]rdimm[4:3|8:6]10C.FLDSP(RV32/64)

001imm[5]rd≠0imm[4|9:6]10C.LQSP(RV128;RES,rd=0)

010imm[5]rd≠0imm[4:2|7:6]10C.LWSP(RES,rd=0)

011imm[5]rdimm[4:2|7:6]10C.FLWSP(RV32)

011imm[5]rd≠0imm[4:3|8:6]10C.LDSP(RV64/128;RES,rd=0)

1000rs1≠0010C.JR(RES,rs1=0)

1000rd≠0rs2≠010C.MV(HINT,rd=0)

10010010C.EBREAK

1001rs1≠0010C.JALR

1001rd≠0rs2≠010C.ADD(HINT,rd=0)

101imm[5:3|8:6]rs210C.FSDSP(RV32/64)

101imm[5:4|9:6]rs210C.SQSP(RV128)

110imm[5:2|7:6]rs210C.SWSP

111imm[5:2|7:6]rs210C.FSWSP(RV32)

111imm[5:3|8:6]rs210C.SDSP(RV64/128)

表0.6:RVC指令列表,10部分



Copyright?2010-2015,TheRegentsoftheUniversityofCalifornia.Allrightsreserved.

19



1.9指令压缩统计

下面一些表格给出了一些数据,我们使用这些数据来指导选择将什么指令包含到RVC

中。

表0.7列出了标准RVC指令,按照使用频度从高到低排序,给出了对静态代码大下,

单条指令的贡献,然后运行了总共3个实验。对RV32,RVC在Dhrystone减少了静态代码

24.5%,在CoreMark减少了30.9%。对RV64,RVC在SPECint减少了静态代码26.3%,在SPECfp

减少了25.8%,在Linuxkernel减少了31.1%。

表0.8根据典型动态频率对RVC指令进行了排序。对RV32,RVC在Dhrystone减少了

取指的动态字节29.2%,在CoreMark减少了29.3%。对RV64,RVC在SPECint减少了取指的

动态字节26.9%,在SPECfp减少了22.4%,在启动Linuxkernel时减少了26.11%。



Copyright?2010-2015,TheRegentsoftheUniversityofCalifornia.Allrightsreserved.

20



指令

RV32GCRV64GC

MAXDhry-

stone

Core-

Mark

SPEC

2006

SPEC

2006

Linux

Kernel

C.MV1.785.034.063.6255.03

C.LWSP4.512.82.890.490.144.51

C.LDSP———3.24.444.44

C.SWSP4.192.452.760.450.184.19

C.SDSP———2.753.793.79

C.LI2.993.742.812.352.863.74

C.ADDI2.163.281.871.190.953.28

C.ADD0.511.641.942.280.912.28

C.LW2.11.6820.740.622.1

C.LD———1.142.092.09

C.J0.321.711.630.971.531.71

C.SW1.590.850.730.270.261.59

C.JR1.521.160.490.441.051.52

C.BEQZ0.381.140.760.551.241.24

C.SLLI0.061.090.570.930.571.09

C.ADDI16SP0.190.260.320.421.011.01

C.SRLI00.810.050.120.310.81

C.BNEZ0.190.530.530.320.80.8

C.SD———0.250.790.79

C.ADDIW———0.770.50.77

C.JAL0.380.590.05——0.59

C.ADDI4SPN0.570.370.450.50.30.57

C.LUI0.320.370.440.560.520.56

C.SRAI0.130.480.070.030.030.48

C.ANDI00.420.20.070.350.42

C.FLD000.160.3900.39

C.FLDSP00.020.20.3100.31

C.FSDSP0.130.090.150.2600.26

C.SUB0.250.090.130.060.110.25

C.AND000.070.030.210.21

C.FSD000.080.18—0.18

C.OR0.060.180.090.040.140.18

C.JALR0.130.070.170.10.140.17

C.ADDW———0.160.120.16

C.EBREAK00.02000.080.08

C.FLW000.05——0.05

C.XOR00.040.010.010.030.04

C.SUBW———0.040.030.04

C.FLWSP000.03——0.03

C.FSW000.02——0.02

C.FSWSP000.02——0.02

总计24.4630.9225.7825.9825.98—

表0.7:按典型静态频率排序的RVC指令。表中的数据给出了每条指令在静态代码大小中节约的

比例。这个列表是由通过一个压缩汇编器产生的,它对RISC-VGCC编译器的输出进行处理。对

RV32GC使用了Dhrystone、CoreMark和SPECCPU2006,对RV64GC使用了SPEC

CPU2006和Linuxkernel3.14.29版本。表中的横线表示该指令没有在这个地址大小下面

的定义。



Copyright?2010-2015,TheRegentsoftheUniversityofCalifornia.Allrightsreserved.

21



指令

RV32GCRV64GC

MAXDhry-

stone

Core-

Mark

SPEC

2006

Linux

Kernel

C.ADDI3.73.914.361.264.36

C.LW4.153.891.090.874.15

C.MV1.934.011.71.374.01

C.BNEZ0.442.570.473.623.62

C.SW3.551.620.320.683.55

C.LD——1.433.293.29

C.SWSP3.260.320.20.033.26

C.LWSP2.960.480.140.022.96

C.LI2.221.470.812.732.73

C.ADD2.072.692.641.842.69

C.SRLI02.480.20.382.48

C.JR2.070.340.460.422.07

C.FLD001.6301.63

C.SDSP——1.141.381.38

C.J0.440.460.331.351.35

C.LDSP——1.341.311.34

C.ANDI0.151.30.10.231.3

C.ADDIW——1.261.031.26

C.SLLI0.151.11.240.891.24

C.SD——0.391.131.13

C.BEQZ0.590.950.740.760.95

C.AND000.210.750.75

C.SRAI00.720.020.010.72

C.JAL0.590.26——0.59

C.ADDI4SPN0.440.160.070.050.44

C.FLDSP000.400.4

C.ADDI16SP0.130.180.280.380.38

C.FSD000.2900.29

C.FSDSP000.2500.25

C.ADDW——0.190.040.19

C.XOR00.190.060.020.19

C.OR0.150.080.050.040.15

C.SUB0.150.030.050.040.15

C.LUI0.020.060.090.10.1

C.JALR00.050.050.030.05

C.SUBW——0.040.020.04

C.EBREAK00000

C.FLW00———

C.FLWSP00———

C.FSW00———

C.FSWSP00———

总计29.1829.2924.0326.11—

表0.8:按典型动态频率排序的RVC指令。表中的数据给出了每条指令在动态代码大小中节

约的比例。这个列表是通过执行来获得的。对RV32GC执行了Dhrystone、CoreMark,对RV64GC

执行了SPECCPU2006,对于SPEC,我们使用了参考输入集。Linux启动时间包括引导

内核、执行init进程、执行shell以及poweroff命令。



Copyright?2010-2015,TheRegentsoftheUniversityofCalifornia.Allrightsreserved.

22





参考文献

[1]G.M.Amdahl,G.A.Blaauw,andJr.F.P.Brooks.ArchitectureoftheIBMSystem/360.

IBMJournalofR.&D.,8(2),1964.

[2]WernerBuchholz,editor.Planningacomputersystem:ProjectStretch.McGraw-Hill

BookCompany,1962.

[3]JamesE.Thornton.ParalleloperationintheControlData6600.InProceedingsofthe

October27-29,1964,FallJointComputerConference,PartII:VeryHighSpeed

ComputerSystems,AFIPS''64(Fall,partII),pages33-40,1965.

[4]AndrewWaterman.ImprovingenergyefficiencyandreducingcodesizewithRISC-V

compressed.Master''sthesis,UniversityofCalifornia,Berkeley,2011.

献花(0)
+1
(本文系guitarhua首藏)