{% tip success %}参考文档:Armv8-A Instruction Set Architecture.pdf {% endtip %}

一、前言

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>}

其中尖括号是必须的,花括号是可选的

  • A32Rd => {R0R14}
  • A64Rd =>Xt => {X0X30}

1.2 指令分类

类型Note
跳转指令条件跳转、无条件跳转(#immregister)指令
异常产生指令系统调用类指令(SVCHVCSMC
系统寄存器指令读写系统寄存器,如 :MRSMSR指令 可操作PSTATE的位段寄存器
数据处理指令包括各种算数运算、逻辑运算、位操作、移位*(shift)*指令
load/store内存访问指令load/store {批量寄存器、单个寄存器、一对寄存器、非-暂存、非特权、独占}以及load-Acquirestore-Release指令 (A64没有LDM/STM指令)
协处理指令A64没有协处理器指令

1.3 指令助记符

整型
W/R32bit整数
X64bit整数
加载*/存储、符号-0*扩展
B无符号8bit字节
SB带符号8bit字节
H无符号16bit半字
SH带符号16bit半字
W无符号32bit
SW带符号32bit
PPair(一对)
寄存器宽度改变
H高位(dst gets top half
N有限位(dst < src
LLongdst > src
WWide (dst==src1,src1>src2)

1.4 指令条件码

编码助记符描述标记
0000EQ运算结果相等为1Z==1
0001NE运算结果不等为0Z==0
0010HS/CS无符号高或者相同进位,发生进位为1C==1
0011LO/CC无符号低清零,发生借位为0C==0
0100MI负数为1N==1
0101PL非负数0N==0
0110VS有符号溢出为1V==1
0111VC没用溢出为0V==0
1000HI无符号*>*C==1 && Z==0
1001LS无符号*<=*!(C==1 && Z==0)
1010GE带符号*>=*N==V
1011LT带符号*<*N!=V
1100GT带符号*>*Z==0 && N==V
1101LE带符号*<=*!( Z==0 && N==V)
1110AL**无条件执行Any
1111NV

二、跳转指令

通常,处理器按程序顺序执行指令。这意味着处理器按照指令在内存中设置的顺序执行指令。更改此

顺序的一种方法是使用分支指令。分支指令改变程序流程并用于循环、决策和函数调用。

A64指令集还有一些条件分支指令。这些指令会根据先前指令的结果更改其执行方式。

2.1 无条件跳转指令

# 无条件跳转label指令
B <label>
BL <label>

# 无条件跳转register指令
BR Xn
BLR Xn
RET {Xn}

2.2 条件跳转指令

 B.<cond> <label>
 CBZ <Xn> <label>
 CBNZ <Xn> <label>
 TBZ <Xn>, #<imm>, <label>
 TBNZ <Xn>, #<imm>, <label>

2.3 举例说明

第一个例子:如果a等于5,则b等于5

if (a == 5)
    b = 5;

使用汇编编写,如下所示

CMP W0, #5
B.NE skip
MOV W8, #5

skip:
    ....

第二个例子:当a不等于0时,b+c赋值给b,a-1赋值给a

while(a!=0)
{
    b = b+c;
    a = a-1;
}

使用汇编

loop:
CBZ W8, skip        # 判断W8是否为0,也就是a是否为0,如果为0跳出循环
ADD W9, W9, W10     # 否则W9/W10寄存器中的值相加赋值给W9,也就是b+c赋值给b
SUB W8, W8, #1      # W8-1赋值给W8
B loop              # 跳转循环

skip:
...

三、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 实例

  1. 本代码取自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

五、异常产生和返回指令

指令解释
SVCSVC系统调用,目标异常等级为EL1
HVCHVC系统调用,目标异常等级为EL2
SMCSMC系统调用,目标异常等级为EL3
ERET异常返回,使用当前的SPSR_ELxELR_ELx

六、系统存储器指令

指令解释
MRS*R <- S:通用寄存器<=*系统寄存器
MSR*S <- R:系统寄存器<=*通用寄存器

七、数据运算指令

算数运算逻辑运算数据传输地址生成位段移动移位运算
ADDSANDSMOVADRPBFMASR
SUBSEORMOVZADRSBFMLSL
CMPORRMOVKUBFMLSR
SBCMOVIBFIROR
RSBTSTBFXIL
RSCSBFIZ
CMNSBFX
MADDUBFIZ
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 X1X2 // 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
LDRLDURLDRLDPLDNPLDTRLDXRLDAR
LDRBLDURBLDRSWLDRSWSTNPLDTRBLDXRBLDARB
LDRSBLDURSBSTPLDTRSBLDXRHLDARH
LDRHLDURHLDTRHLDXPSTLR
LDRSHLDURSHLDTRSHSTXRSTLRB
LDRSWLDURSWLDTRSWSTXRBSTLRH
STRSTURSTTRSTXRHLDAXR
STRBSTURBSTTRBSTXPLDAXRB
STRHSTURHSTTRHLDAXRH
LDAXP
STLXR
STLXRB
STLXRH
STLXP

8.1 Load/Store (Scaled Offset)

所谓ScaledUnscaled其实就是可以见到理解为对齐和非对齐,本质就是是否乘以一个常量,因为scaled的总是可以乘以一个常量来达到对齐,而Unscaled就不需要,是多少就多少,更符合人类自然的理解

指令解释
LDRMemory地址addr中读取双字/字节/半字/字数据到目标寄存器Xt/Wt中带”S”表示需要符号扩展*.***
LDRB
LDRSB
LDRH
LDRSH
LDRSW
STRXn/Wn中的双字*/字节/半字数据写入到Memory地址addr*中
STRB
STRH

8.2 Load/Store (Unscaled Offset)

指令解释
LDURMemory地址addr中读取双字*/字节/半字/字数据到目标寄存器Xt/Wt中带”S”表示需要符号扩展.立即数偏移#simm9 = { -256 ~ +256 }的任意整数,不需要对齐规则.*
LDURB
LDURSB
LDURH
LDURSH
LDURSW
STURXn/Wn中的双字*/字节/半字数据写入到Memory地址addr中立即数偏移#simm9 = { -256 ~ +256 }的任意整数,不需要对齐规则.*
STURB
STURH

8.3 Load/Store PC-relative(PC相对寻址)

指令解释
LDRMemory地址addr中读取双字*/字数据到目标寄存器Xt/Wt中带”S”表示需要符号扩展.*
LDRSW

8.4 Load/Store Pair(一对)

指令解释
LDPMemory地址addr处读取两个双字*/字数据到目标寄存器Xt1*,Xt2带”S”表示需要符号扩展*.*
LDRSW
STPXt1Xt2两个双字*/字数据写到Memory地址addr*中

8.5 Load/Store Non-temporal(非暂存) Pair

所谓Non-temporal就是就是用于你确定知道该地址只加载一次,不需要触发缓存,避免数据被刷新,优化性能,其它指令都默认会写Cache

指令解释
LDNPMemory地址addr处读取两个双字*/字数据到目标寄存器Xt1*,Xt2,标注非暂存访问,不更新cache带”S”表示需要符号扩展*.*
STNPXt1Xt2两个双字*/字数据写到Memory地址addr中,标注非暂存访问,不更新cache*

8.6 Load/Store Unprivileged(非特权)

所谓Unprivileged就是说EL0/EL1的内存有不同的权限控制,这条指令以EL0的权限存取,用于模拟EL0的行为,该指令应用于EL1和EL0之间的交互.

指令解释
LDTRMemory地址addr中读取双字*/字节/半字/字数据到目标寄存器Xt/Wt中,当执行在EL1的时候使用EL0*的权限带”S”表示需要符号扩展
LDTRB
LDTRSB
LDTRH
LDTRSH
LDTRSW
STTRXn/Wn中的双字*/字节/半字数据写入到Memory地址addr中,当执行在EL1的时候使用EL0*的权限
STTRB
STTRH

8.7 Load/Store Exclusive(独占)

在多核CPU下,对一个地址的访问可能引起冲突,这个指令解决了冲突,保证原子性(所谓原子操作简单理解就是不能被中断的操作),是解决多个CPU访问同一内存地址导致冲突的一种机制。

比如2个CPU同时写,其中一条的Ws就会返回失败值。通常用于锁,比如spinlock,可以参考代码:arch/arm64/include/asm/spinlock.h

指令解释
LDXRMemory地址addr中读取双字*/字节/半字数据到目标寄存器Xt/Wt*中,标记物理地址是独占访问的
LDXRB
LDXRH
LDXPMemory地址addr中读取一对双字数据到目标寄存器Xt1Xt2中,标记物理地址是独占访问的
STXRXn/Wn中的双字*/字节/半字数据写入到Memory地址addr*中,返回是否独占访问成功状态(Ws
STXRB
STXRH
STXPXt1Xt2一对双字字数据写入到Memory地址addr中,返回是否独占访问成功状态

8.8 Load-Acquire/Store-Release

指令解释
Non-exclusive(非独占)
LDARMemory地址addr中读取一个双字*/字节/半字数据到目标寄存器Xt/Wt*中,标记物理地址为非独占访问
LDARB
LDARH
STLR把一个双字*/字节/半字数据Xt/Wt写到Memory地址addr*中,返回是否独占访问成功状态
STLRB
STLRH
Exclusive(独占)
LDAXRMemory地址addr中读取一个双字*/字节/半字数据到目标寄存器Xt/Wt中,标记物理地址为独占访问LDAXPPair* 访问
LDAXRB
LDAXRH
LDAXP
STLXR把一个双字*/字节/半字数据Xt/Wt写到Memory地址addr中,返回是否独占访问成功状态STLXPPair* 访问
STLXRB
STLXRH
STLXP

九、内存屏蔽指令

指令翻译解释
DMB数据内存屏障指令保证该指令前的所有内存访问结束,而该指令之后引起的内存访问只能在该指令执行结束后开始,其它数据处理指令等可以越过DMB屏障乱序执行
DSB数据同步屏障指令DSBDMB管得更宽,DSB屏障之后的所有得指令不可越过屏障乱序执行
ISB指令同步屏障指令ISBDSB管的更宽,ISB屏障之前的指令保证执行完,屏障之后的指令直接flush掉再重新从Memroy中取指

在第4.2 地址翻译指令AT的案例中也使用了dsb的内存屏蔽指令