The C11 standard appears to imply that iteration statements with constant controlling expressions should not be optimized out. I\'m taking my advice from this answer, which
I have been convinced this is just a plain old bug. I leave the my tests below and in particular the reference to the discussion in the standard committee for some reasoning I previously had.
I think this is undefined behavior (see end), and Clang just has one implementation. GCC indeed works as you expect, optimizing out only the unreachable print statement but leaving the loop. Some how Clang is oddly making decisions when combining the in-lining and determining what it can do with the loop.
The behavior is extra weird - it removes the final print, so "seeing" the infinite loop, but then getting rid of the loop as well.
It's even worse as far as I can tell. Removing the inline we get:
die: # @die
.LBB0_1: # =>This Inner Loop Header: Depth=1
jmp .LBB0_1
main: # @main
push rax
mov edi, offset .Lstr
call puts
.Lstr:
.asciz "begin"
so the function is created, and the call optimized out. This is even more resilient than expected:
#include
void die(int x) {
while(x);
}
int main() {
printf("begin\n");
die(1);
printf("unreachable\n");
}
results in a very non-optimal assembly for the function, but the function call is again optimized out! Even worse:
void die(x) {
while(x++);
}
int main() {
printf("begin\n");
die(1);
printf("unreachable\n");
}
I made a bunch of other test with adding a local variable and increasing it, passing a pointer, using a goto etc... At this point I would give up. If you must use clang
static void die() {
int volatile x = 1;
while(x);
}
does the job. It sucks at optimizing (obviously), and leaves in the redundant final printf. At least the program does not halt. Maybe GCC after all?
Addendum
Following discussion with David, I yield that the standard does not say "if the condition is constant, you may not assume the loop terminates". As such, and granted under the standard there is no observable behavior (as defined in the standard), I would argue only for consistency - if a compiler is optimizing out a loop because it assume it terminates, it should not optimize out following statements.
Heck n1528 has these as undefined behavior if I read that right. Specifically
A major issue for doing so is that it allows code to move across a potentially non-terminating loop
From here I think it can only devolve into a discussion of what we want (expected?) rather than what is allowed.