在ARM架构中,加载(Load)和存储(Store)指令构成了处理器与内存交互的基础机制。这类指令采用典型的RISC设计哲学,通过明确的"加载-运算-存储"分离流程实现高效执行。与CISC架构不同,ARM的所有数据处理指令都直接在寄存器间操作,内存访问则专门由加载/存储指令完成。
这种分离设计带来三个显著优势:
ARMv7架构的加载/存储指令支持多种数据宽度访问:
关键提示:当使用小于32位的加载指令时,目标寄存器的高位会根据指令类型进行零扩展(无符号加载)或符号扩展(带符号加载),这个特性在数据类型转换时非常有用。
LDR(Load Register)和STR(Store Register)是最基础的指令对,其通用格式为:
code复制LDR{type}{cond} Rt, [Rn {, #offset}]
STR{type}{cond} Rt, [Rn {, #offset}]
其中:
示例代码:
armasm复制LDR R0, [R1] ; 将R1指向的32位数据加载到R0
STRH R2, [R3, #4] ; 将R2的低16位存储到R3+4地址处
LDRSB R4, [R5, R6] ; 加载R5+R6地址的8位数据并符号扩展到R4
LDM(Load Multiple)和STM(Store Multiple)支持高效的多寄存器传输,特别适用于:
指令格式:
code复制LDM{addr_mode}{cond} Rn{!}, reglist
STM{addr_mode}{cond} Rn{!}, reglist
地址模式决定了基址寄存器的更新方式:
栈操作专用变体:
armasm复制PUSH {R0-R3, LR} ; 等价于 STMDB SP!, {R0-R3, LR}
POP {R0-R3, PC} ; 等价于 LDMIA SP!, {R0-R3, PC}
LDREX/STREX指令对实现了原子内存访问,是多核同步的基础:
code复制LDREX Rt, [Rn] ; 独占加载
STREX Rd, Rt, [Rn] ; 独占存储(成功时Rd=0)
执行流程:
典型使用模式:
armasm复制retry:
LDREX R1, [R0] ; 加载当前值
ADD R1, R1, #1 ; 修改值
STREX R2, R1, [R0] ; 尝试存储
CMP R2, #0 ; 检查是否成功
BNE retry ; 失败则重试
ARM提供灵活的寻址方式满足不同场景需求:
code复制[Rn, offset]
[R1, #0x20][R2, R3][R4, R5, LSL #2] (常用于数组访问)前索引(Pre-index):
code复制LDR R0, [R1, #4]! ; R1 = R1 + 4 然后加载
后索引(Post-index):
code复制LDR R0, [R1], #4 ; 先加载,然后 R1 = R1 + 4
LDR支持PC相对寻址,是实现位置无关代码(PIC)的关键:
armasm复制LDR R0, [PC, #offset] ; 从PC+offset处加载数据
编译器常用此方式实现全局变量访问和跳转表。
ARMv7定义三个特权级:
LDRT/STRT等非特权指令:
在多核系统中,内存访问顺序可能影响程序正确性:
code复制DMB ; 数据内存屏障(确保屏障前的存储指令先于后面的完成)
DSB ; 数据同步屏障(确保所有指令等待内存访问完成)
ISB ; 指令同步屏障(清空流水线)
NEON扩展提供强大的向量加载/存储能力:
armasm复制VLD1.32 {D0}, [R0] ; 从R0加载单个32位元素到D0
VST1.16 {D1}, [R1] ; 存储D1中的16位元素到R1
armasm复制VLD2.16 {D0,D1}, [R0]! ; 交错加载两个16位元素序列
这种指令特别适合图像处理中的像素数据存取。
ARMv7要求:
检查对齐代码:
armasm复制TST R0, #0x3 ; 检查地址是否字对齐
BNE handle_unaligned
PLD(Preload Data)指令提示处理器预取数据:
armasm复制PLD [R0, #256] ; 预取R0+256处的数据
最佳预取距离需要根据具体微架构调整。
可能原因:
排查步骤:
使用性能计数器监测:
优化手段: