5.常见优化问题¶
开发期间可能会遇到这样一种情形:当编译器优化被禁用 (-Ooff
) 时,应用程序正常运行,但在使用更高级别的优化 (-O1,-O2,-O3or-O4
) 时无法正常运行。造成这种情况的典型原因包括:
- 从主程序和中断服务例程 (ISR) 访问共享数据
- Volatile 限定符
- 原子更新
- 在不使用 Volatile 的情况下访问存储器映射外设寄存器
- 使用 C 语言代码调用 asm 函数而不遵循 C 语言规则
- 未经初始化的变量
5.2.外设访问¶
- 访问代表存储器映射外设的存储器位置时,必须使用 Volatile 关键字。
- 此类存储器位置可能会以编译器无法预测的方式更改值。
- 这可确保编译器完全按照 C 语言代码中的要求保留对存储器进行读写的次数。
- 缺少 Volatile 限定符可能会导致编译器错误地优化掉或重新排序读取/写入。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | staticinlinevoidGPIO_writePin(uint32_tpin,uint32_toutVal){volatileuint32_t*gpioDataReg;uint32_tpinMask;//// Check the arguments.//ASSERT(GPIO_isPinValid(pin));gpioDataReg=(uint32_t*)GPIODATA_BASE+((pin/32U)*GPIO_DATA_REGS_STEP);pinMask=(uint32_t)1U<<(pin%32U);if(outVal==0U){gpioDataReg[GPIO_GPxCLEAR_INDEX]=pinMask;}else{gpioDataReg[GPIO_GPxSET_INDEX]=pinMask;}} |
5.3.原子访问¶
- 对最多 32 位全局变量的写入是原子操作(int、float 等)。
- 对大于 32 位(64 位 long double,结构)全局变量的写入,禁用/重新启用与写入相关的中断。这可确保写入器会在读取器访问整个变量之前更新该变量,并避免使变量处于不一致或不完整的状态。
列表 2.3 是来自 C2000Ware 中的数字控制库的代码片段,说明了禁用与结构更新相关的中断以确保对整个结构进行原子更新。请参阅 TMS320C28x 优化 C/C++ 编译器用户指南中的表 7-6“TMS320C28x C/C++ 编译器内在函数”,详细了解 __enable_interrupt()
和 __disable_interrupt()
内在函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | uint16_tval=__disable_interrupts();p->Kp=p->sps->Kp;p->Ki=p->sps->Ki;p->Kd=p->sps->Kd;p->Kr=p->sps->Kr;p->c1=p->sps->c1;p->c2=p->sps->c2;p->Umax=p->sps->Umax;p->Umin=p->sps->Umin;DCL_restoreInts(v);// If interrupts were originally enabled, re-enable themif(0U==(val&0x1))__enable_interrupts(); |
5.4.从 C 语言代码调用 asm 函数¶
从 C 语言代码调用任何 asm 函数都必须遵循 C 语言调用和寄存器规则。请参阅 TMS320C28x 优化 C/C++ 编译器用户指南中的第 7.2 节“寄存器规则”、第 7.3 节“函数结构和调用规则”和第 7.5 节“同时使用 C/C++ 与汇编语言”。
任何违反这些规则的行为都可能导致应用程序在 -Ooff 时可以运行,但在更高的优化级别下无法运行。
5.5.未经初始化的变量¶
- 使用未经初始化的变量可能会导致未定义的行为
- 应用程序使用未经初始化变量的行为可能会随着优化级别而改变,从而使调试变得困难
- 局部变量
- 使用之前必须在应用程序中显式初始化
- 全局变量
- C 语言标准规定,在程序开始运行之前,必须将没有显式初始化的全局 (extern) 和静态变量初始化为 0。
- C 语言运行时初始化行为在 COFF 和 EABI 中有所不同
- 有关详细信息,请参阅 TMS320C28x 优化 C/C++ 编译器用户指南 - 第 7.10.3 节“COFF 变量的自动初始化”和第 7.10.4 节“EABI 变量的自动初始化”。