无条件分支和无条件跳转有什么区别(MIPS中的说明)?

33 assembly mips

您可以查看维基百科学生的简短摘要.每个人都说同一件事有两条指令.但没有人说出原因?

emb*_*yle 29

分支机构允许条件.但允许条件在指令中占用更多位.因此,分支的地址只有2 ^ 16位,只允许您向后分支2 ^ 15 - 1个指令或向前分支2 ^ 15个指令.

跳转是无条件的,通过省略条件保存的位可以用于地址.跳转允许26位地址,因此可以在代码中跳转远远超过分支.以不受条件限制为代价.

  • 这无法解决无条件分支和无条件跳转之间是否存在任何差异的问题. (8认同)
  • 请注意,这可能适用于MIPS,但不适用于所有CPU助记符.Z80具有相对跳跃,条件跳跃和条件相对跳跃.分支是Z80汇编中不存在的一个词. (4认同)
  • @HelloWorld通常?是.但总是?没有.我能想到的一个情况是内存非常小的设备.在这种情况下,你可能没有多远,并希望尽可能多地保存位.因此,无条件分支更合适. (2认同)

Igo*_*sky 29

Branches(b)使用PC相对位移,而jumps(j)使用绝对地址.区别对于与位置无关的代码很重要.此外,只有跳转可用于间接控制传输(jr使用寄存器值).


old*_*mer 14

如前所述,分支具有较少的位,较短的范围并且是相对的.跳跃有更多的位,是绝对的.

举个例子

b l0
nop
beq $0,$1,l1
nop
j l2
nop

l0: .word 0,0
l1: .word 0,0
l2: .word 0,0
Run Code Online (Sandbox Code Playgroud)

你明白了

00000000 <l0-0x1c>:
   0:   10000006    b   1c <l0>
   4:   00000000    nop
   8:   10010006    beq zero,at,24 <l1>
   c:   00000000    nop
  10:   0800000b    j   2c <l2>
  14:   00000000    nop
  18:   00000000    nop

0000001c <l0>:
    ...

00000024 <l1>:
    ...

0000002c <l2>:
    ...
Run Code Online (Sandbox Code Playgroud)

现在其他答案可能没有提到的是,无条件分支至少由gnu汇编器编码,如果相等,则使用相同的寄存器进行编码.mips中没有无条件分支,如果相等则存在分支,如果不相等则可以分支.

你看上面的跳转使用了一个0xB,它是字地址,0xB*4 = 0x2C是目的地址,其中条件使用相对寻址pc +(signed_offset*4)其中pc = instruction_address + 4; 或者使用instruction_address + 4 +(signed_offset*4)来获取目标地址.

将别名b用于分支而不是j用于跳转将创建与位置无关的代码.跳转不会,如果你四处走动则必须重新连接,因为近似跳跃可能更好地使用分支而不是跳转,即使它是别名.如果你是纯粹主义者,那么你可以使用真正的指令beq $ 0,$ 0,标签或选择任何寄存器beq $ 4,$ 4,label.寄存器0是特殊的,快速可能是更好的选择.


Cer*_*vEd 7

MIPS 中的跳转和无条件分支是不同的。

分支和跳转指令都将数据写入程序计数器寄存器,以便在下一个读取周期时,将读取不同的指令,而不是程序存储器中的下一条指令。从这个意义上说,它们执行相同类型的操作。

它们的不同之处在于分支是有条件的,它们仅在满足特定条件时更改要执行的下一条指令。这可以通过语句中执行代码的差异if或调用函数来说明。

if (a == 0) {
    a = 1
}
setAtoOne()
Run Code Online (Sandbox Code Playgroud)

仅当满足以下条件时,该if语句才会跳转到要设置的指令:a = 1a == 0。不管怎样,该函数都会跳转到该指令。

在本例中,我们讨论的是条件始终为真的分支。这只是另一种写作方式

beq $zero, $zero, (int)offset
Run Code Online (Sandbox Code Playgroud)

$zero 始终等于 $zero,因此它始终分支到指定的偏移量。就像这样的 if 语句

