ARM、Thumb和Thumb 2指令编码有何区别?

75

我对指令集有点困惑。有Thumb、ARM和Thumb 2。从我所看到的,Thumb指令都是16位的,但在ARMv7M用户手册(第六页)中提到了Thumb 16位和Thumb 32位指令。

现在我必须克服这种困惑。据说Thumb 2支持16位和32位指令。那么ARMv7M是否实际上支持Thumb 2指令而不仅仅是Thumb呢?

还有一件事。我可以说Thumb(32位)和ARM指令(也都是32位)是相同的吗?


3
我可以说Thumb(32位)和ARM指令是相同的,因为它们也是32位的吗?答案是否定的。Thumb2是32/16位混合体,其编码与直接的ARM 32位不同。 - artless noise
6个回答

112

哦,ARM和他们愚蠢的命名...

这是一个常见的误解,但正式来说并不存在所谓的“Thumb-2指令集”。

忽略ARMv8(其中一切都被重新命名,AArch64使事情变得复杂),从ARMv4T到ARMv7-A,有两个指令集:ARM和Thumb。它们都是“32位”的,因为它们在32位宽度的寄存器中处理最多32位宽度的数据和32位地址。实际上,在它们重叠的地方,它们代表完全相同的指令 - 只是指令编码不同,CPU有效地具有两个不同的解码前端,可以在其管道中切换。为了清晰起见,我现在故意避免使用“32位”和“16位”这些术语...

ARM指令具有固定宽度的4字节编码,需要4字节对齐。Thumb指令具有可变长度(2或4字节,现在称为“narrow”和“wide”)的编码,需要2字节对齐 - 大多数指令具有2字节编码,但blblx总是具有4字节编码*。真正令人困惑的部分出现在ARMv6T2中,它引入了“Thumb-2技术”。 Thumb-2不仅将更多指令(大多数采用4字节编码)添加到Thumb中,使其几乎与ARM相当,而且还扩展了执行状态,以允许对大多数Thumb指令进行条件执行,并最终引入了全新的汇编语法(UAL,“统一汇编语言”),取代了先前分别使用的ARM和Thumb语法,并允许编写一次代码,无需修改即可组装为任一指令集。

Cortex-M架构只实现Thumb指令集 - ARMv7-M(Cortex-M3/M4/M7)支持大多数“Thumb-2技术”,包括条件执行和VFP指令的编码,而ARMv6-M(Cortex-M0/M0 +)仅使用少量的4字节系统指令形式的Thumb-2。

因此,新的4字节编码(以及在ARMv7修订版中后来添加的编码)仍然是Thumb指令。它们的“Thumb-2”方面在于它们可以具有4字节编码,并且它们(大多数情况下)可以通过it有条件地执行。我认为,它们的助记符仅在UAL中定义。
注:在ARMv6T2之前,bl(或blx)实际上是一个复杂的实现细节,因为它们是否作为4字节指令或一对2字节指令执行是一个问题。体系结构定义是后者,但由于它们只能按顺序作为一对执行,因此出于性能原因将它们融合成单个指令(除了能够在中断中断开它们之外),并不会有太多损失。ARMv6T2重新定义了事物,以便基于融合的单指令执行。

9
此外,在 Thumb2 中,随着时间的推移,已经添加了一些操作码。因此,并非所有的 Thumb2 版本都相同。从主 CPU 的角度来看,不存在所谓的“正式” Thumb2 模式(我想这就是您所说的?);当然,ARM 公司似乎认为 Thumb2 是一种混合了 16/32 位编码的模式,但除此之外就不清楚了。 - artless noise
谢谢!这为我解决了问题。不管怎样,我两天前去了在纽伦堡举办的嵌入式世界博览会,感觉非常棒。我获得了很多有关ARM的信息。真是一个惊人的活动! - 71GA
1
参数-mthumb-interwork是否允许将Thumb16与Thumb32或Thumb16和Thumb32与ARM结合使用?目前,我正在使用-mthumb参数进行汇编,并在源文件中使用.syntax unified以便汇编器允许使用Thumb16和Thumb32编码。但是,我可以从我的源文件中删除.syntax unified并在汇编时使用-mthumb-interwork参数吗?文档对此并不十分清楚... - 71GA
1
Cortex M4 是 armv7e-m - ConsistentProgrammer
1
我知道这是一个旧答案,但我不同意“不存在Thumb-2指令集”的说法,因为ARM的官方网站有一个页面标题为"The Thumb-2 instruction set" - GandhiGandhi
显示剩余2条评论

