Skip to content

[clang] Function with empty loop has no return, has a duplicate address #60588

Closed as not planned
@SvizelPritula

Description

@SvizelPritula

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    duplicateResolved as duplicatellvm:optimizationsquestionA question, not bug report. Check out https://llvm.org/docs/GettingInvolved.html instead!

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions