目录
- 前言:为什么要读汇编?
- 🔔一、核心概念:寄存器与加载-存储架构
-
- [🎵1)核心搬运工:LDR 和 STR](#🎵1)核心搬运工:LDR 和 STR)
- [🎵2)栈的管理:PUSH 和 POP](#🎵2)栈的管理:PUSH 和 POP)
- [🎵3) 常用语法细节解密](#🎵3) 常用语法细节解密)
- 🔔二、后端视角:汇编指令与高级语言的映射
- 🔔三、推荐学习资源
读者注意:汇编不是用来写的,是用来"读"的,你无需记住全部指令。
✨另:本篇博客为下一篇"实现一个"微型多任务调度器" (Mini Task Scheduler)"做前置知识铺垫。
前言:为什么要读汇编?
在高级语言中,我们看到的是变量和对象;在CPU眼里,只有 寄存器(Register)和内存(Memory) 。
.s文件中的指令是CPU指令集的助记符。
🔔一、核心概念:寄存器与加载-存储架构
ARM是一种Load-Store架构的处理器。这意味着:
- CPU不能直接在内存中进行运算(比如不能直接让内存里的 A +B)
- 必须现将数据从内存加载(Load)到寄存器,计算完后,再存储(Store)回内存。
🎵1)核心搬运工:LDR 和 STR
- LDR (Load Register):从内存读取数据到寄存器。
- 语法:LDR R0, [R1] → 含义:将 R1 地址处的内容加载到 R0。
- STR (Store Register):将寄存器内容存入内存。
- 语法:STR R0, [R1] → 含义:将 R0 的值存入 R1 指向的内存地址。
🎵2)栈的管理:PUSH 和 POP
栈(Stack)是程序运行的基石,用于存放局部变量和保护现场。ARM 的栈是"向下生长"的(地址由高变低)。
- PUSH:入栈。将寄存器的值压入栈。
- 语法:PUSH {R4-R7, LR}
- 动作:栈指针(SP)减小,数据存入 SP 指向的位置。
- 意义:把寄存器 R4, R5, R6, R7 和 LR 里的内容全部压进栈,保存现场。在函数开始时,把会被破坏的寄存器存起来。
- POP:出栈。从栈中恢复数据到寄存器。
- 语法:POP {R4-R7, PC}
- 动作:从 SP 指向的位置读数据,SP 增大。
- 注意:如果我们存的时候是 LR(返回地址),拿的时候放进 PC(指令计数器),CPU 就会自动跳回到刚才存的地方继续执行。
ARM中核心寄存器总共16个,编号为R0-R15。此外还有一个记录状态的CPSR。
- R0-R3:主要用于函数调用时的参数传递。
- R4-R11:用于存放函数内部的局部变量和中间计算结果。
- R12:被称为 IP (Intra-Procedure-call scratch register)。主要由编译器在处理函数跳转时临时用一下,普通开发者很少直接操作它。
- R13(SP,Stack Pointer)------栈指针,永远指向自栈顶。
- R14(LR,Link Register)------连接寄存器,存放返回地址。
- R15 (PC, Program Counter) ------ 程序计数器,存放 下一条将要执行的指令地址。
- 状态监视器:CPSR (Current Program Status Register),它不是用来存数据的,而是用来存"状态"(标志位、中断控制、处理器模式)的。
🎵3) 常用语法细节解密
- 寄存器组操作 {}
汇编支持一次操作多个寄存器。- PUSH {R0, R1, R2}:比写三行代码效率更高。
- 偏移量与地址模式 []
- LDR R0, [R1, #4]:读取 R1 + 4 字节地址处的数据(常见于结构体成员访问)。
- 立即数 #
- MOV R0, #100:将常数 100 放入 R0。
🔔二、后端视角:汇编指令与高级语言的映射
- 上下文切换(Context Switch):
- 在高并发系统(如 Go 协程)中,切换任务的本质就是执行一连串的 STR(保存当前寄存器)和 LDR(加载新任务寄存器)。
- 函数调用开销:
- 每一个 C 函数开头都有 PUSH,结尾都有 POP。如果函数体极短但调用频繁,这个开销就会凸显。这也是为什么 AI 算子优化里常用 inline 或宏定义的原因。
- 内存对齐:
- LDR 指令在某些架构下访问非对齐地址(如在奇数地址读 4 字节)会触发异常或性能剧降。
🔔三、推荐学习资源
- ARM官方文档
或自己搜索ARM Instruction Set Quick Reference Card - Compiler Explorer
左边写 C++ 代码,右边实时显示对应的汇编指令。