MDK ARM编译器无优化下__forceinline函数执行更慢问题求助
在STM32F030芯片上使用无优化的MDK ARM编译器时,发现被__forceinline声明的函数执行时间比未声明的更长,已通过示波器验证。简化后的示例代码如下:
static void __forceinline inline_dbg() { //cs down GPIOA->BRR = (uint32_t)GPIO_PIN_4; while((GPIOB->IDR & GPIO_PIN_10) == 0){} //cs up GPIOA->BSRR = (uint32_t)GPIO_PIN_4; }
移除__forceinline后函数执行更快。查看反汇编发现,__forceinline函数中while((GPIOB->IDR & GPIO_PIN_10) == 0){}语句前有两个NOP,而未内联的函数中仅有一个NOP。
完整测试用例:
while(1) { no_inline_dbg(); inline_dbg(); } static void no_inline_dbg() { //cs down GPIOA->BRR = (uint32_t)GPIO_PIN_4; while((GPIOB->IDR & GPIO_PIN_10) == 0){} //cs up GPIOA->BSRR = (uint32_t)GPIO_PIN_4; } static __forceinline void inline_dbg() { //cs down GPIOA->BRR = (uint32_t)GPIO_PIN_4; while((GPIOB->IDR & GPIO_PIN_10) == 0){} //cs up GPIOA->BSRR = (uint32_t)GPIO_PIN_4; }
对应的反汇编代码:
0x0800CB98 F7FEF85E BL.W no_inline_dbg (0x0800AC58) -------------------THIS IS INLINE SECTION!!!!!!! 0x0800CB9C BF00 NOP --------------cs down (chip select down)------------- 0x0800CB9E 2010 MOVS r0,#0x10 0x0800CBA0 2109 MOVS r1,#0x09 0x0800CBA2 06C9 LSLS r1,r1,#27 0x0800CBA4 6288 STR r0,[r1,#0x28] ----------------------------------------------- 0x0800CBA6 BF00 NOP //<---- WHY THERE IS TWO NOPS HERE??? 0x0800CBA8 BF00 NOP ------------while((GPIOB->IDR & GPIO_PIN_10) == 0){}------------- 0x0800CBAA 4807 LDR r0,[pc,#28] 0x0800CBAC 6900 LDR r0,[r0,#0x10] 0x0800CBAE 2101 MOVS r1,#0x01 0x0800CBB0 0289 LSLS r1,r1,#10 0x0800CBB2 4008 ANDS r0,r0,r1 0x0800CBB4 2800 CMP r0,#0x00 0x0800CBB6 D0F8 BEQ 0x0800CBAA ---------------cs up (chip select up)------------ 0x0800CBB8 2010 MOVS r0,#0x10 0x0800CBBA 2109 MOVS r1,#0x09 0x0800CBBC 06C9 LSLS r1,r1,#27 0x0800CBBE 6188 STR r0,[r1,#0x18] ------------------------------------------------ 0x0800CBC0 BF00 NOP 0x0800CBC2 E7E9 B 0x0800CB98 ------------------THIS IS NOT INLINE SECTION!!!!!! ------------cs down (chip select down)------------- 0x0800AC58 2010 MOVS r0,#0x10 0x0800AC5A 2109 MOVS r1,#0x09 0x0800AC5C 06C9 LSLS r1,r1,#27 0x0800AC5E 6288 STR r0,[r1,#0x28] ---------------------------------------- 0x0800AC60 BF00 NOP ------------while((GPIOB->IDR & GPIO_PIN_10) == 0){}----------- 0x0800AC62 4806 LDR r0,[pc,#24] ; @0x0800AC7C 0x0800AC64 6900 LDR r0,[r0,#0x10] 0x0800AC66 2101 MOVS r1,#0x01 0x0800AC68 0289 LSLS r1,r1,#10 0x0800AC6A 4008 ANDS r0,r0,r1 0x0800AC6C 2800 CMP r0,#0x00 0x0800AC6E D0F8 BEQ 0x0800AC62 --------------cs up (chip select up)------------- 0x0800AC70 2010 MOVS r0,#0x10 0x0800AC72 2109 MOVS r1,#0x09 0x0800AC74 06C9 LSLS r1,r1,#27 0x0800AC76 6188 STR r0,[r1,#0x18] ----------------goes to 0x0800CB9C----------------- 0x0800AC78 4770 BX lr
请问这是怎么回事?为何编译器会根据函数是否内联来修改函数内部代码?
这是MDK ARM编译器在**无优化(-O0)**模式下的典型行为,核心原因是内联代码与非内联代码的代码对齐策略不同:
指令对齐需求
STM32F030基于Cortex-M0内核,虽然内核本身支持半字/字节对齐的指令,但MDK编译器在无优化模式下,会为内联后的代码块添加额外NOP来满足隐含的对齐规则——比如将循环入口(while语句)对齐到4字节边界。
观察反汇编:内联版本中while循环的起始地址0x0800CBAA是4字节对齐的(16进制末两位AA能被4整除),而前面的两个NOP就是为了把循环入口推到这个对齐位置。
非内联函数中,while循环起始地址0x0800AC62不是4的倍数,编译器在无优化模式下仅做了半字对齐,只插入了一个NOP。无优化模式的特殊性
当开启优化(如-O1及以上)时,编译器会自动移除多余的NOP,同时优化指令顺序,内联函数的执行效率通常会高于非内联版本。但在-O0模式下,编译器优先保证代码原始结构和调试友好性,而非执行效率,因此会插入额外NOP维持对齐,哪怕这会拖慢执行速度。__forceinline的影响
使用__forceinline后,函数代码被直接嵌入调用处,编译器会将嵌入后的代码块作为整体处理对齐规则,而非像独立函数那样仅考虑局部对齐。这就导致内联版本出现了额外的NOP,进而增加了执行时间。
总结:这种现象是无优化模式下编译器对齐策略差异导致的,并非内联本身的问题。若追求执行效率,建议开启合适的优化等级(如-O1),此时多余的NOP会被自动移除,内联函数的优势也会体现出来。
内容的提问来源于stack exchange,提问作者zulunation

