Re: bad optimization

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 21/08/13 16:49, Andy Falanga (afalanga) wrote:
>> -----Original Message-----
>> From: gcc-help-owner@xxxxxxxxxxx [mailto:gcc-help-owner@xxxxxxxxxxx] On
>> Behalf Of Andrew Haley
>> Sent: Tuesday, August 20, 2013 1:06 PM
>> To: Brian Budge
>> Cc: GCC-help
>> Subject: Re: bad optimization
>>
>> On 08/20/2013 07:34 PM, Brian Budge wrote:
>>> Is this an example of undefined behavior, or should I file a bug?
>>
>> It's an example of undefined behavior.  No bug.
>>
>> Andrew.
> 
> For the ignorant (me), can you explain why it's undefined?
> 
> Andy
> 

In case Andrew's correct, but somewhat technical, explanation is not
what you are after, here is an alternative.  The concept of "undefined
behaviour" can be difficult when you first meet it.

Signed overflow is when you do something with signed ints that gives a
result bigger than the int can express.  For example, adding 2e9 + 1e9
with 32-bit ints - the ideal result is 3e9 which takes 33 bits for a
signed integer.  When faced with that, there are three things the C
standards, and therefore the compiler, could do.

One is that it could "saturate" at the largest valid positive integer.
This is an expensive operation for many processors, but it's used in
special cases (mostly DSP work - and preferably using the new C "_Sat"
types).

It could also assume you were using a two's compliment processor (valid
for virtually all cpu's you'll ever see), and just do the addition -
ending up with an integer value of around -1e9.  This is an easy
operation, and is the way Java does it (and gcc generally, if you use
the "-fwrapv" option).  But it doesn't make mathematical sense - you
added two positive numbers, and the result is negative.

Or it could assume that if there is a bug in the source code and you are
trying to do something non-sensical, then it doesn't matter what the
compiler does with the results.  This is what is meant by "undefined
behaviour".  The great thing about "undefined behaviour" from the
compiler's viewpoint is that it can generate whatever code is easiest
and fastest for the "defined" range, with no regard for what happens in
the "undefined" range.

In your case, abs(-2147483648) is undefined (with 32-bit ints), because
+2147483648 is outside the range of integers.  So the compiler can
implement the abs() function like this:

int abs(int x) {
	if (x == -2147483648) {
		eatYourHardDiskForLunch();
	} else if (x >= 0) {
		return x;
	} else {
		return -x;
	}
}

And because this means the result of abs() is always non-negative if it
is defined, the compiler is then able to do more optimisations on it -
such as assuming that "abs(i) < 0" will always fail.


You might ask why the compiler can't spot the undefined behaviour at
compile time, and give an error message.  The answer is that it can
sometimes, especially when you enable warnings (-Wall, -Wextra) and
optimisation, but often it can't.  (I think it should be possible to
spot /this/ particular undefined behaviour, but my slightly outdated gcc
fails to warn about it.)


Basically, "undefined behaviour" is the technical term for "garbage in,
garbage out" - the compiler assumes you don't care about the quality of
the garbage out when you put garbage in, and uses it to give you better
code when you put good data in.  It is /your/ responsibility to avoid
putting garbage in in the first place.






[Index of Archives]     [Linux C Programming]     [Linux Kernel]     [eCos]     [Fedora Development]     [Fedora Announce]     [Autoconf]     [The DWARVES Debugging Tools]     [Yosemite Campsites]     [Yosemite News]     [Linux GCC]

  Powered by Linux