Je crois avoir trouvé un bogue dans GCC lors de l'implémentation du PCG PRNG d'O'Neill. ( Code initial sur Godbolt's Compiler Explorer )
Après la multiplication oldstate
par MULTIPLIER
, (résultat stocké dans rdi), GCC n'ajoute pas ce résultat à INCREMENT
, movabs'ing INCREMENT
à rdx à la place, qui est ensuite utilisé comme valeur de retour de rand32_ret.state
Un exemple reproductible minimum ( Explorateur du compilateur ):
#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;
}
Assemblage généré (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
Fait intéressant, la modification de la structure pour avoir le uint64_t comme premier membre produit le code correct , tout comme le changement des deux membres pour être uint64_t
x86-64 System V renvoie des structures inférieures à 16 octets dans RDX: RAX, lorsqu'elles sont copiables de manière triviale. Dans ce cas, le 2ème membre est en RDX parce que la moitié haute de RAX est le rembourrage pour l'alignement ou .b
quand .a
est un type plus étroit. ( sizeof(retstruct)
est 16 de toute façon; nous n'utilisons pas, __attribute__((packed))
donc il respecte alignof (uint64_t) = 8.)
Ce code contient-il un comportement non défini qui permettrait à GCC d'émettre l'assembly "incorrect"?
Sinon, cela devrait être signalé sur https://gcc.gnu.org/bugzilla/