Skip to content

Commit 671cef0

Browse files
authored
[AggressiveInstcombine] Fold away shift in or reduction chain. (#137875)
If we have `icmp eq or(a, shl(b)), 0` then the shift can be removed so long as it is nuw or nsw. It is still comparing that some bits are non-zero. https://alive2.llvm.org/ce/z/nhrBVX. This is also true of ne, and true for longer or chains.
1 parent 69f4e60 commit 671cef0

File tree

2 files changed

+65
-18
lines changed

2 files changed

+65
-18
lines changed

llvm/lib/Transforms/AggressiveInstCombine/AggressiveInstCombine.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,62 @@ static bool foldConsecutiveLoads(Instruction &I, const DataLayout &DL,
827827
return true;
828828
}
829829

830+
/// Combine away instructions providing they are still equivalent when compared
831+
/// against 0. i.e do they have any bits set.
832+
static Value *optimizeShiftInOrChain(Value *V, IRBuilder<> &Builder) {
833+
auto *I = dyn_cast<Instruction>(V);
834+
if (!I || I->getOpcode() != Instruction::Or || !I->hasOneUse())
835+
return nullptr;
836+
837+
Value *A;
838+
839+
// Look deeper into the chain of or's, combining away shl (so long as they are
840+
// nuw or nsw).
841+
Value *Op0 = I->getOperand(0);
842+
if (match(Op0, m_CombineOr(m_NSWShl(m_Value(A), m_Value()),
843+
m_NUWShl(m_Value(A), m_Value()))))
844+
Op0 = A;
845+
else if (auto *NOp = optimizeShiftInOrChain(Op0, Builder))
846+
Op0 = NOp;
847+
848+
Value *Op1 = I->getOperand(1);
849+
if (match(Op1, m_CombineOr(m_NSWShl(m_Value(A), m_Value()),
850+
m_NUWShl(m_Value(A), m_Value()))))
851+
Op1 = A;
852+
else if (auto *NOp = optimizeShiftInOrChain(Op1, Builder))
853+
Op1 = NOp;
854+
855+
if (Op0 != I->getOperand(0) || Op1 != I->getOperand(1))
856+
return Builder.CreateOr(Op0, Op1);
857+
return nullptr;
858+
}
859+
860+
static bool foldICmpOrChain(Instruction &I, const DataLayout &DL,
861+
TargetTransformInfo &TTI, AliasAnalysis &AA,
862+
const DominatorTree &DT) {
863+
CmpPredicate Pred;
864+
Value *Op0;
865+
if (!match(&I, m_ICmp(Pred, m_Value(Op0), m_Zero())) ||
866+
!ICmpInst::isEquality(Pred))
867+
return false;
868+
869+
// If the chain or or's matches a load, combine to that before attempting to
870+
// remove shifts.
871+
if (auto OpI = dyn_cast<Instruction>(Op0))
872+
if (OpI->getOpcode() == Instruction::Or)
873+
if (foldConsecutiveLoads(*OpI, DL, TTI, AA, DT))
874+
return true;
875+
876+
IRBuilder<> Builder(&I);
877+
// icmp eq/ne or(shl(a), b), 0 -> icmp eq/ne or(a, b), 0
878+
if (auto *Res = optimizeShiftInOrChain(Op0, Builder)) {
879+
I.replaceAllUsesWith(Builder.CreateICmp(Pred, Res, I.getOperand(1)));
880+
return true;
881+
}
882+
883+
return false;
884+
}
885+
830886
// Calculate GEP Stride and accumulated const ModOffset. Return Stride and
831887
// ModOffset
832888
static std::pair<APInt, APInt>
@@ -1253,6 +1309,7 @@ static bool foldUnusualPatterns(Function &F, DominatorTree &DT,
12531309
MadeChange |= tryToRecognizeTableBasedCttz(I);
12541310
MadeChange |= foldConsecutiveLoads(I, DL, TTI, AA, DT);
12551311
MadeChange |= foldPatternedLoads(I, DL);
1312+
MadeChange |= foldICmpOrChain(I, DL, TTI, AA, DT);
12561313
// NOTE: This function introduces erasing of the instruction `I`, so it
12571314
// needs to be called at the end of this sequence, otherwise we may make
12581315
// bugs.

llvm/test/Transforms/AggressiveInstCombine/or-shift-chain.ll

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33

44
define i1 @remove_shift_nuw_ab(i8 %a, i8 %b, i8 %s) {
55
; CHECK-LABEL: @remove_shift_nuw_ab(
6-
; CHECK-NEXT: [[T:%.*]] = shl nuw i8 [[A:%.*]], [[S:%.*]]
7-
; CHECK-NEXT: [[OR:%.*]] = or i8 [[T]], [[B:%.*]]
6+
; CHECK-NEXT: [[OR:%.*]] = or i8 [[T:%.*]], [[B:%.*]]
87
; CHECK-NEXT: [[IC:%.*]] = icmp eq i8 [[OR]], 0
98
; CHECK-NEXT: ret i1 [[IC]]
109
;
@@ -16,8 +15,7 @@ define i1 @remove_shift_nuw_ab(i8 %a, i8 %b, i8 %s) {
1615

1716
define i1 @remove_shift_nuw_ba(i8 %a, i8 %b, i8 %s) {
1817
; CHECK-LABEL: @remove_shift_nuw_ba(
19-
; CHECK-NEXT: [[T:%.*]] = shl nuw i8 [[A:%.*]], [[S:%.*]]
20-
; CHECK-NEXT: [[OR:%.*]] = or i8 [[B:%.*]], [[T]]
18+
; CHECK-NEXT: [[OR:%.*]] = or i8 [[B:%.*]], [[T:%.*]]
2119
; CHECK-NEXT: [[IC:%.*]] = icmp eq i8 [[OR]], 0
2220
; CHECK-NEXT: ret i1 [[IC]]
2321
;
@@ -29,8 +27,7 @@ define i1 @remove_shift_nuw_ba(i8 %a, i8 %b, i8 %s) {
2927

3028
define i1 @remove_shift_nsw(i8 %a, i8 %b, i8 %s) {
3129
; CHECK-LABEL: @remove_shift_nsw(
32-
; CHECK-NEXT: [[T:%.*]] = shl nsw i8 [[A:%.*]], [[S:%.*]]
33-
; CHECK-NEXT: [[OR:%.*]] = or i8 [[T]], [[B:%.*]]
30+
; CHECK-NEXT: [[OR:%.*]] = or i8 [[T:%.*]], [[B:%.*]]
3431
; CHECK-NEXT: [[IC:%.*]] = icmp eq i8 [[OR]], 0
3532
; CHECK-NEXT: ret i1 [[IC]]
3633
;
@@ -42,8 +39,7 @@ define i1 @remove_shift_nsw(i8 %a, i8 %b, i8 %s) {
4239

4340
define i1 @remove_shift_nuw_ne(i8 %a, i8 %b, i8 %s) {
4441
; CHECK-LABEL: @remove_shift_nuw_ne(
45-
; CHECK-NEXT: [[T:%.*]] = shl nuw i8 [[A:%.*]], [[S:%.*]]
46-
; CHECK-NEXT: [[OR:%.*]] = or i8 [[T]], [[B:%.*]]
42+
; CHECK-NEXT: [[OR:%.*]] = or i8 [[T:%.*]], [[B:%.*]]
4743
; CHECK-NEXT: [[IC:%.*]] = icmp eq i8 [[OR]], 0
4844
; CHECK-NEXT: ret i1 [[IC]]
4945
;
@@ -55,8 +51,7 @@ define i1 @remove_shift_nuw_ne(i8 %a, i8 %b, i8 %s) {
5551

5652
define i1 @remove_shift_nsw_ne(i8 %a, i8 %b, i8 %s) {
5753
; CHECK-LABEL: @remove_shift_nsw_ne(
58-
; CHECK-NEXT: [[T:%.*]] = shl nsw i8 [[A:%.*]], [[S:%.*]]
59-
; CHECK-NEXT: [[OR:%.*]] = or i8 [[T]], [[B:%.*]]
54+
; CHECK-NEXT: [[OR:%.*]] = or i8 [[T:%.*]], [[B:%.*]]
6055
; CHECK-NEXT: [[IC:%.*]] = icmp eq i8 [[OR]], 0
6156
; CHECK-NEXT: ret i1 [[IC]]
6257
;
@@ -81,9 +76,8 @@ define i1 @remove_shift_wraps(i8 %a, i8 %b, i8 %s) {
8176

8277
define i1 @remove_shift_chain_d(i8 %a, i8 %b, i8 %c, i8 %d, i8 %s) {
8378
; CHECK-LABEL: @remove_shift_chain_d(
84-
; CHECK-NEXT: [[DT:%.*]] = shl nuw i8 [[D:%.*]], [[S:%.*]]
8579
; CHECK-NEXT: [[OR1:%.*]] = or i8 [[A:%.*]], [[B:%.*]]
86-
; CHECK-NEXT: [[OR2:%.*]] = or i8 [[C:%.*]], [[DT]]
80+
; CHECK-NEXT: [[OR2:%.*]] = or i8 [[C:%.*]], [[DT:%.*]]
8781
; CHECK-NEXT: [[OR:%.*]] = or i8 [[OR1]], [[OR2]]
8882
; CHECK-NEXT: [[IC:%.*]] = icmp eq i8 [[OR]], 0
8983
; CHECK-NEXT: ret i1 [[IC]]
@@ -98,12 +92,8 @@ define i1 @remove_shift_chain_d(i8 %a, i8 %b, i8 %c, i8 %d, i8 %s) {
9892

9993
define i1 @remove_shift_chain_abcd(i8 %a, i8 %b, i8 %c, i8 %d, i8 %s) {
10094
; CHECK-LABEL: @remove_shift_chain_abcd(
101-
; CHECK-NEXT: [[AT:%.*]] = shl nuw i8 [[A:%.*]], [[S:%.*]]
102-
; CHECK-NEXT: [[BT:%.*]] = shl nuw i8 [[B:%.*]], 2
103-
; CHECK-NEXT: [[CT:%.*]] = shl nuw i8 [[C:%.*]], 1
104-
; CHECK-NEXT: [[DT:%.*]] = shl nuw i8 [[D:%.*]], [[S]]
105-
; CHECK-NEXT: [[OR1:%.*]] = or i8 [[AT]], [[BT]]
106-
; CHECK-NEXT: [[OR2:%.*]] = or i8 [[CT]], [[DT]]
95+
; CHECK-NEXT: [[OR1:%.*]] = or i8 [[AT:%.*]], [[BT:%.*]]
96+
; CHECK-NEXT: [[OR2:%.*]] = or i8 [[CT:%.*]], [[DT:%.*]]
10797
; CHECK-NEXT: [[OR:%.*]] = or i8 [[OR1]], [[OR2]]
10898
; CHECK-NEXT: [[IC:%.*]] = icmp eq i8 [[OR]], 0
10999
; CHECK-NEXT: ret i1 [[IC]]

0 commit comments

Comments
 (0)