【发布时间】:2020-05-09 06:56:20
【问题描述】:
我相信我在实施 O'Neill 的 PCG PRNG 时在 GCC 中发现了一个错误。 (Initial code on Godbolt's Compiler Explorer)
在将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 作为第一个成员 produces correct code,changing both members to be 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/ 上报告
【问题讨论】:
-
评论不用于扩展讨论;这个对话是moved to chat。
标签: c gcc assembly x86-64 compiler-bug