为什么g ++不优化局部数组而是优化全局数组?

时间:2019-07-19 17:38:41

标签: c++ g++ compiler-optimization

我有以下两个功能基本相同:

enum Direction{
    N = 0,
    NW,
    W,
    SW,
    S,
    SE,
    E,
    NE,
    TOTAL_DIRS
};

char const * const strings[] = {"N", "NW", "W", "SW", "S", "SE", "E", "NE"};

char const *
getDirString2(unsigned dir) {
    if (TOTAL_DIRS > dir)
        return strings[dir];
    return nullptr;
}

char const *
getDirString3(unsigned dir) {
    char const * const strings[] = {"N", "NW", "W", "SW", "S", "SE", "E", "NE"};
    if (TOTAL_DIRS > dir)
        return strings[dir];
    return nullptr;
}

但是,尽管g ++像我期望的那样优化了使用全局数组的函数。它为替代方案创建了更多复杂的代码。 Clang为两者创建相同的代码,如果我改用switch语句,则clang和c ++也会创建与getDirString2相同的代码。

这里是编译器资源管理器https://godbolt.org/z/GxvrTv

的链接

这是我应该为g ++提交错误报告的东西吗?还是有充分的理由呢?

2 个答案:

答案 0 :(得分:3)

我想您可以称这为错过的优化,尽管这对gcc家伙来说有点苛刻。

gcc在编译getDirString3时,完全按照您的要求进行操作-在堆栈上构造一个字符串数组,然后仅返回其中的一个元素。

另一方面,

clang看到此数组从不更改,而是将其构造在静态存储中,请参见:https://godbolt.org/z/24n-N7

要使gcc像clang一样生成代码,请将getDirString3中的数组声明为static(首先是个好主意),请参见:https://godbolt.org/z/henD2Z

答案 1 :(得分:0)

注意:首先,我说这是不符合标准的优化,但是在评论中与Chris Dodd进行讨论之后,我意识到这在一定程度上是不正确的。

更新后的答案:

我应该为此提交g ++错误报告吗?还是有充分的理由?

clang和gcc都可以进行相同的优化,但是默认情况下在gcc中禁用了它。因此,这不是错误,并且有原因。

c做了什么?

在您的示例中,编译器看到本地数组是常量,因此,每次调用该函数时都不需要在堆栈上构造它。

如何在gcc中启用相同的优化?

要在gcc中启用相同的优化,请使用标志-fmerge-all-constants。结果代码的大小将与clang的代码几乎相同(请参见:https://godbolt.org/z/Ldx_qe):

getDirString3(unsigned int):
        xor     eax, eax
        cmp     edi, 7
        ja      .L1
        mov     edi, edi
        mov     rax, QWORD PTR strings.2080[0+rdi*8]

为什么默认情况下在gcc中将其禁用?

从gcc文档website

  

-fmerge-all-constants ...类似C或C ++的语言需要每个变量,包括在同一变量中的多个实例   递归调用以具有不同的位置,因此使用此选项   导致行为不合格。

在上面的示例中,优化是安全的,并且遵循as-if rule,因为未使用此本地数组的地址,也未将其与来自不同调用的地址进行比较。但是,这种优化似乎在某些情况下会导致不冲突的行为。您可以检查以下情况:herehere

那么,您应该使用-fnmerge-all-constants标志吗?

两个编译器都不会检查天气是否使用了常量地址,因此我不建议这样做。在您的示例中显而易见,优化是安全的,但是您无法每次都知道编译器将在何处进行优化(否则您将自己完成优化),并且不确定该优化有时不会导致非冲突行为。