riscv跳转指令
跳转指令可以分为: 直接跳转、寄存器跳转以及条件跳转,这几个概念并不是在一个层面上的。
直接跳转
直接跳转中,跳转的偏移直接编码在指令里,所以跳转地址是固定的。因为指令编码只有
32bit,除去指令op code,用于编码偏移的位数是有限的,所以跳转的距离也是有限的。
比如,jal rd, offset指令的格式是:
1 | +---------+-----------+---------+------------+----+--------+ |
它的偏移编码是20bit,所有支持跳转的范围是+/-1MB。那么如果跳转距离超过范围,就需要
把跳转的目的地址编码到寄存器里,使用如下寄存器跳转的方式完成跳转。
寄存器跳转
寄存器跳转中,跳转指令从gpr里得到跳转的目标地址,这时跳转的目的地址是动态的,跳
转的范围也足够覆盖64bit的地址空间。
riscv里使用jalr rd, offset(rs1)指令完成寄存器跳转,和上面jal一样,jalr也是带rd,
rd用来保存jalr/jal的下一条指令的地址,函数调用时,利用rd保存函数返回的地址。
使用jalr之前需要先把跳转的目的地址加载到rs1里(先认为offset是0),也就是加载一个64
bit数到一个gpr里。显然riscv 32bit的指令编码一条指令是无法搞定这个操作的。这里一般
使用auipc + addi/ld?
auipc rd, imm的意思是Add Upper Immediate to PC
1 | +------------+----+--------+ |
imm做符号扩展并左移12bit后和PC相加,结果保存到rd。可以见imm是0,可以用auipc得到PC。
如果一个符号和auipc的距离在32bit描述范围之内,编译器就可以把这个offset的高20bit
编码到auipc的imm,再用一条addi指令加上offset的低12bit,大概的示意图如下:
1 | +------------+----+--------+ |
条件跳转
条件跳转根据两个输入寄存器和判断条件决定是否跳转,可见条件跳转可以实现高级语言里
的分支语句。riscv里条件跳转的跳转偏移被直接编码到指令里,所以除去指令的op code以
及两个输入寄存器,留给offset的编码已经比较小。
比如,bge rs1, rs2, offset指令的格式是:
1 | +---------+-----------+-----+-----+--------+----------+---------+--------+ |
它的偏移编码是12bit,所有支持跳转的范围是+/-4KB。
riscv里的条件跳转指令还有如下,可见有些比较的模式是相互之间做等价的,比如,大于
和小于的比较用一个指令就好,只要交换一下两个比较寄存器就好。
1 | beq rs1, rs2, offset |