Я полагаю, что обнаружил ошибку в G CC при реализации PCG PRNG О'Нила. ( Исходный код в проводнике компилятора Годболта )
После умножения oldstate
на MULTIPLIER
(результат сохраняется в rdi), G CC не добавляет этот результат к INCREMENT
, вместо * movabs'ing INCREMENT
в rdx, который затем используется как возвращаемое значение rand32_ret.state
Минимальный воспроизводимый пример ( Проводник компилятора ):
#include <stdint.h>
struct retstruct {
uint32_t a;
uint64_t b;
};
struct retstruct fn(uint64_t input)
{
struct retstruct ret;
ret.a = 0;
ret.b = input * 11111111111 + 111111111111;
return ret;
}
Сгенерированная сборка (G CC 9.2, x86_64, -O3):
fn:
movabs rdx, 11111111111 # multiplier constant (doesn't fit in imm32)
xor eax, eax # ret.a = 0
imul rdi, rdx
movabs rdx, 111111111111 # add constant; one more 1 than multiplier
# missing add rdx, rdi # ret.b=... that we get with clang or older gcc
ret
# returns RDX:RAX = constant 111111111111 : 0
# independent of input RDI, and not using the imul result it just computed
Интересно, что при изменении структуры на uint64_t в качестве первого элемента будет получен правильный код, так же как изменяя оба члена на uint64_t
x86-64 System V действительно возвращает структуры размером менее 16 байт в RDX: RAX, когда они тривиально копируются. В этом случае второй член находится в RDX, потому что верхняя половина RAX является отступом для выравнивания или .b
, когда .a
является более узким типом. (sizeof(retstruct)
равно 16 в любом случае; мы не используем __attribute__((packed))
, поэтому он учитывает alignof (uint64_t) = 8.)
Содержит ли этот код какое-либо неопределенное поведение, которое позволило бы G CC чтобы испустить "неправильную" сборку?
Если нет, об этом должно быть сообщено https://gcc.gnu.org/bugzilla/