Wydaje mi się, że znalazłem błąd w GCC podczas wdrażania PCN PRNG O'Neilla. ( Kod początkowy w Godbolt's Compiler Explorer )
Po pomnożeniu oldstate
przez MULTIPLIER
(wynik przechowywany w rdi), GCC nie dodaje tego wyniku do INCREMENT
movabs'ing INCREMENT
zamiast do rdx, który następnie jest wykorzystywany jako wartość zwracana rand32_ret.state
Minimalny odtwarzalny przykład ( Eksplorator kompilatorów ):
#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;
}
Wygenerowany zespół (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
Co ciekawe, modyfikacja struktury tak, aby miała uint64_t jako pierwszy element, powoduje wygenerowanie poprawnego kodu , podobnie jak zmiana obu elementów na uint64_t
x86-64 System V zwraca struktury mniejsze niż 16 bajtów w RDX: RAX, gdy można je w prosty sposób skopiować. W tym przypadku drugi element jest w RDX, ponieważ górna połowa RAX jest wypełnieniem do wyrównania lub .b
gdy .a
jest węższy. ( sizeof(retstruct)
ma 16 w obu kierunkach; nie korzystamy, __attribute__((packed))
więc respektuje alignof (uint64_t) = 8.)
Czy ten kod zawiera jakieś niezdefiniowane zachowanie, które pozwoliłoby GCC na wysłanie „niepoprawnego” zestawu?
Jeśli nie, należy to zgłosić na https://gcc.gnu.org/bugzilla/