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

时间:2011-08-05 00:40:47

标签: assembly mips machine-code

我是汇编语言的新手。我正在阅读 MIPS 架构,我坚持使用跳转目标地址分支目标地址如何计算每个他们。

4 个答案:

答案 0 :(得分:64)

1。分支地址计算

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

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

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

一步一步:

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

Branch address calculation


2。跳转地址计算

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

一步一步:

  • 将46位值与4相乘。
  • 由于我们相对于PC + 4值跳转,所以将PC + 4值的前四位连接到跳转地址的左边。
  • 结果地址是跳转值。

换句话说,用向前移位2位的取指令的低26位替换PC + 4的低28位。

enter image description here

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

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

答案 1 :(得分:18)

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


func:
  slti $t0, $a0, 2
  beq $t0, $zero, cont
  ori $v0, $zero, 1
  jr $ra
cont:
  ...
  jal func
  ... 

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

假设我们正在谈论SPIM案例,而我们的函数首先在内存中,slti指令将驻留在0x400000beq位于0x400004,所以上。现在我们快到了!对于beq指令,分支目标地址cont0x400010)查看MIPS instruction reference的指令,我们看到它被编码为相对于下一条指令,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

beq $t0, $zero, cont

的编码
0001 00ss ssst tttt iiii iiii iiii iiii
---------------------------------------
0001 0001 0000 0000 0000 0000 0000 0010

如您所见,您可以分支到-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

您可以快速验证这一点,并使用此online MIPS assembler运行不同的指令(注意它不支持所有操作码,例如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

答案 2 :(得分:0)

对于像这样的小函数,您可以从分支指令下的指令手动计算它对目标的跳数。如果它向后分支使得跳数为负数。如果该数字不需要全部16位,那么对于最重要的跳数左边的每个数字,将它们设为1,如果跳数为正,则将它们全部设为0,因为大多数分支都接近它们目标,这为大多数情况节省了大量额外的算术。

  • 克里斯

答案 3 :(得分:-1)

我认为计算这些是非常困难的,因为分支目标地址是在运行时确定的,并且预测是在硬件中完成的。如果你更深入地解释了这个问题,并描述了你想要做的事情,那将会更容易帮助。 (: