GCC 优化忽略循环条件

Posted

技术标签:

【中文标题】GCC 优化忽略循环条件【英文标题】:GCC optimalizations ignore for loop condition 【发布时间】:2021-07-05 15:17:32 【问题描述】:

所以我写了这段代码:

#include <iostream>

constexpr
int fibonacci (int n) 
    int a = 0;
    int b = 1;

    for(auto i = 0; i < n; i++) 
        b += a;
        a = b - a;
    
    return b;


template<int N, int (T)(int)>
struct array 
    using type = decltype(T(0));

    constexpr array() : arr() 
        for (auto i = 0; i < N; ++i) 
            arr[i] = T(i);
        
    
    const type &operator[](int i) const  return arr[i]; 

private:
    type arr[N];
;

int main() 
    constexpr auto x = array<10, fibonacci>();

    for (int i = 0; i < 11; i++) 
        std::cout << i << " " << x[i] << std::endl;
    


如果没有优化,它会按预期工作,打印 11 个值,最后一个是随机值。但是,一旦我移至 -O2,我就会随机获得一长串数字,其中包含崩溃和分段错误。 我在 godbolt.org (https://godbolt.org/z/4MqjbPbxE) 上检查了这个结果,它似乎不是问题,例如,clang。

我的问题是,这是 gcc 中的错误吗?为什么优化会删除/不检查 for 循环中的条件?

【问题讨论】:

索引越界是 UB。在不同的优化级别得到不同的结果是 UB 一个完全合理的结果。 array&lt;10, fibonacci&gt; 让我很困惑。我以为它应该是std::array 如果 i &gt;= 10 那么你有未定义的行为。允许编译器注意到这一点,并假设i &lt; 10 始终为真。有了这个假设,i &lt; 11 就是true 为什么会做出这样的假设?速度。它需要测试的东西少了一件。程序要做的越少,它就越快。您的代码不是计算机要执行的指令列表。它是对行为的描述。编译器的工作是接受该行为并生成指令列表。优化级别越高,编译器将花费更多时间寻找更快的方法来产生所描述的行为,并且它会在不改变行为的情况下对您的代码做任何想做的事情。如果你描述的行为甚至有点错误...... 【参考方案1】:

您正在通过使用超出范围的x[i] 来调用未定义的行为

再分配一个以避免超出范围的访问。

换句话说,

    constexpr auto x = array<10, fibonacci>();

应该是

    constexpr auto x = array<11, fibonacci>();

定义常量以避免拼写错误更好:

int main() 
    constexpr int num = 11;
    constexpr auto x = array<num, fibonacci>();

    for (int i = 0; i < num; i++) 
        std::cout << i << " " << x[i] << std::endl;
    

【讨论】:

【参考方案2】:

修复检查索引。

for (int i = 0; i ; i++)

【讨论】:

以上是关于GCC 优化忽略循环条件的主要内容,如果未能解决你的问题,请参考以下文章

为啥 GCC 没有尽可能地优化这组分支和条件?

优化器忽略过滤的索引条件

使向量大小()脱离循环条件进行优化

Javascript - IE8 忽略条件

循环中的常量条件:编译器优化

条件分支和循环