比较指令
指令名称 | 功能 | ISA | type | 用法 | 含义 | BEQ | 相等时分支 | RV32I/RV64I | B | beq rs1, rs2, offset | 若寄存器 x[rs1]和寄存器 x[rs2]的值相等,把 pc 的值设为当前值加上符号位扩展的偏移 offset。 | BEQZ | 等于0时分支 | RV32I/RV64I | 伪指令 | beqz rs1, offset | 可视为beq rs1, x0, offset | BGE | 大于等于时分支 | RV32I/RV64I | B | bge rs1, rs2, offset | 若寄存器 x[rs1]的值大于等于寄存器 x[rs2]的值(均视为二进制补码),把 pc 的值设为当前值加上符号位扩展的偏移 offset | BGEU | 无符号大于等于分支 | RV32I/RV64I | B | bgeu rs1, rs2, offset | 若寄存器 x[rs1]的值大于等于寄存器 x[rs2]的值(均视为无符号数),把 pc 的值设为当前值加上符号位扩展的偏移 offset | BGEZ | 大于等于零时分支 | RV32I/RV64I | 伪指令 | bgez rs1, offset | 可视为bge rs1, x0, offset | BGT | 大于分支 | RV32I/RV64I | 伪指令 | bgt rs1, rs2, offset | 可视为blt rs2, rs1, offset | BGTU | 无符号大于时分支 | RV32I/RV64I | 伪指令 | bgtu rs1, rs2, offset | 可视为bltu rs2, rs1, offset | BGTZ | 大于0时分支 | RV32I/RV64I | 伪指令 | bgtz rs1, offset | 可视为blt x0, rs2, offset | BLE | 小于等于时分支 | RV32I/RV64I | 伪指令 | ble rs1, rs2, offset | 可视为 bge rs2, rs1, offset | BLEU | 无符号小于等于时分支 | RV32I/RV64I | 伪指令 | bleu rs1, rs2, offset | 可视为 bgeu rs2, rs1, offset | BLEZ | 小于等于0时分支 | RV32I/RV64I | 伪指令 | blez rs2, offset | 可视为 bge x0, rs2, offset | BLT | 小于时分支 | RV32I/RV64I | B | blt rs1, rs2 offset | 若寄存器 x[rs1]的值小于寄存器 x[rs2]的值(均视为二进制补码),把 pc 的值设为当前值加上符号位扩展的偏移 offset。 | BLTZ | 小于零时分支 | RV32I/RV64I | 伪指令 | bltz rs2, offset | 可视为 blt rs1, x0, offset | BLTU | 无符号小于时分支 | RV32I/RV64I | B | bltu rs1, rs2, offset | 若寄存器 x[rs1]的值小于寄存器 x[rs2]的值(均视为无符号数),把 pc 的值设为当前值加上符号位扩展的偏移 offset。 | BNE | 不相等时分支 | RV32I/RV64I | B | bne rs1, rs2, offset | 若寄存器 x[rs1]和寄存器 x[rs2]的值不相等,把 pc 的值设为当前值加上符号位扩展的偏移offset | BNEZ | 不等于0时分支 | RV32I/RV64I | 伪指令 | bnez rs1, offset | 可视为 bne rs1, x0, offset |
跳转指令
指令名称 | 功能 | ISA | type | 用法 | 含义 | J | 跳转 | RV32I/RV64I | 伪指令 | j offset | 把 pc 设置为当前值加上符号位扩展的 offset,等同于 jal x0, offset. | JAL | 跳转并链接 | RV32I/RV64I | J | jal rd, offset | 把下一条指令的地址(pc+4),然后把 pc 设置为当前值加上符号位扩展的offset。rd 默认为 x1( rd为return?address) | JALR | 跳转并寄存器链接(jump and link register) | RV32I/RV64I | I | jalr rd, offset(rs1) | 把 pc 设置为 x[rs1] + sign-extend(offset),把计算出的地址的最低有效位设为 0,并将原 pc+4的值写入f[rd]。 rd 默认为 x1。 | JR | 寄存器跳转 | RV32I/RV64I | 伪指令 | jr rs1 | 把 pc 设置为 x[rs1],等同于 jalr x0, 0(rs1)。 | CALL | 调用 | RV32I/RV64I | 伪指令 | call rd, symbol | 把下一条指令的地址(pc+4) 写入 x[rd],然后把 pc 设为 symbol。等同于 auipc rd, offestHi,再加上一条 jalr rd, offsetLo(rd). 若省略了 rd,默认为 x1. | RET | 返回 | RV32I/RV64I | 伪指令 | ret | 从子过程返回。实际被扩展为 jalr x0, 0(x1)。 |
跳转指令实例
1.RET指令
#
# unsigned long jump_isa_ret(void)
# 测试目的:将ret指令替换为jalr x0, 0(ra)指令,测试此函数是否能正常返回
# 将pc设置为(ra)
.globl jump_isa_ret
jump_isa_ret:
li a0, 1
jalr x0, 0(ra) #ra is return address
以上是将ret指令替换为jalr x0, 0(ra)指令的汇编代码。ret指令是伪代码,实际的代码是jalr x0, 0(ra)。其中x0是zero,ra是返回地址。
jalr指令将pc赋值为了ra,(这里为什么要给ra加上括号,rsic-v括号表示是取地址上的数据出来),即实现了返回。
2.JAL指令
#
# unsigned long jump_isa_jal(unsigned long i)
# 下面的代码无法返回,因为ra被修改了。
.globl jump_isa_jal
jump_isa_jal:
addi a0, a0, 1
jal ra, jump_isa_jal_depend
ret
.globl jump_isa_jal_depend
jump_isa_jal_depend:
addi a0, a0, 1
ret
1.了解jal指令用法
把下一条指令的地址(pc+4)存放到ra中,然后把 pc 设置为当前值加上符号位扩展的offset(即跳转到jump_isa_jal_depend函数)。
2.问题
上面汇编代码在执行到jump_isa_jal的ret函数后将无法返回到调用jump_isa_jal的函数中。根据risc-v的函数调用规则,ret指令是将ra地址赋值给pc。
修改:
#
# unsigned long jump_isa_jal_yes(unsigned long i)
# 在c语言调用jump_isa_jal_yes时,已经在使用ra寄存器了,
# 但是在jump_isa_jal_yes中调用jump_isa_jal_depend_yes
# 函数时需要使用ra作为返回地址,所以,这里必须要保存ra的值。
#
.globl jump_isa_jal_yes
jump_isa_jal_yes:
add t1, ra, x0
addi a0, a0, 1
jal ra, jump_isa_jal_depend_yes
add ra, t1, x0
ret
.globl jump_isa_jal_depend_yes
jump_isa_jal_depend_yes:
addi a0, a0, 1
ret
参考代码
|