llvm / llvm/llvm-project

[InstCombine] Fold byte-aligned lshr of a GEP-based load into the load offset

Open
#198,082 0 comments 0 reactions 1 assignee Claimed by @ParkHanbum View on GitHub
llvm:instcombine missed-optimization
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.