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.
 
    