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

ElP*_*roo 6 riscv

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

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

  2. 将 LSB 设置为零。

关于这个,我有几个问题。

问题 1

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

000000000000 to 111111111110
Run Code Online (Sandbox Code Playgroud)

也就是说,4KiB。

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

[000000000000]0 to [111111111111]0      
Run Code Online (Sandbox Code Playgroud)

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

  1. 左移一点点给出地址。

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

问题2

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

Eri*_*idt 7

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

其操作是pc := pc + sxt ( imm20 << 1 ).

从公式中可以看出,分支是与 pc 相关的。立即数可以达到 +/- 1 MBJAL。立即数移位一位,真正的 LSB 始终为零,因此不进行编码。

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

除了执行分支之外,JAL还可以选择使用寄存器操作数来捕获返回地址。

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


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

其操作是pc := ( rs1 + sxt ( imm12 ) ) & -2.

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

同样JALJALR也可以捕获返回地址。

JALR用于从函数返回(也称为RET汇编。在这种形式中,$ra 用作源寄存器,并且不捕获返回地址)。这使用零作为偏移量(即不需要偏移量)。

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

JALR也可以与 顺序使用AUIPC


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

其操作是rd := pc + ( imm20 << 12 ).

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

与 结合使用JALR,可以完成 32 位 pc 相关的分支或调用。

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
Run Code Online (Sandbox Code Playgroud)