[InstCombine] Fold byte-aligned lshr of a GEP-based load into the load offset
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
## Description
I found a missed optimization opportunity where LLVM emits a GEP-based wide integer load followed by a byte-aligned logical right shift, and then only the shifted high part is used.
For little-endian targets, the byte-aligned shift can be folded into the GEP/load offset and the wide load can be replaced by a narrower load.
For example:
```llvm
%p = getelementptr inbounds i8, ptr %base, i64 64
%v = load i64, ptr %p, align 8
%shr = lshr i64 %v, 32
%r = trunc i64 %shr to i32
```
The result is the high 32 bits of the i64 loaded from %base + 64.
On little-endian targets, those bits are exactly the 4 bytes at %base + 68, so this can be represented as:
```llvm
%p = getelementptr i8, ptr %base, i64 68
%r = load i32, ptr %p, align 4
```
So the intended transform is:
trunc (lshr (load i64, gep(base, 64)), 32) to i32
=>
load i32, gep(base, 68)
This removes the wide i64 load and the lshr, and folds the byte portion of the shift into the GEP offset.
### General pattern
For little-endian targets, when a GEP-based load is shifted right by a byte-aligned amount and only the shifted high part is used:
```llvm
%p = getelementptr ..., ptr %base, ..., ConstOffset
%v = load iN, ptr %p
%shr = lshr iN %v, C
%r = trunc iN %shr to iM
```
where:
```
C % 8 == 0
M == N - C
M is a natural integer width, such as 8, 16, or 32
```
this can be folded into:
```
load iM from ConstOffset + C / 8
```
Alive Proof : https://alive2.llvm.org/ce/z/ysRAPJ
RealWorld Usage : ruby/vm.ll:43029
Contributor guide
Assessment
This issue has not been assessed yet.