<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/136603>136603</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
Loop unrolling confuses __builtin_dynamic_object_size() and __builtin_constant_p()
</td>
</tr>
<tr>
<th>Labels</th>
<td>
new issue
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
kees
</td>
</tr>
</table>
<pre>
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
</pre>
<img width="1" height="1" alt="" src="http://email.email.llvm.org/o/eJzsWE1v4zjS_jXMpWBDoSzbOfhgJx1g8Pa8O5jpRR8FiixZTFOkl6SSeH79oijaiR27e3r3tMAEhq2Ixecp1heLFCHorUVcsWrDqocbMcTO-dU3xHDTOLVffel0AB2g9a6HLsZdYOWa8UfGH7c6dkMzla5n_PHeCLvdDNrEz9oOr4w_mvyrQxgwMP7Ii8UcXjr0CNL1vY7A1S0uRVG2FWLbzpZSLe74UhVF2cpb3i74suFyIVsIiH2A6KATzwj4unMBFQTXI3iUaCMkVviG3qIB6RSSdOwQjHM7GKx3xqAHxpexc8O2A6eU2YOz6QvWv__K-N0U1jFiv4vabmk-vkYvZAQBvba6FwY87rxTg0QPnQjQIFoQIDthDNotMn4PzRBBRxC7HQofIHYiJk1jR6iicUN8rxS9lM62Q8AAbF7UdUNW1LaWzoYobKx3jC8Zv2PzYgqfaeaz8Fo0BgNk_6nRPyfT1d6KXsvaNU8oYx30n3jEydrBgYL0Tp4RAXRL6usABtsImtbXC-kdycQO9yCd9yij2UOIYg-ZZ8qKNSvWv8SDs2jdglSyzmMcvCXexgsrO0IXJjhoyH__GrRHlfzl9XaLHmKnwxTWdj_aLEEpHcYlnzmVBFr9mgd23jUGe_Lzs07sjJc7L7a9IHrngV7LTtgtBlpcQiLbQJb__3_-Wq8fHn7_I829m8LGa7sd1aDQi-itMOO8ZD4vtI0Uk0ZLTVZR7sWCtjn-tLXoR3Ey_7x4AlbewxnNaI5xHToebMn4ogfrIoTBI7yQGaID5cDia3q0wnv3kuw10raDjx36DEDY6SPpn5S0IClRYfJVGAOTf3CYyDGopxImLj-6JF5qK82gkPQNUWk37Vj56dKQ0c3VMW3jtTEy7HEsDStstUWoa-n6nTYYdY_1i_BW2y3jyz5syVZ1LWL0uhki1nWK6WV9kEovRrn8OQU-BONlkMMovTlMLtaj0-HZaXVMvVoKI-u3_FlSDNVxVO-y9ozzw2yg2YMRUTtLyfCGw4m13JyxpgT8zyjT1J_laz2Kb24gEvp_pDjm8Xtxin3ZofzG-FITYEY7lRjsN-te7HEox2I_hEg1wGBIBcPCbUFZcpbeRvc6svLx3JvHHILZCBuiH2TMKccWRAb5b7C5UMpOeBBKeVZtOKseWLmBM3W2HkVMZYg0ojwLnXuBbNZUNkbliHXxcFxVpu-1UgbP-E9Ua_YRQ572cb4b4gf1T6GF92LPqs1bEakePsIlT6aEfufGi7CZka_TwwhwmZnx9fh0KkQ-1rRByCP7YShjlw_QC2OczHHr2hSemXFMthPIA2H5MEJMWPlpXPeJWOvSpq6TYJFUyI_lBnQqtTn4gPE54_M0_r7-khzjm_Q5t89F71HwJA622MDR4OdTyCRPH6xxHG1J62PiHKsNZWbUdsCzVDnuYrl-v8sQxpd5b8wQ2SRPb3YYtxw5xvk62-F8H-KJiqQ_WCOVCxjLDTynViKB_7jZmOdoqTZPrHqYjoHP78lTB3-_5fPo9xH-v4QejT5a-UpH9ZyhDpa_WtnfBC86-tSlV8j6M7LLBb0_ZfohWX9wxT0ccyqVtiPPD_7e1fkT1vO19NjLfvddszM-H6kv6kLhkjM06dUQ8WV7UkZdS5inA478_vpkDuETghE4fx86o3HBv41NfSAOZ-F1Oa_nM-hcSJ3x49dfvibzrM_aqpSes7_cUuWXrFxXJX2xcn3YVOhR0vzogPHFtVhcgEJpBLXLLzp2JHrc7BdwbGgIrRm22z09_KjnAFZtJl-PcycHwCq7oSqBLe6vp8dZYpAsqz79by73b5X_VvmiytVb97dFi15EVNOPdeSrjt3H8_14ck5HNAzpPIe9jkfEKXzpcNwRJo0IqCDdBbj2Z87zHi3jiwgegzPPqKiSvXSaztoxoHl3hdIgCLJKKszjdQWdU4_mGVVwFgOhTuGPHUrdavLKPt8T2J_QTAdokLp4hRF9r6kHFxHyeQXowAIiwE74SEv-eLonTh2hlsJaF2vwKGQHBUwmF-53dAB8lYjqcGDvxavuh_5wXh8sdaPCKhjvcdKVDOhIHqV4aHDvrEozGzdYFQ46pdaTPEURI0iQLEqnmzRct7qNXZ1a0X7nQtBjPzYGcxZqtBV-T-3sbogXd5PDVvXqfPpFlbpq-nkbTJBXMmNW_Pb5y8e4PLu4c6pxJk6d3zL--Cfjj_il-j_15bV5vlGrUt2Vd-IGV7eLWcXni2Ixu-lW1byVt82tVPOSiwUvq6ZYNpKLYrFUcr6Y3-gVL3hVzPgtL_isnE2xuluqBmfVrWrbO75kswJ7oc3UmOeeuG_SzeDqtpzPi_LGiAZNSPeQnFt8gTRKB9Tq4cavaNKkGbaBzQqjQwxvMFFHg6vPV67U_kKYpni4fu92M3iz-s7VJymSfyY77wj8_bVnXt_ziv87AAD__x2UzUI">