26
除了Notlikethat的回答之外,ARMv8引入了一些新术语以尝试减少混淆(当然还添加了更多新术语):
有一个32位执行状态(AArch32)和一个64位执行状态(AArch64)。
32位执行状态支持两种不同的指令集:T32(“Thumb”)和A32(“ARM”)。64位执行状态只支持一种指令集 - A64。
所有A64指令,像所有A32指令一样,都是32位(4字节)大小,需要4字节对齐。
许多/大多数A64指令可以在32位和64位寄存器上操作(或者可以说是相同基础64位寄存器的32位或64位视图)。
所有实现AArch32的ARMv8处理器(就像所有实现ARMv7处理器的一样)都支持T32指令集中的Thumb-2指令。
并非所有的ARMv8-A处理器都实现了AAarch32,有些不实现AArch64。一些处理器同时支持两种执行状态,但只在更低的异常级别下支持AArch32。

2
我没有足够的声望直接评论unixsmurf的答案,但它需要进行微调。ARMv8A处理器核心可以选择根本不实现aarch32状态。这样的核心将不支持T32或A32指令。如果给定的处理器核心确实实现了aarch32状态(例如ARM有限公司的cortex-A53、A57和A72),那么它也支持该状态下的Thumb-2模式。 - wmills
@wmills:你刚刚直接在它上面发表了评论 :) 你可以将自己的澄清作为答案添加进去。 - unixsmurf
1
@wmills确实,自从这个答案被写出来后,有些处理器也不支持AArch64 ;) - Notlikethat

12

Thumb: 16位指令集。

ARM: 32位指令集,因此具有更灵活的指令和较少的代码密度。

Thumb2 (混合16/32位): 是一种介于 ARM 和 thumb(16)之间的折衷方案(将它们混合使用),以获得ARM性能/灵活性和Thumb指令密度。因此,Thumb2指令既可以是带有32位宽指令的ARM(仅为子集),也可以是带有16位宽指令的Thumb指令。


3
实际上,这是一个很好的简单答案,可以用于通用理解,因为细节很繁琐。Thumb1 有限的寄存器对性能产生了影响。使用 Thumb2,您几乎拥有 ARM32 的所有功能,并且具有压缩指令流。结果是,Thumb2 在几乎所有情况下都比较高效(指令获取更少),并且代码大小更小。 - artless noise
1
Thumb2的32位指令与其ARM模式等效指令具有不同的编码方式。把Thumb2看作包含实际ARM指令可能是一种有用的简化方式,但如果你看细节,就会发现它们之间有所区别。ARM模式在每个指令中使用4位进行预测。Thumb2模式使用一位来表示指令是1个还是2个16位块,并且仍然需要一个“it”指令来预测后面的指令。 - Peter Cordes

6

对我来说,Cortex M3具有4字节指令,但却不能执行ARM指令,或者某些能够使用2字节和4字节操作码的CPU也可以执行ARM指令,这让我感到困惑。所以我读了一本有关Arm的书,现在稍微理解多了一点,但是命名和重叠部分仍然让我感到困惑。我认为先比较几个CPU的功能和重叠部分,然后再谈论ISA会很有趣。

比较一下几个CPU及其功能和重叠部分:

Cortex M0/M0+/M1/M23 是 Thumb(Thumb-1)指令集,可以执行 2-byte 的 Opcode。然而,一些指令如 mrs、msr、bl、dmb、dsb、isb 等是 Thumb-2 指令并需要 4-byte。Cortex M0/M0+/M1 的 ARMv6,而 Cortex M23 是 ARMv8。Thumb-1 指令在 ARMv7 中得到了扩展,因此可以说 ARMv8 Cortext M23 支持比 ARMv6 Cortex M0/M0+ 更完整的 Thumb-1(除 it 指令外),后者只支持 ISA 的子集(特别缺少 it、cbz 和 cbnz 指令)。我可能错了(如果不正确请纠正我),但注意到一个有趣的事情,那就是我所看到的仅支持 Thumb-1 的 CPU 都已经支持 Thumb-2,我不知道是否存在仅支持 Thumb-1 并支持 100% Thumb-1 的 CPU。我认为这是因为 it 被视为 Thumb-2 Opcode,它是 2-byte,实质上被添加到 Thumb-1。在 Thumb-1 CPU 上,4-byte Opcode 可以被视为两个 2-byte 表示 4-byte Opcode。

