ARM Cortex M7 未对齐访问和 memcpy
ARM Cortex M7 unaligned access and memcpy
我正在使用 GCC 为 Cortex M7 编译此代码:
// copy manually
void write_test_plain(uint8_t * ptr, uint32_t value)
{
*ptr++ = (u8)(value);
*ptr++ = (u8)(value >> 8);
*ptr++ = (u8)(value >> 16);
*ptr++ = (u8)(value >> 24);
}
// copy using memcpy
void write_test_memcpy(uint8_t * ptr, uint32_t value)
{
void *px = (void*)&value;
memcpy(ptr, px, 4);
}
int main(void)
{
extern uint8_t data[];
extern uint32_t value;
// i added some offsets to data to
// make sure the compiler cannot
// assume it's aligned in memory
write_test_plain(data + 2, value);
__asm volatile("": : :"memory"); // just to split inlined calls
write_test_memcpy(data + 5, value);
... do something with data ...
}
然后我得到了以下带有 -O2 的 Thumb2 程序集:
// write_test_plain(data + 2, value);
800031c: 2478 movs r4, #120 ; 0x78
800031e: 2056 movs r0, #86 ; 0x56
8000320: 2134 movs r1, #52 ; 0x34
8000322: 2212 movs r2, #18 ; 0x12
8000324: 759c strb r4, [r3, #22]
8000326: 75d8 strb r0, [r3, #23]
8000328: 7619 strb r1, [r3, #24]
800032a: 765a strb r2, [r3, #25]
// write_test_memcpy(data + 5, value);
800032c: 4ac4 ldr r2, [pc, #784] ; (8000640 <main+0x3a0>)
800032e: 923b str r2, [sp, #236] ; 0xec
8000330: 983b ldr r0, [sp, #236] ; 0xec
8000332: f8c3 0019 str.w r0, [r3, #25]
谁能解释一下 memcpy
版本的工作原理?这看起来像是目标地址的内联 32 位存储,但这不是问题吗,因为 data + 5
肯定没有与 4 字节边界对齐?
这可能是由于我的源代码中某些未定义的行为而发生的一些优化吗?
对于 Cortex-M 处理器,字节、半字和字的未对齐加载和存储通常是允许的,并且大多数编译器在生成代码时使用它,除非它们被指示不要这样做。如果你想阻止 gcc 假定未对齐访问是正确的,你可以使用 -mno-unaligned-access
编译器标志。
如果指定此标志,gcc 将不再内联对 memcpy
和 write_test_memcpy
的调用,看起来像
write_test_memcpy(unsigned char*, unsigned long):
push {lr}
sub sp, sp, #12
movs r2, #4
add r3, sp, #8
str r1, [r3, #-4]!
mov r1, r3
bl memcpy
add sp, sp, #12
ldr pc, [sp], #4
Cortex-M 7 , M4, M3 M33, M23 支持未对齐访问
M0、M+不支持未对齐访问
但是您可以通过在配置和控制寄存器中设置位 UNALIGN_TRP 来禁用 cortexm7 中未对齐访问的支持,任何未对齐访问都会产生使用错误。
从编译器的角度来看,默认设置是生成的汇编代码进行未对齐访问,除非您使用编译标志禁用它 -mno-unaligned-access
我正在使用 GCC 为 Cortex M7 编译此代码:
// copy manually
void write_test_plain(uint8_t * ptr, uint32_t value)
{
*ptr++ = (u8)(value);
*ptr++ = (u8)(value >> 8);
*ptr++ = (u8)(value >> 16);
*ptr++ = (u8)(value >> 24);
}
// copy using memcpy
void write_test_memcpy(uint8_t * ptr, uint32_t value)
{
void *px = (void*)&value;
memcpy(ptr, px, 4);
}
int main(void)
{
extern uint8_t data[];
extern uint32_t value;
// i added some offsets to data to
// make sure the compiler cannot
// assume it's aligned in memory
write_test_plain(data + 2, value);
__asm volatile("": : :"memory"); // just to split inlined calls
write_test_memcpy(data + 5, value);
... do something with data ...
}
然后我得到了以下带有 -O2 的 Thumb2 程序集:
// write_test_plain(data + 2, value);
800031c: 2478 movs r4, #120 ; 0x78
800031e: 2056 movs r0, #86 ; 0x56
8000320: 2134 movs r1, #52 ; 0x34
8000322: 2212 movs r2, #18 ; 0x12
8000324: 759c strb r4, [r3, #22]
8000326: 75d8 strb r0, [r3, #23]
8000328: 7619 strb r1, [r3, #24]
800032a: 765a strb r2, [r3, #25]
// write_test_memcpy(data + 5, value);
800032c: 4ac4 ldr r2, [pc, #784] ; (8000640 <main+0x3a0>)
800032e: 923b str r2, [sp, #236] ; 0xec
8000330: 983b ldr r0, [sp, #236] ; 0xec
8000332: f8c3 0019 str.w r0, [r3, #25]
谁能解释一下 memcpy
版本的工作原理?这看起来像是目标地址的内联 32 位存储,但这不是问题吗,因为 data + 5
肯定没有与 4 字节边界对齐?
这可能是由于我的源代码中某些未定义的行为而发生的一些优化吗?
对于 Cortex-M 处理器,字节、半字和字的未对齐加载和存储通常是允许的,并且大多数编译器在生成代码时使用它,除非它们被指示不要这样做。如果你想阻止 gcc 假定未对齐访问是正确的,你可以使用 -mno-unaligned-access
编译器标志。
如果指定此标志,gcc 将不再内联对 memcpy
和 write_test_memcpy
的调用,看起来像
write_test_memcpy(unsigned char*, unsigned long):
push {lr}
sub sp, sp, #12
movs r2, #4
add r3, sp, #8
str r1, [r3, #-4]!
mov r1, r3
bl memcpy
add sp, sp, #12
ldr pc, [sp], #4
Cortex-M 7 , M4, M3 M33, M23 支持未对齐访问 M0、M+不支持未对齐访问
但是您可以通过在配置和控制寄存器中设置位 UNALIGN_TRP 来禁用 cortexm7 中未对齐访问的支持,任何未对齐访问都会产生使用错误。
从编译器的角度来看,默认设置是生成的汇编代码进行未对齐访问,除非您使用编译标志禁用它 -mno-unaligned-access