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