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

Assembly 如何计算跳转目标地址和分支目标地址?,assembly,mips,machine-code,Assembly,Mips,Machine Code,我不熟悉汇编语言。我正在阅读有关MIPS体系结构的书籍,我一直在学习跳目标地址和分支目标地址,以及如何计算它们。用硬件完成。如果你能更深入地解释一下这个问题,并描述一下你正在尝试做什么,那么会更容易得到帮助。(:通常您不必担心计算它们,因为您的汇编程序(或链接器)将需要正确计算。假设您有一个小函数: func: slti $t0, $a0, 2 beq $t0, $zero, cont ori $v0, $zero, 1 jr $ra cont: ... jal fun

我不熟悉汇编语言。我正在阅读有关MIPS体系结构的书籍,我一直在学习跳目标地址和分支目标地址,以及如何计算它们。

用硬件完成。如果你能更深入地解释一下这个问题,并描述一下你正在尝试做什么,那么会更容易得到帮助。(:

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


func:
  slti $t0, $a0, 2
  beq $t0, $zero, cont
  ori $v0, $zero, 1
  jr $ra
cont:
  ...
  jal func
  ... 
当将上述代码转换成二进制指令流时,汇编器(或链接器,如果您第一次组装成一个对象文件),它将确定函数将驻留在内存中的什么位置(现在我们忽略位置无关的代码)。它将驻留在内存中的位置通常在ABI中指定,或者在您使用模拟器时提供给您(例如,在-注意,链接中还包含对该过程的良好解释)

假设我们讨论的是SPIM案例,我们的函数是内存中的第一个,那么
slti
指令将位于
0x400000
beq
位于
0x400004
等等。现在我们差不多到了!对于
beq
指令,分支目标地址是
cont
0x400010
)查看a,我们看到它被编码为相对于下一条指令的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
如您所见,您可以在
-0x1ffc..0x20000
字节内进行分支。如果出于某种原因,您需要进一步跳转,您可以使用蹦床(无条件跳转到给定限制内的实际目标)

与分支目标地址不同,跳转目标地址使用绝对地址(再次除以4)进行编码。由于指令编码使用6位作为操作码,因此地址只剩下26位(如果最后2位为0,则有效为28位)因此,在形成地址时使用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
您可以快速验证这一点,并使用我遇到的不同指令进行操作(请注意,它不支持所有操作码,例如
slti
,因此我只是在此处将其更改为
slt
):

00400000:;func:
00400000:0000002a;slt$t0,$a0,2
00400004:11000002;贝币$t0,$0,续
00400008:34020001;ori$v0,$0,1
0040000c:03e00008;jr$ra
00400010:;续:
00400010:0c100000;日本航空公司
(在下面的图表和文本中,
PC
是分支指令本身的地址。
PC+4
是分支指令本身的结束和分支延迟槽的开始。绝对跳转图中除外。)

1.分支地址计算 在MIPS中,分支指令只有16位偏移量来确定下一条指令。我们需要将一个寄存器添加到这个16位值来确定下一条指令,而这个寄存器实际上是由体系结构暗示的。它是PC寄存器,因为PC在获取周期中得到更新(PC+4),所以它保存下一条指令的地址

我们还将分支距离限制为分支指令后的指令的
-2^15到+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下一个指令地址已经可用于跳转和分支,并且相对于跳转自身地址的计算需要额外的工作来保存该地址。)


来源:Bilkent University CS 224课程幻灯片

对于这样的小函数,您可以手动计算从分支指令下的指令到目标的跳数。如果它向后分支,则使该跳数为负数。如果该数不为负数
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