type
status
date
slug
summary
tags
category
icon
password
URL
参考文档:Armv8-A Instruction Set Architecture.pdf
一、前言
Armv8-A 中的指令集 Armv8-A 支持三种指令集:A32、T32 和 A64。
在AArch64执行状态下执行时使用A64指令集。它是一个固定长度的32位指令集。名称中的 “64”指的是 AArch64 执行状态对该指令的使用。它不是指内存中指令的大小。
A32 和 T32 指令集也分别称为“ARM”和“Thumb”。这些指令集在 AArch32 执行状态下执行时使用。在本指南中,我们不介绍 A32 和 T32 指令集。
每个版本的 Arm 架构都有自己的 Arm 架构参考手册(Arm ARM),可以在 Arm 开发者网站上找到。每个Arm ARM都提供了每条指令的详细说明,包括:
- 编码——指令在内存中的表示。
- 参数 - 指令的输入。
- 伪代码 - 指令的作用,以 Arm 伪代码语言表示。
- 限制 - 当指令不能使用时,或者它可以触发的异常。
A64 的指令描述也以 XML 和 HTML 形式提供。如果您需要经常参考说明,则 XML 和 HTML 格式
非常有用。 XML 和 HTML 格式可以在 Arm 开发者网站上找到。
1.1 指令概况
指令基本格式
<Opcode>{<Cond>}<S> <Rd>, <Rn> {,<Opcode2>}
- 其中尖括号是必须的,花括号是可选的
- A32: Rd => {R0–R14}
- A64: Rd =>Xt => {X0–X30}
1.2 指令分类
类型 | Note |
跳转指令 | 条件跳转、无条件跳转(#imm、register)指令 |
异常产生指令 | 系统调用类指令(SVC、HVC、SMC) |
系统寄存器指令 | 读写系统寄存器,如 :MRS、MSR指令 可操作PSTATE的位段寄存器 |
数据处理指令 | 包括各种算数运算、逻辑运算、位操作、移位(shift)指令 |
load/store内存访问指令 | load/store {批量寄存器、单个寄存器、一对寄存器、非-暂存、非特权、独占}以及load-Acquire、store-Release指令 (A64没有LDM/STM指令) |
协处理指令 | A64没有协处理器指令 |
1.3 指令助记符
整型 | ㅤ |
W/R | 32bit整数 |
X | 64bit整数 |
加载/存储、符号-0扩展 | ㅤ |
B | 无符号8bit字节 |
SB | 带符号8bit字节 |
H | 无符号16bit半字 |
SH | 带符号16bit半字 |
W | 无符号32bit字 |
SW | 带符号32bit字 |
P | Pair(一对) |
寄存器宽度改变 | ㅤ |
H | 高位(dst gets top half) |
N | 有限位(dst < src) |
L | Long (dst > src) |
W | Wide (dst==src1,src1>src2) ? |
1.4 指令条件码
编码 | 助记符 | 描述 | 标记 |
0000 | EQ | 运算结果相等为1 | Z==1 |
0001 | NE | 运算结果不等为0 | Z==0 |
0010 | HS/CS | 无符号高或者相同进位,发生进位为1 | C==1 |
0011 | LO/CC | 无符号低清零,发生借位为0 | C==0 |
0100 | MI | 负数为1 | N==1 |
0101 | PL | 非负数0 | N==0 |
0110 | VS | 有符号溢出为1 | V==1 |
0111 | VC | 没用溢出为0 | V==0 |
1000 | HI | 无符号 > | C==1 && Z==0 |
1001 | LS | 无符号 <= | !(C==1 && Z==0) |
1010 | GE | 带符号 >= | N==V |
1011 | LT | 带符号 < | N!=V |
1100 | GT | 带符号 > | Z==0 && N==V |
1101 | LE | 带符号 <= | !( Z==0 && N==V) |
1110 | AL | 无条件执行 | Any |
1111 | NV | ㅤ | ㅤ |
ㅤ | ㅤ | ㅤ | ㅤ |
二、跳转指令
通常,处理器按程序顺序执行指令。这意味着处理器按照指令在内存中设置的顺序执行指令。更改此
顺序的一种方法是使用分支指令。分支指令改变程序流程并用于循环、决策和函数调用。
A64指令集还有一些条件分支指令。这些指令会根据先前指令的结果更改其执行方式。
2.1 无条件跳转指令
2.2 条件跳转指令
2.3 举例说明
第一个例子:如果a等于5,则b等于5
使用汇编编写,如下所示
第二个例子:当a不等于0时,b+c赋值给b,a-1赋值给a
使用汇编
三、PC相对寻址
读取PC的方法:PC相对地址指令(ADR, ADRP),以及branch-and-link指令(BL和BLR)会将PC地址存储在LR寄存器
修改PC的方法:使用显示控制流指令:条件分支、无条件分支、异常产生和异常返回指令
相对于A32的操作PC的MOV指令,A64已经不支持
3.1 ADR指令
使用格式:ADR register exper
编译时,首先会计算出当前PC到exper的偏移量#offset_to_exper
然后会用ADD或者SUB指令,来替换这个指令;例如等效于:ADD register,PC,#offset_to_exper
register就是exper的地址
3.2 ADRP指令
使用格式:ADRP register exper
编译时,会计算出当前PC到exper的偏移量#offset_to_exper
pc的低12位清零,然后加上偏移量给register,得到的地址时含有label的4kb对齐的内存区域的base地址
3.3 实例
- 本代码取自warm项目开机汇编代码的head.S的部分

解释:
- 第365行:读取init_idmap_pd_dir的地址并写入到x0寄存器
- 第366行:读取init_idmap_pd_end的地址并写入x1寄存器
- 第372行:读取init_pd_dir的地址并写入x0寄存器
备注:这部分的地址在vmlinux.lds.S中被定义

四、系统操作指令
4.1 cache操作指令DC/IC
分为DC/IC指令,两者的区别在于DC为操作D-cache的指令,IC为操作I-cache的指令
这部分详看Armv8-A.pdf中的第D4.4.8 A64 Cache maintenance instructions部分
这部分还没有看,到后面专门学cache后来补充这部分内容,暂时设为TODO
4.2 地址翻译指令AT

以AT S1E1R为例

就是说给一个EL1或者EL2权限的虚拟地址,执行此AT指令后,就可以在PAR_EL1寄存器中读到翻译后的地址
下面请看这段c代码



read_sys_reg_par函数其实就是用的MRS指令读取PAR_EL1寄存器
4.3 TLBI指令
TLBI指令用于使 TLBs 中的条目无效。此指令的语法为:
TLBI < type >< level >{IS|OS} {, < xt >}
其中,
< type >,哪些条目无效
All - 所有条目
VA - 匹配在 Xt 的 VA 和 ASID 的条目 [ Entry matching VA and ASID in Xt ]
VAA - 匹配在 Xt 中的 VA ,任何 ASID 的条目
ASID - 匹配在 Xt 中的 ASID 的任何条目
...
< level >,要操作的地址空间
E1 = EL0/1虚拟地址空间
E2 = EL2虚拟地址空间
E3 = EL3虚拟地址空间
< IS|OS >,无论一个操作是内部可共享(IS)还是外部可共享(OS)
当 IS 添加到操作时,它将广播到内部共享域中的其他核心
当 OS 添加到操作时,它将广播到外部共享域的其他核心(在Armv8.4-A中添加)
< Xt >,操作哪个地址或ASID
仅用于按地址或ASID进行的操作
关于TLB的介绍,后面在学习到TLB章节时详细描述,本节仅描述此指令的功能以及用法
以linux内核api flush_tlb_all函数为例


最终调用的指令位tlbi vmalle1is

五、异常产生和返回指令
指令 | 解释 |
SVC | SVC系统调用,目标异常等级为EL1 |
HVC | HVC系统调用,目标异常等级为EL2 |
SMC | SMC系统调用,目标异常等级为EL3 |
ERET | 异常返回,使用当前的SPSR_ELx和ELR_ELx |
六、系统存储器指令
指令 | 解释 |
MRS | R <- S: 通用寄存器 <= 系统寄存器 |
MSR | S <- R: 系统寄存器 <= 通用寄存器 |
七、数据运算指令
算数运算 | 逻辑运算 | 数据传输 | 地址生成 | 位段移动 | 移位运算 |
ADDS | ANDS | MOV | ADRP | BFM | ASR |
SUBS | EOR | MOVZ | ADR | SBFM | LSL |
CMP | ORR | MOVK | ㅤ | UBFM | LSR |
SBC | MOVI | ㅤ | ㅤ | BFI | ROR |
RSB | TST | ㅤ | ㅤ | BFXIL | ㅤ |
RSC | ㅤ | ㅤ | ㅤ | SBFIZ | ㅤ |
CMN | ㅤ | ㅤ | ㅤ | SBFX | ㅤ |
MADD | ㅤ | ㅤ | ㅤ | UBFIZ | ㅤ |
MSUB | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ |
MUL | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ |
SMADDL | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ |
SDIV | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ |
UDIV | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ |
7.1 算术运算指令
指令 | 解释 |
ADDS | 加法指令,若S存在,则更新条件位flag |
ADCS | 带进位的加法,若S存在,则更新条件位flag |
SUBS | 减法指令,若S存在,则更新条件位flag |
SBC | 将操作数 1 减去操作数 2,再减去 标志位C的取反值 ,结果送到目的寄存器Xt/Wt |
RSB | 逆向减法,操作数 2 –操作数 1,结果 Rd |
RSC | 带借位的逆向减法指令,将操作数 2 减去操作数 1,再减去 标志位C的取反值 ,结果送目标寄存器Xt/Wt |
CMP | 比较相等指令 |
CMN | 比较不等指令 |
NEG | 取负数运算,NEG X1,X2 // X1 = X2按位取反+1(负数=正数补码+1) |
MADD | 乘加运算 |
MSUB | 乘减运算 |
MUL | 乘法运算 |
SMADDL | 有符号乘加运算 |
SDIV | 有符号除法运算 |
UDIV | 无符号除法运算 |
7.2 逻辑运算指令
指令 | 解释 |
ANDS | 按位与运算,如果S存在,则更新条件位标记 |
EOR | 按位异或运算 |
ORR | 按位或运算 |
TST | 例如:TST W0, #0X40 //指令用来测试W0[3]是否为1,相当于:ANDS WZR,W0,#0X40 |
不再多介绍,这部分遇到了直接查
八、load/store指令
对齐偏移 | 非对齐偏移 | PC-相对寻址 | 访问一对 | 非暂存 | 非特权 | 独占 | AcquireRelease |
LDR | LDUR | LDR | LDP | LDNP | LDTR | LDXR | LDAR |
LDRB | LDURB | LDRSW | LDRSW | STNP | LDTRB | LDXRB | LDARB |
LDRSB | LDURSB | ㅤ | STP | ㅤ | LDTRSB | LDXRH | LDARH |
LDRH | LDURH | ㅤ | ㅤ | ㅤ | LDTRH | LDXP | STLR |
LDRSH | LDURSH | ㅤ | ㅤ | ㅤ | LDTRSH | STXR | STLRB |
LDRSW | LDURSW | ㅤ | ㅤ | ㅤ | LDTRSW | STXRB | STLRH |
STR | STUR | ㅤ | ㅤ | ㅤ | STTR | STXRH | LDAXR |
STRB | STURB | ㅤ | ㅤ | ㅤ | STTRB | STXP | LDAXRB |
STRH | STURH | ㅤ | ㅤ | ㅤ | STTRH | ㅤ | LDAXRH |
ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | LDAXP |
ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | STLXR |
ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | STLXRB |
ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | STLXRH |
ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | STLXP |
8.1 Load/Store (Scaled Offset)
所谓Scaled 和Unscaled其实就是可以见到理解为对齐和非对齐,本质就是是否乘以一个常量,因为scaled的总是可以乘以一个常量来达到对齐,而Unscaled就不需要,是多少就多少,更符合人类自然的理解
指令 | 解释 |
LDR | 从Memory地址addr中读取双字/字节/半字/字数据到目标寄存器Xt/Wt中带”S”表示需要符号扩展. |
LDRB | ㅤ |
LDRSB | ㅤ |
LDRH | ㅤ |
LDRSH | ㅤ |
LDRSW | ㅤ |
STR | 把Xn/Wn中的双字/字节/半字数据写入到Memory地址addr中 |
STRB | ㅤ |
STRH | ㅤ |
8.2 Load/Store (Unscaled Offset)
指令 | 解释 |
LDUR | 从Memory地址addr中读取双字/字节/半字/字数据到目标寄存器Xt/Wt中带”S”表示需要符号扩展.立即数偏移 #simm9 = { -256 ~ +256 } 的任意整数,不需要对齐规则. |
LDURB | ㅤ |
LDURSB | ㅤ |
LDURH | ㅤ |
LDURSH | ㅤ |
LDURSW | ㅤ |
STUR | 把Xn/Wn中的双字/字节/半字数据写入到Memory地址addr中立即数偏移 #simm9 = { -256 ~ +256 } 的任意整数,不需要对齐规则. |
STURB | ㅤ |
STURH | ㅤ |
8.3 Load/Store PC-relative(PC相对寻址)
指令 | 解释 |
LDR | 从Memory地址addr中读取双字/字数据到目标寄存器Xt/Wt中带”S”表示需要符号扩展. |
LDRSW | ㅤ |
8.4 Load/Store Pair(一对)
指令 | 解释 |
LDP | 从Memory地址addr处读取两个双字/字数据到目标寄存器Xt1,Xt2带”S”表示需要符号扩展. |
LDRSW | ㅤ |
STP | 把Xt1,Xt2两个双字/字数据写到Memory地址addr中 |
8.5 Load/Store Non-temporal(非暂存) Pair
所谓Non-temporal就是就是用于你确定知道该地址只加载一次,不需要触发缓存,避免数据被刷新,优化性能,其它指令都默认会写Cache
指令 | 解释 |
LDNP | 从Memory地址addr处读取两个双字/字数据到目标寄存器Xt1,Xt2, 标注非暂存访问,不更新cache带”S”表示需要符号扩展. |
STNP | 把Xt1,Xt2两个双字/字数据写到Memory地址addr中,标注非暂存访问,不更新cache |
8.6 Load/Store Unprivileged(非特权)
所谓Unprivileged就是说EL0/EL1的内存有不同的权限控制,这条指令以EL0的权限存取,用于模拟EL0的行为,该指令应用于EL1和EL0之间的交互.
指令 | 解释 |
LDTR | 从Memory地址addr中读取双字/字节/半字/字数据到目标寄存器Xt/Wt中,当执行在EL1的时候使用EL0的权限带”S”表示需要符号扩展 |
LDTRB | ㅤ |
LDTRSB | ㅤ |
LDTRH | ㅤ |
LDTRSH | ㅤ |
LDTRSW | ㅤ |
STTR | 把Xn/Wn中的双字/字节/半字数据写入到Memory地址addr中,当执行在EL1的时候使用EL0的权限 |
STTRB | ㅤ |
STTRH | ㅤ |
8.7 Load/Store Exclusive(独占)
在多核CPU下,对一个地址的访问可能引起冲突,这个指令解决了冲突,保证原子性(所谓原子操作简单理解就是不能被中断的操作),是解决多个CPU访问同一内存地址导致冲突的一种机制。
比如2个CPU同时写,其中一条的Ws就会返回失败值。通常用于锁,比如spinlock,可以参考代码:arch/arm64/include/asm/spinlock.h
指令 | 解释 |
LDXR | 从Memory地址addr中读取双字/字节/半字数据到目标寄存器Xt/Wt中,标记物理地址是独占访问的 |
LDXRB | ㅤ |
LDXRH | ㅤ |
LDXP | 从Memory地址addr中读取一对双字数据到目标寄存器Xt1,Xt2中,标记物理地址是独占访问的 |
STXR | 把Xn/Wn中的双字/字节/半字数据写入到Memory地址addr中,返回是否独占访问成功状态(Ws) |
STXRB | ㅤ |
STXRH | ㅤ |
STXP | 把Xt1,Xt2一对双字字数据写入到Memory地址addr中,返回是否独占访问成功状态 |
8.8 Load-Acquire/Store-Release
指令 | 解释 |
Non-exclusive(非独占) | ㅤ |
LDAR | 从Memory地址addr中读取一个双字/字节/半字数据到目标寄存器Xt/Wt中,标记物理地址为非独占访问 |
LDARB | ㅤ |
LDARH | ㅤ |
STLR | 把一个双字/字节/半字数据Xt/Wt写到Memory地址addr中,返回是否独占访问成功状态 |
STLRB | ㅤ |
STLRH | ㅤ |
Exclusive(独占) | ㅤ |
LDAXR | 从Memory地址addr中读取一个双字/字节/半字数据到目标寄存器Xt/Wt中,标记物理地址为独占访问 LDAXP 是Pair 访问 |
LDAXRB | ㅤ |
LDAXRH | ㅤ |
LDAXP | ㅤ |
STLXR | 把一个双字/字节/半字数据Xt/Wt写到Memory地址addr中,返回是否独占访问成功状态 STLXP 是Pair 访问 |
STLXRB | ㅤ |
STLXRH | ㅤ |
STLXP | ㅤ |
九、内存屏蔽指令
指令 | 翻译 | 解释 |
DMB | 数据内存屏障指令 | 保证该指令前的所有内存访问结束,而该指令之后引起的内存访问只能在该指令执行结束后开始,其它数据处理指令等可以越过DMB屏障乱序执行 |
DSB | 数据同步屏障指令 | DSB比DMB管得更宽,DSB屏障之后的所有得指令不可越过屏障乱序执行 |
ISB | 指令同步屏障指令 | ISB比DSB管的更宽,ISB屏障之前的指令保证执行完,屏障之后的指令直接flush掉再重新从Memroy中取指 |
在第4.2 地址翻译指令AT的案例中也使用了dsb的内存屏蔽指令