一直在搞C语言,有时候会遇到编译器开优化导致的问题。有没有相关的编程标准,可以让代码更耐优化。我听说UCOS在编译器优化选项开到最高时,仍然可以正常执行。
在知乎,第一次说谢邀。
我对C语言的应用,从前集中于avr,现在侧重于stm32f103。
我的经验是:未优化的c程序可正常运行,优化后不能运行,那一定是我的程序有问题。我还没经历过不是我程序的情况。
发现这种不易发现的问题,需要看汇编码。
避免的方法,我的经验:写c程序,尽量规矩;似是而非的概念,一定要搞清楚,别侥幸。因为侥幸而留的雷,现在不出问题,将来一定会出问题;不优化不出问题,优化就出问题。
最后要说,每个应用程序,都让他开优化运行,只要时间允许,一定要查出开优化后出问题的原因。时间不允许,只能不开优化凑合着,在有时间的时候继续查问题。
没啥好办法,我觉得唯一有效的办法是读标准
标准咋说你咋写,不要有任何未定义行为未指定行为。
连实现定义行为都最好不要有,除非你不换编译器并且很清楚编译器的实现定义符合你的预期。
如果以上都做到了,编译器还优化错,报编译器bug吧
别依赖UB,慎重依赖ID行为。
延时loop这种极可能被折叠的代码也换个办法写。
那的看导致问题是因为编译器bug还是因为使用了语言Undefined Behavior,后者是不推荐使用的。如果是编译器bug,没看到固定标准。不过从经验看,编译器代码生成bug大多出现在比较大的,逻辑复杂的函数里面,某些特殊模式触发优化的bug没有被编译器测试覆盖到。重要的函数分解成小的简单函数可能有帮助吧。
泻药,在大神面前谈谈我实际的工程经历吧 :)
编译器的BUG:可能GCC 6.x“错误”的call order在8.x的时候修复了 BackendUtil's EmitAssemblyHelper::EmitAssembly generated call in wrong order 我通过objdump对比不同的工具链生成的ELF的反汇编,然后可以“人工”修改call order来模拟复现该BUG,还没有经验如何修改GIMPLE,学会了修改LLVM IR,来验证PASS前后是否是预期的变换 [llvm-dev]LLVM 6.0's LoopUnroll PASS is not able to work? 但是也有理解不一致的地方 [llvm-dev]LLVM v6.0 Internalize and GlobalDCE PASS can not work together?
手写汇编,我偏好写GCC compatible ? D38029[AVR]Override ParseDirective 对寄存器别名的支持还是希望汇编器能更普适些,而不是编译器开发者让用户修改自己的代码233 ? D39712[ARM]Add an alias for psr and psr_nzcvq 嵌入式开发我更倾向使用GNU工具链,因为有些WiFi驱动是二进制“闭源”的,如果都像ST经典的DISCO_L475VG_IOT01A开发板那样提供源代码,倒是可以用LLVM工具链 Initial adding LLVM 6.0 Toolchain by xiangzhai · Pull Request #5574 · ARMmbed/mbed-os 我用LLVM工具链实践了简单的Hello World、mini-arm-os Build ARMmbed with LLVM Toolchain
补充:最近在看《龙三》10.5有关LoopUnroll后把循环的多个迭代放到一个大BasicBlock,然后使用简单的Scheduler,使之指令级并行,挺有意思 :)
以上,谢谢!