clang fails to vectorize a select-of-two-array-loads on AArch64 (gcc does)
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
At `-O3` for AArch64, clang leaves the loop below scalar while gcc vectorizes it (same flags, same target). The loop reads `a[i]` and `b[i]`, compares a field, and stores a value from whichever is larger. gcc emits `ld2 + cmge + bsl`; clang keeps it scalar.
I hit this in a Viterbi decoder (EEMBC viterb00) — the add-compare-select loop is this exact pattern, and it's a big reason the clang build is slower than gcc there.
### Testcase
```c
typedef short s16;
struct S { s16 tag; s16 key; };
void f(s16 *dst, const struct S *a, const struct S *b, int n)
{
for (int i = 0; i < n; i++) {
s16 ka = a[i].key;
s16 kb = b[i].key;
if (ka >= kb)
dst[i] = a[i].tag;
else
dst[i] = b[i].tag;
}
}
```
Flags: `-O3 -mcpu=cortex-a57`
- clang (trunk): https://godbolt.org/z/hGEa687ae — not vectorized
- gcc (trunk): https://godbolt.org/z/PhY5cW3Ed — vectorized
gcc output:
```
ld2 {v30.8h - v31.8h}, [x4], 32
ld2 {v28.8h - v29.8h}, [x6], 32
cmge v29.8h, v31.8h, v29.8h
bsl v29.16b, v30.16b, v28.16b
```
clang output: scalar loop (loads both keys, compares, csel/branch, loads the chosen tag, stores). No `ld2`/`bsl`.
### Root cause
Before the loop vectorizer runs, the conditional read is already a load from a selected pointer:
```llvm
%cmp = icmp slt i16 %ka, %kb
%spec.select = select i1 %cmp, ptr %arrayidx2, ptr %arrayidx
%v = load i16, ptr %spec.select
```
The load address depends on the compare, so the vectorizer can't widen it. To match gcc it needs to be `select(cmp, load a[i], load b[i])` — load both (contiguous) and select the value.
### Observation
InstCombine has this fold in `visitLoadInst`: `load(select(c,p1,p2)) -> select(c, load p1, load p2)`. But it only fires when `isSafeToLoadUnconditionally` holds for both pointers, which fails for `a[i]`/`b[i]`: those checks have no loop context, so they can't prove per-iteration dereferenceability or add a runtime check.
There's already a loop-aware check, `isDereferenceableAndAlignedInLoop` (`Analysis/Loads.cpp`): it uses SCEV, can emit runtime predicates, and the vectorizer already calls it elsewhere (e.g. `LoopVectorizationLegality::canVectorizeWithIfConvert`). It just isn't wired up for this case.
Contributor guide
Research direction
Start with Analysis/Loads.cpp and the loop-aware isDereferenceableAndAlignedInLoop check, then trace InstCombine’s visitLoadInst and LoopVectorizationLegality::canVectorizeWithIfConvert. Compile the supplied C testcase for AArch64 with -O3 -mcpu=cortex-a57 and compare the generated code; done means the safe load-and-select pattern is vectorized without losing the required safety checks.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c
- Domain
- compilers, performance
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100