【发布时间】:2020-12-03 11:27:10
【问题描述】:
我设置了一些类似于下面示例的代码。这是针对内存有限的嵌入式 ARM 系统,所以我的堆栈对于这个线程只有 800 字节。
基本思想是我从模块获取故障编号,值为 0 会清除该模块的所有故障,唯一编号可能会触发特定故障。 我布置功能的方式的主要目标是只写一次唯一编号,并且与故障本身在同一行。 目前我正在处理可以通过这种方式触发的 91 个故障。
enum {
MOD_1, // 0
MOD_2,
MOD_3,
MOD_4,
MOD_5,
MOD_6,
MOD_7,
// end
MOD_LAST,
};
extern short mod_fault[7];
struct fault_mess {
short MOD_1_SOME_FAULT;
short MOD_1_DIFFERENT_FAULT;
... //more mod 1 faults
short MOD_1_FINAL_FAULT;
... //other mod faults
short MOD_7_SOME_FAULT;
short MOD_7_DIFFERENT_FAULT;
... //more mod 7 faults
short MOD_7_FINAL_FAULT;
}
struct fault_mess fault;
void set_mod_fault(void)
{
short x, tmp, tmp2;
for (x=0; x < MOD_LAST; x++) // check all modules
{
if (x == MOD_1)
{
tmp = mod_fault[x];
tmp2 = !!tmp;
if(!tmp2 | tmp == 67) fault.MOD_1_SOME_FAULT = tmp2;
if(!tmp2 | tmp == 44) fault.MOD_1_DIFFERENT_FAULT = tmp2;
...
if(!tmp2 | tmp == 69) fault.MOD_1_FINAL_FAULT = tmp2;
}
... //more else if cases
else if (x == MOD_7)
{
tmp = mod_fault[x];
tmp2 = !!tmp;
if(!tmp2 | tmp == 52) fault.MOD_7_SOME_FAULT = tmp2;
if(!tmp2 | tmp == 81) fault.MOD_7_DIFFERENT_FAULT = tmp2;
...
if(!tmp2 | tmp == 17) fault.MOD_7_FINAL_FAULT = tmp2;
}
}
}
我遇到的问题是 gcc 生成此代码指令的方式。 (这是为 32 位 ARM,v4T 编译的,使用 -O2)
我已经逐步完成了生成的汇编代码,它正在预先计算每个if(!tmp2 | tmp == 52) 的结果并将值推入堆栈。然后它进入循环并根据堆栈中的值有条件地存储tmp2 的值。堆栈上的值是 4 字节宽,相当于用于此“优化”的 364 字节堆栈。如果我在 -O0 下编译,则没有过多的堆栈使用。
所以代码在技术上是正确的,因为如果我将堆栈增加到足够大,它将运行而不会崩溃。但是编译器的行为似乎不直观,而且有些错误。
我可以通过重构代码来解决这个问题,但我很好奇这个问题是否有据可查,或者其他人是否认为这是应该解决的问题。
编辑:我尝试使用“-fstack-usage”进行编译,它生成了一个 *.su 文件。 该文件表明该函数在-O0下编译时需要16字节的堆栈,而在-O2下编译时实际上需要960字节的堆栈。
这是一个网络示例。它的性能不如我的嵌入式编译器,使用 -O2 仅使用 200 个字节,(与使用 -O0 的 16 个字节相比)http://tpcg.io/w3KBdVZ6
【问题讨论】:
-
好的,第一件事。表达式
!tmp2 | tmp == 52使用了不正确的运算符(|而不是||)或存在优先级问题。 -
第二 - 这需要minimal reproducible example。
extern short mod_fault[7];实际定义在哪里?第三 - 问题最有可能在您的代码中,而不是在优化中。 -
我真的怀疑编译器会将中间结果存储在堆栈上。
-
如果代码看起来像是使用了大量的复制/粘贴来创建它,那么它可能需要清理。
-
@EugeneSh.:但我认为在这种特殊情况下,
|与||具有相同的效果。
标签: c gcc arm compiler-optimization