关于--mno-unaligned-access编译选项的精确行为问询
关于ARM Compiler --mno-unaligned-access选项的行为疑问
根据ARM Compiler armclang参考指南6.4版本的描述:
如果禁用非对齐访问,打包数据结构中的字会逐字节访问。
但在实际测试中,读取打包结构体数组的第二个元素的int成员时,编译生成的机器码先执行了一次单字节访问(1e0行),随后使用了对齐的字访问(1e2行),而非预期的全字节访问。现确认:启用--mno-unaligned-access选项后,是否即使是打包结构体,只要目标地址对齐就可以用字访问?
示例代码
typedef struct __attribute__((packed, aligned(1))) MyStruct{ int A; short B; char C; } MyStruct_t; int main(void) { MyStruct_t M[2]; int D, E; M[0].A = 0xffffffff; M[1].A = 0xeeeeeeee; D = M[0].A; E = M[1].A; D = E; return 0 ; }
编译命令(MCUXpresso IDE)
arm-none-eabi-gcc -nostdlib -Xlinker -Map="m7_experiments.map" -Xlinker --cref -Xlinker --gc-sections -Xlinker -print-memory-usage -mcpu=cortex-m7 -mfpu=fpv5-sp-d16 -mfloat-abi=hard -mthumb -T "m7_experiments_Debug.ld" -o "m7_experiments.axf" $(OBJS) $(USER_OBJS) $(LIBS)
使用--mno-unaligned-access编译得到的机器码
000001b0 <main>: 1b0: b480 push {r7} 1b2: b087 sub sp, #28 1b4: af00 add r7, sp, #0 1b6: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff 1ba: 603b str r3, [r7, #0] 1bc: 2300 movs r3, #0 1be: f063 0311 orn r3, r3, #17 1c2: 71fb strb r3, [r7, #7] 1c4: 2300 movs r3, #0 1c6: f063 0311 orn r3, r3, #17 1ca: 723b strb r3, [r7, #8] 1cc: 2300 movs r3, #0 1ce: f063 0311 orn r3, r3, #17 1d2: 727b strb r3, [r7, #9] 1d2: 727b strb r3, [r7, #9] 1d4: 2300 movs r3, #0 1d6: f063 0311 orn r3, r3, #17 1da: 72bb strb r3, [r7, #10] 1dc: 683b ldr r3, [r7, #0] 1de: 617b str r3, [r7, #20] 1e0: 79fb ldrb r3, [r7, #7] 1e2: 68ba ldr r2, [r7, #8] 1e4: f022 427f bic.w r2, r2, #4278190080 ; 0xff000000 1e8: 0212 lsls r2, r2, #8 1ea: 4313 orrs r3, r2 1ec: 613b str r3, [r7, #16] 1ee: 693b ldr r3, [r7, #16] 1f0: 617b str r3, [r7, #20] 1f2: 2300 movs r3, #0 1f4: 4618 mov r0, r3 1f6: 371c adds r7, #28 1f8: 46bd mov sp, r7 1fa: f85d 7b04 ldr.w r7, [sp], #4 1fe: 4770 bx lr
补充:使用-munaligned-access编译得到的机器码
000001b0 <main>: 1b0: b480 push {r7} 1b2: b087 sub sp, #28 1b4: af00 add r7, sp, #0 1b6: f04f 33ff mov.w r3, #4294967295 ; 0xffffffff 1ba: 603b str r3, [r7, #0] 1bc: 2300 movs r3, #0 1be: f063 0311 orn r3, r3, #17 1c2: 71fb strb r3, [r7, #7] 1c4: 2300 movs r3, #0 1c6: f063 0311 orn r3, r3, #17 1ca: 723b strb r3, [r7, #8] 1cc: 2300 movs r3, #0 1ce: f063 0311 orn r3, r3, #17 1d2: 727b strb r3, [r7, #9] 1d4: 2300 movs r3, #0 1d6: f063 0311 orn r3, r3, #17 1da: 72bb strb r3, [r7, #10] 1dc: 683b ldr r3, [r7, #0] 1de: 617b str r3, [r7, #20] 1e0: f8d7 3007 ldr.w r3, [r7, #7] 1e4: 613b str r3, [r7, #16] 1e6: 693b ldr r3, [r7, #16] 1e8: 617b str r3, [r7, #20] 1ea: 2300 movs r3, #0 1ec: 4618 mov r0, r3 1ee: 371c adds r7, #28 1f0: 46bd mov sp, r7 1f2: f85d 7b04 ldr.w r7, [sp], #4 1f6: 4770 bx lr
解答
核心结论
是的,--mno-unaligned-access的核心规则是禁止生成非对齐的字/半字访问指令,而非强制所有打包结构体成员必须逐字节访问。编译器会在遵守规则的前提下尽可能优化:当要读取的多字节数据的部分地址处于对齐位置时,会优先使用对齐的字访问提升效率。
具体分析
你的示例中,M[1].A的起始地址r7+7是非对齐的,但从r7+8开始是4字节对齐地址。编译器拆分了读取操作:
- 用
ldrb读取r7+7处的第一个字节; - 用对齐的
ldr读取r7+8开始的4字节; - 通过
bic(清除高位字节)、lsl(左移)、orr(拼接)操作,把第一个字节和后续3字节组合成完整的int值。
这种做法既符合--mno-unaligned-access禁止非对齐字访问的要求,又利用了对齐访问的高效性,属于编译器的合理优化。
而启用-munaligned-access时,编译器直接生成ldr.w指令读取非对齐地址的完整int,这也符合允许非对齐访问的规则。
内容的提问来源于stack exchange,提问作者e.ad
相关产品推荐
相关产品推荐

