O'NeillのPCG PRNGの実装中にGCCのバグを見つけたと思います。(Godboltのコンパイラエクスプローラの初期コード)
、(rdiに格納された結果)を乗算oldstate
した後MULTIPLIER
、GCCはその結果をINCREMENT
に追加せず、INCREMENT
代わりにrdxに移動し、rand32_ret.state の戻り値として使用されます。
最小限の再現可能な例(コンパイラエクスプローラ):
#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は、簡単にコピーできる場合、RDX:RAXで16バイトより小さい構造体を返します。この場合、2番目のメンバーはRDXにあります。これは、RAXの上位半分が位置合わせのためのパディングであるか.b
、または.a
幅が狭いタイプだからです。(sizeof(retstruct)
どちらの方法でも16です。使用していない__attribute__((packed))
ため、alignof(uint64_t)= 8を尊重します。)
このコードには、GCCが「誤った」アセンブリを発行できるようにする未定義の動作が含まれていますか?
そうでない場合は、https://gcc.gnu.org/bugzilla/で報告されます。