Ich glaube, ich habe bei der Implementierung von O'Neills PCG PRNG einen Fehler in GCC gefunden. ( Anfangscode im Compiler Explorer von Godbolt )
Nach dem Multiplizieren oldstate
mitMULTIPLIER
, (Ergebnis in RDI gespeichert), GCC trägt nicht das Ergebnis an INCREMENT
, movabs'ing INCREMENT
stattdessen RDX, die dann als der Rückgabewert von rand32_ret.state verwendet wird
Ein minimal reproduzierbares Beispiel ( 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;
}
Generierte Baugruppe (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
Interessanterweise führt das Ändern der Struktur so, dass uint64_t das erste Mitglied ist, zu korrektem Code , ebenso wie das Ändern beider Elemente in uint64_t
x86-64 System V gibt in RDX: RAX Strukturen zurück, die kleiner als 16 Byte sind, wenn sie trivial kopierbar sind. In diesem Fall befindet sich das 2. Element in RDX, da die hohe Hälfte von RAX die Polsterung für die Ausrichtung ist oder .b
wenn .a
es sich um einen schmaleren Typ handelt. ( sizeof(retstruct)
ist so oder so 16; wir verwenden es nicht, __attribute__((packed))
daher wird alignof (uint64_t) = 8 berücksichtigt.)
Enthält dieser Code ein undefiniertes Verhalten, das es GCC ermöglichen würde, die "falsche" Assembly auszugeben?
Wenn nicht, sollte dies unter https://gcc.gnu.org/bugzilla/ gemeldet werden.