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

Why does g++ not optimize a local array but a global?

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

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.

相同的代码

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

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

我想你可以称之为优化失误,尽管这对 gcc 人员来说有点苛刻。

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

clang,另一方面,看到这个数组永远不会改变并在静态存储中构造它,参见:https://godbolt.org/z/24n-N7

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

注意:一开始我说这是一个non-conforming的优化,但是在评论中和Chris Dodd讨论后,我意识到这部分不正确。

更新后的答案:

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

clang和gcc都可以做同样的优化,但是gcc默认是禁用的。所以这不是错误,这是有原因的。

clang做了什么?

在您的示例中,编译器发现局部数组是常量,因此每次调用函数时都不需要在堆栈上构造它。

如何在 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 ... Languages like C or C++ require each variable, including multiple instances of the same variable in recursive calls, to have distinct locations, so using this option results in non-conforming behavior.

在上面的例子中,优化是安全的,遵循了 as-if rule, because the address of this local array wasn't used or compared with an address from different invocation. However, it seems that this optimization can lead in some cases to non-confroming behavior. You can check these cases: here and here.

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

两个编译器都不检查常量地址是否被使用,所以我不推荐它。在您的示例中很明显优化是安全的,但是您无法每次都知道您的编译器将在哪里优化(否则您会自己完成)并且您无法确定它有时不会导致non-confroming 行为。