Creo que encontré un error en GCC al implementar PCG PRNG de O'Neill. ( Código inicial en el Explorador del compilador de Godbolt )
Después de multiplicar oldstate
por MULTIPLIER
, (resultado almacenado en rdi), GCC no agrega ese resultado a INCREMENT
, movabs'ing INCREMENT
a rdx, que luego se usa como el valor de retorno de rand32_ret.state
Un ejemplo mínimo reproducible ( 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;
}
Ensamblaje generado (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
Curiosamente, la modificación de la estructura para tener el uint64_t como primer miembro produce el código correcto , al igual que el cambio de ambos miembros para que sea uint64_t
x86-64 System V devuelve estructuras de menos de 16 bytes en RDX: RAX, cuando se pueden copiar trivialmente. En este caso, el segundo miembro está en RDX porque la mitad alta de RAX es el relleno para la alineación o .b
cuando .a
es un tipo más estrecho. ( sizeof(retstruct)
es 16 de cualquier manera; no lo estamos utilizando, __attribute__((packed))
por lo que respeta alignof (uint64_t) = 8.)
¿Este código contiene algún comportamiento indefinido que permitiría a GCC emitir el ensamblado "incorrecto"?
Si no, esto debería ser informado en https://gcc.gnu.org/bugzilla/