jal

jal

RISC-V 汇编 参考

lyszt.net 更详细地介绍了 jal 和其他 RISC-V 指令。

编码格式

R
31–25funct7
24–20rs2
19–15rs1
14–12funct3
11–7rd
6–0opcode
I
31–20imm[11:0]
19–15rs1
14–12funct3
11–7rd
6–0opcode
S
31–25imm[11:5]
24–20rs2
19–15rs1
14–12funct3
11–7imm[4:0]
6–0opcode
B
31–25imm[12,10:5]
24–20rs2
19–15rs1
14–12funct3
11–7imm[4:1,11]
6–0opcode
U
31–12imm[31:12]
11–7rd
6–0opcode
J
31–12imm[20,10:1,11,19:12]
11–7rd
6–0opcode

描述

jal 调用一个函数。函数是一段有名字的指令块,可以从多个地方运行,然后返回,因此逻辑只需编写一次,就可以在任何地方使用。调用一个函数需要两件事。程序跳转到函数所在的位置,并记住它来自哪里,以便之后返回。jal 同时完成这两件事,它的名字就是 jump and link。

跳转到一个标记该函数的标签。链接部分发生在跳转之前。jal 将下一条指令的地址,也就是调用之后紧接着的那条指令的地址,保存到一个寄存器中。函数使用这个保存的地址来返回。

按照约定,保存的地址会放入一个名为 ra 的寄存器,表示返回地址。下面这一行跳转到 square 函数,并把要返回的地址留在 ra 中。

jal ra, square

当函数结束时,它通过跳转到 ra 中的地址来返回,这正是 ret 指令所做的事情。同一个调用的常见简写如下所示。

call square

RISC-V 手册说,跳转偏移量以 2 字节的倍数计算,进行符号扩展并加到跳转指令的地址上。这给出了正负 1 MiB 的范围,其中 1 MiB 是 1,048,576 字节。jal 将下一条指令的地址,即 pc 加 4,存入 rd。标准软件调用约定使用 x1 作为返回地址寄存器,x5 作为备用链接寄存器。无条件跳转指令使用 pc 相对寻址,这支持位置无关代码,也就是无论加载到内存中的何处都能工作的代码。

jal 会用新的返回地址覆盖 ra。如果一个函数调用另一个函数,内部调用会替换外部调用保存的地址。一个自身进行调用的函数必须先保存 ra,并在返回前恢复它。这个例子调用 square,并把返回地址留在 ra 中。

示例

jal ra, square   # call square; ra holds the return address