Cortex-M架构下32位变量加载优化的两类技术疑问
关于ARM Cortex-M系列内存访问编译优化的问题
测试代码
void load_data_8(uint32_t value, void* d) { uint8_t* d_ptr = d; *d_ptr++ = (value>>0)&0xFF; *d_ptr++ = (value>>8)&0xFF; *d_ptr++ = (value>>16)&0xFF; *d_ptr++ = (value>>24)&0xFF; *d_ptr++ = (value>>24)&0xFF; *d_ptr++ = (value>>16)&0xFF; *d_ptr++ = (value>>8)&0xFF; *d_ptr++ = (value>>0)&0xFF; } void load_data_32(uint32_t value, void* d) { uint32_t* d_ptr = d; *d_ptr = value; }
Cortex-M7编译结果(ARM GCC 11.2.1,-mcpu=cortex-m7 -O3)
该内核支持非对齐内存访问指令,生成汇编如下:
load_data_8: rev r3, r0 str r0, [r1] @ unaligned str r3, [r1, #4] @ unaligned bx lr load_data_32: str r0, [r1] bx lr main: movs r0, #0 bx lr
Cortex-M0+编译结果(ARM GCC 11.2.1,-mcpu=cortex-m0plus -O3)
该内核非对齐内存访问能力较弱,生成汇编如下:
load_data_8: push {r4, lr} lsrs r3, r0, #8 lsrs r2, r0, #16 uxtb r4, r0 uxtb r3, r3 uxtb r2, r2 lsrs r0, r0, #24 strb r4, [r1] strb r3, [r1, #1] strb r2, [r1, #2] strb r0, [r1, #3] strb r0, [r1, #4] strb r2, [r1, #5] strb r3, [r1, #6] strb r4, [r1, #7] pop {r4, pc} load_data_32: str r0, [r1] bx lr
问题与解答
1. Cortex-M7场景下,load_data_8汇编带@ unaligned标记,load_data_32却没有?编译器如何判断load_data_32的指针不会非对齐?
load_data_8的原始逻辑是逐字节访问,编译器优化后改用两次32位写入。但原函数输入是void*,转成uint8_t*后,编译器无法确定d指向的地址是否为32位对齐——uint8_t*允许指向任意地址,因此生成的32位str指令会被标记为非对齐访问。load_data_32中代码将void*转为uint32_t*,根据C语言类型规则和ARM平台ABI约定,uint32_t*指向的地址必须是32位对齐的。编译器会默认调用者遵守这个规则,因此生成的str指令按对齐访问处理,无需标记非对齐。
2. Cortex-M0+场景下,两种方式都是小端序写入32位数据,为何生成代码不同?从内核角度看8位与32位访问的差异是什么?
- 生成代码不同的核心原因是编译器优化策略和内核硬件限制:
load_data_32直接生成32位str指令,这是最高效的写入方式。但Cortex-M0+的32位非对齐访问会触发硬件异常,编译器同样假设uint32_t*指向对齐地址,因此直接生成对齐的32位写入指令。load_data_8的原始逻辑是逐字节写入,编译器无法优化为32位写入——因为uint8_t*允许指向非对齐地址,而M0+不支持非对齐的32位访问,一旦优化会导致异常,因此只能严格生成逐字节的strb指令。
- 内核角度的访问差异:
- 8位访问(
strb/ldrb):无论地址是否对齐,内核都能正常处理,因为字节访问本身不存在对齐问题,硬件直接操作对应地址的单个字节。 - 32位访问(
str/ldr):Cortex-M0+要求地址必须4字节对齐,非对齐访问会触发UsageFault异常;Cortex-M7则支持非对齐32位访问,硬件会自动拆分、合并总线操作,无需软件干预。
- 8位访问(
内容的提问来源于stack exchange,提问作者unalignedmemoryaccess
相关产品推荐
相关产品推荐

