关于RISC-V rv64汇编器跳转与分支寻址方式的疑问
关于RISC-V rv64汇编器跳转指令寻址逻辑的困惑
我对RISC-V rv64汇编器的可读汇编行为逻辑存在困惑(机器指令行为已明确):
beq rs1, rs2, x在R[rs1] = R[rs2]时会将PC ← x(而非PC ← PC + x),这点没问题,但j x指令到底是设置PC ← x还是PC ← PC + x,似乎存在不一致性。
测试过程
测试程序1
addi x1, x1, 1 beq x1, x1, bob addi x2, x2, 3 bob: addi x3, x3, 4 addi x4, x4, 5
汇编后反汇编结果:
addi ra,ra,1 beq ra,ra,0xc addi sp,sp,3 addi gp,gp,4 addi tp,tp,5 # 0x5
由此我最初认为beq采用绝对寻址。
测试程序2
addi ra,ra,1 bne ra,ra,0xc j 0x8 addi sp,sp,3 addi gp,gp,4 addi tp,tp,5 # 0x5
反汇编结果里的j 0x8看起来是相对偏移(否则会陷入自循环),符合预期。
测试程序3
addi x1, x1, 1 j bob addi x2, x2, 3 bob: addi x3, x3, 4 addi x4, x4, 5
汇编反汇编后结果:
addi ra,ra,1 j 0xc addi sp,sp,3 addi gp,gp,4 addi tp,tp,5 # 0x5
如果j 0xc是相对偏移,跳转至bob:会跳过addi x3, x3, 4,但通过gcc内联汇编测试发现该指令实际被执行,说明j 0xc是绝对寻址,和之前的结论矛盾。
补充带地址的反汇编结果
0000000000000000 <.text>: 0: 00108093 addi ra,ra,1 4: 00109463 bne ra,ra,c <.text+0xc> 8: 0000006f j 8 <.text+0x8> c: 00310113 addi sp,sp,3 10: 00418193 addi gp,gp,4 14: 00520213 addi tp,tp,5 # 5 <.text+0x5>
矛盾依然存在,希望得到现象解释(实际使用中知道用标签,但想理解原理)。
问题解释
核心原因是汇编器的输入处理逻辑和反汇编器的输出显示逻辑共同造成的混淆:
- RISC-V机器指令的本质:所有跳转/分支指令(
beq/j等)在机器码层面都是相对PC的偏移寻址,不存在绝对寻址的跳转指令(除了jr类寄存器跳转)。 - 汇编器对输入的处理:
- 当输入标签(比如
beq ... bob或j bob)时,汇编器会自动计算该标签相对于当前指令下一条指令的PC偏移量,将其编码进机器指令。 - 当输入立即数地址(比如
j 0x8)时,汇编器会将这个立即数视为绝对地址,然后计算它相对于当前指令下一条指令的PC偏移量,再编码进机器指令。
- 当输入标签(比如
- 反汇编器的显示逻辑:反汇编器会把机器指令里的相对偏移量,加上当前指令的PC值,转换成绝对地址显示出来,而不是显示原始的偏移量。
对应测试案例拆解
- 测试程序1:
beq x1,x1,bob中,bob是标签,汇编器计算出相对偏移后编码,反汇编时把偏移转换成绝对地址0xc显示,所以你看到的beq ra,ra,0xc是反汇编后的绝对地址,而非汇编器输入的偏移。 - 测试程序2:
j 0x8是输入绝对地址,汇编器计算相对偏移(当前j指令在地址0x8,下一条指令地址是0xc,所以偏移是0x8 - 0xc = -4),编码进机器指令。反汇编时把偏移转回绝对地址0x8显示,此时执行j 0x8会跳回自身,形成循环,符合预期。 - 测试程序3:
j bob是标签,汇编器计算出bob的绝对地址是0xc,当前j指令在0x4,下一条指令地址是0x8,所以偏移是0xc - 0x8 = 4,编码进机器指令。反汇编时把偏移转换成绝对地址0xc显示,此时执行j 0xc是跳转到绝对地址0xc,也就是bob:的位置,所以addi x3,x3,4会被执行,不存在矛盾。
简单来说:你在汇编代码里写的是标签或绝对地址,汇编器都会转成机器指令需要的相对偏移;而反汇编时,会把机器指令里的相对偏移转成绝对地址给你看,这就造成了“寻址方式不一致”的错觉。
内容的提问来源于stack exchange,提问作者pdeqx
相关产品推荐
相关产品推荐

