behavior when for-loop variable overflow and compiler optimization - c

While reading http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html about undefined behavior in c, I get a question on this example.
for (i = 0; i <= N; ++i) { ... }
In this loop, the compiler can assume that the loop will iterate
exactly N+1 times if "i" is undefined on overflow, which allows a
broad range of loop optimizations to kick in. On the other hand, if
the variable is defined to wrap around on overflow, then the compiler
must assume that the loop is possibly infinite (which happens if N is
INT_MAX) - which then disables these important loop optimizations.
This particularly affects 64-bit platforms since so much code uses
"int" as induction variables.
This example is to show the C compiler could take advantage of the undefined behavior to make assumption that the execution times would be exact N+1. But I don't understand why this assumption is valid.
I can understand that if the variable is defined to wrap around on overflow and N is INT_MAX, then the for loop will be infinite because i will go from 0 to INT_MAX and overflow to INT_MIN, then loop to INT_MAX and restart from INT_MIN etc. So the compiler could not make this assumption about execution times and can not do optimization on this point.
But what about when i is undefined on overflow? In this case, i loops normally from 0 to INT_MAX, then i will be assigned INT_MAX+1, which would overflow to an undefined value such as between 0 and INT_MAX. If so, the condition i<= INT_MAX is still valid, should the for-loop not continue and also be infinite?

… then i will be assigned INT_MAX+1, which would overflow to an undefined value such as between 0 and INT_MAX.
No, that is not correct. That is written as if the rule were:
If ++i overflows, then i will be given some int value, although it is not specified which one.
However, the rule is:
If ++i overflows, the entire behavior of the program is undefined by the C standard.
That is, if ++i overflows, the C standard allows any of these things to happen:
i stays at INT_MAX.
i changes to INT_MIN.
i changes to zero.
i changes to 37.
The processor generates a trap, and the operating system terminates your process.
Some other variable changes value.
Program control jumps out of the loop, as if it had ended normally.
Anything.
Now consider this assumption used in optimization by the compiler:
… the compiler can assume that the loop will iterate exactly N+1 times…
If ++i can only set i to some int value, then the loop will not terminate, as you conclude. On the other hand, if the compiler generates code that assumes the loop will iterate exactly N+1 times, then something else will happen in the case when ++i overflows. Exactly what happens depends on the contents of the loop and what the compiler does with them. But it does not matter what: Generating this code is allowed by the C standard because whatever happens when ++i overflows is allowed by the C standard.

