c - 由于奇怪的优化,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
解决方案
它预先计算每个 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
推荐阅读
- python - 如何在字符串中模拟 DELETE 击键?
- oracle - 在交互式网格 Oracle APEX 中未触发列验证
- c - 避免使用 strcpy() 进行堆栈粉碎的 NUL 终止字符
- mod-rewrite - apache2静默重定向未按预期工作
- sql - 是否指定 INSERT IGNORE... SELECT 是按顺序执行的?
- javascript - 如何从角色的频道中删除权限-discord.js?
- c# - Linq to SQL 返回列表对象
- python - 得分随着使用 PCA 的组件数量而增加
- java - 如何在 Java 中将泛型 pbject 作为方法参数传递?
- html - css transform translate 在y轴上不起作用