[X86] Form PEXT for canonical v4i8 low-two-bit packing
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
## Summary
LLVM currently retains the following pattern after `default`:
```llvm
%shifted = shl <4 x i8> %x,
%masked = and <4 x i8> %shifted,
%packed = call i8 @llvm.vector.reduce.or.v4i8(<4 x i8> %masked)
```
On X86's little-endian layout, this is equivalent to:
```llvm
%bits = bitcast <4 x i8> %x to i32
%extracted = call i32 @llvm.pext.i32(i32 %bits, i32 50529027)
%packed = trunc i32 %extracted to i8
```
`50529027` is `0x03030303`. It selects bits `0..1` from each byte and packs
them into result bits `0..7`, producing:
```text
(x[0] & 3)
| ((x[1] & 3) << 2)
| ((x[2] & 3) << 4)
| ((x[3] & 3) << 6)
```
This looks suitable for an X86 target-aware combine when i32 PEXT is both legal
and profitable.
## Motivation
A complete optimized corpus contains 26 occurrences across five functions in
three ONNX Runtime x86-64-v4 modules. They all belong to the same `Int2x4`
packing lineage.
For the isolated x86-64-v4 case, the manually formed PEXT version lowers to:
```asm
vmovd %xmm0, %eax
movl $50529027, %ecx
pextl %ecx, %eax, %eax
retq
```
Local static measurements were:
| Metric | Current | PEXT |
| ------------------------------ | ------: | ---: |
| Executable instructions | 11 | 4 |
| `.text` bytes | 57 | 15 |
| Block reciprocal throughput | 3.2 | 1.0 |
These are code-generation and llvm-mca results, not application-level
benchmarks.
## Validation
The exact source and target relations were proven equivalent in both directions
with Alive2 for arbitrary `<4 x i8> %x`.
The current `default` pipeline retains the source form, while the explicit
`llvm.pext.i32` form selects native `pextl` for the tested x86-64-v4 target.
Contributor guide
Assessment
This issue has not been assessed yet.