Tôi tin rằng tôi đã tìm thấy một lỗi trong GCC khi triển khai PCG PRNG của O'Neill. ( Mã ban đầu trên Godbolt's Compiler Explorer )
Sau khi nhân oldstate
với MULTIPLIER
, (kết quả được lưu trữ trong rdi), GCC không thêm kết quả đó vào INCREMENT
, INCREMENT
thay vào đó, hãy chuyển sang ndx, sau đó được sử dụng làm giá trị trả về của rand32_ret.state
Một ví dụ tái sản xuất tối thiểu ( Trình biên dịch 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;
}
Lắp ráp được tạo (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
Thật thú vị, sửa đổi cấu trúc để có uint64_t là thành viên đầu tiên tạo mã chính xác , cũng như thay đổi cả hai thành viên thành uint64_t
x86-64 System V không trả về các cấu trúc nhỏ hơn 16 byte trong RDX: RAX, khi chúng có thể sao chép một cách tầm thường. Trong trường hợp này, thành viên thứ 2 nằm trong RDX vì một nửa RAX cao là phần đệm để căn chỉnh hoặc .b
khi nào .a
là loại hẹp hơn. ( sizeof(retstruct)
là 16 theo cách nào đó; chúng tôi không sử dụng __attribute__((packed))
vì vậy nó tôn trọng alignof (uint64_t) = 8.)
Mã này có chứa bất kỳ hành vi không xác định nào sẽ cho phép GCC phát ra cụm "không chính xác" không?
Nếu không, điều này sẽ được báo cáo trên https://gcc.gnu.org/ormszilla/