[hexagon] clang crashes w/__atomic_fetch_add in unrolled loop w/cond early return
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
Here's a failing test case reduced from `stress-ng`.
```
$ clang --target=hexagon-unknown-linux-musl -mcpu=hexagonv73 -O2 ... -c stress-cacheline.c
$ echo $?
139
```
`stress-cacheline.c`:
```c
/*
* HexagonGenInsert stack overflow -- reduced reproducer.
*
* Reduced from stress-ng 0.21.00 stress-cacheline.c
* (stress_cacheline_atomicinc), which makes clang die with a bare SIGSEGV --
* no crash banner, no stack dump, no .sh/.c reproducer written out. The build
* system sees only exit status 139.
*
* HexagonGenInsert::collectInBlock() (HexagonGenInsert.cpp:906) walks the
* machine dominator tree by plain recursion with no depth limit, while each
* frame keeps two RegisterSet objects live across the recursive call -- those
* are sized by the function's vreg count (~1 MB per frame was observed in the
* debugger). Deep tree + fat frames = blown stack.
*
* Three ingredients, all required:
*
* 1. a fully unrolled loop (#pragma unroll with a constant trip count);
* 2. relaxed atomic RMWs in the body -- on hexagon each __atomic_fetch_add
* expands to an LL/SC (memw_locked) retry loop, so every unrolled
* iteration contributes several basic blocks rather than one. This is the
* multiplier that gets the depth over the edge: an earlier attempt at a
* reducer using only rotates and no atomics did NOT crash even at
* UNROLL=1024;
* 3. a conditional early return, which chains the blocks into a deep
* dominator *spine* rather than a shallow fan-out.
*
* clang --target=hexagon-unknown-linux-musl -mcpu=hexagonv73 -O2 \
* -DUNROLL=1024 -c cacheline.c # SIGSEGV (139)
*
* Workaround: -mllvm -hexagon-insert=false
*/
#include
#ifndef UNROLL
#define UNROLL 1024
#endif
#ifndef NATOMIC
#define NATOMIC 7
#endif
#define ATOMIC_INC(ptr) \
do { __atomic_fetch_add(ptr, 1, __ATOMIC_RELAXED); } while (0)
int cacheline_atomicinc(volatile uint8_t *data8)
{
register int i;
register uint8_t val8 = *(data8);
#pragma unroll
for (i = 0; i < UNROLL; i++) {
ATOMIC_INC(data8);
ATOMIC_INC(data8);
ATOMIC_INC(data8);
ATOMIC_INC(data8);
ATOMIC_INC(data8);
ATOMIC_INC(data8);
ATOMIC_INC(data8);
val8 += NATOMIC;
if (__builtin_expect(*data8 != val8, 0))
return 1;
}
return 0;
}
```
Contributor guide
Assessment
This issue has not been assessed yet.