if (true) { a = 1 }
Run Code Online (Sandbox Code Playgroud)

分支指令和跳转指令之间还有另一个区别。跳转指令指定 PC 将被设置到的绝对地址,而分支指令则偏移程序计数器中的地址。

PC = 32-bit address # Jump
PC += 16-bits lower
Run Code Online (Sandbox Code Playgroud)

事实上,这并不完全正确。我们使用绝对地址和偏移量编写汇编,但在跳转和分支中,它都会被编译为偏移量。这就是为什么您不能跳转或分支到内存中的任何位置,除非使用跳转到寄存器jr指令。这是因为 MIPS 的基本设计是固定长度的单字指令。

所有 MIPS 指令的长度均为 1 个字(即 4 字节/32 位)。它们包含一个 6 位的指令 ID(称为操作码)以及执行该指令所需的其他信息。这可能是寄存器的 ID 或“立即”值,基本上是指令中编码的整数。

0x00000000MIPS 内存中的每个字节在-之间都有一个地址0xFFFFFFFF。为了获取这些字节之一,我们需要指定地址。如果幸运地将地址存储在寄存器中,我们就可以jr使用已经存储在寄存器中的地址。然而,我们不是。

这就出现了问题,我们的指令只有 32 位,我们需要所有这些位来指定该范围内的地址。我们还必须放弃处理器用来识别指令的 6 位。现在我们还剩 26 位。

更糟糕的是,当我们分支时,我们需要 10 个额外的位来指定我们要根据条件进行比较的两个寄存器。解决方案是使用偏移量。

假设我们位于地址处0x12345678,并且正在执行无条件跳转到内存中的下一个地址j 0x1234567c。这是汇编代码,我将展示如何将其转换为机器代码并执行。

首先我们作一点小作弊。我们知道指令是一个字(4 个字节),并且在 MIPS 中指定它们必须位于字边界内。这意味着所有指令的地址间隔为 4 个字节,并且在二进制表示中它们始终以 00 结尾。太好了,我们可以去掉那两个无意义的部分了。我们还会删除前 6 个,但别担心,我们稍后会将它们恢复。

jump 0001 0010 0011 0100 0101 0110 0111 1100
jump 0001 0010 0011 0100 0101 0110 0111 1100
0000 1000 1000 1101 0001 0101 1001 1111 #in machine code # jump op = 0000 10
Run Code Online (Sandbox Code Playgroud) 当我们执行这个时,我们采取
00 1000 1101 0001 0101 1001 1111
0000 0000 1000 1101 0001 0101 1001 1111  # extend >> 6
0000 0010 0011 0100 0101 0110 0111 1100  # << 2 
Run Code Online (Sandbox Code Playgroud) 然后我们与 PC(我们执行的地方)和 0xf0000000

0001 0010 0011 0100 0101 0110 0111 1000
1111 0000 0000 0000 0000 0000 0000 0000
AND
0001 0000 0000 0000 0000 0000 0000 0000
Run Code Online (Sandbox Code Playgroud)

我们知道将其结果与我们的指令整数进行“或”运算

0001 0000 0000 0000 0000 0000 0000 0000
0000 0010 0011 0100 0101 0110 0111 1100
OR
0001 0010 0011 0100 0101 0110 0111 1100
Run Code Online (Sandbox Code Playgroud)

这是0x1234567c十六进制的,我们想去的地方,现在我们跳到那里。这就是为什么你不能从当前指令跳转到超过 256MB(2^28 位)的位置(除非你跳转到寄存器的值)jr

同样的基本思想也适用于分支,只不过现在你还需要比较 2 个寄存器(需要 10 位),因此你只有 16 位可用于偏移,这就是为什么你不能用分支跳转那么远。

一般来说,这很好,因为我们主要在过程中使用分支来实现循环并执行条件分配。

这都是 MIPS 架构设计的结果。完全有可能有这样的指令,其中分支和跳转之间的唯一区别是条件方面,并且“无条件”分支的行为与无条件跳转相同。