Description
Consider the following code sample:
#include <stdlib.h>
#include <stdio.h>
void a()
{
for (unsigned int i = 0; i >= 0; i++)
;
}
void b()
{
puts("b");
}
Function a
contains an infinite loop without a constant controlling expression and without side-effects. The C11 and C17 standards allow implementations to assume that such a loop terminates. This code also happens to be valid C++ code. In C++11 and later, side-effect free infinite loops may also be assumed to terminate, as any thread can be assumed to eventually terminate or perform some kind of side-effect. The b
function obviously has no strange or undefined behaviour.
When the function a
is compiled with either clang
or clang++
with at least -O1
for x86-64, the result will be an instructionless function:
a: # @a
b: # @b
lea rdi, [rip + .L.str]
jmp puts@PLT # TAILCALL
.L.str:
.asciz "b"
This means calling a
will have the same result as calling b
. It's unclear whether this is allowed by either standard, as both just say the loop "may be assumed to terminate", not specifically that an infinite loop has undefined behaviour. One could say this only allows a compiler to remove a loop it cannot prove to terminate, not to assume a loop it can prove not to terminate will never be called. (Edit: It's well understood to be undefined behaviour.)
Either way, this has another effect: a
and b
now have the same address.
We can append a small main
function to the above snippet:
typedef void (*fptr)();
fptr identity(fptr f)
{
volatile fptr f2 = f;
return f2;
}
int main()
{
printf("%d %d\n", a == b, identity(a) == identity(b));
}
The identity
function just returns its argument in a way clang
cannot inline. When we compile (with -O1
or higher) and run the full program, we get 0 1
, which means a
and b
are both identical and different. This seems to be a violation of the spec, since:
a) different functions shouldn't be equal, and
b) two different values shouldn't suddenly become equal after passing them through an identity function.
This was tried with clang
and clang++
version 14.0.0-1ubuntu1, and it can be observed to happen in version 15.0.0 through the Compiler Explorer.