gcc的优化工具在这里做了些什么奇怪的事吗?

时间:2017-10-03 11:44:23

标签: c gcc gcc-warning

我有一个程序在使用任何类型的优化(-Wmaybe-uninitialized-O1-O2时触发-O3,以下是我可以复制的最小代码这种行为。

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>

struct result {
    int res;
};

struct graph {
    int vertices[5];
};

// In reality this is a backtracking search.
void computational_search (struct graph *g, struct result *out) {
    out->res = 0;
    int i;
    for (i=0; i<5; i++) {
        out->res += g->vertices[i];
    }
}

// In reality queries a database of geometrical graphs.
void next_graph (struct graph *g)
{
    int i;
    for (i=0; i<5; i++) {
        g->vertices[i] += rand();
    }
}

enum format_t {
    FMT = 1,
    FMT_1 = 2
};

int main()
{
    int val = rand()%10;
    int num_graphs = 5;

    struct graph g;
    struct result res;

    uint64_t *count;
    if (val & FMT) {
        count = malloc(sizeof(uint64_t)*num_graphs);
    }

    int i;
    for (i=0; i<num_graphs; i++) {
        next_graph (&g);
        computational_search (&g, &res);
        if (val & FMT) {
            count[i] = res.res; /* ERROR HERE */
        }
    }
    return 0;
}

我知道有一条执行路径count未初始化,但未在那里使用。优化器可以做一些可能使用count未初始化的内容吗?

使用gcc -Wall -O1 test.c的汇编在gcc 5.4.0上输出:

test.c: In function ‘main’:
test.c:43:15: warning: ‘count’ may be used uninitialized in this function [-Wmaybe-uninitialized]
     uint64_t *count;
               ^

-O2-O3也是如此,但-O0却没有。

2 个答案:

答案 0 :(得分:2)

编译器显示正确。它没有注意到你对两个用例都有相同的if语句,所以后者与前者分开处理它被视为声明一个没有内存的指针然后使用它就好像它后面有内存一样。

uint64_t *count;
...
        if (val & FMT) {
            count[i] = res.res; /* ERROR HERE */
        }

gcc并不像你想象的那样聪明。

修改

多年来gcc一直没有抱怨过这个

unsigned int fun ( unsigned int x )
{
    switch(x&3)
    {
        case 0: return(x);
        case 1: return(x<<1);
        case 2: return(x>>1);
        case 3: return(x+1);
    }
    return(3);
}

但是如果你在转换后没有回复,过去常常抱怨。坚持你输入无法达到的死代码,然后在你做的时候抱怨。没有胜利的情况。

尝试使用5.4.0对抗x86并且它不会抱怨任何一种方式。尝试用7.2.0反对武装,它不会抱怨任何一种方式。

很高兴看到他们删除了至少一个问题,但希望同时看到这两个问题。

我发现了其他优化错误,并希望将来能够找到更多错误。欢迎您尝试将其作为错误提交,并查看他们所说的内容。

答案 1 :(得分:1)

让我直截了当地说:-Wmaybe-uninitialized很糟糕。 GCC实际上尝试做的是首先执行大量的优化转换,然后检查变量的每个使用是否都在初始化之前(希望优化器足够智能以消除所有不可能的路径)。 IIRC它还有一些简单的启发式方法。通过这种方法,您应该会出现误报。

例如在-O3,编译器应该在main中取消切换循环并将其转换为如下所示:

if (val & FMT) {
    for (i=0; i<num_graphs; i++) {
        next_graph (&g);
        computational_search (&g, &res);
        count[i] = res.res;
    }
} else {
    for (i=0; i<num_graphs; i++) {
        next_graph (&g);
        computational_search (&g, &res);
    }
}

然后它应该注意到我们已经有val & FMT检查并合并相应的分支:

if (val & FMT) {
    count = malloc(sizeof(uint64_t)*num_graphs);
    for (i=0; i<num_graphs; i++) {
        next_graph (&g);
        computational_search (&g, &res);
        count[i] = res.res;
    }
} else {
    for (i=0; i<num_graphs; i++) {
        next_graph (&g);
        computational_search (&g, &res);
    }
}

涉及未初始化使用的路径已不复存在。现在,回到现实:不开关(由于某种原因)。如果你添加-fno-inline(注意:它只是一个测试,我并没有提出它作为解决方案),它确实发生了,一切都按照我之前的描述工作(没有警告)。恕我直言,这个警告是非常脆弱和不可预测的。

  

优化器可以做一些可能使用count的东西   未初始化?

不,除非它有错误。