Yes, it has optimization benefits. That doesn't mean it's actually a good feature.
Overloading "signedness" with "cannot overflow" makes no sense, it's simply an accident of history.
But back in history, C compilers weren't smart enough to take advantage of the leeway given: they in fact did exhibit implementation-defined behavior, not undefined behavior, in the face of a signed overflow. They acted like the hardware acts upon signed overflow. It's only fairly recently that optimizers have taken advantage of this loophole in the standard that allows them to blow up your program if you have any signed int overflows.
Of course, assuming that an unsigned int cannot overflow would also have optimization benefits! Does it really make sense that a loop gets slower just because you declare the loop counter as an "unsigned int" instead of an "int"?
Posted May 25, 2011 2:48 UTC (Wed) by iabervon (subscriber, #722)
[Link]
Actually, if the compiler were forced to consider overflow, it would just have to think a little harder before making the same optimizations. The compiler could determine that 1<<32 / GCD(1<<32, 0x04040404) > 256/64 and thus that the first time i >= 256/64, val == 0x04030200 with unsigned math, and val != 0x04030200 before that. Your loop would have to get slower (or, more likely, take an additional register) if it was going to use the same value in "val" multiple times, simply because it becomes necessary to track another piece of information.
(Also note that it doesn't matter if you declare the loop counter as an "unsigned int"; the nominal loop counter actually gets discarded entirely, in favor of a proxy loop counter, which is what could overflow.)
Sure, but then you deserve what you get...
Posted May 25, 2011 3:31 UTC (Wed) by iabervon (subscriber, #722)
[Link]
Actually, if the compiler is forced to consider overflow, it just has to think a little harder before making the same optimizations. The compiler can determine that 1<<32 / GCD(1<<32, 0x04040404) > 256/4 and thus that the first time i >= 256/4, val == 0x04030200 with unsigned math, and val != 0x04030200 before that. Your loop would have to get slower (or, more likely, take an additional register) if it was going to use the same value in "val" multiple times, simply because it becomes necessary to track another piece of information.
(Also note that it doesn't matter if you declare the loop counter as an "unsigned int"; the nominal loop counter actually gets discarded entirely, in favor of a proxy loop counter, which is what could overflow.)
With gcc 4.4.5, replacing "int val" with "unsigned int val" makes it actually generate what you would expect of:
for (val = 0x03020100; val != 0x04030200; val += 0x04040404, value++)
*value = val;
which avoids the "i = 0", "*value = val" is a simpler instruction than "value[i] = val", and uses one fewer register; but it still actually works. If the constant you're adding is 0x04000000, the optimization doesn't work, and gcc produces the slower code. The code it produces for "unsigned int" is the fastest working code possible, so it's not getting slower in any meaningful way by using "unsigned int" (I mean, it loops faster without testing the end condition, but...).
Sure, but then you deserve what you get...
Posted May 26, 2011 17:21 UTC (Thu) by anton (guest, #25547)
[Link]
Does it really make sense that a loop gets slower just because you declare the loop counter as an "unsigned int" instead of an "int"?
It gets slower? Doesn't the code with int loop infinitely when compiled with gcc? Great optimization!
Sure, but then you deserve what you get...
Posted May 28, 2011 20:48 UTC (Sat) by BenHutchings (subscriber, #37955)
[Link]
C compilers weren't smart enough to take advantage of the leeway given: they in fact did exhibit implementation-defined behavior, not undefined behavior, in the face of a signed overflow. They acted like the hardware acts upon signed overflow.
Which was to crash, in many cases. Signed overflow caused a processor exception, just like division by zero, because the result could not be represented.