On Wed, 10 Apr 2024, stefan@franke.ms wrote: > Hi all, > > I just stumbled over an issue, which is present in almost all gcc versions. > I worked around using inline assembly… > Maybe gcc behaves correct and I am wrong? Here is the code: > > https://godbolt.org/z/cW8jcdh56 > > typedef unsigned long long int u64; > typedef unsigned int u32; > typedef unsigned short u16; > > u64 foo(u16 a, u16 b) { >     u32 x = a * b; >     u64 r = x; >     return r; > } > > And on gcc 13.2 x86.64 you get > > foo: >         movzx   esi, si >         movzx   edi, di >         imul    edi, esi >         movsx   rax, edi >         ret > > > There is a sign extension! The optimizer step discards the information > >  x_6 = (u32) _3; > > and uses _3 directly instead, which is signed. > > Am I wrong or is it gcc? GCC is not wrong. When your code computes x: u32 x = a * b; 'a' and 'b' are first promoted to int according to C language rules, and the multiplication happens in the signed int type, with UB on overflow. The compiler deduces the range of signed int temporary holding the result of the multiplication is [0, 0x7fffffff], which allows to propagate it to the assignment of 'r' (which in the end produces a sign extension, as you observed, so the propagation did not turn out to be useful). u16 * u16 is a famous footgun for sure. I'd suggest 'x = 1u * a * b' as a fix for the code. Alexander