Issue 136603
Summary Loop unrolling confuses __builtin_dynamic_object_size() and __builtin_constant_p()
Labels new issue
Assignees
Reporter kees
    This is from https://github.com/ClangBuiltLinux/linux/issues/2076 where commit 2d1e8a03f5eeff48cd7928d003fc12f728b2c7cf seems to have exposed some recent Linux kernel code to the loop unroller (though oddly only on ARM). Attempting to extract a minimal reproducer has been a challenge, but it appears that something about loop unrolling confuses `__builtin_constant_p()`. Loop variables assigned from `__builtin_dynamic_object_size()` appear constant, whereas if it is left in a macro, they correctly stay dynamic.

It seems that a `noreturn` branch is also be required to trigger this. Anything that disables the loop unrolling fixes the problem (via `#pragma` or via changes in loop size via `NUM_ADDRS`). Bringing the external loop constraint explicitly down into the inner loop (`j < NUM_ADDRS`) also fixes it.

I'm not sure what to do next to narrow this down further.

```c
// clang -Wall -O2 -c repro.c -o repro.o
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#define __compiletime_warning(msg) __attribute__((__warning__(msg)))
#define __noreturn __attribute__((__noreturn__))

extern void variable_calc_constant(size_t) __compiletime_warning("variable calculation is constant");
extern void macro_calc_constant(size_t) __compiletime_warning("macro calculation is constant");
extern void freakout(void) __noreturn;
extern int check(int);

extern int unknown;

// must be less than 10 (loop unrolling limit?)
#define NUM_ADDRS 4

struct inner {
        unsigned char addr[2]; // must be greater than 1 to show warnings in unroll
};

struct middle {
        struct inner bytes;
};

struct outer {
        struct middle array[NUM_ADDRS];
};

void repro(void) {
        struct outer *outer;
        struct middle *middle;
        int i, c;

        outer = malloc(sizeof(*outer));
        middle = outer->array;
        for (i = 0, c = 0; i < unknown && c < NUM_ADDRS; i++) {
                struct inner addr = { };
                int j;

                if (check(i))
 continue;

//#pragma clang loop unroll(disable)
 for (j = 0; j < c /*&& j < NUM_ADDRS*/; j++) {
 const size_t v_size = __builtin_dynamic_object_size(&middle[j].bytes, 0);
 #define      m_size __builtin_dynamic_object_size(&middle[j].bytes, 0)

 if (__builtin_constant_p(v_size))
 variable_calc_constant(v_size);
                        if (__builtin_constant_p(m_size))
 macro_calc_constant(m_size);

                        if (m_size < sizeof(addr))
                                freakout();

 if (__builtin_memcmp(&middle[j].bytes, &addr, sizeof(addr)) == 0)
 break;
                }
                if (j == c)
                        c++;
        }
}
```

Produces (on x86_64 host, FWIW):

```
$ clang -Wall -O2 -c repro.c -o repro.o
repro.c:53:5: warning: call to 'variable_calc_constant' declared with 'warning' attribute: buggy: variable calculation is constant [-Wattribute-warning]
   53 | variable_calc_constant();
      | ^
repro.c:53:5: warning: call to 'variable_calc_constant' declared with 'warning' attribute: buggy: variable calculation is constant [-Wattribute-warning]
repro.c:53:5: warning: call to 'variable_calc_constant' declared with 'warning' attribute: buggy: variable calculation is constant [-Wattribute-warning]
repro.c:53:5: warning: call to 'variable_calc_constant' declared with 'warning' attribute: buggy: variable calculation is constant [-Wattribute-warning]
repro.c:53:5: warning: call to 'variable_calc_constant' declared with 'warning' attribute: buggy: variable calculation is constant [-Wattribute-warning]
5 warnings generated.
```

Without loop unrolling, this does not emit warnings. The macro-based uses of `__builtin_dynamic_object_size()` aren't resolved (which itself seems to be a bug), but the variable based ones are. Specifically, when `__builtin_dynamic_object_size()` is being determined at compile time as part of the loop unrolling, it _cannot_ reach 0 -- the loop unroller is exceeding the maximum loop counter and producing a iteration beyond the bounds of the array. This can be seen in the _fifth_ (impossible) call in the binary output:

```
        xor     edi, edi
        call variable_calc_constant@PLT
```

https://godbolt.org/z/eT5KdTxbv
_______________________________________________
llvm-bugs mailing list
llvm-bugs@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs

Reply via email to