ARM汇编指令学习
在学习操作系统课程的过程中,我接触到了ARM汇编语言。汇编语言是最接近机器码的编程语言,理解它对于掌握计算机体系结构、操作系统原理以及程序性能优化都至关重要。本文将通过一个快速排序算法的ARM64汇编实现,系统地介绍常用的ARM汇编指令及其应用场景。
一、ARM64架构基础
1.1 寄存器体系
ARM64(ARMv8-A)架构提供了31个通用寄存器(X0-X30)和一个特殊的零寄存器(XZR)。每个寄存器可以作为64位使用(X寄存器)或32位使用(W寄存器):
参数1/返回值"] X1["X1
参数2"] X2["X2
参数3"] X3["X3
参数4"] X4["X4
参数5"] X5["X5
参数6"] X6["X6
参数7"] X7["X7
参数8"] end subgraph G2["临时寄存器 - Caller-saved"] direction LR X8["X8
间接结果"] X9["X9-X15
临时变量"] X16["X16-X17
IP0/IP1"] X18["X18
平台保留"] end subgraph G3["Callee-saved 寄存器 - 函数必须保存"] direction LR X19["X19-X28
跨调用保持"] end subgraph G4["特殊用途寄存器"] direction LR X29["X29 (FP)
帧指针"] X30["X30 (LR)
返回地址"] SP["SP
栈指针"] XZR["XZR/WZR
零寄存器"] end end style G1 fill:#e3f2fd,stroke:#1976d2 style G2 fill:#fff3e0,stroke:#f57c00 style G3 fill:#e8f5e9,stroke:#388e3c style G4 fill:#fce4ec,stroke:#c2185b style X0 fill:#bbdefb style X29 fill:#f8bbd9 style X30 fill:#f8bbd9 style SP fill:#f8bbd9
64位 vs 32位: X0-X30 为64位寄存器,对应的 W0-W30 为其低32位。例如 W0 是 X0 的低32位。
寄存器用途说明:
- X0-X7: 用于传递函数参数和返回值
- X8: 间接结果寄存器
- X9-X15: 临时寄存器(caller-saved)
- X16-X17: 过程调用临时寄存器
- X18: 平台寄存器(特殊用途)
- X19-X28: callee-saved寄存器(被调用者保存)
- X29: 帧指针(FP)
- X30: 链接寄存器(LR,存储返回地址)
- SP: 栈指针
1.2 调用约定
根据ARM的AAPCS64(Procedure Call Standard):
- 前8个整型参数通过X0-X7传递
- 返回值通过X0传递
- X19-X28是callee-saved,函数必须保存和恢复
- X9-X15是caller-saved,调用者负责保存
二、核心ARM汇编指令详解
2.1 数据传送指令
MOV - 数据移动
mov x19, x0 // x19 = x0
mov x2, #0 // x2 = 0
MOV指令用于在寄存器之间或将立即数传送到寄存器。
LDR - 加载数据
ldr w5, [x19, x4, lsl #2] // w5 = *(x19 + x4 * 4)
ldr w6, [x19, x2, lsl #2] // w6 = arr[i]
LDR从内存加载数据到寄存器。lsl #2表示左移2位(相当于乘以4),因为int类型占4字节。
寄存器位宽说明:
使用场景:
- X寄存器: 用于地址计算、指针操作 (如
ldr w5, [X19, ...]) - W寄存器: 用于int类型数据 (int占32位)
STR - 存储数据
str w7, [x19, x2, lsl #2] // *(x19 + x2 * 4) = w7
STR将寄存器的值存储到内存。
STP/LDP - 成对存储/加载
stp x29, x30, [sp, #-32]! // 预索引模式,先sp=sp-32,再存储
ldp x19, x20, [sp, #16] // 从sp+16处加载到x19和x20
STP和LDP用于同时操作两个寄存器,提高效率。感叹号!表示预索引模式。
2.2 算术和逻辑指令
ADD/SUB - 加法和减法
add x2, x2, #1 // x2 = x2 + 1 (i++)
sub x3, x1, #1 // x3 = x1 - 1 (j = n-1)
LSR - 逻辑右移
lsr x4, x4, #1 // x4 = x4 >> 1 (除以2)
用于计算中点索引 n/2。
LSL - 逻辑左移
在索引计算中用于乘法:
lsl #2 // 左移2位,相当于乘以4
2.3 比较和分支指令
CMP - 比较
cmp x1, #1 // 比较 x1 和 1
cmp x2, x3 // 比较 i 和 j
cmp w6, w5 // 比较 arr[i] 和 pivot
CMP执行减法操作但不保存结果,只设置条件标志位(NZCV)。
条件标志位 (NZCV):
结果为负"] Z["Z (Zero)
结果为零"] C["C (Carry)
进位/借位"] V["V (Overflow)
有符号溢出"] end style N fill:#ffcdd2 style Z fill:#c8e6c9 style C fill:#bbdefb style V fill:#fff9c4
示例: cmp x2, x3 (执行 x2 - x3)
| 情况 | x2 | x3 | 结果 | N | Z | 后续分支 |
|---|---|---|---|---|---|---|
| 情况1 | 5 | 3 | 2 | 0 | 0 | b.gt 跳转 (5 > 3) |
| 情况2 | 3 | 5 | -2 | 1 | 0 | b.gt 不跳转 |
| 情况3 | 3 | 3 | 0 | 0 | 1 | b.eq 跳转 (相等) |
条件分支指令
ble L_ret // 小于等于时跳转 (<=)
b.gt L_done // 大于时跳转 (>)
b.ge L_right // 大于等于时跳转 (>=)
b.le L_swap // 小于等于时跳转 (<=)
blt L_no_left // 小于时跳转 (<)
常用条件码:
- EQ: 等于(Equal)
- NE: 不等于(Not Equal)
- GT: 大于(Greater Than)
- GE: 大于等于(Greater or Equal)
- LT: 小于(Less Than)
- LE: 小于等于(Less or Equal)
B - 无条件跳转
b L_left // 无条件跳转到L_left标签
BL - 带链接的跳转(函数调用)
bl _quicksort // 调用函数,返回地址保存在X30(LR)
RET - 函数返回
ret // 返回到X30(LR)中保存的地址
2.4 汇编伪指令
.text // 代码段
.globl _quicksort // 声明全局符号
.align 2 // 4字节对齐(2^2=4)
三、实践案例:快速排序算法分析
3.0 快速排序算法流程图
在深入分析汇编代码之前,先通过流程图理解快速排序的整体逻辑:
保存 x19, x20, x29, x30"] D --> E["初始化
i=0, j=n-1
pivot=arr[n/2]"] E --> F["L_left: 左扫描
while arr[i] < pivot
i++"] F --> G["L_right: 右扫描
while arr[j] > pivot
j--"] G --> H{"i > j ?"} H -->|No| I["L_swap:
swap arr[i], arr[j]
i++, j--"] I --> F H -->|Yes| J["L_done: 分区完成
左: [0..j]
右: [i..n-1]"] J --> K["递归左半部分
quicksort(arr, j+1)"] K --> L["递归右半部分
quicksort(arr+i, n-i)"] L --> M["恢复寄存器并返回
x19, x20, x29, x30"] style A fill:#4CAF50,color:#fff style B fill:#FF9800,color:#fff style C fill:#9E9E9E,color:#fff style D fill:#2196F3,color:#fff style E fill:#2196F3,color:#fff style F fill:#9C27B0,color:#fff style G fill:#9C27B0,color:#fff style H fill:#FF9800,color:#fff style I fill:#E91E63,color:#fff style J fill:#00BCD4,color:#fff style K fill:#8BC34A,color:#fff style L fill:#8BC34A,color:#fff style M fill:#607D8B,color:#fff
3.1 函数框架
_quicksort:
// 边界检查
cmp x1, #1
ble L_ret // n <= 1 直接返回
// 建立栈帧
stp x29, x30, [sp, #-32]!
stp x19, x20, [sp, #16]
mov x29, sp
关键点:
- 先检查递归终止条件,如果 n≤1 则直接返回,避免不必要的栈帧创建
- 使用STP一次保存两对寄存器,提高效率
- 总共分配32字节栈空间(保存4个64位寄存器)
3.2 初始化分区变量
mov x19, x0 // 保存数组基址
mov x20, x1 // 保存数组长度
mov x2, #0 // i = 0
sub x3, x1, #1 // j = n - 1
// 选择中间元素作为pivot
mov x4, x1
lsr x4, x4, #1 // x4 = n / 2
ldr w5, [x19, x4, lsl #2] // pivot = arr[n/2]
寄存器使用映射图:
| 寄存器 | 保存内容 | 说明 |
|---|---|---|
| X0 | arr (初始) | 参数传递,后被X19替代 |
| X1 | n (初始) | 参数传递,后被X20替代 |
| X2 | i (左指针) | 左侧扫描索引 |
| X3 | j (右指针) | 右侧扫描索引 |
| X4 | n/2 (临时) | 计算pivot索引 |
| W5 | pivot | 基准元素值 |
| W6 | arr[i] | 左侧当前元素 |
| W7 | arr[j] | 右侧当前元素 |
| X19 | arr (保存) | callee-saved,跨调用保持 |
| X20 | n (保存) | callee-saved,跨调用保持 |
| X29 | FP | 帧指针 |
| X30 | LR | 返回地址 |
寄存器生命周期可视化:
技巧:
- 使用X19-X20保存需要跨函数调用保持的变量
- 使用逻辑右移实现高效的除以2操作
- 通过
lsl #2实现数组索引计算:address = base + index * 4
3.3 双指针分区算法
L_left:
cmp x2, x3
b.gt L_done
ldr w6, [x19, x2, lsl #2]
cmp w6, w5
b.ge L_right // arr[i] >= pivot
add x2, x2, #1
b L_left
L_right:
cmp x2, x3
b.gt L_done
ldr w7, [x19, x3, lsl #2]
cmp w7, w5
b.le L_swap // arr[j] <= pivot
sub x3, x3, #1
b L_right
这段代码实现了经典的双指针分区算法:
- 左指针i向右扫描,找到 arr[i] ≥ pivot
- 右指针j向左扫描,找到 arr[j] ≤ pivot
- 交换两个元素,继续扫描
双指针分区过程可视化:
假设数组为 [9, 3, 7, 1, 8, 2, 6], pivot = 7 (中间元素)
9
↑i"] ~~~ I1["[1]
3"] ~~~ I2["[2]
7"] ~~~ I3["[3]
1"] ~~~ I4["[4]
8"] ~~~ I5["[5]
2"] ~~~ I6["[6]
6
↑j"] end Init --> Step1 subgraph Step1["步骤1: arr[i]=9≥7, arr[j]=6≤7, 交换!"] direction LR S1_0["[0]
6
✓"] ~~~ S1_1["[1]
3
↑i"] ~~~ S1_2["[2]
7"] ~~~ S1_3["[3]
1"] ~~~ S1_4["[4]
8"] ~~~ S1_5["[5]
2
↑j"] ~~~ S1_6["[6]
9
✓"] end Step1 --> Step2 subgraph Step2["步骤2: arr[i]=3<7, i++; arr[i]=7≥7, arr[j]=2≤7, 交换!"] direction LR S2_0["[0]
6"] ~~~ S2_1["[1]
3"] ~~~ S2_2["[2]
2
✓"] ~~~ S2_3["[3]
1
↑i"] ~~~ S2_4["[4]
8
↑j"] ~~~ S2_5["[5]
7
✓"] ~~~ S2_6["[6]
9"] end Step2 --> Step3 subgraph Step3["步骤3: arr[i]=1<7, i++; arr[j]=8>7, j--; i>j 结束!"] direction LR S3_0["[0]
6"] ~~~ S3_1["[1]
3"] ~~~ S3_2["[2]
2"] ~~~ S3_3["[3]
1
↑j"] ~~~ S3_4["[4]
8
↑i"] ~~~ S3_5["[5]
7"] ~~~ S3_6["[6]
9"] end style I0 fill:#ffcdd2 style I6 fill:#bbdefb style S1_0 fill:#c8e6c9 style S1_6 fill:#c8e6c9 style S2_2 fill:#c8e6c9 style S2_5 fill:#c8e6c9 style Step3 fill:#fff9c4
分区步骤详解:
| 步骤 | i | j | 操作说明 | 数组状态 |
|---|---|---|---|---|
| 初始 | 0 | 6 | arr[0]=9≥7, arr[6]=6≤7 | [9, 3, 7, 1, 8, 2, 6] |
| 1 | 1 | 5 | 交换后 i++, j– | [6, 3, 7, 1, 8, 2, 9] |
| 2 | 2 | 5 | arr[1]=3<7, 继续 i++ | [6, 3, 7, 1, 8, 2, 9] |
| 3 | 3 | 4 | arr[2]=7≥7, arr[5]=2≤7, 交换 | [6, 3, 2, 1, 8, 7, 9] |
| 4 | 4 | 3 | arr[3]=1<7, i++; arr[4]=8>7, j– | [6, 3, 2, 1, 8, 7, 9] |
| 结束 | 4 | 3 | i > j, 分区完成 | 左:[6,3,2,1] 右:[8,7,9] |
分界
||"] subgraph Right["右半部分 (≥ pivot)"] direction LR R0["8"] ~~~ R1["7"] ~~~ R2["9"] end Left --> DIV --> Right end Final --> Recurse["递归排序两个子数组"] style Left fill:#c8e6c9,stroke:#2e7d32 style Right fill:#ffcdd2,stroke:#c62828 style DIV fill:#fff,stroke:#666 style Recurse fill:#e3f2fd,stroke:#1565c0
3.4 元素交换
L_swap:
str w7, [x19, x2, lsl #2] // arr[i] = w7
str w6, [x19, x3, lsl #2] // arr[j] = w6
add x2, x2, #1
sub x3, x3, #1
b L_left
直接在内存中交换两个元素,利用之前已经加载到寄存器的值。
3.5 递归处理子数组
L_done:
// 递归左半部分: [0 .. j]
mov x0, x19
add x1, x3, #1 // left_len = j + 1
cmp x1, #2
blt L_no_left
bl _quicksort
L_no_left:
// 递归右半部分: [i .. n-1]
add x0, x19, x2, lsl #2 // base = arr + i
mov x1, x20
sub x1, x1, x2 // right_len = n - i
cmp x1, #2
blt L_no_right
bl _quicksort
L_no_right:
优化点:
- 递归前检查子数组长度,长度<2时不递归
- 使用BL指令调用函数,自动保存返回地址到X30
- 右半部分通过地址偏移直接传递子数组起始位置
3.6 栈帧清理和返回
ldp x19, x20, [sp, #16]
ldp x29, x30, [sp], #32
L_ret:
ret
使用LDP成对恢复寄存器,后索引模式], #32表示先加载再调整SP。
四、内存寻址模式详解
ARM64支持多种灵活的寻址模式:
4.1 基址加偏移
ldr w5, [x19, x4, lsl #2] // *(base + offset * scale)
可视化说明:
0x1000"] --- M1["arr[1]
0x1004"] --- M2["arr[2]
0x1008"] --- M3["arr[3]
0x100C"] --- M4["arr[4]
0x1010"] --- M5["arr[5]
0x1014"] end X19["X19 = 0x1000
(基址)"] --> M0 X4["X4 = 3
(索引)"] --> CALC["地址计算:
0x1000 + (3 << 2)
= 0x1000 + 12
= 0x100C"] CALC --> M3 style M3 fill:#4CAF50,color:#fff style X19 fill:#2196F3,color:#fff style X4 fill:#FF9800,color:#fff
计算过程: ldr w5, [x19, x4, lsl #2]
- 目标地址 = x19 + (x4 « 2) = 0x1000 + (3 × 4) = 0x100C
- 读取 arr[3] 的值到 w5
4.2 预索引模式
stp x29, x30, [sp, #-32]! // sp = sp - 32; *(sp) = x29, x30
先修改基址寄存器,再进行内存操作。
栈操作可视化:
执行顺序:
sp = sp - 32(先调整栈指针)- 将x29, x30存入新SP位置
4.3 后索引模式
ldp x29, x30, [sp], #32 // x29, x30 = *(sp); sp = sp + 32
先进行内存操作,再修改基址寄存器。
栈恢复可视化:
执行顺序:
- 从SP读取x29, x30 (先读取数据)
sp = sp + 32(再调整栈指针)
五、栈帧管理
栈是函数调用的核心机制。在ARM64中,栈向下增长(从高地址到低地址)。
5.1 快速排序的栈帧结构
栈帧内存布局:
| 偏移 | 内容 | 说明 |
|---|---|---|
| SP+24 | X30 (LR) | 返回地址 |
| SP+16 | X29 (FP) | 帧指针 |
| SP+8 | X20 | 保存的n值 |
| SP | X19 | 保存的arr地址 |
5.2 栈帧操作详解
// 建立栈帧 (函数入口)
stp x29, x30, [sp, #-32]! // 1) sp=sp-32 2) 存储FP和LR
stp x19, x20, [sp, #16] // 在sp+16位置存储x19,x20
mov x29, sp // 设置当前帧指针
// 清理栈帧 (函数出口)
ldp x19, x20, [sp, #16] // 从sp+16恢复x19,x20
ldp x29, x30, [sp], #32 // 1) 恢复FP和LR 2) sp=sp+32
ret // 返回到LR地址
内存布局对应关系:
[sp]→ X19 (第一个callee-saved寄存器)[sp, #8]→ X20 (第二个callee-saved寄存器)[sp, #16]→ X29/FP (帧指针)[sp, #24]→ X30/LR (返回地址)
关键要点:
- 使用
!的预索引模式,先调整SP再存储,确保栈对齐 - 使用后索引模式恢复,先读取再调整SP
- callee-saved寄存器(X19-X28)必须保存和恢复
- 栈必须保持16字节对齐(AAPCS64要求)
六、性能优化技巧
通过这个快速排序实现,可以学到以下优化技巧:
6.1 优化技巧总结
| 优化技术 | 实现方式 | 性能提升 |
|---|---|---|
| 减少内存访问 | 使用X19-X20保存arr和n | 避免重复从栈读取 |
| 批量操作 | STP/LDP成对存储/加载 | 减少指令数,提高吞吐 |
| 早期返回 | n≤1时不建栈帧直接返回 | 避免不必要的栈操作 |
| 位运算优化 | LSR实现除法,LSL实现乘法 | 比算术指令快数倍 |
| 寄存器重用 | W6,W7在交换时直接使用 | 减少LDR次数 |
| 指令合并 | 索引计算与LDR合并 | 减少ADD指令 |
6.2 优化实例对比
未优化版本 (假设的C语言直译):
// 计算 arr[i] 的地址 (5条指令)
mov x10, #4
mul x11, x2, x10 // x11 = i * 4
add x12, x19, x11 // x12 = base + offset
ldr w6, [x12] // 加载
优化后版本 (实际代码,1条指令):
ldr w6, [x19, x2, lsl #2] // 地址计算+加载合并!
性能对比:
| 方法 | 指令数 | 周期数 | 相对速度 |
|---|---|---|---|
| 未优化(分步) | 5 | ~8 | 1x |
| 优化(合并) | 1 | ~3 | 2.7x |
6.3 关键优化点分析
使用索引缩放寻址模式
[base, index, lsl #shift] // 一条指令完成: base + (index << shift)选择合适的寄存器
- X19-X28: 存储跨函数调用需要保留的值(arr, n)
- X2-X3: 存储循环变量(i, j),无需保存
- W5-W7: 存储临时数据(pivot, arr[i], arr[j])
减少分支预测失败
// 好的做法: 先检查边界,大概率情况 cmp x2, x3 b.gt L_done // 大部分时间 i <= j // 避免: 复杂的嵌套条件分支
七、课后练习:测试代码
以下是配套的C语言测试程序:
#include <stdio.h>
void quicksort(int *arr, int n);
int main() {
int a[] = {9, 3, 7, 1, 8, 2, 6, 4, 5, 0, 10};
int n = sizeof(a) / sizeof(a[0]);
quicksort(a, n);
for (int i = 0; i < n; ++i)
printf("%d ", a[i]);
printf("\n");
return 0;
}
qsort.s
.text
.globl _quicksort
.align 2
// void quicksort(int *arr, int n);
// x0 = arr
// x1 = n
_quicksort:
// if (n <= 1) return;
cmp x1, #1
ble L_ret // 小于等于 1 直接返回(不建栈帧)
// 建立栈帧,保存 callee-saved 寄存器
stp x29, x30, [sp, #-32]!
stp x19, x20, [sp, #16]
mov x29, sp
mov x19, x0 // x19 = arr base
mov x20, x1 // x20 = n
// i = 0, j = n - 1
mov x2, #0 // x2 = i
sub x3, x1, #1 // x3 = j = n-1
// pivot = arr[n/2]
mov x4, x1
lsr x4, x4, #1 // x4 = n / 2
ldr w5, [x19, x4, lsl #2] // w5 = pivot
// ===== 分区循环 =====
// 外层从左扫描开始
L_left:
// while (i <= j && arr[i] < pivot) i++
cmp x2, x3
b.gt L_done // i > j,分区结束
ldr w6, [x19, x2, lsl #2] // w6 = arr[i]
cmp w6, w5
b.ge L_right // arr[i] >= pivot,开始从右侧扫描
add x2, x2, #1 // i++
b L_left
// 从右侧扫描:while (i <= j && arr[j] > pivot) j--
L_right:
cmp x2, x3
b.gt L_done // i > j,分区结束
ldr w7, [x19, x3, lsl #2] // w7 = arr[j]
cmp w7, w5
b.le L_swap // arr[j] <= pivot,交换
sub x3, x3, #1 // j--
b L_right
// 交换 arr[i] 和 arr[j]
L_swap:
// 此时:
// w6 = arr[i]
// w7 = arr[j]
str w7, [x19, x2, lsl #2]
str w6, [x19, x3, lsl #2]
add x2, x2, #1 // i++
sub x3, x3, #1 // j--
b L_left // 回到从左扫描
// ===== 分区完成,递归两边 =====
L_done:
// 现在:
// 左半部分索引范围 [0 .. j]
// 右半部分索引范围 [i .. n-1]
//
// 左长度 left_len = j + 1
// 右长度 right_len = n - i
// ---- 递归左半部分 ----
mov x0, x19 // base = arr
add x1, x3, #1 // left_len = j + 1
cmp x1, #2 // 长度 <=1 则不递归
blt L_no_left
bl _quicksort
L_no_left:
// ---- 递归右半部分 ----
add x0, x19, x2, lsl #2 // base = arr + i
mov x1, x20 // n
sub x1, x1, x2 // right_len = n - i
cmp x1, #2
blt L_no_right
bl _quicksort
L_no_right:
// 恢复寄存器 & 返回
ldp x19, x20, [sp, #16]
ldp x29, x30, [sp], #32
L_ret:
ret
编译和运行:
# macOS (ARM64)
gcc -c qsort.s -o qsort.o
gcc main.c qsort.o -o qsort
./qsort
# 输出: 0 1 2 3 4 5 6 7 8 9 10·
八、总结
MOV, LDR, STR
ADD, SUB"] B["控制流
CMP, B, BL, RET"] C["内存管理
栈帧, 寄存器保存"] D["寻址模式
基址, 索引, 预/后索引"] E["调用约定
AAPCS64"] F["优化技巧
位运算, 批量操作"] end A --> G["快速排序实现"] B --> G C --> G D --> G E --> G F --> G style A fill:#e3f2fd style B fill:#f3e5f5 style C fill:#e8f5e9 style D fill:#fff3e0 style E fill:#fce4ec style F fill:#e0f7fa style G fill:#4CAF50,color:#fff
通过实现快速排序算法,我们系统学习了:
- 基础指令: MOV, LDR, STR, ADD, SUB等数据处理指令
- 控制流: CMP, B, BL, RET等分支和跳转指令
- 内存管理: 栈帧创建、寄存器保存与恢复
- 寻址模式: 基址偏移、索引缩放、预/后索引
- 调用约定: 参数传递、寄存器使用规范
- 优化技巧: 减少内存访问、使用位运算
ARM汇编虽然语法简洁,但其强大的寻址模式和丰富的指令集使其能够编写出高效的底层代码。掌握汇编语言不仅能帮助我们理解编译器的工作原理,还能在性能关键场景下进行精细优化。
附录:ARM64汇编指令速查表
A. 数据传送指令
| 指令 | 语法 | 功能 |
|---|---|---|
| MOV | mov Xd, Xn | Xd = Xn (寄存器复制) |
| MOV | mov Xd, #imm | Xd = 立即数 |
| LDR | ldr Wd, [Xn] | Wd = *(Xn) (从内存加载) |
| LDR | ldr Wd, [Xn, #off] | Wd = *(Xn + off) |
| STR | str Wd, [Xn] | *(Xn) = Wd (存储到内存) |
| STP | stp Xd1, Xd2, [Xn] | 成对存储两个寄存器 |
| LDP | ldp Xd1, Xd2, [Xn] | 成对加载两个寄存器 |
B. 算术和逻辑指令
| 指令 | 语法 | 功能 |
|---|---|---|
| ADD | add Xd, Xn, Xm | Xd = Xn + Xm |
| ADD | add Xd, Xn, #imm | Xd = Xn + 立即数 |
| SUB | sub Xd, Xn, Xm | Xd = Xn - Xm |
| MUL | mul Xd, Xn, Xm | Xd = Xn * Xm |
| LSL | lsl Xd, Xn, #imm | Xd = Xn « imm (逻辑左移) |
| LSR | lsr Xd, Xn, #imm | Xd = Xn » imm (逻辑右移) |
| AND | and Xd, Xn, Xm | Xd = Xn & Xm (按位与) |
| ORR | orr Xd, Xn, Xm | Xd = Xn | Xm (按位或) |
| EOR | eor Xd, Xn, Xm | Xd = Xn ^ Xm (按位异或) |
C. 比较和分支指令
| 指令 | 语法 | 功能 |
|---|---|---|
| CMP | cmp Xn, Xm | 比较 Xn 和 Xm,设置标志位 |
| CMP | cmp Xn, #imm | 比较 Xn 和立即数 |
| B | b label | 无条件跳转到label |
| BL | bl label | 调用函数,返回地址存入LR |
| B.EQ | b.eq label | 相等时跳转 (Z=1) |
| B.NE | b.ne label | 不等时跳转 (Z=0) |
| B.GT | b.gt label | 大于时跳转 (有符号) |
| B.GE | b.ge label | 大于等于时跳转 (有符号) |
| B.LT | b.lt label | 小于时跳转 (有符号) |
| B.LE | b.le label | 小于等于时跳转 (有符号) |
| RET | ret | 返回 (跳转到LR) |
D. 寻址模式速查
| 寻址模式 | 示例 | 含义 |
|---|---|---|
| 基址寻址 | [Xn] | *(Xn) |
| 偏移寻址 | [Xn, #16] | *(Xn + 16) |
| 索引寻址 | [Xn, Xm] | *(Xn + Xm) |
| 缩放索引 | [Xn, Xm, lsl #2] | (Xn + Xm4) |
| 预索引 | [Xn, #16]! | Xn += 16; *(Xn) |
| 后索引 | [Xn], #16 | *(Xn); Xn += 16 |
E. 常用伪指令
| 伪指令 | 功能 |
|---|---|
.text | 声明代码段 |
.data | 声明数据段 |
.globl symbol | 声明全局符号 |
.align n | 2^n 字节对齐 |
.word value | 定义4字节数据 |
.quad value | 定义8字节数据 |
.ascii "str" | 定义字符串(不含结束符) |
.asciz "str" | 定义字符串(含\0结束符) |