Credo di aver trovato un bug in GCC durante l'implementazione del PRNG PCG di O'Neill. ( Codice iniziale su Godbolt's Compiler Explorer )
Dopo aver moltiplicato oldstate
per MULTIPLIER
, (risultato memorizzato in rdi), GCC non aggiunge quel risultato a INCREMENT
, INCREMENT
movabs'ing invece a rdx, che viene quindi utilizzato come valore di ritorno di rand32_ret.state
Un esempio riproducibile minimo ( 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;
}
Assemblea generata (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
È interessante notare che modificando la struttura in modo che uint64_t come primo membro produca il codice corretto , così come cambiando entrambi i membri in uint64_t
x86-64 System V restituisce le strutture più piccole di 16 byte in RDX: RAX, quando sono banalmente copiabili. In questo caso il 2o membro è in RDX perché la metà alta di RAX è l'imbottitura per l'allineamento o .b
quando .a
è un tipo più stretto. ( sizeof(retstruct)
è 16 in entrambi i modi; non lo stiamo usando, __attribute__((packed))
quindi rispetta alignof (uint64_t) = 8.)
Questo codice contiene comportamenti indefiniti che consentirebbero a GCC di emettere l'assembly "errato"?
In caso contrario, questo dovrebbe essere segnalato su https://gcc.gnu.org/bugzilla/