Lets consider an actual case:
#include <limits.h>
#include <stdio.h>
unsigned long long test_int(unsigned long long L, int N) {
for (int i = 0; i <= N; ++i) {
L++;
return L;
}
unsigned long long test_unsigned(unsigned long long L, unsigned N) {
for (unsigned i = 0; i <= N; ++i) {
L++;
return L;
}
int main() {
fprintf(stderr, "int: %llu\n", test_int(0, INT_MAX));
fprintf(stderr, "unsigned: %llu\n", test_unsigned(0, UINT_MAX));
return 0;
}
The point of the blog article is the of possible behavior of the compiler for the above code:
for test_int() the compiler can determine that for argument values from INT_MIN to -1, the function should return L unchanged, for values between 0 and INT_MAX-1, the return value should be L + N + 1 and for INT_MAX the behavior is undefined, so returning L + N + 1 is OK too, hence the code can be simplified as
unsigned long long test_int(unsigned long long L, int N) {
if (N >= 0)
L += N + 1;
return L;
}
for test_unsigned(), the same analysis yields: for argument values below UINT_MAX, the return value is L + N + 1 and for UINT_MAX there is an infinite loop:
unsigned long long test_unsigned(unsigned long long L, unsigned N) {
if (N != UINT_MAX)
return L + N + 1;
for (;;);
}
As can be seen on https://godbolt.org/z/abafdE8P4 both gcc and clang perform this optimisation for test_int, taking advantage of undefined behavior on overflow but generate iterative code for test_unsigned.

Signed integer overflow invokes the Undefined Behaviour. Programmer cannot assume that a portable program will behave the particular way.
On the other hand, a program compiled for the particular platform using particular version of the compiler and using the same versions of the libraries will behave deterministic way. But you do not know if any of those change (ie. compiler, compiler version etc etc) that the behaviour will remain the same.
So your assumptions can be valid for the particular build and execution environment, but are invalid in general.

Related

C: How to iterate over all possible values of `signed int`, from `INT_MIN` to `INT_MAX`?

I want to iterate over all possible values of signed int, from INT_MIN to INT_MAX. The obvious code
for (int x = INT_MIN; x <= INT_MAX; x++)
do_something(x);
is broken due to undefined behavior caused by signed integer overflow. What is worth noting is that so is its apparently clever variant
int x = INT_MIN;
do {
do_something(x);
} while (x++ < INT_MAX);
What is interesting is that in this second example clang -O2 -fsanitize=undefined correctly reports the runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int', while gcc -O2 -fsanitize=undefined does not. I imagine gcc's -fsanitize=signed-integer-overflow is as broken as -ftrapv.
The only ways I could find to express this iteration are with a goto:
int x = INT_MIN;
loop: {
do_something(x);
if (x < INT_MAX) {
x++;
goto loop;
}
}
using an infinite loop and breaking manually:
int x = INT_MIN;
while (1) {
do_something(x);
if (x < INT_MAX)
x++;
else
break;
}
and exploiting the short-circuit evaluation to increment the variable only when it's safe to do so:
int x = INT_MIN;
do {
do_something(x);
} while (x < INT_MAX && ++x != INT_MIN);
As pointed out by Steve Jessop, in the do-while solution one can use while (x < INT_MAX && (++x, 1)); instead.
Among the three version, I'm not particularly against the goto version, I don't like the do-while version very much (especially the ++x != INT_MIN bit...), but all in all I prefer the while (1) version. My question is the following.
Is a C compiler allowed to completely eliminate the infinite while (1) loop in case do_something doesn't perform input/output operations, nor accesses volatile objects?
I know that it cannot for C11, but I'm not so sure about previous versions.
Edit
I will try to describe more in detail what I'm worried about. Let's say that I'm using this loop to validate the correctness of some other code. For instance, I might do
#include <stdio.h>
#include <limits.h>
int add_wrap_unsigned(int x, int y) {
return (unsigned) x + (unsigned) y;
}
int add_wrap_builtin(int x, int y) {
int res;
__builtin_add_overflow(x, y, &res);
return res;
}
int check() {
int x = INT_MIN;
while (1) {
if (add_wrap_unsigned(x, 1) != add_wrap_builtin(x, 1))
return 1;
if (x < INT_MAX)
x++;
else
break;
}
return 0;
}
int main() {
if (check())
printf("counterexample found");
else
printf("no counterexample");
return 0;
}
This correctly reports no counterexample with clang. check gets compiled to a simple return 0. However, changing a single line (line 22 from break; to x = INT_MIN;) changes completely the behavior: check becomes a noop infinite loop, but main now prints counterexample found. All this happens even with -std=c11.
My worry is that I cannot trust the first version, because the compiler might not have done the correct thing, as in the second case.
The question is whether the C compiler is allowed to remove an infinite loop. No, it is not allowed to remove an infinite loop whose controlling expression is constant (for (;;) {} or while (1) { }), but the C standard explicitly states in C11/C17 6.8.5p6 that
An iteration statement whose controlling expression is not a constant expression,156) that performs no input/output operations, does not access volatile objects, and performs no synchronization or atomic operations in its body, controlling expression, or (in the case of a for statement) its expression-3, may be assumed by the implementation to terminate.157)
I.e. the compiler is allowed to optimize out a loop that does not have any side effects and that does have a non-constant controlling expression. I.e. it is an utterly useless loop when it comes to the as-if rule. This exception has been granted in the C11 revision of the standard. This did not apply before C11 so if that behaviour was present then then it wasn't standards-compliant.
You do not need to guard the increment, just break:
for (int x = INT_MIN; ; x++) {
do_something(x);
if (x == INT_MAX) break;
}
In this case, the controlling expression is a constant expression (implicit true value), and hence must not be optimized out as per C11/C17 6.8.5p6, but the compiler must prove it explicitly to terminate.
Now what this gets optimized to depends on the compiler and the flags - with -O3 Clang 9.0 compiles it to the equivalent of
for (int x = INT_MIN; x != INT_MIN; x++)
do_something(x)
if the overflow were defined with wraparound - but as we know, x86 machine code does have well-defined wraparound.
The behaviour of the machine code produced by GCC 9.2 is similar to
int x = INT_MIN;
do_something(x);
do {
do_something(++x);
} while (x < INT_MAX);
which written like that in C would be free of undefined behaviour and virtually indistinguishable in performance from that of by Clang (but not in size).

Integer overflow vs implicit conversion from long long to int

