【问题标题】:Is it a gcc -O2 optimization bug (different result from -O1)?它是 gcc -O2 优化错误(与 -O1 的结果不同)吗?
【发布时间】:2020-08-05 18:58:01
【问题描述】:

我写了一个非常简单的程序,没有-O2它表现正常:

#include <stdio.h>
#include <stdint.h>

int main()
{
    uint32_t A[4] = { 1, 2, 3, 4 };
    float B[4] = { 0, 0, 0, 0 };
    float C[4] = { 5, 6, 7, 8 };
    int i;

    // convert integer A to float B
    for (i = 0; i < 4; i++) 
        B[i] = (float)A[i];

    // memory copy from B to C
    uint32_t *src = (uint32_t*)(B);
    uint32_t *dst = (uint32_t*)(C);
    dst[0] = src[0];
    dst[1] = src[1];
    dst[2] = src[2];
    dst[3] = src[3];

#if 0
    // open this to correct the error
    __asm__("":::"memory");
#endif

    // print C, C should be [1.0, 2.0, 3.0, 4.0]
    for (i = 0; i < 4; i++) 
        printf("%f\n", C[i]);

    return 0;
}

不使用-O2编译:

$ gcc error.c -o error
$ ./error
1.0000
2.0000
3.0000
4.0000

它按预期工作。但是如果我添加了-O2:

$ gcc -O2 error.c -o error
$ ./error
-6169930235904.000000
0.000000
-6169804406784.000000
0.000000

另外,如果您将#if 0 切换为#if 1,它会再次正常工作。 asm ("":::"memory") 在同一个线程中应该是不必要的。

-O2优化的bug吗??

有什么我可以告诉编译器照顾它的吗?我有一个将 xmm 寄存器存储到 (void*) 指针的函数,例如:

inline void StoreRegister(void *ptr, const __m128& reg)
{
#if DONT_HAVE_SSE
    const uint32_t *src = reinterpret_cast<const uint32_t*>(&reg);
    uint32_t *dst = reinterpret_cast<uint32_t*>(ptr);
    dst[0] = src[0];
    dst[1] = src[1];
    dst[2] = src[2];
    dst[3] = src[3];
#else
    _mm_storeu_si128(reinterpret_cast<__m128*>(ptr), _mm_castps_si128(reg));
#endif
}

dst 是上面代码中的C,任何方法都可以在不修改函数签名的情况下使其正确。

【问题讨论】:

  • 看起来像是严格的别名违规导致的未定义行为
  • uint32_t *src = (uint32_t*)(B); uint32_t *dst = (uint32_t*)(C); 违反了严格的别名规则。
  • 一般经验法则:如果您看到“优化错误”,则很可能是代码中未定义的行为。
  • 使用std::copy (C++) 或std:: memcpy (C/C++)。
  • gcc -Q -v -O2 main.cpp 详细显示 gcc 正在使用的优化标志。注意:并非-O2 下的所有优化都有相应的单独标志。 -O0 禁用所有优化,除了 C 标准或 C++ 标准要求的优化。 (因为代码具有未定义的行为而禁用优化只是一种创可贴,而不是良好的编程习惯。)

标签: c++ c gcc optimization


【解决方案1】:

不,这不是编译器错误的表现。

由于您使用了(uint32_t*)(B) &c 的转换结果,您的代码的行为是未定义。这违反了严格别名

编译器——尤其是 gcc——在处理未定义的结构时变得越来越激进。标准允许它们假设未定义的行为不会发生,并且可以删除包含它的任何分支。

【讨论】:

  • 有什么我可以告诉编译器注意的吗?我有一个将 xmm 寄存器存储到 (void*) 指针的函数,这在我的问题中是 B。
  • @skywind3000 std::copy 来救援!
  • memcpy,因为尽管有 C++ 标签,这似乎是 C 代码。
猜你喜欢
  • 1970-01-01
  • 2015-01-15
  • 1970-01-01
  • 2020-12-11
  • 2010-12-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多