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

fae0ux8s  于 2023-10-19  发布在  其他
关注(0)|答案(4)|浏览(163)

我是汇编语言的新手。我在阅读有关MIPS体系结构的文章,但我被跳转目标地址分支目标地址以及如何计算它们中的每一个所困扰。

hfsqlsce

hfsqlsce1#

在下面的图表和文本中,PC是分支指令本身的地址。PC+4是分支指令本身的结束,以及下一条指令(使用延迟槽的MIPS中的分支延迟槽)的开始。
除了在绝对跳转图中,我们实际上取跳转后指令地址的高4位。只有different at the end of a 256 MiB region。这些图在他们所谓的PC中彼此不一致,或者可能是通过简化PC+4与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位替换为左移2位的取出指令的低26位。

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

**来源:**Bilkent University CS 224 Course Slides

rfbsl7qr

rfbsl7qr2#

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

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),因此,4位的PC寄存器的最高有效位时,使用形成地址(不会有关系,除非你打算跨越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
u0sqgete

u0sqgete3#

我认为很难计算这些,因为分支目标地址是在运行时确定的,而预测是在硬件中完成的。如果你更深入地解释这个问题,并描述你正在努力做什么,那么帮助会更容易一些。(:

os8fio9y

os8fio9y4#

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

  • 克里斯

相关问题