RISC-V 中 JAL 和 JALR 指令的偏移地址

Offset address for JAL and JALR instrctions in RISC-V

在RISC-V规范中,写了JAL和JALR指令中的立即数转换为跳转偏移量为:

  1. 将给定的立即数符号扩展到 XLEN 位。

  2. 将 LSB 设置为零。

关于这个我有几个问题。

问题 1

对于 JAL,这给出了一个范围:

000000000000 to 111111111110

也就是4KiB。

在这里,如果 LSB 必须始终为零,为什么立即数不被视为地址的强制性 0 LSB 之前的 12 位,从而将地址范围增加到:

[000000000000]0 to [111111111111]0      

[ ]表示给定的立即数偏移量,在给定的立即数偏移量的末尾内部添加一个零。也就是说,

  1. 左移一位给地址

  2. 将结果符号扩展为 XLEN 位。

问题 2

如何区分正偏移和负偏移?是否使用了给定偏移量的 MSB?

JAL 有一个 20 位偏移量和一个寄存器作为操作数。

它的运算是pc := pc + sxt ( imm20 << 1 ).

从公式可以看出,分支是相对于 pc 的。立即数可以从 JAL 本身达到 +/- 1 MB。立即数移动一位,真正的 LSB 始终为零,因此未编码。

因为 RISC V 支持 16 位(两个字节)的倍数指令,我们不能假设下一个 LSB 也为零,就像 MIPS(具有 32 位指令)一样。

除了执行分支之外,JAL 中的寄存器操作数可选择用于捕获 return 地址。

JAL 的功能是使用其 20 位范围执行适度远的 pc 相关分支或调用。 (与 RISC V 条件分支指令对比,后者只有 12 位用于 +/- 4 KB 范围。)


JALR 有一个 12 位偏移量和两个寄存器作为操作数。

它的运算是pc := ( rs1 + sxt ( imm12 ) ) & -2.

从公式可以看出,分支是寄存器间接的,相对于rs1中的值。

JAL一样,JALR也可以捕获return地址。

JALR 用于从函数 return(在汇编中又名 RET。在这种形式中 $ra 用作源寄存器,没有 return地址被捕获)。这使用零作为偏移量(即不需要偏移量)。

JALR 也用于执行间接函数调用:通过函数指针调用、虚拟方法分派等。这些使用也使用零作为偏移量。

JALR也可以和AUIPC顺序使用。


AUIPC 有一个 20 位偏移量和一个寄存器作为操作数。

它的运算是rd := pc + ( imm20 << 12 ).

它计算与 pc 相关的立即数的上半部分(同时还提供与 pc 无关的下半部分)。

结合JALR,可以完成32位pc-relative分支或调用。

AUIPC r5, labelFarAway      # AUIPC encodes upper 20 bits of label's distance from pc
JALR r5, $ra, labelFarAway  # JALR encodes the lower 12 bits of same