本文源自《书香度年华》「ARM 架构专栏」,是一系列由浅入深、循序渐进的文章,文章之间有一定的前后关联性,所以按顺序阅读,建议收藏专栏。
目录
一. 摘要
Arm采用精简指令集RISC架构,RISC构架的指令格式和长度通常是固定的、且指令少、简单,大多数指令在一个周期内就可以执行完毕。
本文将从多个维度勾勒 Arm 指令集的轮廓,这些维度包括:集合/扩展分类、功能分类、状态分类、权限分类。
二. 集合分类
指令集回顾
2.1 Thumb 指令集
Thumb 是16位的指令集(更确切的说应该是在 Thumb 状态下执行的指令),相比32位 Arm 指令集,通过精简功能和效能,能够获得更好的代码密度。
Thumb 指令集没有协处理器指令,信号量指令以及访问CPSR 或SPSR 的指令,没有乘加指令及64 位乘法指令等,且指令的第二操作数受到限制;除了跳转指令B 有条件执行功能外,其它指令均为无条件执行;大多数Thumb 数据处理指令采用2 地址格式。
Thumb 指令编码格式
Thumb指令集与ARM 指令的区别一般有如下几点:
跳转指令
程序相对转移,特别是条件跳转与 ARM 代码下的跳转相比,在范围上有更多的限制,转向子程序是无条件的转移。
数据处理指令
数据处理指令是对通用寄存器进行操作,在大多数情况下,操作的结果须放入其中一个操作数寄存器中,而不是第3 个寄存器中。数据处理操作比 ARM 状态的更少,访问寄存器 R8~R15 受到一定限制。除 MOV 和 ADD 指令访问器 R8~R15 外,其它数据处理指令总是更新 CPSR 中的 ALU 状态标志。访问寄存器 R8~R15 的 Thumb 数据处理指令不能更新 CPSR 中的 ALU 状态标志。
单寄存器加载和存储指令
在 Thumb 状态下,单寄存器加载和存储指令只能访问寄存器R0~R7。
批量寄存器加载和存储指令
LDM 和STM 指令可以将任何范围为 R0~R7 的寄存器子集加载或存储。PUSH 和POP 指令使用堆栈指令 R13 作为基址实现满减堆栈。除 R0~R7 外,PUSH 指令还可以存储链接寄存器R14,并且 POP 指令可以加载程序指令 PC。
2.2 Jazelle 指令集
Jazelle 技术是 ARM 提供的组合型硬件和软件解决方案。ARM Jazelle 技术软件是功能丰富的多任务 Java 虚拟机 (JVM),经过高度优化,可利用许多 ARM 处理器内核中提供的 Jazelle 技术体系结构扩展。。
2.3 Thumb-2 指令集
Thumb-2 是对 Thumb 指令集的扩充,吸收了 Arm32 指令,在保持代码密度的同时,依然能够获得很好的内存性能。
2.4 Thumb-EE
ThumbEE,也就是所谓的 Thumb-EE,业界称为 Jazelle RCT技术,于2005年发表,首见于 Cortex-A8 处理器。ThumbEE 提供从 Thumb-2 而来的一些扩充性,在所处的执行环境(Execution Environment)下,使得指令集能特别适用于执行阶段(Runtime)的编码产生(例如即时编译)。Thumb-2EE 是专为一些语言如 Limbo、Java、C#、Perl 和 Python,并能让 即时编译器 能够输出更小的编译码却不会影响到效能。
ThumbEE 所提供的新功能,包括在每次存取指令时自动检查是否有无效指标,以及一种可以执行阵列范围检查的指令,并能够分支到分类器( handlers ),其包含一小部份经常呼叫的编码,通常用于高阶语言功能的实作,例如对一个新物件做内存配置。
2.5 NEON&VFP&SVE&SME&Helium
进阶 SIMD 延伸集,业界称为 NEON 技术,它是一个结合 64 和 128 bit 的 SIMD(Single Instruction Multiple Data 单指令多重数据)指令集,其针对多媒体和讯号处理程式具备标准化加速的能力。NEON 可以在 10 MHz 的 CPU 上执行 MP3 音效解码,且可以执行 13 MHz 频率以下的 GSM AMR (Adaptive Multi-Rate) 语音编码。NEON具有一组广泛的指令集、各自的寄存器阵列,以及独立执行的硬件。NEON 支援 8-, 16-, 32- 和 64-bit 的整数及单精度浮点数据,并以 SIMD 的方式运算,执行图形和游戏处理中关于语音/视讯的部分。SIMD 在 向量超级处理机 中是个决定性的要素,它具备同时多项处理功能。在 NEON 技术中,SIMD 最高可支援到同时 16 个运算。
VFP 是在协同处理器针对ARM架构的衍生技术。它提供低成本的单精度和倍精度浮点运算能力,并完全相容于ANSI/IEEE Std 754-1985 二进制浮点算数标准。VFP 提供大多数适用于浮点运算的应用,例如PDA、智慧手机、语音压缩与解压、3D图像以及数位音效、打印机、机上盒,和汽车应用等。VFP 架构也支援 SIMD(单指令多重数据)平行化的短向量指令执行。这在图像和讯号处理等应用上,非常有助于降低编码大小并增加输出效率。
在 ARM-based 处理器中,其他可见的浮点、或 SIMD 的协同处理器还包括了 FPA, FPE, iwMMXt。他们提供类似 VFP 的功能但在opcode层面上来说并不具有相容性。
协助 ARM64 架构去加强向量处理能力以满足以下几个领域的计算需求:HPC,数据分析,计算机视觉和机器学习。引入扩展的概念,使其能够在现在或将来的多应用场景下实现伸缩,允许 CPU 设计者自由选择向量的长度来满足 PPA 目标。应避免由于矢量的长度变化而增加软件开发成本,甚至在可能的情况下通过提高编译器自动矢量化技术来降低软件开发成本。
SVE 允许矢量寄存器长度可以在128到2048之间选择实现。它支持与矢量长度无关的编程模型,允许代码在所有矢量长度上自动运行和缩放,而无需重新编译。它还引入了几个创新性的特性,这些特性可以克服一些传统的自动矢量化障碍。不同的应用场景对向量长度有不同的需求,所以没有哪一个单一的长度是最佳的。出于这个原因,SVE 的向量长度是可选择的(从128到2048,以128递增)。更重要的是,编程模型可以动态调整到合适的向量长度,而不需要重新编译高级语言或者重写手工编码的SVE组件或编译器。更长的矢量只是 SVE 解决方案的一部分,要实现显著的加速也需要高水平的矢量利用率,所以 SVE 特性具有以下特点:
可伸缩的向量长度:通过选择合适的长度来提高并行度
丰富的寻址方式.实现非线性数据访问
单通道 predication:允许包含复杂控制流的循环矢量化
Predicate-driven 的循环控制和管理:降低了将标量代码的矢量化开销
丰富的并行化操作:适用于更多类型的可分解 loop-carried 依赖关系
向量分区和软件管理的推测:支持将数据依赖的 loop 矢量化
可伸缩的向量内子循环允许对具有更复杂 loop-carried 依赖关系的循环进行矢量化
可伸缩矩阵扩展(Scalable Matrix Extension, SME),SME是一序列增强CPU构架对矩阵运算支持的最新更新。
Arm 宣布针对其下一代 Armv8.1-M 架构推出基于 M-Profile Vector Extension (MVE) 矢量扩充方案的 Arm Helium 技术。
五、寄存器
寄存器在计算机中是用来寄存数据的,因为是寄存,所以其存放的数据变化是非常频繁的。
通常来讲,从数据生命周期长短来看,寄存器<SRAM<DRAM<非易失存储器(闪存/磁盘)。正因为其高频使用,寄存器在ARM架构中重要性不言而喻。
通用寄存器,顾名思义,用来实现计算模型的通用寄存器。
X0~X30共31个64位寄存器,W0~W30对应Xn寄存器低32位,可以认为是兼容32位用法;
X29为Frame Pointer,存放当前栈帧;X30为Link Rigister,记录程序返回地址。
特殊寄存器,用来使完善、优化、辅助计算模型的寄存器;
Zero寄存器
用来存放0,相比立即数#0,可以减少指令数,提高效率;XZR/WZR分别是64位和32位,在指令编码中,被编码成31,故没有X31寄存器。向Zero寄存器写入数据,相当于丢弃数据。
程序计数器PC
记录当前程序运行的指令地址,在ARMv8后,不允许显示的将PC作为目标寄存器,即不允许mov PC, xx这种用法,能够让计算流更加确定,易追踪。
堆栈指针SP
栈指针主要用来存放局部数据存放的地址,因为寄存器数量有限,而参与计算的临时或者局部数据可能会很多,所以需要内存来存放这些数据,这块内存称之为栈,就像客栈那样是临时寄宿的一样。
ARMv8后要求16字节对齐,每个执行级别都有自己的SP寄存器,EL0只能访问SP_EL0,而其它执行级别可以访问所有级别的SP;SP_EL0也可以通过PSTATE.SP访问。
程序状态寄存器CPSR/SPSR
计算模型在计算过程中会产生很多状态,比如有没有进位、有没有溢出,还会包含当前程序运行的其他状态,这些状态保存在当前程序状态寄存器 CPSR中,因为各个异常级别公共一个CPSR,所以每个异常级别都有用于保存自己CPSR的寄存器 SPSR。在异常级别切换时(无论是手动的还是自动的),CPSR都会保存进SPSR,等下一次返回时,再从SPSR恢复到CPSR中。
异常返回地址寄存器ELR
系统寄存器,用来对计算模型进行配置并记录其状态的寄存器;
系统配置寄存器SCR,TBD
系统控制寄存器SCTLR,TBD
转换表基址寄存器TTBR,TBD
异常向量基址寄存器VBAR,TBD