Ich glaube, ich habe bei der Implementierung von O’Neills PCG PRNG einen Fehler in GCC gefunden. (Ursprünglicher Code im Compiler Explorer von Godbolt)
Nach dem Multiplizieren oldstate
durch MULTIPLIER
(Ergebnis in rdi gespeichert), fügt GCC dieses Ergebnis nicht hinzu INCREMENT
movabs’ing INCREMENT
stattdessen an rdx, das dann als 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 Assembly (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 wird die Struktur so geändert, dass uint64_t das erste Mitglied ist erzeugt korrekten Codeebenso wie Ändern Sie beide Mitglieder in uint64_t
x86-64 System V gibt Strukturen kleiner als 16 Byte in RDX:RAX zurück, wenn sie trivial kopierbar sind. In diesem Fall befindet sich das 2. Mitglied in RDX, da die obere Hälfte von RAX die Polsterung für die Ausrichtung oder ist .b
Wenn .a
ist ein schmaler Typ. (sizeof(retstruct)
ist so oder so 16; wir verwenden nicht __attribute__((packed))
es respektiert also alignof(uint64_t) = 8.)
Enthält dieser Code ein undefiniertes Verhalten, das es GCC ermöglichen würde, die “falsche” Assembly auszugeben?
Wenn nicht, sollte dies gemeldet werden https://gcc.gnu.org/bugzilla/
Kommentare sind nicht für längere Diskussionen gedacht; diese Konversation wurde in den Chat verschoben.
– Samuel Liew
♦
24. Januar 2020 um 5:06 Uhr