Creo que encontré un error en GCC al implementar PCG PRNG de O'Neill. ( Código inicial en Compiler Explorer de Godbolt )
Después de multiplicar oldstate por MULTIPLIER (resultado almacenado en rdi), GCC no agrega ese resultado a INCREMENT , sino que mueve 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; }Asamblea generada (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 computedCuriosamente, modificar la estructura para tener uint64_t como el primer miembro produce el código correcto , al igual que cambiar ambos miembros para que sean uint64_t
x86-64 System V devuelve estructuras de menos de 16 bytes en RDX:RAX, cuando son trivialmente copiables. En este caso, el segundo miembro está en RDX porque la mitad superior 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 estamos usando __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"?
De lo contrario, esto debería informarse en https://gcc.gnu.org/bugzilla/
¿Este código contiene algún comportamiento indefinido que permitiría a GCC emitir el ensamblado "incorrecto"?
El comportamiento del código presentado en la pregunta está bien definido con respecto a los estándares del lenguaje C C99 y posteriores. En particular, C permite que las funciones devuelvan valores de estructura sin restricciones.
No veo ningún UB aquí; sus tipos no están firmados, por lo que el UB de desbordamiento firmado es imposible, y no hay nada extraño. (E incluso si está firmado, tendría que producir salidas correctas para las entradas que no causen un desbordamiento de UB, como rdi=1 ). También está roto con el front-end C++ de GCC.
Además, GCC8.2 lo compila correctamente para AArch64 y RISC-V (a una instrucción madd después de usar movk para construir constantes, o RISC-V mul y agregar después de cargar las constantes). Si GCC estaba encontrando UB, generalmente esperaríamos que lo encontrara y rompiera su código para otros ISA también, al menos aquellos que tienen anchos de tipo y anchos de registro similares.
Clang también lo compila correctamente.
Esto parece ser una regresión de GCC 5 a 6; GCC5.4 compila correctamente, 6.1 y versiones posteriores no. ( Golpe de Dios ).
Puede informar esto en bugzilla de GCC usando el MCVE de su pregunta.
Realmente parece que es un error en el manejo de retorno de estructura x86-64 System V, tal vez de estructuras que contienen relleno. Eso explicaría por qué funciona cuando se inserta y cuando se amplía a uint64_t (evitando el relleno).
Esto ha sido corregido en trunk / master .
Aquí está el compromiso relevante .
Y este es un parche para solucionar el problema.
Basado en un comentario en el parche, la función reload_combine_recognize_pattern intentaba ajustar USE insns .