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。
将两半放在一起,我们还可以看到 BL
和 BLX
是 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 中,该方案得到了扩展。 BL
和 BLX
成为正确的 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 之前,BL
或 BLX
指令的两部分是独立的指令,可以与其他指令穿插甚至单独给出,但强烈建议将它们发布在连续顺序。 BL
或 BLX
指令的两半之间也可能发生中断,从而使 LR
寄存器的临时内容可见。在包括 ARMv6-M 在内的 Thumb2 目标上,这不再可能并且 BL
和 BLX
表现为 32 位指令。
从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。
将两半放在一起,我们还可以看到 BL
和 BLX
是 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 中,该方案得到了扩展。 BL
和 BLX
成为正确的 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 之前,BL
或 BLX
指令的两部分是独立的指令,可以与其他指令穿插甚至单独给出,但强烈建议将它们发布在连续顺序。 BL
或 BLX
指令的两半之间也可能发生中断,从而使 LR
寄存器的临时内容可见。在包括 ARMv6-M 在内的 Thumb2 目标上,这不再可能并且 BL
和 BLX
表现为 32 位指令。