Take for example int a=INT_MAX-1; and int b=INT_MAX-1; and assume that int is 32-bit and a function
int product(int a,int b)
{
return a*b;
}
Now here the product a*b overflows resulting in undefined behavior from the standard:
If an exceptional condition occurs during the evaluation of an
expression (that is, if the result is not mathematically defined or
not in the range of representable values for its type), the behavior
is undefined.
However if we have instead
int product(int a,int b)
{
long long x = (long long)a*b;
return x;
}
Then assuming this answer is correct and applies to long long as well by the standard the result is implementation-defined.
I'm thinking that undefined behavior can cause anything including a crash so it's better to avoid it all costs, hence that the second version is preferable. But I'm not quite sure if my reasoning is okay.
Question: Is second version preferable or is the first one or are they equally preferable?
Both of the options are bad because they do not produce the desired result. IMHO it is a moot point trying to rank them in badness order.
My advice would be to fix the function to be well-defined for all use cases.
If you (the programmer) will never (ever!) pass values to the product() function that will cause undefined behavior, then the first version, why not.
The second version returns the sizeof(int)*CHAR_BIT least significant bits of the result (this is implementation defined behavior) and still may overflow on architectures where LLONG_MAX == INT_MAX. The second version may take ages to execute on a 8-bit processor with real bad support for long long multiplication and maybe you should handle the overflow when converting long long to int with some if (x > INT_MAX) return INT_MAX;, unless you are only really interested in only the least significant bits of the product result.
The preferable version is that, where no undefined behavior exists. If you aren't sure if multiplication a and b will result in undefined behavior or not, you should check if it will and prepare for such a case.
#include <assert.h>
#include <limits.h>
int product(int a, int b)
{
assert(a < INT_MAX/b && b < INT_MAX/a);
if (!(a < INT_MAX/b && b < INT_MAX/a))
return INT_MAX;
return a * b;
}
or in GNUC:
int product(int a, int b) {
int c;
if (__builtin_sadd_overflow(a, b, &c)) {
assert(0);
return INT_MAX;
}
return c;
}
I believe that slightly tweaked second version might be interesting for you:
int product(int a, int b)
{
long long x = (long long)a * b;
if (x < INT_MIN || x > INT_MAX)
{
fprintf(stderr, "Error in product(): Result out of range of int\n");
abort();
}
return x;
}
This function takes two integers as long ints, computes their product and checks if
the result is in range of int. If it is, we can return it from the function without any bad consequences. If it is not, we can print error message and abort, or do exception handling of a different kind.
EDIT 1: But this code stil expects that (long long)a * b does not overflow, which is not guaranteed when i. e. sizeof(long long) == sizeof(int). In such case, an overflow check should be added to make sure this does not happen. The (6.54) Integer Overflow Builtins could be interesting for you if you don't mind using GCC-dependent code. If you want to stay in C without any extensions, there are methods to detect multiplication overflow as well, see this StackOverflow answer: https://stackoverflow.com/a/1815371/1003701

Finding maximum value of a short int variable in C

I was working on Exercise 2-1 of K&R, the goal is to calculate the range of different variable types, bellow is my function to calculate the maximum value a short int can contain:
short int max_short(void) {
short int i = 1, j = 0, k = 0;
while (i > k) {
k = i;
if (((short int)2 * i) > (short int)0)
i *= 2;
else {
j = i;
while (i + j <= (short int)0)
j /= 2;
i += j;
}
}
return i;
}
My problem is that the returned value by this function is: -32768 which is obviously wrong since I'm expecting a positive value. I can't figure out where the problem is, I used the same function (with changes in the variables types) to calculate the maximum value an int can contain and it worked...
I though the problem could be caused by comparison inside the if and while statements, hence the typecasting but that didn't help...
Any ideas what is causing this ? Thanks in advance!
EDIT: Thanks to Antti Haapala for his explanations, the overflow to the sign bit results in undefined behavior NOT in negative values.
You can't use calculations like this to deduce the range of signed integers, because signed integer overflow has undefined behaviour, and narrowing conversion at best results in an implementation-defined value, or a signal being raised. The proper solution is to just use SHRT_MAX, INT_MAX ... of <limits.h>. Deducing the maximum value of signed integers via arithmetic is a trick question in standardized C language, and has been so ever since the first standard was published in 1989.
Note that the original edition of K&R predates the standardization of C by 11 years, and even the 2nd one - the "ANSI-C" version predates the finalized standard and differs from it somewhat - they were written for a language that wasn't almost, but not quite, entirely unlike the C language of this day.
You can do it easily for unsigned integers though:
unsigned int i = -1;
// i now holds the maximum value of `unsigned int`.
Per definition, you cannot calculate the maximum value of a type in C, by using variables of that very same type. It simply doesn't make any sense. The type will overflow when it goes "over the top". In case of signed integer overflow, the behavior is undefined, meaning you will get a major bug if you attempt it.
The correct way to do this is to simply check SHRT_MAX from limits.h.
An alternative, somewhat more questionable way would be to create the maximum of an unsigned short and then divide that by 2. We can create the maximum by taking the bitwise inversion of the value 0.
#include <stdio.h>
#include <limits.h>
int main()
{
printf("%hd\n", SHRT_MAX); // best way
unsigned short ushort_max = ~0u;
short short_max = ushort_max / 2;
printf("%hd\n", short_max);
return 0;
}
One note about your code:
Casts such as ((short int)2*i)>(short int)0 are completely superfluous. Most binary operators in C such as * and > implement something called "the usual arithmetic conversions", which is a way to implicitly convert and balance types of an expression. These implicit conversion rules will silently make both of the operands type int despite your casts.
You forgot to cast to short int during comparison
OK, here I assume that the computer would handle integer overflow behavior by changing into negative integers, as I believe that you have assumed in writing this program.
code that outputs 32767:
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
short int max_short(void)
{
short int i = 1, j = 0, k = 0;
while (i>k)
{
k = i;
if (((short int)(2 * i))>(short int)0)
i *= 2;
else
{
j = i;
while ((short int)(i + j) <= (short int)0)
j /= 2;
i += j;
}
}
return i;
}
int main() {
printf("%d", max_short());
while (1);
}
added 2 casts

