Я полагаю, что обнаружил ошибку в GCC при реализации PCG PRNG О'Нила. ( Исходный код в проводнике компилятора Годболта )
После умножения oldstate
на MULTIPLIER
(результат сохраняется в rdi), GCC не добавляет этот результат INCREMENT
, перемещая INCREMENT
вместо него значение rdx, которое затем используется как возвращаемое значение rand32_ret.state.
Минимальный воспроизводимый пример ( Compiler Explorer ):
#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;
}
Созданная сборка (GCC 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, когда они тривиально копируемы. В этом случае 2-й элемент находится в RDX, потому что верхняя половина RAX является отступом для выравнивания или .b
когда .a
используется более узкий тип. ( sizeof(retstruct)
16 в любом случае; мы не используем, __attribute__((packed))
поэтому он учитывает alignof (uint64_t) = 8.)
Содержит ли этот код какое-либо неопределенное поведение, которое позволило бы GCC выдавать «неправильную» сборку?
Если нет, об этом следует сообщить на https://gcc.gnu.org/bugzilla/