我相信我在实现奥尼尔的 PCGPRNG 时在 GCC 中发现了一个错误
在将 oldstate
乘以 MULTIPLIER
(结果存储在 rdi 中)之后,GCC 不会将这个结果添加到 INCREMENT
,而是将 INCREMENT
移动到 rdx,然后使用 rdx 作为 rand32 _ ret 的返回值。国家
最小可重复示例(编译器资源管理器) :
#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
有趣的是,修改 struct 使 uint64 _ t 作为第一个成员 产生正确的代码,将两个成员都更改为 uint64 _ t也是如此
X86-64 System V 在 RDX: RAX 中返回小于16字节的结构,当它们可以轻松复制时。在这种情况下,第二个成员在 RDX 中,因为 RAX 的高半部分是用于对齐的填充,或者当 .a
是较窄的类型时,用于填充 .b
。(sizeof(retstruct)
是16; 我们没有使用 __attribute__((packed))
,所以它尊重 alignof (uint64 _ t) = 8。)
这段代码是否包含任何未定义的行为,从而允许 GCC 发出“不正确”程序集?
如果没有,这应该得到报告的 https://gcc.gnu.org/bugzilla/