Iterating an array backward in C using an unsigned index

Is the following code, safe to iterate an array backward?
for (size_t s = array_size - 1; s != -1; s--)
array[s] = <do something>;
Note that I'm comparing s, which is unsigned, against -1;
Is there a better way?
This code is surprisingly tricky. If my reading of the C standard is correct, then your code is safe if size_t is at least as big as int. This is normally the case because size_t is usually implemented as something like unsigned long int.
In this case -1 is converted to size_t (the type of s). -1 can't be represented by an unsigned type, so we apply modulo arithmetic to bring it in range. This gives us SIZE_MAX (the largest possible value of type size_t). Similarly, decrementing s when it is 0 is done modulo SIZE_MAX+1, which also results in SIZE_MAX. Therefore your loop ends exactly where you want it to end, after processing the s = 0 case.
On the other hand, if size_t were something like unsigned short (and int bigger than short), then int could represent all possible size_t values and s would be converted to int. In other words, the comparison would be done as (int)SIZE_MAX != -1, which would always return false, thus breaking your code. But I've never seen a system where this could happen.
You can avoid any potential problems by using SIZE_MAX (which is provided by <stdint.h>) instead of -1:
for (size_t s = array_size - 1; s != SIZE_MAX; s--)
...
But my favorite solution is this:
for (size_t s = array_size; s--; )
...
Well, s will never be -1, so your ending condition will never happen. s will go from 0 to SIZE_MAX, at which point your program will probably segfault from a memory access error. The better solution would be to start at the max size, and subtract one from everywhere you use it:
for (size_t s = array_size; s > 0; s--)
array[s-1] = <do something>;
Or you can combine this functionality into the for loop's syntax:
for (size_t s = array_size; s--;)
array[s] = <do something>;
Which will subtract one before going into the loop, but checks for s == 0 before subtracting 1.
IMO in the iterations use large enough signed value. It ss easier to read by humans.

Where is the second overflow in this piece of code

Here is the piece of code from GNU C reference manual Pg 74:
If your code uses a signed loop index, make sure that the index cannot
overflow, along with all signed expressions derived from the index.
Here is a contrived example of problematic code with two instances of
overflow.
for( i = INT_MAX - 10 ; i <= INT_MAX; i++)
if( i+1 < 0 ) //first overflow
{
report_overflow();
break;
}
Because of the two overflows, a compiler might optimize away or
transform the two comparisons in a way that is incompatible with the
wraparound assumption.
What GNU C reference manual means is that you have two possible overflows. The first one is the i++ statement in
for( i = INT_MAX - 10 ; i <= INT_MAX; i++)
and the second one would be i+1 in
if( i+1 < 0 ) //first overflow
The example C code avoids an eternal loop with the
if( i+1 < 0 ) //first overflow
{
report_overflow();
break;
}
piece of code, and to do that you're relying in signed wraparound behaviour.
However the A.3 apendix tells you that you shouldn't rely on signed wraparound behaviour because the optimizer exploits its undefined behaviour and could generate code that would behave differently from what you expect. This is the case with if( i+1 < 0 ) piece of code, which relies in that wraparound will happen when i is INT_MAX.
As a conclusion, above code could fail after being optimized by the compiler.
Converting from comment:
i <= INT_MAX is always true, so loop can never quit. So this is a bug because i++ overflows.
Because it is always true, compiler may optimize this condition out, which is obviously not what expected.
due to the break, there should be none
without the break this would be an eternal loop, and overflow on ++i
since i <= INT_MAX is true for all values of i (assuming i is an integer)

Resources