Есть ли причина, по которой Clang не оптимизирует этот код? - PullRequest
7 голосов
/ 13 мая 2019

Рассмотрим эту функцию, которую я нашел в этот вопрос :

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);
}

Поскольку x и dest могут указывать на одну и ту же память, компилятору не разрешено оптимизировать это за один ход qword (каждая строка может изменять значение x).

Пока все хорошо.

Но если вместо этого вы передадите x по значению, этот аргумент больше не будет сохраняться. И действительно, GCC оптимизирует это до простой mov инструкции, как и ожидалось: https://godbolt.org/z/iYj1or

Однако Clang не делает: https://godbolt.org/z/Hgg5z9

Я предполагаю, что, поскольку даже не гарантируется, что x вообще занимает какую-либо память стека, любая попытка dest указать на x до вызываемой функции будет приводит к неопределенному поведению, и, таким образом, компилятор может предположить, что этого просто никогда не произойдет. Это означало бы, что у кланга здесь не хватает возможности. Но я не уверен. Может кто-нибудь уточнить?

1 Ответ

2 голосов
/ 13 мая 2019

Код, который вы дали, слишком сложен. Вы можете заменить его на:

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

Да, здесь используется Linux-ism htole64(), но если вы работаете на другой платформе, вы можете легко переопределить это.

Clang и GCC прекрасно оптимизируют это как для платформ с младшим, так и для старшего байтов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...