Saya yakin saya menemukan bug di GCC saat menerapkan O'Neill's PCG PRNG. ( Kode awal pada Penjelajah Kompiler Godbolt )
Setelah dikalikan oldstate
dengan MULTIPLIER
, (hasil disimpan dalam rdi), GCC tidak menambahkan hasil itu ke INCREMENT
, INCREMENT
sebagai gantinya pindah ke rdx, yang kemudian digunakan sebagai nilai pengembalian rand32_ret.state
Contoh direproduksi minimum ( Penjelajah Kompiler ):
#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;
}
Rakitan yang dihasilkan (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
Menariknya, memodifikasi struct untuk memiliki uint64_t sebagai anggota pertama menghasilkan kode yang benar , seperti halnya mengubah kedua anggota menjadi uint64_t
x86-64 Sistem V tidak mengembalikan struct lebih kecil dari 16 byte di RDX: RAX, ketika mereka sepele disalin. Dalam hal ini anggota ke-2 berada di RDX karena bagian tinggi dari RAX adalah padding untuk perataan atau .b
ketika .a
adalah tipe yang lebih sempit. (dengan sizeof(retstruct)
16 cara; kami tidak menggunakan __attribute__((packed))
sehingga menghormati alignof (uint64_t) = 8.)
Apakah kode ini mengandung perilaku tidak terdefinisi yang memungkinkan GCC memancarkan unit "salah"?
Jika tidak, ini harus dilaporkan di https://gcc.gnu.org/bugzilla/