riscv指令集在设计的过程中,吸取了其他指令集增量式设计方法的经验教训,采用模块化的设计方法
riscv的每条指令宽度为32位,但是可以支持64位的寻址。这是因为指令集是基于寄存器加载和存储的体系结构设计,所有的数据加载、存储和处理都是在通用寄存器中完成的。而riscv的通用寄存器个数为32,及2^5,所以在指令编码中只需要5位宽就可以索引所有的通用寄存器。源寄存器1+源寄存器2+目标寄存器只需要15位就可以索引到,剩余17位用于指令分类。32位宽的指令比64位宽的指令,提高了指令密度以及instructions cache命中率
加载指令
l{d|w|h|b}{u} rd, offset(rs1)
- {d|w|h|b}表示加载的数据宽度
- {u}表示加载的数据为无符号数
- rd为目标寄存器
- (rs1)表示以rs1寄存器的值为基地址进行寻址
- offset表示以源寄存器的值为基地址的偏移量,是12位的有符号数,取值范围在-2048到2047之间,超过这个区间会出现编译报错
存储指令
s{d|w|h|b} rs2, offset(rs1)
将rs2寄存器中的值存储到以rs1寄存器的值为基地址加上offset的地址处
PC相对寻址
程序计数器(program counter PC)用来指示下一条指令的地址。为了保证CPU正确的执行程序的指令代码,CPU必须知道下一条指令的地址。通常pc是一个寄存器,在程序执行之前,把程序的入口地址写入pc寄存器中,CPU从pc寄存器指向的地址取值,然后依次执行,当执行完一条指令后会自动修改pc的内容,使其指向下一条指令的地址。
riscv指令集提供了一条pc相对寻址的指令AUIPC
AUIPC rd, imm
这条指令将imm立即数左移12位并且带符号扩展到64位之后,加上当前pc的值,然后存储到rd寄存器中。imm左移并扩展之后的立即数表示的是地址的高20位部分,并且是一个有符号的立即数,因此这条指令能寻址的范围是基于当前pc的正负2GB,并且只能寻址到与4KB对齐的地址。
在实际开发中可能很少会直接使用AUIPC,因为编写汇编代码时并不知道当前pc为多少。riscv基于AUIPC定义了一些伪指令。伪指令时对汇编器发出的命令,它在源程序汇编期间由汇编器处理,可以被分解为多条指令的集合。
PIC表示生成与位置无关的代码(position independent code),GOT表示全局偏移量表(global offset table)。gcc中的-fpic编译选项,会让编译器在生成的代码中使用相对地址,而不是绝对地址。所有绝对地址的访问都需要通过GOT实现,这种方式往往运用在共享库中,无论共享库被加载到内存什么位置,代码都能正常执行。
移位操作
- sll:shift left logical逻辑左移,最高位会丢弃,最低位补0
- srl:shift right logical逻辑右移,最高位补0,最低位会丢弃
- sra:shift right arithmetic算术右移,最低位会被丢弃,最高位会按照符号进行扩展
riscv指令集中没有算数左移的指令
在RV64指令集中,sll、srl、sra指令只会使用rs2寄存器中低6位数据做移位操作
算术指令
比较指令
RVI64支持4条基本比较指令。
RVI64只支持小于比较指令,但是为了方便编程,架构提供了一些较为常用的比较伪指令。
无条件跳转指令
JAL(jump and link,跳转与链接)指令使用J类型的指令编码,其中操作数offset[20:1]由指令编码的 Bit[31:12]构成,它默认是2的倍数,因此它的跳转范围大约为当前PC值偏移±1 MB范围。
把返回地址(即PC + 4)存储到rd寄存器中。
如果把返回地址存储到ra寄存器中,则可以实现函数返回。
JALR(jump and link register)指令使用I类型指令编码,要跳转的地址由rs1寄存器和offset操作数组成,其中offset是一个12位的有符号立即数。
为了方便编程,架构根据jal和jalr指令扩展了多条无条件跳转伪指令
条件跳转指令
上述条件跳转指令都采用B 类型的指令编码。
其中操作数offset表示label的地址基于当前pc地址的偏移量。offset是13位有符号立即数,offset[0]默认为0,offset默认是2的倍数,所以它的寻址范围是-4KB~4KB
为了方便编程,架构扩展了多条跳转伪指令
CSR指令
RISC-V体系结构不仅提供了一组系统寄存器,还提供了一组指令来访问这些系统寄存器。