35

我是汇编语言的新手。我正在阅读有关MIPS架构的信息,但我被Jump Target AddressBranch Target Address以及如何计算它们困住了。

4

4 回答 4

81

(在下面的图表和文字中,PC是分支指令本身的地址。 PC+4是分支指令本身的结束,也是分支延迟槽的开始。绝对跳转图中除外。)

1. 分行地址计算

在 MIPS 中,分支指令只有 16 位偏移来确定下一条指令。我们需要一个寄存器添加到这个 16 位值来确定下一条指令,这个寄存器实际上是由架构暗示的。它是 PC 寄存器,因为 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 值的前四位连接到跳转地址的左侧。
  • 结果地址是跳转值。

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

在此处输入图像描述

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

资料来源:比尔肯特大学 CS 224 课程幻灯片

于 2012-03-20T22:05:07.047 回答
21

通常你不必担心计算它们,因为你的汇编器(或链接器)会正确计算。假设您有一个小功能:


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

当将上述代码翻译成二进制指令流时,汇编器(或链接器,如果您首先组装到目标文件中)将确定函数将驻留在内存中的哪个位置(现在让我们忽略与位置无关的代码)。它在内存中的驻留位置通常在 ABI 中指定,或者如果您使用的是模拟器(例如加载代码的SPIM0x400000 - 请注意该链接还包含对该过程的良好解释)。

假设我们正在讨论 SPIM 案例并且我们的函数首先在内存中,那么slti指令将驻留在0x400000beqat0x400004等处。现在我们快到了!对于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

的编码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

您可以使用我遇到的这个在线 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
于 2011-08-05T09:23:30.390 回答
0

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

  • 克里斯
于 2014-02-24T21:27:45.090 回答
-1

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

于 2011-08-05T04:41:03.350 回答