我最近在工作中发现,由于存在编译器错误的风险,我们的政策是不对硬实时嵌入式系统使用编译器优化(我们主要使用gcc,但政策也扩展到其他编译器)。显然这个政策的开始是因为过去有人因为优化器的错误而被烧毁。我的直觉是,这是过度偏执所以我已经开始寻找关于这个问题的数据,但问题是我找不到任何关于此的硬数据。
有谁知道实际获取此类数据的方法?是否可以使用gcc bugzilla页面生成一些错误与编译器优化级别的统计信息?是否有可能获得这样的无偏见数据?
答案 0 :(得分:2)
使用编译器是否安全?
编译器按照设计将您的代码转换为另一种形式。 通常应该正确转换它,但是因为所有软件都可能存在潜伏的错误。所以没有它不安全。
什么可以使代码安全?
测试/使用。
对于要显示的错误,包含它们的代码必须在特定配置中运行。对于任何非平凡的软件,证明缺少错误几乎是不可能的,但是大量测试和大量使用往往至少会清除一些执行路径。
那么,我如何才能安全?
好吧,使用与其他人相同的路径。这给了你最好的机会,这条路径没有错误,所有人都已经通过那里。
对于gcc呢?我会使用-O2
或-Os
(就像Linux一样),因为这些可能会受到大量的直接或间接审查。
你应该启用优化吗?
但是,将优化引入工具链是破坏性。它需要的不仅仅是切换翻盖。您需要执行大量测试,以确保在您的条件下没有任何不良情况发生。
更具体地说,编译器依赖未定义的行为来执行许多优化。如果您的代码从未接受过优化,那么它很可能会依赖于此处的未定义行为,并且转向优化可能暴露这些错误(不会引入它们)。
它不会比切换编译器更具破坏性。
答案 1 :(得分:1)
我没有任何数据(并且没有听说过有人......)但是......
在选择禁用优化之前,我会选择使用哪个编译器。换句话说,我不会使用任何我不相信优化的编译器。
linux内核是用-Os编译的。 很多对我来说比任何bugzilla分析更有说服力。
就个人而言,我对任何版本的gcc linux都没关系。
作为另一个数据点,Apple已经从gcc转换为llvm,无论是否有铿锵声。传统上llvm存在一些C ++的问题,虽然llvm-gcc现在好多了,但是clang ++似乎仍然存在问题。但这只是证明了这种模式:虽然Apple(据称)现在用clang编译OS X和iOS,但如果有任何C ++和Objective C ++,它们就不会使用太多。所以对于纯C和Objective C,我会信任clang,但我仍然不相信clang ++。
答案 2 :(得分:1)
您假设编译器没有优化而没有错误,只有优化是危险的。编译器本身就是程序,并且经常在使用或不使用某些功能时出现错误。当然,这些功能可能会让它变得更好,或者可能会让它变得更糟。
Llvm在另一个答案中被提及,有一个众所周知的llvm优化错误,他们似乎没有兴趣修复
while(1) continue;
得到优化,只是消失...有时...而其他类似但不完全无限的循环也会在llvm优化器中消失。让您使用与您的源代码不匹配的二进制文件。我知道在gcc和llvm的编译器中可能还有更多。
gcc是一种几乎没有用胶带和捞丝捆绑在一起的怪物。这就像看过死亡电影的那些面孔或类似的东西,一旦你的头脑中有这些图像,你不能不看它们,它们会在那里被终身烧掉。所以值得一提的是,通过看幕后来了解gcc是多么可怕。但你可能无法忘记你所看到的。对于各种目标-O0 -O1 -O2 -O3可以并且在某些时间点用一些代码惨败。同样,修复有时会优化更多而不是更少。
当你编写一个程序时,希望编译器按照它所说的做,就像你希望你的程序完成你所说的那样。但情况并非总是如此,当源代码完美时,调试不会结束,当二进制文件完美时它会结束,并且包含您希望定位的任何二进制和操作系统(gcc的不同次要版本会生成不同的二进制文件,不同的linux目标对程序的反应不同。)
最重要的建议是使用目标优化级别进行开发和测试。如果您通过始终为调试器构建开发和测试,那么您已经创建了一个在调试器中工作的程序,当您希望在其他位置使其工作时,您可以重新开始。 gcc' s -O3经常工作,但是人们害怕它并且没有足够的用法来正确调试,所以它不那么可靠。 -O2和没有优化--O0获得了很多里程,很多错误报告,很多修复,选择其中一个或作为另一个答案说,与Linux使用。或者使用firefox使用的内容或者使用chrome使用的内容。
现在硬实时嵌入式系统。人的任务系统,直接影响生命或财产的系统。首先为什么要使用gcc?其次,是的,优化器通常不会在这些环境中使用,它会产生太多风险和/或大大增加测试和验证工作。通常你想使用经过大量测试的编译器,它的疣和陷阱是众所周知的。你想成为打开优化器的人吗?因此飞行计算机在一个上学日将飞机撞毁到一所小学?从旧的计时器中可以学到很多东西。是的,他们有很多战争故事,很多人担心新的事情。不要重复历史,要从中学习。 "他们不像过去那样建立他们。意味着它不仅仅是一种说法。那些遗留系统稳定可靠,仍然运行有一定原因,部分是旧计时器和他们学到的困难,部分原因是新建的东西制造成本更低,质量更低。
对于这类环境,您绝对不会停留在源代码中,您的资金和时间将用于验证BINARY。每次更改二进制文件时,都需要重新开始验证。与运行的硬件没有什么不同,您可以更换一个组件,预热一个焊点,从一开始就重新开始验证测试。一个区别可能是在某些环境中,每个焊点在废弃整个装置之前只允许最大循环次数。但是在软件中可能就是这种情况,在废弃舞会之前只有很多的舞会周期,而在废弃电路板/单元之前只有很多返工周期。关闭优化器并找到更好,更稳定的编译器和/或编程语言。
现在,如果这个艰难的实时环境在崩溃时不会伤害人或财产(除了运行之外),那么这就是另一个故事。也许它是一个蓝光播放器,它在这里和那里跳过一个框架或显示一些坏像素,大不了。打开优化器,群众不再关心这个级别的质量,他们满足于youtube质量的图像,压缩的视频格式等等。必须关闭和重新启动汽车,无线电或蓝牙工作。不要打扰他们一点,打开优化器并声称性能优于竞争对手。如果软件太麻烦不能容忍,客户会解决它或只是购买别人,当那个失败时,他们会回到你身边并用新的固件购买你的新模型。他们将继续这样做,因为他们想要跳舞,他们不想要稳定性和质量。这些东西花费太多。
您应该收集自己的数据,尝试环境中软件的优化器,并通过完整的验证套件运行产品。如果它没有中断,那么当天的代码优化器是可以的,或者测试系统需要更多的工作。如果你不能这样做那么你至少可以反汇编和分析编译器对你的代码做了什么。我会假设(并且根据个人经验知道)gcc和llvm bug系统都存在与优化级别相关的错误,这是否意味着您可以根据优化级别对它们进行排序?不知道,这些是开源的,基本上是不受控制的接口,所以你不能依靠群众来准确和完整地定义输入字段,如果bug报告表上有一个优化字段,它可能总是被设置为默认的表格/网页。您必须检查问题报告本身,以查看用户是否遇到与优化程序相关的问题。如果这是一个公司的封闭系统,员工绩效评估可能会因为没有遵循正确填写表格等程序而得到负面反映,那么您将拥有更好的可搜索数据库来从中提取信息。
优化器确实会增加您的风险。让我们说50%的编译器用于获得没有优化的输出,另外10%用于获得-O1,你增加了风险,使用了更多的编译器代码,更多的bug风险,输出中的风险更大。还有更多代码用于获取-O2和-O3。减少优化不会完全消除风险,但会降低几率。
答案 3 :(得分:0)
根据我的理解,除了调度和重新排列优化之外,大多数编译器优化对所有程序都是安全的。因为这种类型的优化可能会改变原始程序的行为。
有关此问题的数据,您可以查看:
答案 4 :(得分:0)
我不知道gcc错误,但C编程语言不适合当前的硬件。请记住,它是在20世纪70年代构思出来的,甚至不清楚2的补码算法是未来。好的,你在C中添加了2个无符号整数。规范说你不允许溢出。编译器可以假设在添加之后进位标志是清空的,并基于此进行进一步的优化。你假设2的补充算术(这些天不会是谁)和ka-boom你只需设置进位标志。像这样的事情是安全问题的主要来源。我认为Java可能更好,即使对于低级别的代码也是如此,因为它的定义要好得多,当前的HotSpot及时编译器会生成运行速度快的C代码。你也可以查看D编程语言,可能它定义得很好太
答案 5 :(得分:0)
在嵌入式系统中,经常需要通过写入寄存器来控制硬件。在C语言中,这非常简单,只需使用寄存器地址初始化一个指针,然后就可以了。
如果程序的其他部分没有读取或写入寄存器,则优化器很可能会删除该分配。打破代码。
可以使用" volatile"修复此特定问题。关键词。但是不要忘记优化器也会改变命令的顺序。因此,如果您的硬件希望寄存器按特定顺序写入,则可能会被烧毁。
优化器应该产生正确的结果,但是临时步骤可能会发生变化,这是优化者可能会受到伤害的地方。