首页 > 解决方案 > 由于奇怪的优化,gcc 堆栈溢出

问题描述

我设置了一些与下面的示例类似的代码。这适用于内存有限的嵌入式 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

标签: cgccarmcompiler-optimization

解决方案


它预先计算每个 if(!tmp2 | tmp == 52) 的结果并将值推入堆栈。

直到您编辑您的问题以向我们展示使用选项生成的汇编程序-S我对此表示严重怀疑

...如果我将堆栈增加到足够大,它将运行而不会崩溃,则总计 364 字节的堆栈

即使您所说的推送是否正确,是否还有 364 个字节都不会爆炸您的堆栈,原因在其他地方,您很可能在某处有未定义的行为

该代码在技术上是正确的

很抱歉,但你的代码真的很乱!

struct fault_mess似乎 91 个字段 ( ) 是没有意义的7*('M' - 'A' + 1),为什么你不使用例如char fault[MOD_LAST]['M' - 'A' + 1]允许进行第一级简化:

for (x=0; x < MOD_LAST; x++)      // check all modules
{
  if (!mod_fault[x])
  {
    memset(fault[x], 0, sizeof(fault[x]));
  }
  else
  {
    tmp = mod_fault[x];

    switch (x) {
    case MOD_1:
      switch (tmp) {
      case 67: fault[MOD_1]['A'-'A'] = 1; break;
      case 44: fault[MOD_1]['B'-'A'] = 1; break;
      ...
      }
      break;
    ...
    case MOD_7:
      switch (tmp) {
      case 52: fault[MOD_7]['A'-'A'] = 1; break;
      case 81: fault[MOD_7]['B'-'A'] = 1; break;
      ...
      }
      break;
    }
  }
}

我使用了一个错误数组,char但你保存(0 或 1)bool

可以进行第二级简化,定义一个常量二维数组,char其第一维是MOD_LAST,第二维是 13 ( 'M' - 'A' + 1)

arr[MOD_1]['A'-'A'] = 67
arr[MOD_1]['B'-'A'] = 44
...
arr[MOD_7]['A'-'A'] = 52
arr[MOD_7]['B'-'A'] = 81
..

只需要短代码:

for (x=0; x < MOD_LAST; x++)      // check all modules
{
  if (!mod_fault[x])
  {
    memset(fault[x], 0, sizeof(fault[x]));
  }
  else
  {
    short tmp = mod_fault[x];
    short * p = arr[x];

    for (i = 0; i != 'M' - 'A' + 1; ++i)
    {
       if (tmp == p[i])
       {
          fault[x][i] = 1;
          break;
       }
    }
  }
}

请注意,如果 的可能值的范围mod_fault很小,您还可以使用值的常量数组或任何超出范围的值来删除循环for以检测这种情况,然后char [MOD_LAST][max-range]0 .. 'M' - 'A' + 1

#define MIN_VALUE ??
#define MAX_VALUE ??
#define NOT_INDEX 'Z' 
...
for (x=0; x < MOD_LAST; x++)      // check all modules
{
  if (!mod_fault[x])
  {
    memset(fault[x], 0, sizeof(fault[x]));
  }
  else if ((mod_fault[x] >= MIN_VALUE) && (mod_fault[x] <= MAX_VALUE))
  {
    char index = arr[x][mod_fault[x] - MIN_VALUE];
   
    if (index != NOT_INDEX)
      fault[x][index] = 1;
  }
}

arr[MOD_1][76 - MIN_VALUE] = 0; // A
arr[MOD_1][44 - MIN_VALUE] = 1; // B
...
arr[MOD_7][52 - MIN_VALUE] = 0; // A
arr[MOD_7][81 - MIN_VALUE] = 1; // B
...

和其他条目重视NOT_INDEX


推荐阅读