5. ARMv8 指令集介绍
ARMv8 架构引入的最重大变革之一是增加了 64 位指令集。该指令集对现有的 32 位指令集架构起到了补充作用。这一新增功能使得可以使用 64 位宽的整数寄存器和进行数据操作,并且具备使用 64 位大小的内存指针的能力。这些新指令被称为 A64,它们在 AArch64 执行状态下运行。ARMv8 还包含原有的 ARM 指令集,现在称为 A32,以及 Thumb(T32)指令集。A32 和 T32 都在 AArch32 状态下执行,并且提供了与 ARMv7 的向后兼容性。
尽管 ARMv8 - A 提供了与 32 位 ARM 架构的向后兼容性,但 A64 指令集与旧的指令集架构是相互独立且不同的,并且采用了不同的编码方式。A64 在增加一些额外功能的同时,也去除了一些可能会限制高性能实现的速度或能源效率的特性。ARMv8 架构也对 32 位指令集(A32 和 T32)进行了一些改进。然而,使用这些改进特性的代码与较旧的 ARMv7 实现并不兼容。
不过,A64 指令集中的指令操作码仍然是 32 位长,而非 64 位。想要详细了解 A64 汇编语言的程序员可以参考《ARM® 编译器 armasm 参考指南 v6.01》。
5.1 ARMv8 指令集(The ARMv8 instruction sets)
新的 A64 指令集与现有的 A32 指令集类似。指令宽度为 32 位,且语法相近。
这些指令集在 ARMv8 架构中采用了通用的命名约定,因此原来的 32 位指令集状态现在被称为:
-
A32:处于 AArch32 状态时,该指令集在很大程度上与 ARMv7 兼容,不过也存在一些差异。具体可参考《ARMv8 - A 架构参考手册》。它还提供了一些新指令,以与 A64 指令集中引入的部分特性保持一致。
-
T32:Thumb 指令集最早被包含在 ARM7TDMI 处理器中,最初仅包含 16 位指令。16 位指令能显著减小程序大小,但会牺牲一定的性能。包括 Cortex - A 系列在内的 ARMv7 处理器支持 Thumb - 2 技术,该技术对 Thumb 指令集进行了扩展,使其能够提供 16 位和 32 位指令的混合使用。这在保留代码大小缩减优势的同时,实现了与 ARM 相近的性能。由于其在大小和性能方面的优势,如今越来越多的 32 位代码在编译或汇编时都会利用 Thumb - 2 技术。
当内核处于 AArch64 状态时,引入了一种新的指令集。按照命名约定,并考虑到 64 位操作,该指令集被称为:
-
A64:A64 提供了与 AArch32 或 ARMv7 中的 A32 和 T32 指令集类似的功能。新的 A64 指令集设计带来了多项改进:
-
一致的编码方案:A32 中一些指令的后期添加导致了编码方案存在不一致性。例如,LDR 和 STR 对半字的支持编码方式与主流的字节和字传输指令略有不同。这就使得寻址模式也稍有差异。
-
广泛的常量范围:A64 指令为常量提供了大量的选择,每种选择都针对特定指令类型的需求进行了定制。
-
算术指令通常接受 12 位立即数常量。
-
逻辑指令通常接受 32 位或 64 位常量,其编码存在一定的限制。
-
-
MOV 指令接受 16 位立即数,该立即数可以移动到任意 16 位边界。
-
-
地址生成指令适用于按 4KB 页面大小对齐的地址。 用于位操作指令的常量规则稍显复杂。不过,位域操作指令可以对源操作数或目标操作数中的任意连续位序列进行操作。 A64 提供了灵活的常量,但对它们进行编码,甚至确定某个特定常量在特定上下文中能否合法编码,都并非易事。
-
数据类型处理更简便
A64 能自然地处理 64 位有符号和无符号数据类型,它为操作 64 位整数提供了更简洁、高效的方式。这对于像 C 或 Java 等提供 64 位整数的所有编程语言而言,都颇具优势。
-
长偏移量
A64 指令通常能提供更长的偏移量,无论是对于基于程序计数器(PC)的相对分支跳转,还是偏移寻址都是如此。 分支跳转范围的扩大,使得跨代码段跳转的管理变得更为轻松。动态生成的代码一般存放在堆中,实际上它可以位于任何位置。运行时系统借助更大的分支跳转范围,能更轻松地管理代码,而且所需的修正操作也更少。 长期以来,文字池(嵌入在代码流中的文字数据块)一直是 ARM 指令集的一个特性,在 A64 中依然存在。不过,更大的基于 PC 的相对加载偏移量极大地有助于文字池的管理,使得每个编译单元使用一个文字池成为可能。这就避免了在长代码序列中为多个文字池设置位置的麻烦。
-
指针
在 AArch64 中,指针是 64 位的,这使得可以寻址更大范围的虚拟内存,并且在地址映射方面有了更多的灵活性。 然而,使用 64 位指针确实会带来一些成本。同一段代码在使用 64 位指针运行时,通常比使用 32 位指针时占用更多的内存。每个指针在内存中存储时需要 8 个字节,而不是 4 个字节。这听起来可能微不足道,但累积起来会是相当大的开销。此外,转向 64 位所带来的内存空间使用增加,可能会导致缓存命中次数下降。缓存命中率的降低会影响性能。 一些语言(如 Java)可以通过实现压缩指针来规避性能问题。
-
采用条件构造代替 IT 块
IT 块是 T32 指令集的一个实用特性,它能够形成高效的指令序列,避免在未执行指令周围使用短向前分支跳转。不过,硬件有时难以高效处理这些 IT 块。A64 去掉了这些 IT 块,取而代之的是条件指令,如 CSEL(条件选择)和 CINC(条件递增)。这些条件构造更为直接,处理起来也更简单,无需特殊情况处理。
-
移位和循环移位行为更直观
A32 或 T32 的移位和循环移位行为并不总是能轻松对应高级语言所期望的行为。 ARMv7 提供了一个桶形移位器,可作为数据处理指令的一部分使用。然而,指定移位类型和移位量需要占用一定数量的操作码位,而这些位原本可以用于其他用途。 因此,A64 指令去掉了很少使用的选项,转而添加了新的显式指令来执行更复杂的移位操作。
-
代码生成
在为常见的算术函数静态或动态生成代码时,A32 和 T32 通常需要不同的指令或指令序列,这是为了处理不同的数据类型。而在 A64 中,这些操作要一致得多,因此为不同大小的数据类型的简单操作生成通用的指令序列要容易得多。
例如,在 T32 中,同一条指令根据所使用的寄存器(低寄存器或高寄存器)不同可能有不同的编码。
A64 指令集的编码更加规范和合理。因此,用于 A64 的汇编器通常比用于 T32 的汇编器所需的代码行数更少。
-
定长指令
与可变长度指令集 T32 不同,所有 A64 指令的长度都是相同的。这使得对生成的代码序列的管理和跟踪更加容易,尤其对动态代码生成器有积极影响。
-
三操作数映射更优
一般来说,A32 在数据处理操作中保留了真正的三操作数结构。而 T32 则包含大量的二操作数指令格式,这在代码生成时灵活性稍差。A64 坚持统一的三操作数语法,这进一步增强了指令集的规范性和一致性,有利于编译器的工作。
-
5.1.1 区分 32 位和 64 位 A64 指令Distinguishing between 32-bit and 64-bit A64 instructions
A64 指令集中的大多数整数指令有两种形式,可对 64 位通用寄存器组中的 32 位或 64 位值进行操作。
当查看指令所使用的寄存器名称时:
-
若寄存器名称以 X

814

被折叠的 条评论
为什么被折叠?



