Clang为什么不优化这段代码?

7
考虑我在这个问题中找到的这个函数:
void to_bytes(uint64_t const& x, uint8_t* dest) {
    dest[7] = uint8_t(x >> 8*7);
    dest[6] = uint8_t(x >> 8*6);
    dest[5] = uint8_t(x >> 8*5);
    dest[4] = uint8_t(x >> 8*4);
    dest[3] = uint8_t(x >> 8*3);
    dest[2] = uint8_t(x >> 8*2);
    dest[1] = uint8_t(x >> 8*1);
    dest[0] = uint8_t(x >> 8*0);
}

由于变量 xdest 可能指向同一块内存,编译器不允许将其优化为单个 qword 移动(每行可能会更改 x 的值)。
到目前为止还好。
但是,如果您通过值传递 x,则这个参数不再有效。事实上,GCC 将其优化为一个简单的 mov 指令,与预期相同:https://godbolt.org/z/iYj1or 然而,clang 没有这样做:https://godbolt.org/z/Hgg5z9 我认为,由于不能保证 x 占用任何堆栈内存,任何在调用函数之前使 dest 指向 x 的尝试都将导致未定义的行为,因此编译器可以假设这永远不会发生。这意味着 clang 在这里错过了一些机会。但我不确定。有人可以澄清吗?

对齐是首要考虑的问题。不确定是否可以使用未对齐的qword mov。 - nwp
1
dest 不能指向本地变量,因为它还不存在。可能是漏报了一个优化错误。 - M.M
你是否在问为什么Clang没有进行一些/全部GCC所做的优化?可能是因为这并不是必需的。 - Öö Tiib
@ÖöTiib 我在询问Clang是否只是错过了这个机会,或者我是否错过了一些边缘情况,其中“dest”可以有效地指向“x”,因此Clang不优化这个(相反,这意味着gcc存在错误)。 - sebrockm
对于 C 和 C++17 之前的版本,dest[...]xx>>... 中是无序的,因此如果它们引用同一对象,则会导致未定义行为。因此,在 C++17 之前进行优化时,x 应该是一个引用。 - Oliv
显示剩余3条评论
1个回答

2

你提供的代码过于复杂了。可以用以下代码替换:

最初的回答

void to_bytes(uint64_t x, uint8_t* dest) {
    x = htole64(x);
    std::memcpy(dest, &x, sizeof(x));
}

是的,这里使用了Linux特有的函数htole64(),但如果你在其他平台上,可以轻松地重新实现它。Clang和GCC可以在小端和大端平台上完美地优化这个函数。
最初的回答:此处使用了Linux特有的函数htole64(),但如果您在其他平台上,可以轻松地重新实现它。Clang和GCC可以在小端和大端平台上完美地优化此函数。

好吧,如果我必须重新实现它,最终会得到我发布的代码 :) 但很高兴知道这样做clang可以看到优化机会。 - sebrockm
在Windows上,它简单地是 htonll / ntohll - phuclv

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接