这个clang优化是一个错误吗?

时间:2018-04-10 19:11:21

标签: c optimization clang compiler-optimization

在使用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转到{{1 }}

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

我使用以下版本的clang:

-O2

2 个答案:

答案 0 :(得分:14)

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替换为1ULLunsigned long long至少为64位,并且可以移动那么远。

答案 1 :(得分:8)

i在循环中为15时,2*i+2为32,并且您将unsigned int移位unsigned int中的位数,这是未定义的。

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

printf的正确uint64_t格式为get_morton_code(99,159,46) = %" PRIu64 "\n"PRIu64标头中定义了<inttypes.h>