Cortex M3/M4/M7/M33/M35P/M55 可以执行 2-byte 和 4-byte Opcode,都是 Thumb-1 和 Thumb-2,并支持全套 ISAs。2-byte 和 4-byte Opcode 混合得更均匀,而 Cortex M0/M0+/M1/M23 更倾向于大部分时间使用 2-byte Opcode。Cortex M3/M4/M7 是 ARMv7,而 Cortex M33/M35P/M55 是 ARMv8。

Cortex A/R 可接受 ARM 和 Thumb Opcode,因此有 2-byte 和 4-byte。要切换模式,需要将 PC 偏移一个字节(强制不对齐),例如可以使用 bx 分支指令来实现这一点,它设置 CPSR 的 T 位并根据地址的最低位切换模式。例如,在调用子例程时,PC(及其模式)被保存,然后在子例程内可以切换到 Thumb 模式,但在从 Thumb 模式返回时,它将恢复 PC(及其 T 位)并切换回调用者所在的模式(ARM 或 Thumb 模式),没有任何问题。

ARM7 仅支持 ARMv3 的 4-byte ISA。

ARM7T 支持 Thumb-1 和 ARM ISAs(2-byte 和 4-byte)。

ARM11(ARMv6、ARMv6T2、ARMv6Z、ARMv6K)支持 Thumb-1、Thumb-2 和 ARM ISAs。

我参考的书中提到,在ARMv7 及更高版本架构中,为了获得更好的性能,从Von Neumann(数据和指令共享一个总线)切换到Harvard(专用总线)。然而,“及更高版本”这个绝对术语并不准确,因为ARMv8是更高版本,但ARMv8 Cortex M23是Von Neumann。

ISA(指令集架构)包括:

ARM有16个寄存器(R0-R12, SP, LR, PC),只有4字节的操作码,ISA有不同的修订版本,但它们只是4字节的操作码。
Thumb(又名Thumb-1)将16个寄存器分成较低(R0-R7)和较高(R8-R12、SP、LR、PC)两部分,大多数指令只能访问较低的一组,而只有部分指令可以访问较高的一组。只有2字节的操作码。在具有16位总线(必须通过两步执行32位字访问)的低端设备上,执行2字节操作码时性能更好,因为它匹配了它们的总线。命名有些令人困惑,Thumb可以被用作两种ISAs的家族术语,包括Thumb-1和Thumb-2,或者有时仅指Thumb-1。我认为Thumb-1不是Arm的正式术语,只是我见过的人们用来使Thumb ISAs系列与第一个Thumb ISA之间的区别更清晰。ARM中的指令可以使用可选的后缀来更新CPSR寄存器(例如ands,orrs,movs,adds,subs指令),而在Thumb-1中,s始终打开并且会一直保存CPSR寄存器。在一些旧的工具链中,隐含的s是不必要的,然而在统一汇编语言(UAL)的努力中现在要求即使没有不使用s的选项也必须显式指定
Thumb-2是Thumb的扩展,可以像ARM一样访问所有寄存器,具有4字节的操作码,并与ARM相比有一些区别。在汇编中,可以用.n和.w后缀强制执行Thumb-1的2字节窄操作码和Thumb-2的4字节宽操作码(例如orr.w)。ARM和Thumb-2的操作码格式/编码不同,它们的能力也不同。指令的条件执行可以使用,但只能在if-then指令/块之前添加。这可以显式地完成,也可以暗示(并由用户背后的工具链完成)。混淆可能实际上很好,因为Arm公司希望它们相似,花费了大量精力用于统一汇编语言(UAL),以便为ARM编写的汇编文件可以在无需更改的情况下编译为Thumb-2。如果我理解正确,这不能保证100%,可能会出现一些边缘情况,其中ARM汇编无法编译为Thumb-2,这是另一个不完全正确的绝对语句。例如,ARM7的bl指令可以寻址+-32MB,而在Cortex M3上只能寻址+-16MB。与Thumb-1的另一个区别是数据处理指令。ARM和Thumb-2都支持8位立即数,而ARM只能向右旋转位,且只能旋转偶数位,而Thumb可以进行向左的旋转,并且可以进行偶数/奇数数量的位旋转,还允许重复的字节模式,例如0xXYXYXYXY、0x00XY00XY或0xXY00XY00。由于移位是旋转的,左移和右移可以通过“溢出”实现,将其向一个方向移动得足够多,它就等效于向相反方向移动 <1 << (32 - n) == 1 >> n>。

