从函数返回结构时可能出现 GCC 错误
Possible GCC bug when returning struct from a function
我相信我在实施 O'Neill 的 PCG PRNG 时在 GCC 中发现了一个错误。 (Initial code on Godbolt's Compiler Explorer)
将oldstate
乘以MULTIPLIER
后(结果存储在rdi中),GCC不会将该结果添加到INCREMENT
,将INCREMENT
移动到rdx相反,它随后被用作 rand32_ret.state
的 return 值
最小可重现示例 (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, as does changing both members to be uint64_t
x86-64 System V 在 RDX:RAX 中 return 小于 16 字节的结构,当它们可以被简单地复制时。在这种情况下,第二个成员在 RDX 中,因为 RAX 的高半部分是用于对齐的填充,或者 .b
当 .a
是较窄的类型时。 (sizeof(retstruct)
无论如何都是 16;我们没有使用 __attribute__((packed))
所以它尊重 alignof(uint64_t) = 8。)
此代码是否包含任何允许 GCC 发出 "incorrect" 程序集的未定义行为?
如果没有,应该在 https://gcc.gnu.org/bugzilla/
上报告
Does this code contain any undefined behaviour that would allow GCC to emit the "incorrect" assembly?
关于 C99 和更高版本的 C 语言标准,问题中给出的代码的行为得到了很好的定义。特别是,C 允许函数 return 结构值不受限制。
我在这里没有看到任何 UB;你的类型是无符号的,所以有符号溢出 UB 是不可能的,也没有什么奇怪的。 (即使签名,它也必须为 不会 导致溢出 UB 的输入产生正确的输出,例如 rdi=1
)。它也被 GCC 的 C++ 前端破坏了。
此外,GCC8.2 将其编译为 correctly for AArch64 and RISC-V(在使用 movk
构造常量后为 madd
指令,或在加载常量后为 RISC-V mul 和 add)。如果 GCC 发现的是 UB,我们通常希望它能找到它并破坏其他 ISA 的代码,至少是那些具有相似类型宽度和寄存器宽度的 ISA。
Clang 也能正确编译。
这似乎是从 GCC 5 到 6 的倒退; GCC5.4 编译正确,6.1 及更高版本则不正确。 (Godbolt).
您可以使用问题中的 MCVE 在 GCC's bugzilla 上报告此问题。
看起来确实像是 x86-64 System V struct-return 处理中的错误,可能是包含填充的结构。 这可以解释为什么它在以下情况下起作用内联,以及将 a
加宽到 uint64_t(避免填充)时。
这已在 trunk
/master
上修复。
这里是 relevant commit.
这是 a patch 解决问题的方法。
根据补丁中的评论,reload_combine_recognize_pattern
函数试图调整 USE insns。
我相信我在实施 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, as does changing both members to be uint64_t
x86-64 System V 在 RDX:RAX 中 return 小于 16 字节的结构,当它们可以被简单地复制时。在这种情况下,第二个成员在 RDX 中,因为 RAX 的高半部分是用于对齐的填充,或者 .b
当 .a
是较窄的类型时。 (sizeof(retstruct)
无论如何都是 16;我们没有使用 __attribute__((packed))
所以它尊重 alignof(uint64_t) = 8。)
此代码是否包含任何允许 GCC 发出 "incorrect" 程序集的未定义行为?
如果没有,应该在 https://gcc.gnu.org/bugzilla/
上报告Does this code contain any undefined behaviour that would allow GCC to emit the "incorrect" assembly?
关于 C99 和更高版本的 C 语言标准,问题中给出的代码的行为得到了很好的定义。特别是,C 允许函数 return 结构值不受限制。
我在这里没有看到任何 UB;你的类型是无符号的,所以有符号溢出 UB 是不可能的,也没有什么奇怪的。 (即使签名,它也必须为 不会 导致溢出 UB 的输入产生正确的输出,例如 rdi=1
)。它也被 GCC 的 C++ 前端破坏了。
此外,GCC8.2 将其编译为 correctly for AArch64 and RISC-V(在使用 movk
构造常量后为 madd
指令,或在加载常量后为 RISC-V mul 和 add)。如果 GCC 发现的是 UB,我们通常希望它能找到它并破坏其他 ISA 的代码,至少是那些具有相似类型宽度和寄存器宽度的 ISA。
Clang 也能正确编译。
这似乎是从 GCC 5 到 6 的倒退; GCC5.4 编译正确,6.1 及更高版本则不正确。 (Godbolt).
您可以使用问题中的 MCVE 在 GCC's bugzilla 上报告此问题。
看起来确实像是 x86-64 System V struct-return 处理中的错误,可能是包含填充的结构。 这可以解释为什么它在以下情况下起作用内联,以及将 a
加宽到 uint64_t(避免填充)时。
这已在 trunk
/master
上修复。
这里是 relevant commit.
这是 a patch 解决问题的方法。
根据补丁中的评论,reload_combine_recognize_pattern
函数试图调整 USE insns。