为什么ARM汇编语言中的B 25或BEQ 25表示"go to PC + 8 + 100"

Why does B 25 or BEQ 25 in ARM assembly language mean "go to PC + 8 + 100"

我是初学者,我正在学习 32 位 ARM 作为我的计算机组织课程的一部分。 B 25BEQ 25 的含义为 go-to PC + 8 + 100。我明白 100 是什么意思。它是偏移量,因为 ARM 是字节寻址的,我们需要将程序计数器递增 25*4。但是 +8 是什么意思?有人可以帮我解决这个问题吗?

当构建第一个 ARM CPU 时,他们尽可能简单。

最简单的设计(需要最少数量的晶体管)导致几乎 (*) 所有读取寄存器的指令的值 (当前指令的地址 + 8) r15.

出于这个原因,指令ADD R15, R15, #100(在那些旧的ARM CPUs上)将跳转到当前指令的地址加上108

为了与现有程序兼容,这在较新的 ARM CPUs 中没有改变。


(*) 顺便说一句:

对于某些指令,结果正式为(当前指令地址+12)。在较新的 ARM CPU 上,这些指令(例如 ADD r0, r15, r1, lsl r2)在读取 r15.

时会导致“不可预测的值”

所以确实有一些指令在第一个 ARM CPUs 上导致可预测的结果,而在现代 ARM CPUs 上不再导致可预测的结果。

可能出于真正的流水线原因,但今天为了反向兼容性,PC 是“领先的”。提前两条指令和 4+4 或 8 字节的全尺寸指令。想想获取、解码、执行,当你执行 pc 的时间提前了两个。

汇编语言特定于汇编器而非目标。 B 25 是该分支指令上的一个奇怪操作数,但根据您的问题,假设是指令中立即编码为单词单位。目标地址将是 PC(B 25 指令!!!)加 25*4 加 8。25 * 4 是因为每个字有 4 个字节。所以目标地址是指令的PC加100加8。

Disassembly of section .text:

00000000 <skip-0x24>:
   0:   e1a00000    nop         ; (mov r0, r0)
   4:   e1a00000    nop         ; (mov r0, r0)
   8:   e1a00000    nop         ; (mov r0, r0)
   c:   e1a00000    nop         ; (mov r0, r0)
  10:   ea000003    b   24 <skip>
  14:   e1a00000    nop         ; (mov r0, r0)
  18:   e1a00000    nop         ; (mov r0, r0)
  1c:   e1a00000    nop         ; (mov r0, r0)
  20:   e1a00000    nop         ; (mov r0, r0)

00000024 <skip>:
  24:   eafffffe    b   24 <skip>

这里的编码:

  10:   ea000003    b   24 <skip>

显示 3 为立即数,指令地址为 0x10 所以

0x10 + (3*4) + 8 = 0x10+12+8= 0x10+20 = 0x10+0x14 = 0x24

这个负数编码为 0xFFFFFFFE 乘以 8 是 0xFFFFFFF8

  24:   eafffffe    b   24 <skip>

所以 0x24 + 0xFFFFFFF8 + 8 = 0x24

请注意,反汇编中的 b 24 表示跳转至地址 0x24,而不是像您的问题所暗示的那样立即跳转到地址 25。

对于拇指模式,他们也提前两个,每条指令 16 位或两个字节,提前 4 个字节 2+2。

Disassembly of section .text:

00000000 <skip-0x12>:
   0:   46c0        nop         ; (mov r8, r8)
   2:   46c0        nop         ; (mov r8, r8)
   4:   46c0        nop         ; (mov r8, r8)
   6:   46c0        nop         ; (mov r8, r8)
   8:   e003        b.n 12 <skip>
   a:   46c0        nop         ; (mov r8, r8)
   c:   46c0        nop         ; (mov r8, r8)
   e:   46c0        nop         ; (mov r8, r8)
  10:   46c0        nop         ; (mov r8, r8)

00000012 <skip>:
  12:   e7fe        b.n 12 <skip>

所以对于这个(每条指令两个字节,所以 3 * 2)

   8:   e003        b.n 12 <skip>

0x08 + (0x3<<1) + 4 = 0x08 + 0x6 + 4 = 0x12

其他指令集可能会在执行期间将 pc 伪造为指令的地址,或者您经常看到它指向下一条指令。这完全是任意的,就是这样,您的代码或机器代码偏移量必须符合架构。

简短的回答看看 acorn 文档,三级流水线,所以想着获取、解码、执行,所以当你执行 pc 时,它会提前获取两条指令。尽管管道更深,但非橡子 ARM 继续采用该方案。

说明 a、b、c、d(不是真正的说明,只是为了演示目的)

0x10 a
0x14 b
0x18 c
0x1C d

PC
0x10 fetch a
0x14 fetch b decode a
0x18 fetch c decode b execute a
0x1C fetch d decode c execute b

所以指令 a 位于地址 0x10 但当它执行时 pc 是 0x18。这是添加到编码中的 8 字节差异。