ARM64(AArch64)
ARM64 寄存器
ARM64 有 31 个通用寄存器,编号为 x0 ~ x30。
xN:64 位寄存器,如 x0
wN:对应的低 32 位寄存器,如 w0
wN 写入后,会把对应 xN 的高 32 位自动清零
| 64-bit |
32-bit |
常见用途 |
x0 |
w0 |
第 1 个参数 / 返回值 |
x1 |
w1 |
第 2 个参数 / 返回值 |
x2 |
w2 |
第 3 个参数 |
x3 |
w3 |
第 4 个参数 |
x4 |
w4 |
第 5 个参数 |
x5 |
w5 |
第 6 个参数 |
x6 |
w6 |
第 7 个参数 |
x7 |
w7 |
第 8 个参数 |
x8 |
w8 |
间接返回值地址 / 平台相关用途 |
x9 ~ x15 |
w9 ~ w15 |
临时寄存器 |
x16 |
w16 |
IP0,过程调用临时寄存器 |
x17 |
w17 |
IP1,过程调用临时寄存器 |
x18 |
w18 |
平台保留寄存器 / 平台相关 |
x19 ~ x28 |
w19 ~ w28 |
被调用者保存寄存器 |
x29 |
w29 |
帧指针 fp |
x30 |
w30 |
链接寄存器 lr,保存返回地址 |
特殊寄存器
| 寄存器 |
说明 |
常见用途 |
sp |
栈指针 |
指向当前栈顶 x31 |
pc |
程序计数器 |
指向当前执行位置,AArch64 中通常不直接当普通寄存器使用 |
fp |
帧指针 |
一般是 x29,用于访问栈帧 |
lr |
链接寄存器 |
一般是 x30,保存返回地址 |
xzr / wzr |
零寄存器 |
读取恒为 0,写入无效果 |
nzcv |
标志寄存器 |
保存条件标志位 |
| 标志位 |
名称 |
含义 |
N |
Negative |
结果为负 |
Z |
Zero |
结果为 0 |
C |
Carry |
进位 / 借位相关 |
V |
Overflow |
有符号溢出 |
调用约定
前 8 个整数或指针参数通常使用 x0 ~ x7 寄存器,超过 8 个参数时,通常通过栈传递。
前 8 个浮点参数通常使用 v0 ~ v7 寄存器,超过 8 个时也通过栈传递。
| 类型 |
返回寄存器 |
| 整数 / 指针 |
x0 |
| 较大整数或复合返回值 |
x0 / x1 或间接返回 |
| 浮点数 / SIMD |
v0 |
寄存器保存规则
指令
ARM64 指令一般形如:
| GAS |
|---|
| opcode destination, source1, source2
|
0. 最常见指令表
| 指令 |
类型 |
简要说明 |
mov xd, xs |
数据传送 |
复制寄存器值 |
ldr xt, [xn] |
访存 |
从内存加载 |
str xt, [xn] |
访存 |
向内存存储 |
ldp xt1, xt2, [xn] |
访存 |
成对加载 |
stp xt1, xt2, [xn] |
访存 |
成对存储 |
adr xd, label |
地址计算 |
获取近地址 |
adrp xd, label |
地址计算 |
获取页基址 |
add xd, xn, xm/#imm |
算术运算 |
加法 |
sub xd, xn, xm/#imm |
算术运算 |
减法 |
adds xd, xn, xm |
算术运算 |
加法并更新标志位 |
subs xd, xn, xm |
算术运算 |
减法并更新标志位 |
mul xd, xn, xm |
算术运算 |
乘法 |
madd xd, xn, xm, xa |
算术运算 |
乘加 |
sdiv xd, xn, xm |
算术运算 |
有符号除法 |
udiv xd, xn, xm |
算术运算 |
无符号除法 |
neg xd, xn |
算术运算 |
取负 |
and xd, xn, xm |
逻辑运算 |
按位与 |
orr xd, xn, xm |
逻辑运算 |
按位或 |
eor xd, xn, xm |
逻辑运算 |
按位异或 |
mvn xd, xn |
逻辑运算 |
按位取反 |
tst xn, xm |
测试 |
按位与并更新标志位 |
cmp xn, xm/#imm |
比较 |
比较并更新标志位 |
lsl xd, xn, #imm |
移位 |
逻辑左移 |
lsr xd, xn, #imm |
移位 |
逻辑右移 |
asr xd, xn, #imm |
移位 |
算术右移 |
ror xd, xn, #imm |
移位 |
循环右移 |
b label |
跳转 |
无条件跳转 |
b.eq label |
条件跳转 |
相等时跳转 |
b.ne label |
条件跳转 |
不等时跳转 |
b.hi label |
条件跳转 |
无符号大于 |
b.lo label |
条件跳转 |
无符号小于 |
b.gt label |
条件跳转 |
有符号大于 |
b.lt label |
条件跳转 |
有符号小于 |
cbz xt, label |
条件跳转 |
为 0 时跳转 |
cbnz xt, label |
条件跳转 |
非 0 时跳转 |
tbz xt, #bit, label |
条件跳转 |
指定位为 0 时跳转 |
tbnz xt, #bit, label |
条件跳转 |
指定位非 0 时跳转 |
bl label |
流程控制 |
调用函数 |
blr xn |
流程控制 |
间接调用 |
ret |
流程控制 |
返回 |
nop |
空操作 |
无实际操作 |
1. 数据传送类
| 指令 |
类型 |
简要说明 |
mov xd, xs |
数据传送 |
将寄存器值复制到目标寄存器 |
ldr xt, [xn] |
访存 |
从内存加载 64 位数据到寄存器 |
str xt, [xn] |
访存 |
将 64 位寄存器值存到内存 |
ldp xt1, xt2, [xn] |
访存 |
一次加载一对寄存器 |
stp xt1, xt2, [xn] |
访存 |
一次存储一对寄存器 |
adr xd, label |
地址计算 |
获取当前附近地址 |
adrp xd, label |
地址计算 |
获取页对齐基址,常与 add / ldr 配合 |
nop |
空操作 |
不执行有效操作 |
常见寻址形式
| 形式 |
示例 |
说明 |
| 基址寻址 |
ldr x0, [x1] |
从 x1 指向的地址加载 |
| 基址 + 偏移 |
ldr x0, [x1, #8] |
从 x1 + 8 加载 |
| 预索引 |
ldr x0, [x1, #8]! |
先 x1 += 8,再访问 |
| 后索引 |
ldr x0, [x1], #8 |
先访问,再 x1 += 8 |
2. 算术运算类
| 指令 |
类型 |
简要说明 |
add xd, xn, xm |
算术运算 |
xd = xn + xm |
add xd, xn, #imm |
算术运算 |
立即数加法 |
sub xd, xn, xm |
算术运算 |
xd = xn - xm |
sub xd, xn, #imm |
算术运算 |
立即数减法 |
adds xd, xn, xm |
算术运算 |
加法并更新标志位 |
subs xd, xn, xm |
算术运算 |
减法并更新标志位 |
mul xd, xn, xm |
算术运算 |
乘法 |
madd xd, xn, xm, xa |
算术运算 |
乘加,xd = xn*xm + xa |
msub xd, xn, xm, xa |
算术运算 |
乘减,xd = xa - xn*xm |
sdiv xd, xn, xm |
算术运算 |
有符号除法 |
udiv xd, xn, xm |
算术运算 |
无符号除法 |
neg xd, xn |
算术运算 |
取相反数 |
cmp xn, xm |
比较 |
本质常等价于 subs xzr, xn, xm |
cmn xn, xm |
比较 |
比较加法结果,类似“加法版 cmp” |
3. 逻辑与位运算
| 指令 |
类型 |
简要说明 |
and xd, xn, xm |
逻辑运算 |
按位与 |
orr xd, xn, xm |
逻辑运算 |
按位或 |
eor xd, xn, xm |
逻辑运算 |
按位异或 |
bic xd, xn, xm |
逻辑运算 |
按位清零,xn & ~xm |
mvn xd, xn |
逻辑运算 |
按位取反 |
ands xd, xn, xm |
逻辑运算 |
按位与并更新标志位 |
tst xn, xm |
测试 |
本质类似 ands xzr, xn, xm |
lsl xd, xn, #imm |
移位 |
逻辑左移 |
lsr xd, xn, #imm |
移位 |
逻辑右移 |
asr xd, xn, #imm |
移位 |
算术右移 |
ror xd, xn, #imm |
移位 |
循环右移 |
4. 比较与测试
| 指令 |
类型 |
简要说明 |
cmp xn, xm |
比较 |
比较两个寄存器,更新标志位,不保存结果 |
cmp xn, #imm |
比较 |
与立即数比较 |
cmn xn, xm |
比较 |
做加法比较,仅更新标志位 |
tst xn, xm |
测试 |
按位与并更新标志位,不保存结果 |
5. 跳转与流程控制
| 指令 |
类型 |
简要说明 |
b label |
跳转 |
无条件跳转 |
bl label |
函数调用 |
跳转并把返回地址写入 lr |
br xn |
间接跳转 |
跳转到寄存器指定地址 |
blr xn |
间接调用 |
调用寄存器指定地址 |
ret |
函数返回 |
返回到 lr 保存的地址 |
ret xn |
函数返回 |
返回到指定寄存器地址 |
cbz xt, label |
条件跳转 |
若寄存器值为 0,则跳转 |
cbnz xt, label |
条件跳转 |
若寄存器值非 0,则跳转 |
tbz xt, #bit, label |
条件跳转 |
若指定位为 0,则跳转 |
tbnz xt, #bit, label |
条件跳转 |
若指定位非 0,则跳转 |
6. 栈操作
AArch64 没有通用的 push / pop 指令,通常使用:
配合 sp 完成压栈和出栈。
| 指令 |
类型 |
简要说明 |
stp x29, x30, [sp, #-16]! |
栈操作 |
先调整栈,再保存一对寄存器 |
ldp x29, x30, [sp], #16 |
栈操作 |
先恢复寄存器,再回收栈空间 |
str xt, [sp, #-16]! |
栈操作 |
为单个寄存器分配栈空间后保存 |
ldr xt, [sp], #16 |
栈操作 |
恢复后回收栈空间 |
7. 条件跳转
| 指令 |
条件 |
含义 |
b.eq label |
Z = 1 |
相等时跳转 |
b.ne label |
Z = 0 |
不相等时跳转 |
b.hi label |
C=1 且 Z=0 |
无符号大于 |
b.hs label |
C=1 |
无符号大于等于 |
b.lo label |
C=0 |
无符号小于 |
b.ls label |
C=0 或 Z=1 |
无符号小于等于 |
b.gt label |
Z=0 且 N=V |
有符号大于 |
b.ge label |
N=V |
有符号大于等于 |
b.lt label |
N≠V |
有符号小于 |
b.le label |
Z=1 或 N≠V |
有符号小于等于 |
b.mi label |
N=1 |
负数时跳转 |
b.pl label |
N=0 |
非负数时跳转 |
b.vs label |
V=1 |
溢出时跳转 |
b.vc label |
V=0 |
未溢出时跳转 |
b.al label |
无条件 |
总是跳转 |
常见函数栈帧
一个典型 ARM64 函数可能长这样:
| GAS |
|---|
| stp x29, x30, [sp, #-16]!
mov x29, sp
sub sp, sp, #0x20
... 函数体 ...
add sp, sp, #0x20
ldp x29, x30, [sp], #16
ret
|
含义
-
stp x29, x30, [sp, #-16]!
- 保存旧的
fp 和 lr
- 同时分配 16 字节栈空间
-
mov x29, sp
-
sub sp, sp, #0x20
-
add sp, sp, #0x20
-
ldp x29, x30, [sp], #16
-
ret