[LoopVectorize] i8 OR-reduction vectorized with i32 lanes since InstSimplify zext(trunc nuw) fold (#204089)
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
Since 4eb56cf497aa ([InstSimplify] Fold value-preserving zext/sext of trunc, #204089), a byte-wise OR reduction with a `uint8_t` accumulator is vectorized with `<8 x i32>` lanes and a `zext` per load instead of `<32 x i8>` lanes. On x86 with AVX2 the loop body becomes `vpmovzxbd` + `vpor` per 8 bytes instead of one `vpor ymm` per 32 bytes, 4x fewer bytes per instruction. Clang 21 and 22 produce the `<32 x i8>` loop; 23.1.0 and trunk do not.
```c
unsigned char is_ascii(const unsigned char * data, unsigned long len)
{
unsigned char res = 0;
for (unsigned long i = 0; i < len; ++i)
res |= data[i];
return res <= 0x7F;
}
```
Assembly, clang 21/22/23.1/trunk, `-O3 -mavx2`: https://godbolt.org/z/GMTsKTf6x
IR, clang 22 vs 23.1: https://godbolt.org/z/edqh5Tqz8
The accumulator is promoted to `int`, so the loop-carried value is `trunc nuw i32 -> i8` on the backedge and `zext i8 -> i32` on the next use. Before the fold InstCombine narrowed the whole chain to i8. With the fold the phi stays i32 and the vectorizer's type shrinking does not recover the i8 width.
Contributor guide
Research direction
Start by compiling the supplied C reproducer with -O3 -mavx2 and compare the Clang 22, 23.1, and trunk IR and assembly using the linked Godbolt examples. Read the InstSimplify fold, InstCombine narrowing, and LoopVectorize type-shrinking paths. Done means the regression is understood and the byte-wise reduction again uses the intended narrow vector lanes.
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
- 48/100