Я вважаю, що знайшов помилку в GCC під час впровадження програми PCG PRNG O'Neill. ( Початковий код у Провіднику компілятора Godbolt )
Після множення oldstate
на MULTIPLIER
(результат, збережений у rdi), GCC не додає цього результату INCREMENT
, INCREMENT
замість цього, movabs'ing в 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;
}
Створена збірка (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 Система V дійсно повертає структури RDX: RAX, менші ніж 16 байт, коли вони тривіально копіюються. У цьому випадку 2-й член знаходиться в RDX, тому що висока половина RAX є підкладкою для вирівнювання або .b
коли .a
вужчий тип. ( sizeof(retstruct)
це 16 в будь-якому випадку; ми не використовуємо, __attribute__((packed))
тому він поважає alignof (uint64_t) = 8.)
Чи містить цей код якусь невизначену поведінку, яка дозволила б GCC випромінювати "неправильну" збірку?
Якщо ні, про це слід повідомити на https://gcc.gnu.org/bugzilla/