如何计算跳转目标地址和分支目标地址?

fud*_*din 30 assembly mips machine-code

我是汇编语言的新手.我正在阅读有关MIPS架构的内容,我遇到了跳转目标地址分支目标地址以及如何计算它们.

emr*_*azi 66

1.分支地址计算

在MIPS分支指令中,只有16位偏移量来确定下一条指令.我们需要一个寄存器添加到这个16位值来确定下一条指令,这个寄存器实际上是由架构暗示的.它是PC寄存器,因为PC在获取周期期间被更新(PC + 4),因此它保存下一条指令的地址.

(在下图中,PC是分支延迟槽的地址,而不是分支指令本身.但在文中,我们将说PC + 4.)

我们还将分支距离限制为-2^15 to +2^15 - 1来自(指令后)分支指令的指令.然而,这不是真正的问题,因为无论如何大多数分支都是本地的.

所以一步一步:

  • 符号扩展16位偏移值以保留其值.
  • 将结果值乘以4.这背后的原因是如果我们要分支一些地址,并且PC已经是字对齐的,那么立即值也必须是字对齐的.然而,使直接字对齐是没有意义的,因为我们将通过强制它们为00来浪费低两位.
  • 现在我们有32位地址.将此值添加到PC + 4,这是您的分支地址.

分支地址计算


2.跳转地址计算

对于跳转指令,Mips只有26位来确定跳转位置.此外,跳转与MIPS中的PC相关.像分支一样,立即跳转值需要字对齐;因此,我们需要将26位地址乘以4.

再一步一步:

  • 将26位值乘以4.
  • 由于我们相对于PC + 4值跳转,因此将PC + 4值的前四位连接到跳转地址的左侧.
  • 结果地址是跳转值.

换句话说,将取出的指令的低26位替换为PC + 4的低28位,向左移位2位.

在此输入图像描述

跳转是相对于分支延迟槽的区域,不一定是分支本身.在上图中,PC在跳转计算之前已经进入分支延迟槽.(在经典RISC 5阶段流水线中,BD在同一周期内被提取,跳转被解码,因此PC + 4下一个指令地址已经可用于跳转和分支,并且相对于跳转自己的地址计算需要额外的工作来保存该地址.)

资料来源: Bilkent大学CS 224课程幻灯片

  • **我在评论中找到了更详细的答案,我将其留在这里:**跳转指令的立即乘以4是伪直接寻址,最多可达2 ^ 28个地址.它是伪的,这不是真正的直接寻址,因为在32位MIPS系统中有2 ^ 32个地址.我们需要将来自PC + 4的4个最高有效位添加到28位伪直接地址以获得32位直接地址.这意味着只能跳转到跳转指令所在的PC + 4的4个最高有效位的2 ^ 28地址范围内的指令. (3认同)

use*_*653 19

通常您不必担心计算它们,因为汇编程序(或链接程序)将采取正确的计算方法.假设你有一个小功能:


func:
  slti $t0, $a0, 2
  beq $t0, $zero, cont
  ori $v0, $zero, 1
  jr $ra
cont:
  ...
  jal func
  ... 
Run Code Online (Sandbox Code Playgroud)

当将上述代码转换为二进制指令流时,汇编程序(或链接器,如果你第一次组装成目标文件)将确定函数将驻留在内存中的哪个位置(让我们暂时忽略位置无关代码).它在内存中的位置通常在ABI中指定,或者在您使用模拟器时给予您(如SPIM加载代码0x400000- 注意链接也包含对过程的良好解释).

假设我们正在谈论的SPIM情况下,我们的作用是先在内存中,该slti指令将驻留在0x400000中,beq0x400004等.现在我们快到了!对于beq指令,分支目标地址cont(0x400010)查看MIPS指令引用的地址,我们看到它被编码为相对于下一条指令的16位有符号立即数(除以4,因为所有指令必须位于4字节无论如何都要对齐地址).

那是:

Current address of instruction + 4 = 0x400004 + 4 = 0x400008
Branch target = 0x400010
Difference = 0x400010 - 0x400008 = 0x8
To encode = Difference / 4 = 0x8 / 4 = 0x2 = 0b10
Run Code Online (Sandbox Code Playgroud)

编码 beq $t0, $zero, cont

0001 00ss ssst tttt iiii iiii iiii iiii
---------------------------------------
0001 0001 0000 0000 0000 0000 0000 0010
Run Code Online (Sandbox Code Playgroud)

如您所见,您可以分支到-0x1fffc .. 0x20000字节内.如果由于某种原因,你需要进一步跳跃,你可以使用蹦床(无条件跳转到放置在给定限制内的真实目标).

跳转目标地址是,不像分支目标地址,使用所述编码的绝对地址(再除以4).由于指令编码使用6位作为操作码,因此只留下26位用于地址(实际上28位,因为最后2位将为0)因此在形成地址时使用PC寄存器的4位最高有效位(除非你打算跨越256 MB边界,否则无关紧要).

回到上面的例子,编码jal func是:

Destination address = absolute address of func = 0x400000
Divided by 4 = 0x400000 / 4 = 0x100000
Lower 26 bits = 0x100000 & 0x03ffffff = 0x100000 = 0b100000000000000000000

0000 11ii iiii iiii iiii iiii iiii iiii
---------------------------------------
0000 1100 0001 0000 0000 0000 0000 0000
Run Code Online (Sandbox Code Playgroud)

您可以快速验证这一点,并使用我遇到的这个在线MIPS汇编程序来处理不同的指令(例如slti,注意它不支持所有操作码,所以我只是将其改为slt此处):

00400000: <func>    ; <input:0> func:
00400000: 0000002a  ; <input:1> slt $t0, $a0, 2
00400004: 11000002  ; <input:2> beq $t0, $zero, cont
00400008: 34020001  ; <input:3> ori $v0, $zero, 1
0040000c: 03e00008  ; <input:4> jr $ra
00400010: <cont>    ; <input:5> cont:
00400010: 0c100000  ; <input:7> jal func
Run Code Online (Sandbox Code Playgroud)

  • 哦嘿,你找到了我的在线 MIPS 汇编器:) (3认同)
  • 仅供参考,如果有人有兴趣添加说明(例如`slti`),请随时向https://github.com/alanhogan/online-mips-assembler提交拉取请求. (2认同)