代码之家  ›  专栏  ›  技术社区  ›  user545424

这种叮当作响的优化是一个bug吗?

  •  4
  • user545424  · 技术社区  · 7 年前

    在OSX High Sierra上使用clang用-O3编译一些代码时,我遇到了一个有趣的问题。代码如下:

    #include <stdint.h>
    #include <limits.h> /* for CHAR_BIT */
    #include <stdio.h> /* for printf() */
    #include <stddef.h> /* for size_t */
    
    uint64_t get_morton_code(uint16_t x, uint16_t y, uint16_t z)
    {
        /* Returns the number formed by interleaving the bits in x, y, and z, also
         * known as the morton code.
         *
         * See https://graphics.stanford.edu/~seander/bithacks.html#InterleaveTableO
    bvious.
         */
        size_t i;
        uint64_t a = 0;
    
        for (i = 0; i < sizeof(x)*CHAR_BIT; i++) {
            a |= (x & 1U << i) << (2*i) | (y & 1U << i) << (2*i + 1) | (z & 1U << i)
     << (2*i + 2);
        }
    
        return a;
    }
    
    int main(int argc, char **argv)
    {
        printf("get_morton_code(99,159,46) = %llu\n", get_morton_code(99,159,46));
        return 0;
    }
    

    使用编译时 cc -O1 -o test_morton_code test_morton_code.c 我得到以下输出:

    get_morton_code(99,159,46) = 4631995
    

    这是正确的。但是,在使用编译时 cc -O3 -o test_morton_code test_morton_code.c :

    get_morton_code(99,159,46) = 4294967295
    

    这是错误的。

    奇怪的是,当从 -O2 -O3 而在上面的最小工作示例中,从 -O1 -氧气

    这是编译器优化中的一个bug,还是我做了一些愚蠢的事情,只有在编译器进行更积极的优化时才会出现?

    我正在使用以下版本的clang:

    snotdaqs-iMac:snoFitter snoperator$ cc --version
    Apple LLVM version 9.1.0 (clang-902.0.39.1)
    Target: x86_64-apple-darwin17.5.0
    Thread model: posix
    InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
    
    2 回复  |  直到 7 年前
        1
  •  14
  •   a3f    7 年前

    UndefinedBehaviorSanitizer 对于发现此类错误非常有帮助:

    $ clang -fsanitize=undefined -O3 o3.c
    $ ./a.out
    o3.c:19:2: runtime error: shift exponent 32 is too large for 32-bit type 'unsigned int'
    get_morton_code(99,159,46) = 4294967295
    

    可能的修复方法是更换 1U s与 1ULL unsigned long long 至少为64位,并且可以移动到该位置。

        2
  •  8
  •   Eric Postpischil    7 年前

    什么时候 i 循环中为15, 2*i+2 是32,您正在移动 unsigned int 无符号整型 ,未定义。

    显然,您打算在64位字段中工作,因此将移位的左侧转换为 uint64_t

    A适当的 printf 的格式 uint64\u t get_morton_code(99,159,46) = %" PRIu64 "\n" PRIu64 定义在 <inttypes.h> 头球。