ARM GCC 10.3/13.x使用-Os优化时函数指针传参异常问询
问题描述
在Cortex-M7平台下,使用-Os编译选项编译代码时,通过函数指针调用指定地址的函数(+1适配Thumb模式)出现参数传递异常。相关代码如下:
//Some code above... memset(.....) uint32_t (*caller)(uint32_t taskid, ...); caller = (uint32_t(*)(uint32_t, ...))((uint32_t)0x08020000 + 1U); for (size_t i = 0; i < 3; ++i) { caller(i); }
函数被调用3次,但函数内部接收到的参数i并非预期的0、1、2,而是垃圾值、0、1;关闭-Os编译选项后该问题消失。查看反汇编发现循环被展开,且指令顺序被重排:
memset(shared_ram, 0x00, sizeof(*shared_ram)); 8000968: 2000 movs r0, #0 caller(i); 800096a: 4d1a ldr r5, [pc, #104] ; (80009d4 <main+0xc0>) memset(shared_ram, 0x00, sizeof(*shared_ram)); 800096c: 6018 str r0, [r3, #0] 800096e: 6058 str r0, [r3, #4] 8000970: 6098 str r0, [r3, #8] caller(i); 8000972: 47a8 blx r5 8000974: 2001 movs r0, #1 8000976: 47a8 blx r5 8000978: 2002 movs r0, #2 800097a: 47a8 blx r5
编译参数如下:
-fdata-sections -ffunction-sections --specs=nano.specs -Wl,--gc-sections -g -mthumb -mcpu=cortex-m7 -mfpu=fpv5-d16 -mfloat-abi=hard -Og -g3 -ggdb -fno-eliminate-unused-debug-symbols -fdata-sections -ffunction-sections -Wall -Wextra -Wpedantic -Wno-unused-parameter -Os -std=gnu11
注:Cortex-M7缓存未启用。
请问这是编译器bug吗?
问题分析与结论
这不是编译器bug,问题根源在于编译器优化逻辑与代码的ABI兼容性冲突:
- 从反汇编可见,
-Os开启的激进优化(指令重排)打乱了执行顺序:原本应先给r0赋值0再调用caller,但实际是先加载函数指针到r5,接着执行memset的内存写入(全程使用r0),之后才调用caller——此时r0的值已被memset操作覆盖,导致第一次调用传递了垃圾值。 - 函数指针被声明为可变参数(
...),编译器会按ARM可变参数ABI处理调用逻辑,同时认为memset与caller的参数设置无依赖关系,因此允许指令重排;关闭-Os后优化程度降低,指令顺序符合代码逻辑,问题消失。
解决方案
可通过以下方式修复:
- 修正函数指针声明:若被调用函数为固定参数,将指针声明改为匹配形式,比如
uint32_t (*caller)(uint32_t taskid);,让编译器明确参数寄存器的依赖关系,避免重排破坏参数传递。 - 插入优化屏障:在
memset与caller调用之间添加__asm__ __volatile__("" ::: "memory"),阻止编译器对两段代码进行重排。 - 局部禁用优化:在包含该循环的函数上添加
__attribute__((optimize("O0"))),强制该函数不使用-Os优化。
内容的提问来源于stack exchange,提问作者unalignedmemoryaccess
相关产品推荐
相关产品推荐

