ฉันเชื่อว่าฉันพบข้อผิดพลาดใน GCC ในขณะที่ใช้ PCG PRNG ของ O'Neill ( รหัสเริ่มต้นในคอมไพเลอร์ Explorer ของ Godbolt )
หลังจากการคูณoldstate
ด้วยMULTIPLIER
(ผลลัพธ์ที่เก็บไว้ใน rdi) GCC จะไม่เพิ่มผลลัพธ์INCREMENT
นั้นย้ายINCREMENT
ไปยัง rdx แทนซึ่งจะถูกใช้เป็นค่าส่งคืนของ rand32_ret.state
ตัวอย่างที่ทำซ้ำได้ขั้นต่ำ ( 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;
}
ชุดประกอบที่สร้างขึ้น (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
ที่น่าสนใจคือการปรับเปลี่ยนโครงสร้างเพื่อให้ uint64_t เป็นสมาชิกคนแรกสร้างรหัสที่ถูกต้องเช่นเดียวกับการเปลี่ยนสมาชิกทั้งสองให้เป็น uint64_t
x86-64 System V ส่งคืนโครงสร้างที่เล็กกว่า 16 ไบต์ใน RDX: RAX เมื่อคัดลอกได้เล็กน้อย ในกรณีนี้สมาชิกที่ 2 อยู่ใน RDX เพราะครึ่งหนึ่งของ RAX ที่สูงคือ padding สำหรับการจัดตำแหน่งหรือ.b
เมื่อ.a
เป็นชนิดที่แคบกว่า ( sizeof(retstruct)
เป็น 16 ทางใดทางหนึ่งเราไม่ได้ใช้งาน__attribute__((packed))
เพื่อให้สอดคล้องกับ alignof (uint64_t) = 8. )
รหัสนี้มีพฤติกรรมที่ไม่ได้กำหนดซึ่งจะทำให้ GCC ปล่อยแอสเซมบลี "ไม่ถูกต้อง" หรือไม่?
หากไม่เป็นเช่นนี้ควรรับรายงานในhttps://gcc.gnu.org/bugzilla/