综上所述,一些 Arm CPU 可以执行以下操作:

  • 仅能执行纯 ARM ISA 的 4 字节操作码指令
  • 使用 2 字节 / 4 字节的 Thumb-1 / Thumb-2 ISA,重点是大部分时间使用 2 字节,并只有少数 4 字节操作码,这些通常被标记为 Thumb (Thumb-1) 2 字节操作码的 CPU(往往不提到极少数的 4 字节操作码)
  • 使用 2 字节 / 4 字节的 Thumb-1 / Thumb-2 ISA,其 2 字节和 4 字节操作码的比例较为均衡,通常被标记为 Thumb-2
  • 通过在 ARM / Thumb 模式之间切换,可以使用 2 字节 / 4 字节操作码

该信息的参考来源:《ARM汇编语言编程与体系结构》Muhammad Ali Mazidi等人,2016年。该书在公司名称从 ARM 更名为 Arm 之前编写,因此有时会混淆引用了公司 Arm 还是 ARM ISA。


2
我想知道M4处理器是否能够执行ARM和Thumb2。你的回答是唯一解决我的问题的答案。谢谢。 - honestSalami
真的吗,答案是什么呢?Thumb-1在任何Cortex-M CPU上都不存在。Thumb-1与Cortex-M0上的东西完全不同。16/32位编码并不是这个问题的唯一相关部分。M4只支持Thumb(2)模式,尽管指令是16/32位混合的。 - artless noise
从Arm的文件https://developer.arm.com/Processors/Cortex-M4中可以看到:“ISA支持:Thumb或Thumb-2,具有硬件除法(2-12个周期),单周期乘法,位域处理,饱和调整支持,DSP扩展”。所以你是说Arm的文档有错误,只支持Thumb-2? - Anton Krug
@artlessnoise 完全不同的生物?你是指M0支持缺少那个“生物”吗?类似于没有“生物”的Thumb1模式?Arm的文档:https://developer.arm.com/Processors/Cortex-M0 上说:“Thumb或Thumb-2子集”。我列出了关于指令及其在M0中的差异的具体细微之处,这样人们就能更好地理解Thumb-1和Thumb-2支持在实际中意味着什么。你所说的“完全不同的生物”是什么意思?当我的列表对你来说不相关或错误时,请列出所有指令及其细微之处。M0相对于其他处理器非常有限,但“完全不同的生物”没有任何信息。 - Anton Krug
是的,ARM正在重新定义这个概念。你可以看到notlikethat的回答。还有其他实体如GCC、Clang、Apple、Samsung、Broadcom等也会使用这些术语。Cortex-M0可以在Thumb模式下处理异常。原始的“Thumb-1”架构要求你在ARM32模式下处理异常。我在我的回答中试图详细说明其他细微差别。正如你可以从UnixSmurf的帖子中看到(https://meta.stackoverflow.com/questions/344456/could-some-of-the-arm-tags-be-merged),一位ARM员工指出了ARM命名中的一些冗余之处。Thumb也存在类似情况。 - artless noise

2
还有一件事。我可以说Thumb(32位)和ARM指令(也是32位)是一样的吗?
不可以。(我的评论已经回答了这个问题)
据说Thumb 2支持16位和32位指令。那么ARMv7M实际上是支持Thumb 2指令而不仅仅是Thumb吗?
这是正确的,因为ARMv7M支持超出初始ARM Thumb的指令。它通常被称为Thumb-2,但本身含糊不清。
这个问题似乎存在很多混淆。问题在于“thumb”是一个负载过重的概念。
1. 它是一些ARM CPU的操作模式。 2. 它是一种ISA。 3. 它是一种程序员模型。
ARM一直试图创建“架构”版本。也就是说,来自同一架构版本的相同CPU应该运行相同的二进制代码。在维基百科上可以找到一个列表。架构通常被列为ARMvN,其中N是一个数字。ARMv1到ARMv4(大约1985年至1998年)只支持ARM 32位ISA。在CPSR或系统模式寄存器中没有Thumb位。从ARMv4开始,一些CPU支持Thumb。这在ARMv4t中有所列明。ARMv5的更新允许一种称为“交互工作”的功能,即32位ARM例程可以调用16位Thumb例程。调用会切换模式寄存器。在ARMv6中,一些CPU支持Thumb-2,它是一种混合的16/32位ISA;但它仅使用CPSR的相同“thumb”位。在ARMv7中,这种混合的16/32位模式成为带有thumb模式位的标准配置。
一些关于命名的问题是,混合16/32位Thumb-2代码可以随着架构的变化而添加不同的操作码。此外,它们可以根据CPU配置和模式具有额外的指令。
Thumb原始版本
这种模式对高寄存器(r8-r15)的使用有限。它使用了一个不同的寄存器作为堆栈。它不允许条件执行指令。所有ALU指令都设置条件码;而ARM32有一个“s”位来选择是否设置条件码。它主要用于减少代码密度。指令集只有16位的操作码。一些使用这个CPU集的流行机器包括Game Boy Advance。
Thumb-2
虽然Thumb-2包括32位指令,但Thumb2的二进制与原始的ARM 32位ISA不兼容。它们完全不同。此外,“Thumb-2”随着时间的推移发展。它向后兼容,但有不同版本的“Thumb-2”。
UAL和条件执行
ARM公司推出了“Thumb-2”和“统一汇编语言”。通过Thumb-2,几乎每个ARM 32位指令集体系架构都有一个操作码映射。其中最明显的不同是条件执行。ARM 32位为每个操作码都添加了一个条件码字段,这占用了大量的操作码空间。这意味着为了实现相同的功能(原始C代码),汇编/目标代码会更大。通过Thumb-2,引入了“it”指令来预设条件寄存器。IT指令可以使接下来的四条指令条件执行。这是产生UAL(一种可生成传统ARM 32位或Thumb32操作码/二进制代码的汇编语法)的最大变化。
另请参阅:ARM it条件指令 系统级别

Thumb-2还添加了指令以访问协处理器并在Thumb-2模式下处理异常。早期版本的Thumb/Thumb-2不允许一些系统级概念,因此这些CPU需要ARM32。这些对Thumb-2 ISA的增加使Cortex-M变体成为可能。

现代

Cortex CPU(ARMv7+)中的Thumb-2操作码比传统的32位ARM操作码编码更紧凑。它们将执行速度更快(除非某些极端情况),并且代码密度更小。从2004年起,所有的ARM CPU都支持这个Thumb-2变体。真的没有必要生成传统的32位ARM二进制代码。只有在2005年左右之前设计的CPU才需要这个功能。

使用原始的'Thumb'的CPU非常受限,因为所有操作码只有八个寄存器可用。没有支持应用程序开发人员动态执行的手机或商业产品将使用'Thumb' ISA。只有深度嵌入式设备(可能用于遗留产品或升级)会使用此ISA。Thumb-2共享编码,并可以运行Thumb二进制文件,但这主要是一个历史注释。条件执行和混合16/32位ISA使得Thumb2能够取代ARM32操作码。

ARM/Thumb的特点是模式位。您必须确定CPU以了解是否适用Thumb/Thumb-2。Cortex-M CPU仅支持Thumb2操作码。它们不支持传统的ARM32操作码。Cortex-A CPU支持两种模式,以便运行遗留的ARM32二进制文件(或由误导的开发人员生成的文件)。


2023年的ARM开始,

T32指令集,在Armv8之前的架构中被称为Thumb,是一种混合的32位和16位长度指令集,为设计者提供了出色的代码密度,以实现最小的系统内存大小和成本。

随着ARM试图重新定义术语,程序员应该注意到历史上的命名,因为其他第三方工具、操作系统配置、SOC文档等可能对“Thumb”和“Thumb-2”这两个词有不同的含义。即使这段文字也是一个误称,因为混合的32/16位ISA被称为“Thumb-2”并引入使用。


1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接