General Reg
入门汇编指令
ARM64寄存器
什么是寄存器?
寄存器是CPU内部的小型、超高速存储单元,用于临时存放数据和指令。可以把它想象成工作台——CPU直接在寄存器上进行计算,比访问内存快得多。
ARM64寄存器分类(AArch64)
1. 通用寄存器(X0-X30)
ARM64有31个64位通用寄存器,编号X0到X30:
| 寄存器 | 别名 | 主要用途 | 说明 |
|---|---|---|---|
| X0 | - | 函数参数1 / 返回值 | 第一个参数和函数返回值 |
| X1-X7 | - | 函数参数2-8 | 额外的函数参数 |
| X8 | - | 间接结果寄存器 | 特殊用途 |
| X9-X15 | - | 临时寄存器 | 调用时不保存,随意使用 |
| X16-X17 | - | 内部临时寄存器 | 系统使用,避免使用 |
| X18 | - | 平台寄存器 | 保留给平台 |
| X19-X28 | - | 被保存寄存器 | 调用时必须保存原值 |
| X29 | FP | 帧指针 | 指向当前栈帧底部 |
| X30 | LR | 链接寄存器 | 存储函数返回地址(保护现场) |
2. 特殊功能寄存器
| 寄存器 | 名称 | 作用 |
|---|---|---|
| XZR | 零寄存器 | 读取总是0,写入无效果 |
| SP | 栈指针 | 指向当前栈顶 |
| PC | 程序计数器 | 指向下一条要执行的指令 |
寄存器大小访问
每个64位寄存器都可以按不同大小访问:
X0 // 访问完整的64位寄存器
W0 // 只访问低32位(高32位清零)
示例:
MOV X0, 0x123456789ABCDEF0 @ X0 = 0x123456789ABCDEF0
MOV W1, 0x12345678 @ W1 = 0x0000000012345678
ADD W0, W1, W1 @ X0 = 0x000000002468ACF0
关键寄存器详解
1. 参数和返回值寄存器(X0-X7)
函数调用示例:
// C代码
int result = add(10, 20, 30);
// 对应的寄存器使用:
// X0 = 10 (第一个参数)
// X1 = 20 (第二个参数)
// X2 = 30 (第三个参数)
// 返回值通过 X0 返回
2. 链接寄存器 LR(X30)
存储函数调用的返回地址:
BL my_function @ 跳转到my_function,同时将返回地址保存到LR
@ 在my_function中:
RET @ 等同于 MOV PC, LR,跳回到调用处
3. 栈指针 SP
管理函数调用的栈空间:
SUB SP, SP, #16 @ 在栈上分配16字节空间
ADD SP, SP, #16 @ 释放栈空间
4. 零寄存器 XZR
提供常数值0,很有用:
MOV X0, XZR @ X0 = 0
CMP X1, XZR @ 比较X1是否等于0
实际编程中的寄存器使用
函数调用约定
@ 函数定义
my_function:
SUB SP, SP, #16 @ 1. 在栈上分配空间
STR X30, [SP, #8] @ 2. 保存返回地址(LR)
STR X29, [SP, #0] @ 3. 保存帧指针(FP)
@ 函数体...
ADD X0, X0, X1 @ 使用X0,X1(参数寄存器)
LDR X29, [SP, #0] @ 恢复FP
LDR X30, [SP, #8] @ 恢复LR
ADD SP, SP, #16 @ 释放栈空间
RET @ 返回
简单计算示例
@ 计算 (a + b) * c
@ 假设: a在X0, b在X1, c在X2
ADD X3, X0, X1 @ X3 = a + b (使用临时寄存器X3)
MUL X0, X3, X2 @ X0 = (a+b) * c,结果放在X0返回
RET
寄存器使用规则
调用者保存的寄存器(临时寄存器)
- X0-X18:在函数调用中可能被修改,调用者需要保存重要数据
被调用者保存的寄存器
- X19-X28:如果函数要使用这些寄存器,必须先在栈上保存原值,返回前恢复
与C语言的对应关系
C函数 ↔ 汇编寄存器
// C代码
int calculate(int a, int b, int c) {
int temp = a + b;
return temp * c;
}
对应的汇编寄存器使用:
- a → X0
- b → X1
- c → X2
- temp → X3(临时寄存器)
- 返回值 → X0
重要提示
- 寄存器是稀缺资源:只有31个通用寄存器,要高效使用
- 理解数据流:观察数据如何在寄存器间流动
- 栈的重要性:当寄存器不够时,使用栈来存储临时数据
- 调用约定:遵循寄存器使用规则,确保函数间正确协作
记住:寄存器是CPU的工作台,所有计算都在这里发生!
ARM32寄存器
ARM32架构有16个32位核心寄存器,所有函数都共享这同一组寄存器:
通用寄存器:
- R0-R3:参数/结果寄存器(调用者保存)- 像"临时工作台",用于传递前4个参数
- R4-R11:被调用者保存寄存器 - 像"个人工作台",用完后必须恢复原样
- R12:内部调用暂存寄存器
特殊功能寄存器:
- R13 (SP):栈指针 - 总管理员座位,也用于传递第5个及以后的参数
- R14 (LR):链接寄存器 - 电话机,记录返回地址
- R15 (PC):程序计数器 - 当前任务指示牌
寄存器分配给函数的规则(AAPCS调用约定)
所有函数共用寄存器,但通过明确规则协作:
参数传递规则:
// 所有函数都遵守统一的参数传递规则
void func(int a, int b, int c, int d, int e, int f);
// 调用时:
// R0 = a, R1 = b, R2 = c, R3 = d (前4个参数)
// [SP+0] = e, [SP+4] = f (第5、6个参数通过栈传递)
返回值规则: - 所有函数都通过R0返回结果
参数数量不同的处理方式
情况1:4个及以下参数(全部用寄存器)
; 调用 func(1, 2, 3, 4)
MOV R0, #1 ; 参数1 → R0
MOV R1, #2 ; 参数2 → R1
MOV R2, #3 ; 参数3 → R2
MOV R3, #4 ; 参数4 → R3
BL func ; 调用函数
情况2:超过4个参数(混合使用寄存器和栈)
; 调用 func(1, 2, 3, 4, 5, 6)
MOV R0, #1 ; 参数1 → R0
MOV R1, #2 ; 参数2 → R1
MOV R2, #3 ; 参数3 → R2
MOV R3, #4 ; 参数4 → R3
SUB SP, SP, #8 ; 为额外参数分配栈空间
MOV R4, #5
STR R4, [SP, #0] ; 参数5 → [SP+0]
MOV R4, #6
STR R4, [SP, #4] ; 参数6 → [SP+4]
BL func ; 调用函数
ADD SP, SP, #8 ; 清理栈空间
函数如何使用这些共享寄存器
函数开场典型模式:
func:
PUSH {R4-R6, LR} ; 如果要使用R4-R11,必须先保存
; 访问参数:
; 前4个参数直接在R0-R3中
MOV R4, R0 ; 参数1保存到R4
MOV R5, R1 ; 参数2保存到R5
; 访问第5个及以后的参数(需要计算栈偏移)
LDR R6, [SP, #16] ; 参数5在[SP+16](跳过PUSH的寄存器)
寄存器使用策略:
- R0-R3:临时计算,任何函数都可以直接用,也用于参数传递
- R4-R11:任何函数想用就必须先保存再恢复
- 栈:用于传递额外参数和保存局部变量
- 所有函数都遵守同样的规则
各个函数如何通过共享寄存器联系
1. 参数和返回值的传递(考虑多参数情况)
; main函数准备参数(6个参数)
main:
MOV R0, #10 ; 参数1 → R0
MOV R1, #20 ; 参数2 → R1
MOV R2, #30 ; 参数3 → R2
MOV R3, #40 ; 参数4 → R3
SUB SP, SP, #8 ; 为额外参数分配空间
MOV R4, #50
STR R4, [SP, #0] ; 参数5 → [SP+0]
MOV R4, #60
STR R4, [SP, #4] ; 参数6 → [SP+4]
BL complex_calc ; 调用函数
; complex_calc函数接收所有参数
complex_calc:
PUSH {R4-R6, LR}
; 接收寄存器中的参数
MOV R4, R0 ; 参数1 = 10
MOV R5, R1 ; 参数2 = 20
; 接收栈中的参数
LDR R6, [SP, #16] ; 参数5 = 50
; ... 计算 ...
MOV R0, R4 ; 结果通过R0返回
POP {R4-R6, PC}
2. 调用者保存寄存器(R0-R3, R12)的协作
main:
MOV R4, R0 ; 如果R0有重要数据,先保存到R4
BL other_func ; 调用其他函数
; 我知道other_func可能会改动R0-R3,所以不依赖它们
MOV R0, R4 ; 恢复我原来的数据
3. 被调用者保存寄存器(R4-R11)的协作
; 任何函数如果想用R4-R11,都必须:
my_func:
PUSH {R4-R6} ; 先保存原来的值
; ... 使用R4-R6 ... ; 随便使用
POP {R4-R6} ; 恢复原样,不影响其他函数
BX LR
完整的协作示例(包含多参数)
// 所有函数共享同一套寄存器,但通过规则协作
int add(int a, int b) {
return a + b; // 用R0,R1接收参数,R0返回结果
}
int complex_calc(int a, int b, int c, int d, int e, int f) {
// 前4个参数在寄存器,后2个在栈中
int sum = a + b + c + d + e + f;
return add(sum, 100); // 继续调用其他函数
}
; 所有函数共用寄存器,但各司其职
add:
; 我知道:R0=a, R1=b来自调用者
ADD R0, R0, R1 ; 计算a+b
; 我知道:调用者期望结果在R0
BX LR ; 返回
complex_calc:
PUSH {R4-R6, LR} ; 我要用R4-R6,所以先保存
; 接收前4个参数:R0=a, R1=b, R2=c, R3=d
MOV R4, R0
MOV R5, R1
ADD R4, R4, R5 ; a + b
ADD R4, R4, R2 ; + c
ADD R4, R4, R3 ; + d
; 接收栈中的第5、6个参数
LDR R5, [SP, #16] ; 参数e (跳过PUSH的4个寄存器)
LDR R6, [SP, #20] ; 参数f
ADD R4, R4, R5 ; + e
ADD R4, R4, R6 ; + f
; 调用add函数
MOV R0, R4 ; 第一个参数 = sum
MOV R1, #100 ; 第二个参数 = 100
BL add
; 我知道:add的结果在R0中
POP {R4-R6, PC} ; 恢复寄存器,返回
关键理解点
- 不是每个函数有专属寄存器,而是所有函数共用16个寄存器
- 通过明确的使用规则来避免冲突
- 参数传递:
- 前4个:R0-R3寄存器(最快)
- 第5个及以后:栈空间(稍慢)
- 返回值:始终通过R0返回
- 长期数据要么用R4-R11(但要保存恢复),要么用栈
这种设计既高效(大多数调用用寄存器)又灵活(支持任意多参数),在性能和实用性间完美平衡!