arm-thumb 指令集的 blx 指令如何支持 4MB 范围

how arm-thumb instruction set's blx instruction support 4MB range

https://www.keil.com/support/man/docs/armasm/armasm_dom1361289866046.htm读到arm-thumb指令的blx指令可以支持最大4MB的跳转范围。

但据我所知,arm-thumb 指令只有 16 位长,那么 16 位怎么可能包含 4MB 的偏移量呢?

在原始的 Thumb 指令集中,BL 指令包含两个编码如下的 16 位指令:

1111 HOOO OOOO OOOO   BL <label>
     |            |
     |            \.. long branch and link offset high/low
     \............... low/high offset
                        0 -- offset high
                        1 -- offset low

两条指令中的第一条必须将H位设置为0。将11位偏移向左移动12,添加到PC并放入LR寄存器。

LR = PC + (offset << 12)

两条指令中的第二条必须将H位设置为1。将11位偏移量左移1,添加到LR寄存器的内容中,用作分支目标。 LR 寄存器设置为 return 地址。

temp = next instruction address
PC = LR + (offset << 1)
LR = temp | 1

在 ARMv5T 中,添加了 BLX 指令的 Thumb 编码,允许 Thumb 代码调用 ARM 代码。这是通过在 BL 指令的后半部分*定义一个新的拇指位来完成的。

111T 1OOO OOOO OOOO   BL/BLX <label> (second half)
   |              |
   |              \.. long branch link exchange offset low
   \................. thumb bit
                        0 -- BLX is encoded
                        1 -- BL  is encoded

BLX的操作类似于BL指令的后半部分,但偏移必须是偶数。该函数在 ARM 状态而不是 Thumb 状态下被调用。

temp = next instruction address
PC = (LR + (offset << 1)) & 0xfffffffc
LR = temp | 1
CSPR T bit = 0

请注意,总共有 22 个立即位给出了半字偏移量,观察到的分支偏移量为 ±4 MiB。

将两半放在一起,我们还可以看到 BLBLX 是 32 位指令,编码如下:

1111 0OOO OOOO OOOO  111T 1OOO OOOO OOOO   BL/BLX <label>
                  |     |              |
                  |     |              \.. 22 bit offset (low half)
                  |     \................. thumb bit
                  \....................... 22 bit offset (high half)

在 Thumb2 中,该方案得到了扩展。 BLBLX 成为正确的 32 位指令,它们的一半必须连续给出。 第二个指令字的一些位被定义为将分支偏移扩展到±16 MiB.

1111 0SOO OOOO OOOO  11AT BOOO OOOO OOOO   BL/BLX <label>
      |           |    || |            |
      |           |    || |            \.. 21 bit offset (low half)
      |           |    || \............... additional bit J2
      |           |    |\................. thumb bit
      |           |    \.................. additional bit J1
      |           \....................... 21 bit offset (high half)
      \................................... sign bit

如果设置了拇指位,则BL 指令被编码。如果清楚,则BLX指令被编码。在后一种情况下,21 位偏移量必须是偶数。然后分支偏移计算如下:

I1 = !(J1 ^ S)
I2 = !(J2 ^ S)
imm32 = (S ? 0xffff << 24 : 0) | (I1 << 23) | (I2 << 22) | (imm21 << 1)
temp = next instruction address
PC = LR + offset
LR = temp | 1
if thumb bit clear
    CSPR T bit = 0 

虽然编码附加偏移位的方案起初看起来很复杂,但它只是将两个附加位编码到分支偏移中同时与 BL 和 [ 的现有编码兼容的最简单方法=23=]说明。


参见 ARM 体系结构参考手册,ARMv7-A 和 ARMv7-R 版, ARM7TDMI 数据 Sheet,以及用于 ARMv5 的 ARM 体系结构参考手册 以供进一步阅读。

* 相关编码1110 0OOO OOOO OOOO编码16位无条件分支指令B <label>.

† 在 Thumb2 之前,BLBLX 指令的两部分是独立的指令,可以与其他指令穿插甚至单独给出,但强烈建议将它们发布在连续顺序。 BLBLX 指令的两半之间也可能发生中断,从而使 LR 寄存器的临时内容可见。在包括 ARMv6-M 在内的 Thumb2 目标上,这不再可能并且 BLBLX 表现为 32 位指令。