Recently, we discovered odd behavior in some old code. This code has worked for ages, but broke on some platform (XBox 360, PowerPC) with compiler optimizations turned on max. Usually, I'd suspect undefined behavior.
Code looks roughly like this:
#include <stdint.h>
uint32_t sign_extend16(uint32_t val)
{
return (int32_t)(int16_t)val;
}
It's part of an emulator so the operation in question shouldn't be too strange. Normally, I'd expect this to only consider the lower 16-bits and sign-extend that to 32-bits. Apparently, this was the behavior it had for ages. On x86_64, GCC gives me this result:
0000000000000000 <sign_extend16>:
0: 0f bf c7 movswl %di,%eax
3: c3 retq
However, from what I could understand of the standard, converting an unsigned to a signed is not defined should it not be possible to represent the value of the unsigned one with the signed type.
Could it then be possible for the compiler to assume that the unsigned value would have to be in the range of [0, 32767]
, as any other value would be undefined? In that case, a cast to int16_t
and yet another cast to int32_t
would do nothing. In this case, would it be legal for the compiler to translate the code to a simple move?
A conversion between two integer types is never undefined behavior.
But some integer conversions are implementation defined.
On integer conversions C says:
(C99, 6.3.1.3p3) "Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised."
what does gcc
on this case is documented here:
http://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html
"For conversion to a type of width N, the value is reduced modulo 2^N to be within range of the type; no signal is raised"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With