[llvm] [Instcombine] Fold away shift in or reduction chain. (PR #137875)

David Green via llvm-commits llvm-commits at lists.llvm.org
Thu May 8 03:43:27 PDT 2025


https://github.com/davemgreen updated https://github.com/llvm/llvm-project/pull/137875

>From 2a9b2f889b2a09562cede2196a6a3de0a6a43ad9 Mon Sep 17 00:00:00 2001
From: David Green <david.green at arm.com>
Date: Tue, 29 Apr 2025 17:07:01 +0100
Subject: [PATCH 1/4] [InstCombine] Add test for shifts from or chains

---
 .../Transforms/InstCombine/icmp-of-or-x.ll    | 119 ++++++++++++++++++
 1 file changed, 119 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/icmp-of-or-x.ll b/llvm/test/Transforms/InstCombine/icmp-of-or-x.ll
index 993325f6ff0b0..d06a2f6526ac0 100644
--- a/llvm/test/Transforms/InstCombine/icmp-of-or-x.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-of-or-x.ll
@@ -409,3 +409,122 @@ define i1 @PR38139(i8 %arg) {
   %r = icmp ne i8 %masked, %arg
   ret i1 %r
 }
+
+define i1 @remove_shift_nuw_ab(i8 %a, i8 %b, i8 %s) {
+; CHECK-LABEL: @remove_shift_nuw_ab(
+; CHECK-NEXT:    [[T:%.*]] = shl nuw i8 [[A:%.*]], [[S:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i8 [[T]], [[B:%.*]]
+; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
+; CHECK-NEXT:    ret i1 [[IC]]
+;
+  %t = shl nuw i8 %a, %s
+  %or = or i8 %t, %b
+  %ic = icmp eq i8 %or, 0
+  ret i1 %ic
+}
+
+define i1 @remove_shift_nuw_ba(i8 %a, i8 %b, i8 %s) {
+; CHECK-LABEL: @remove_shift_nuw_ba(
+; CHECK-NEXT:    [[T:%.*]] = shl nuw i8 [[A:%.*]], [[S:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i8 [[B:%.*]], [[T]]
+; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
+; CHECK-NEXT:    ret i1 [[IC]]
+;
+  %t = shl nuw i8 %a, %s
+  %or = or i8 %b, %t
+  %ic = icmp eq i8 %or, 0
+  ret i1 %ic
+}
+
+define i1 @remove_shift_nsw(i8 %a, i8 %b, i8 %s) {
+; CHECK-LABEL: @remove_shift_nsw(
+; CHECK-NEXT:    [[T:%.*]] = shl nsw i8 [[A:%.*]], [[S:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i8 [[T]], [[B:%.*]]
+; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
+; CHECK-NEXT:    ret i1 [[IC]]
+;
+  %t = shl nsw i8 %a, %s
+  %or = or i8 %t, %b
+  %ic = icmp eq i8 %or, 0
+  ret i1 %ic
+}
+
+define i1 @remove_shift_nuw_ne(i8 %a, i8 %b, i8 %s) {
+; CHECK-LABEL: @remove_shift_nuw_ne(
+; CHECK-NEXT:    [[T:%.*]] = shl nuw i8 [[A:%.*]], [[S:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i8 [[T]], [[B:%.*]]
+; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
+; CHECK-NEXT:    ret i1 [[IC]]
+;
+  %t = shl nuw i8 %a, %s
+  %or = or i8 %t, %b
+  %ic = icmp eq i8 %or, 0
+  ret i1 %ic
+}
+
+define i1 @remove_shift_nsw_ne(i8 %a, i8 %b, i8 %s) {
+; CHECK-LABEL: @remove_shift_nsw_ne(
+; CHECK-NEXT:    [[T:%.*]] = shl nsw i8 [[A:%.*]], [[S:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i8 [[T]], [[B:%.*]]
+; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
+; CHECK-NEXT:    ret i1 [[IC]]
+;
+  %t = shl nsw i8 %a, %s
+  %or = or i8 %t, %b
+  %ic = icmp eq i8 %or, 0
+  ret i1 %ic
+}
+
+define i1 @remove_shift_wraps(i8 %a, i8 %b, i8 %s) {
+; CHECK-LABEL: @remove_shift_wraps(
+; CHECK-NEXT:    [[T:%.*]] = shl i8 [[A:%.*]], [[S:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i8 [[T]], [[B:%.*]]
+; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
+; CHECK-NEXT:    ret i1 [[IC]]
+;
+  %t = shl i8 %a, %s
+  %or = or i8 %t, %b
+  %ic = icmp eq i8 %or, 0
+  ret i1 %ic
+}
+
+define i1 @remove_shift_chain_d(i8 %a, i8 %b, i8 %c, i8 %d, i8 %s) {
+; CHECK-LABEL: @remove_shift_chain_d(
+; CHECK-NEXT:    [[DT:%.*]] = shl nuw i8 [[D:%.*]], [[S:%.*]]
+; CHECK-NEXT:    [[OR1:%.*]] = or i8 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    [[OR2:%.*]] = or i8 [[C:%.*]], [[DT]]
+; CHECK-NEXT:    [[OR:%.*]] = or i8 [[OR1]], [[OR2]]
+; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
+; CHECK-NEXT:    ret i1 [[IC]]
+;
+  %dt = shl nuw i8 %d, %s
+  %or1 = or i8 %a, %b
+  %or2 = or i8 %c, %dt
+  %or = or i8 %or1, %or2
+  %ic = icmp eq i8 %or, 0
+  ret i1 %ic
+}
+
+define i1 @remove_shift_chain_abcd(i8 %a, i8 %b, i8 %c, i8 %d, i8 %s) {
+; CHECK-LABEL: @remove_shift_chain_abcd(
+; CHECK-NEXT:    [[AT:%.*]] = shl nuw i8 [[A:%.*]], [[S:%.*]]
+; CHECK-NEXT:    [[BT:%.*]] = shl nuw i8 [[B:%.*]], 2
+; CHECK-NEXT:    [[CT:%.*]] = shl nuw i8 [[C:%.*]], 1
+; CHECK-NEXT:    [[DT:%.*]] = shl nuw i8 [[D:%.*]], [[S]]
+; CHECK-NEXT:    [[OR1:%.*]] = or i8 [[AT]], [[BT]]
+; CHECK-NEXT:    [[OR2:%.*]] = or i8 [[CT]], [[DT]]
+; CHECK-NEXT:    [[OR:%.*]] = or i8 [[OR1]], [[OR2]]
+; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
+; CHECK-NEXT:    ret i1 [[IC]]
+;
+  %at = shl nuw i8 %a, %s
+  %bt = shl nuw i8 %b, 2
+  %ct = shl nuw i8 %c, 1
+  %dt = shl nuw i8 %d, %s
+  %or1 = or i8 %at, %bt
+  %or2 = or i8 %ct, %dt
+  %or = or i8 %or1, %or2
+  %ic = icmp eq i8 %or, 0
+  ret i1 %ic
+}
+

>From 33d077ddf16fd3db24a4d8a6f3339aa1cae5bdd2 Mon Sep 17 00:00:00 2001
From: David Green <david.green at arm.com>
Date: Tue, 29 Apr 2025 19:08:01 +0100
Subject: [PATCH 2/4] [Instcombine] Fold away shift in or reduction chain.

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 the same bits against 0.
https://alive2.llvm.org/ce/z/nhrBVX.

This is also true of ne, and true of longer or chains.

Thinking out loud, this is kind of like a "are any bits demanded" combine.
---
 .../InstCombine/InstCombineCompares.cpp       | 28 +++++++++++++++++++
 .../Transforms/InstCombine/icmp-of-or-x.ll    | 26 ++++++-----------
 2 files changed, 36 insertions(+), 18 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 252781e54ab06..e4d6179e6ed16 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -5046,6 +5046,29 @@ static Instruction *foldICmpOrXX(ICmpInst &I, const SimplifyQuery &Q,
   return nullptr;
 }
 
+static Value *foldShiftAwayFromOrChain(Instruction &I,
+                                       InstCombiner::BuilderTy &Builder) {
+  if (I.getOpcode() != Instruction::Or)
+    return nullptr;
+  Value *A, *B;
+  if (match(&I, m_c_Or(m_CombineOr(m_NSWShl(m_Value(A), m_Value()),
+                                   m_NUWShl(m_Value(A), m_Value())),
+                       m_Value(B))))
+    return Builder.CreateOr(A, B);
+
+  Value *Op0 = I.getOperand(0);
+  if (isa<Instruction>(Op0))
+    if (auto *X = foldShiftAwayFromOrChain(*cast<Instruction>(Op0), Builder))
+      Op0 = X;
+  Value *Op1 = I.getOperand(1);
+  if (isa<Instruction>(Op1))
+    if (auto *X = foldShiftAwayFromOrChain(*cast<Instruction>(Op1), Builder))
+      Op1 = X;
+  if (Op0 != I.getOperand(0) || Op1 != I.getOperand(1))
+    return Builder.CreateOr(Op0, Op1);
+  return nullptr;
+}
+
 static Instruction *foldICmpXorXX(ICmpInst &I, const SimplifyQuery &Q,
                                   InstCombinerImpl &IC) {
   Value *Op0 = I.getOperand(0), *Op1 = I.getOperand(1), *A;
@@ -7869,6 +7892,11 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) {
     }
   }
 
+  // icmp eq/ne or(shl(a), b), 0 -> icmp eq/ne or(a, b)
+  if (I.isEquality() && match(Op1, m_Zero()) && isa<Instruction>(Op0))
+    if (auto *Res = foldShiftAwayFromOrChain(*cast<Instruction>(Op0), Builder))
+      return new ICmpInst(I.getPredicate(), Res, Op1);
+
   return Changed ? &I : nullptr;
 }
 
diff --git a/llvm/test/Transforms/InstCombine/icmp-of-or-x.ll b/llvm/test/Transforms/InstCombine/icmp-of-or-x.ll
index d06a2f6526ac0..baaa754e52894 100644
--- a/llvm/test/Transforms/InstCombine/icmp-of-or-x.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-of-or-x.ll
@@ -412,8 +412,7 @@ define i1 @PR38139(i8 %arg) {
 
 define i1 @remove_shift_nuw_ab(i8 %a, i8 %b, i8 %s) {
 ; CHECK-LABEL: @remove_shift_nuw_ab(
-; CHECK-NEXT:    [[T:%.*]] = shl nuw i8 [[A:%.*]], [[S:%.*]]
-; CHECK-NEXT:    [[OR:%.*]] = or i8 [[T]], [[B:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i8 [[T:%.*]], [[B:%.*]]
 ; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
 ; CHECK-NEXT:    ret i1 [[IC]]
 ;
@@ -425,8 +424,7 @@ define i1 @remove_shift_nuw_ab(i8 %a, i8 %b, i8 %s) {
 
 define i1 @remove_shift_nuw_ba(i8 %a, i8 %b, i8 %s) {
 ; CHECK-LABEL: @remove_shift_nuw_ba(
-; CHECK-NEXT:    [[T:%.*]] = shl nuw i8 [[A:%.*]], [[S:%.*]]
-; CHECK-NEXT:    [[OR:%.*]] = or i8 [[B:%.*]], [[T]]
+; CHECK-NEXT:    [[OR:%.*]] = or i8 [[B:%.*]], [[T:%.*]]
 ; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
 ; CHECK-NEXT:    ret i1 [[IC]]
 ;
@@ -438,8 +436,7 @@ define i1 @remove_shift_nuw_ba(i8 %a, i8 %b, i8 %s) {
 
 define i1 @remove_shift_nsw(i8 %a, i8 %b, i8 %s) {
 ; CHECK-LABEL: @remove_shift_nsw(
-; CHECK-NEXT:    [[T:%.*]] = shl nsw i8 [[A:%.*]], [[S:%.*]]
-; CHECK-NEXT:    [[OR:%.*]] = or i8 [[T]], [[B:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i8 [[T:%.*]], [[B:%.*]]
 ; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
 ; CHECK-NEXT:    ret i1 [[IC]]
 ;
@@ -451,8 +448,7 @@ define i1 @remove_shift_nsw(i8 %a, i8 %b, i8 %s) {
 
 define i1 @remove_shift_nuw_ne(i8 %a, i8 %b, i8 %s) {
 ; CHECK-LABEL: @remove_shift_nuw_ne(
-; CHECK-NEXT:    [[T:%.*]] = shl nuw i8 [[A:%.*]], [[S:%.*]]
-; CHECK-NEXT:    [[OR:%.*]] = or i8 [[T]], [[B:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i8 [[T:%.*]], [[B:%.*]]
 ; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
 ; CHECK-NEXT:    ret i1 [[IC]]
 ;
@@ -464,8 +460,7 @@ define i1 @remove_shift_nuw_ne(i8 %a, i8 %b, i8 %s) {
 
 define i1 @remove_shift_nsw_ne(i8 %a, i8 %b, i8 %s) {
 ; CHECK-LABEL: @remove_shift_nsw_ne(
-; CHECK-NEXT:    [[T:%.*]] = shl nsw i8 [[A:%.*]], [[S:%.*]]
-; CHECK-NEXT:    [[OR:%.*]] = or i8 [[T]], [[B:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i8 [[T:%.*]], [[B:%.*]]
 ; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
 ; CHECK-NEXT:    ret i1 [[IC]]
 ;
@@ -490,9 +485,8 @@ define i1 @remove_shift_wraps(i8 %a, i8 %b, i8 %s) {
 
 define i1 @remove_shift_chain_d(i8 %a, i8 %b, i8 %c, i8 %d, i8 %s) {
 ; CHECK-LABEL: @remove_shift_chain_d(
-; CHECK-NEXT:    [[DT:%.*]] = shl nuw i8 [[D:%.*]], [[S:%.*]]
 ; CHECK-NEXT:    [[OR1:%.*]] = or i8 [[A:%.*]], [[B:%.*]]
-; CHECK-NEXT:    [[OR2:%.*]] = or i8 [[C:%.*]], [[DT]]
+; CHECK-NEXT:    [[OR2:%.*]] = or i8 [[C:%.*]], [[DT:%.*]]
 ; CHECK-NEXT:    [[OR:%.*]] = or i8 [[OR1]], [[OR2]]
 ; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
 ; CHECK-NEXT:    ret i1 [[IC]]
@@ -507,12 +501,8 @@ define i1 @remove_shift_chain_d(i8 %a, i8 %b, i8 %c, i8 %d, i8 %s) {
 
 define i1 @remove_shift_chain_abcd(i8 %a, i8 %b, i8 %c, i8 %d, i8 %s) {
 ; CHECK-LABEL: @remove_shift_chain_abcd(
-; CHECK-NEXT:    [[AT:%.*]] = shl nuw i8 [[A:%.*]], [[S:%.*]]
-; CHECK-NEXT:    [[BT:%.*]] = shl nuw i8 [[B:%.*]], 2
-; CHECK-NEXT:    [[CT:%.*]] = shl nuw i8 [[C:%.*]], 1
-; CHECK-NEXT:    [[DT:%.*]] = shl nuw i8 [[D:%.*]], [[S]]
-; CHECK-NEXT:    [[OR1:%.*]] = or i8 [[AT]], [[BT]]
-; CHECK-NEXT:    [[OR2:%.*]] = or i8 [[CT]], [[DT]]
+; CHECK-NEXT:    [[OR1:%.*]] = or i8 [[AT:%.*]], [[BT:%.*]]
+; CHECK-NEXT:    [[OR2:%.*]] = or i8 [[CT:%.*]], [[DT:%.*]]
 ; CHECK-NEXT:    [[OR:%.*]] = or i8 [[OR1]], [[OR2]]
 ; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
 ; CHECK-NEXT:    ret i1 [[IC]]

>From e7d14ce2ea277ff35b40ae7320c4a325e296858f Mon Sep 17 00:00:00 2001
From: David Green <david.green at arm.com>
Date: Wed, 30 Apr 2025 20:09:51 +0100
Subject: [PATCH 3/4] Address comments

---
 .../InstCombine/InstCombineCompares.cpp       | 61 ++++++++++---------
 .../Transforms/InstCombine/icmp-of-or-x.ll    | 24 ++++++++
 2 files changed, 57 insertions(+), 28 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index e4d6179e6ed16..f38147d2ce0c4 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -1223,6 +1223,33 @@ Instruction *InstCombinerImpl::foldSignBitTest(ICmpInst &I) {
                           X, ConstantInt::getNullValue(XTy));
 }
 
+/// Combine away instructions providing they are still equivalent when compared
+/// against 0. i.e do they have any bits set.
+static Value *combineAwayHasAnyBitsSetChain(Value *V,
+                                            InstCombiner::BuilderTy &Builder) {
+  auto *I = dyn_cast<Instruction>(V);
+  if (!I || I->getOpcode() != Instruction::Or || !I->hasOneUse())
+    return nullptr;
+
+  // Remove the shl in or(shl(x, y), z) so long as the shl is nuw or nsw.
+  Value *A, *B;
+  if (match(I, m_c_Or(m_CombineOr(m_NSWShl(m_Value(A), m_Value()),
+                                  m_NUWShl(m_Value(A), m_Value())),
+                      m_Value(B))))
+    return Builder.CreateOr(A, B);
+
+  // Look deeper into the chain of or's.
+  Value *Op0 = I->getOperand(0);
+  if (auto *NOp = combineAwayHasAnyBitsSetChain(Op0, Builder))
+    Op0 = NOp;
+  Value *Op1 = I->getOperand(1);
+  if (auto *NOp = combineAwayHasAnyBitsSetChain(Op1, Builder))
+    Op1 = NOp;
+  if (Op0 != I->getOperand(0) || Op1 != I->getOperand(1))
+    return Builder.CreateOr(Op0, Op1);
+  return nullptr;
+}
+
 // Handle  icmp pred X, 0
 Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) {
   CmpInst::Predicate Pred = Cmp.getPredicate();
@@ -1298,6 +1325,12 @@ Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) {
     // eq/ne (mul X, Y)) with (icmp eq/ne X/Y) and if X/Y is known non-zero that
     // will fold to a constant elsewhere.
   }
+
+  // icmp eq/ne or(shl(a), b), 0 -> icmp eq/ne or(a, b), 0
+  if (ICmpInst::isEquality(Pred))
+    if (auto *Res = combineAwayHasAnyBitsSetChain(Cmp.getOperand(0), Builder))
+      return new ICmpInst(Pred, Res, Cmp.getOperand(1));
+
   return nullptr;
 }
 
@@ -5046,29 +5079,6 @@ static Instruction *foldICmpOrXX(ICmpInst &I, const SimplifyQuery &Q,
   return nullptr;
 }
 
-static Value *foldShiftAwayFromOrChain(Instruction &I,
-                                       InstCombiner::BuilderTy &Builder) {
-  if (I.getOpcode() != Instruction::Or)
-    return nullptr;
-  Value *A, *B;
-  if (match(&I, m_c_Or(m_CombineOr(m_NSWShl(m_Value(A), m_Value()),
-                                   m_NUWShl(m_Value(A), m_Value())),
-                       m_Value(B))))
-    return Builder.CreateOr(A, B);
-
-  Value *Op0 = I.getOperand(0);
-  if (isa<Instruction>(Op0))
-    if (auto *X = foldShiftAwayFromOrChain(*cast<Instruction>(Op0), Builder))
-      Op0 = X;
-  Value *Op1 = I.getOperand(1);
-  if (isa<Instruction>(Op1))
-    if (auto *X = foldShiftAwayFromOrChain(*cast<Instruction>(Op1), Builder))
-      Op1 = X;
-  if (Op0 != I.getOperand(0) || Op1 != I.getOperand(1))
-    return Builder.CreateOr(Op0, Op1);
-  return nullptr;
-}
-
 static Instruction *foldICmpXorXX(ICmpInst &I, const SimplifyQuery &Q,
                                   InstCombinerImpl &IC) {
   Value *Op0 = I.getOperand(0), *Op1 = I.getOperand(1), *A;
@@ -7892,11 +7902,6 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) {
     }
   }
 
-  // icmp eq/ne or(shl(a), b), 0 -> icmp eq/ne or(a, b)
-  if (I.isEquality() && match(Op1, m_Zero()) && isa<Instruction>(Op0))
-    if (auto *Res = foldShiftAwayFromOrChain(*cast<Instruction>(Op0), Builder))
-      return new ICmpInst(I.getPredicate(), Res, Op1);
-
   return Changed ? &I : nullptr;
 }
 
diff --git a/llvm/test/Transforms/InstCombine/icmp-of-or-x.ll b/llvm/test/Transforms/InstCombine/icmp-of-or-x.ll
index baaa754e52894..83fe9c8fe4bdc 100644
--- a/llvm/test/Transforms/InstCombine/icmp-of-or-x.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-of-or-x.ll
@@ -518,3 +518,27 @@ define i1 @remove_shift_chain_abcd(i8 %a, i8 %b, i8 %c, i8 %d, i8 %s) {
   ret i1 %ic
 }
 
+define i1 @remove_shift_chain_abcd_multiuse(i8 %a, i8 %b, i8 %c, i8 %d, i8 %s) {
+; CHECK-LABEL: @remove_shift_chain_abcd_multiuse(
+; CHECK-NEXT:    [[AT:%.*]] = shl nuw i8 [[A:%.*]], [[S:%.*]]
+; CHECK-NEXT:    [[BT:%.*]] = shl nuw i8 [[B:%.*]], 2
+; CHECK-NEXT:    [[CT:%.*]] = shl nuw i8 [[C:%.*]], 1
+; CHECK-NEXT:    [[DT:%.*]] = shl nuw i8 [[D:%.*]], [[S]]
+; CHECK-NEXT:    [[OR1:%.*]] = or i8 [[AT]], [[BT]]
+; CHECK-NEXT:    [[OR2:%.*]] = or i8 [[CT]], [[DT]]
+; CHECK-NEXT:    [[OR:%.*]] = or i8 [[OR1]], [[OR2]]
+; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
+; CHECK-NEXT:    call void @use.i8(i8 [[OR]])
+; CHECK-NEXT:    ret i1 [[IC]]
+;
+  %at = shl nuw i8 %a, %s
+  %bt = shl nuw i8 %b, 2
+  %ct = shl nuw i8 %c, 1
+  %dt = shl nuw i8 %d, %s
+  %or1 = or i8 %at, %bt
+  %or2 = or i8 %ct, %dt
+  %or = or i8 %or1, %or2
+  %ic = icmp eq i8 %or, 0
+  call void @use.i8(i8 %or)
+  ret i1 %ic
+}

>From fc1184ac4796056fe4c83c35a9f409d90a396d74 Mon Sep 17 00:00:00 2001
From: David Green <david.green at arm.com>
Date: Thu, 8 May 2025 11:32:39 +0100
Subject: [PATCH 4/4] Move to AggressiveInstCombine

---
 .../AggressiveInstCombine.cpp                 |  57 ++++++++
 .../InstCombine/InstCombineCompares.cpp       |  33 -----
 .../AggressiveInstCombine/AArch64/or-load.ll  |  52 +++++++
 .../AggressiveInstCombine/or-shift-chain.ll   | 137 ++++++++++++++++++
 .../Transforms/InstCombine/icmp-of-or-x.ll    | 133 -----------------
 5 files changed, 246 insertions(+), 166 deletions(-)
 create mode 100644 llvm/test/Transforms/AggressiveInstCombine/or-shift-chain.ll

diff --git a/llvm/lib/Transforms/AggressiveInstCombine/AggressiveInstCombine.cpp b/llvm/lib/Transforms/AggressiveInstCombine/AggressiveInstCombine.cpp
index af994022a8ec1..8f1a216004b08 100644
--- a/llvm/lib/Transforms/AggressiveInstCombine/AggressiveInstCombine.cpp
+++ b/llvm/lib/Transforms/AggressiveInstCombine/AggressiveInstCombine.cpp
@@ -827,6 +827,62 @@ static bool foldConsecutiveLoads(Instruction &I, const DataLayout &DL,
   return true;
 }
 
+/// Combine away instructions providing they are still equivalent when compared
+/// against 0. i.e do they have any bits set.
+static Value *optimizeShiftInOrChain(Value *V, IRBuilder<> &Builder) {
+  auto *I = dyn_cast<Instruction>(V);
+  if (!I || I->getOpcode() != Instruction::Or || !I->hasOneUse())
+    return nullptr;
+
+  Value *A;
+
+  // Look deeper into the chain of or's, combining away shl (so long as they are
+  // nuw or nsw).
+  Value *Op0 = I->getOperand(0);
+  if (match(Op0, m_CombineOr(m_NSWShl(m_Value(A), m_Value()),
+                             m_NUWShl(m_Value(A), m_Value()))))
+    Op0 = A;
+  else if (auto *NOp = optimizeShiftInOrChain(Op0, Builder))
+    Op0 = NOp;
+
+  Value *Op1 = I->getOperand(1);
+  if (match(Op1, m_CombineOr(m_NSWShl(m_Value(A), m_Value()),
+                             m_NUWShl(m_Value(A), m_Value()))))
+    Op1 = A;
+  else if (auto *NOp = optimizeShiftInOrChain(Op1, Builder))
+    Op1 = NOp;
+
+  if (Op0 != I->getOperand(0) || Op1 != I->getOperand(1))
+    return Builder.CreateOr(Op0, Op1);
+  return nullptr;
+}
+
+static bool foldICmpOrChain(Instruction &I, const DataLayout &DL,
+                            TargetTransformInfo &TTI, AliasAnalysis &AA,
+                            const DominatorTree &DT) {
+  CmpPredicate Pred;
+  Value *Op0;
+  if (!match(&I, m_ICmp(Pred, m_Value(Op0), m_Zero())) ||
+      !ICmpInst::isEquality(Pred))
+    return false;
+
+  // If the chain or or's matches a load, combine to that before attempting to
+  // remove shifts.
+  if (auto OpI = dyn_cast<Instruction>(Op0))
+    if (OpI->getOpcode() == Instruction::Or)
+      if (foldConsecutiveLoads(*OpI, DL, TTI, AA, DT))
+        return true;
+
+  IRBuilder<> Builder(&I);
+  // icmp eq/ne or(shl(a), b), 0 -> icmp eq/ne or(a, b), 0
+  if (auto *Res = optimizeShiftInOrChain(Op0, Builder)) {
+    I.replaceAllUsesWith(Builder.CreateICmp(Pred, Res, I.getOperand(1)));
+    return true;
+  }
+
+  return false;
+}
+
 // Calculate GEP Stride and accumulated const ModOffset. Return Stride and
 // ModOffset
 static std::pair<APInt, APInt>
@@ -1253,6 +1309,7 @@ static bool foldUnusualPatterns(Function &F, DominatorTree &DT,
       MadeChange |= tryToRecognizeTableBasedCttz(I);
       MadeChange |= foldConsecutiveLoads(I, DL, TTI, AA, DT);
       MadeChange |= foldPatternedLoads(I, DL);
+      MadeChange |= foldICmpOrChain(I, DL, TTI, AA, DT);
       // NOTE: This function introduces erasing of the instruction `I`, so it
       // needs to be called at the end of this sequence, otherwise we may make
       // bugs.
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index f38147d2ce0c4..252781e54ab06 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -1223,33 +1223,6 @@ Instruction *InstCombinerImpl::foldSignBitTest(ICmpInst &I) {
                           X, ConstantInt::getNullValue(XTy));
 }
 
-/// Combine away instructions providing they are still equivalent when compared
-/// against 0. i.e do they have any bits set.
-static Value *combineAwayHasAnyBitsSetChain(Value *V,
-                                            InstCombiner::BuilderTy &Builder) {
-  auto *I = dyn_cast<Instruction>(V);
-  if (!I || I->getOpcode() != Instruction::Or || !I->hasOneUse())
-    return nullptr;
-
-  // Remove the shl in or(shl(x, y), z) so long as the shl is nuw or nsw.
-  Value *A, *B;
-  if (match(I, m_c_Or(m_CombineOr(m_NSWShl(m_Value(A), m_Value()),
-                                  m_NUWShl(m_Value(A), m_Value())),
-                      m_Value(B))))
-    return Builder.CreateOr(A, B);
-
-  // Look deeper into the chain of or's.
-  Value *Op0 = I->getOperand(0);
-  if (auto *NOp = combineAwayHasAnyBitsSetChain(Op0, Builder))
-    Op0 = NOp;
-  Value *Op1 = I->getOperand(1);
-  if (auto *NOp = combineAwayHasAnyBitsSetChain(Op1, Builder))
-    Op1 = NOp;
-  if (Op0 != I->getOperand(0) || Op1 != I->getOperand(1))
-    return Builder.CreateOr(Op0, Op1);
-  return nullptr;
-}
-
 // Handle  icmp pred X, 0
 Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) {
   CmpInst::Predicate Pred = Cmp.getPredicate();
@@ -1325,12 +1298,6 @@ Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) {
     // eq/ne (mul X, Y)) with (icmp eq/ne X/Y) and if X/Y is known non-zero that
     // will fold to a constant elsewhere.
   }
-
-  // icmp eq/ne or(shl(a), b), 0 -> icmp eq/ne or(a, b), 0
-  if (ICmpInst::isEquality(Pred))
-    if (auto *Res = combineAwayHasAnyBitsSetChain(Cmp.getOperand(0), Builder))
-      return new ICmpInst(Pred, Res, Cmp.getOperand(1));
-
   return nullptr;
 }
 
diff --git a/llvm/test/Transforms/AggressiveInstCombine/AArch64/or-load.ll b/llvm/test/Transforms/AggressiveInstCombine/AArch64/or-load.ll
index 0e3fe56bb58de..cbe3c13c27238 100644
--- a/llvm/test/Transforms/AggressiveInstCombine/AArch64/or-load.ll
+++ b/llvm/test/Transforms/AggressiveInstCombine/AArch64/or-load.ll
@@ -2480,3 +2480,55 @@ define void @bitcast_gep(ptr %p, ptr %dest) {
   store i32 %trunc, ptr %dest, align 4
   ret void
 }
+
+define i1 @loadCombine_4consecutive_rev_icmp0(ptr %p) {
+; LE-LABEL: @loadCombine_4consecutive_rev_icmp0(
+; LE-NEXT:    [[L1:%.*]] = load i32, ptr [[P:%.*]], align 1
+; LE-NEXT:    [[C:%.*]] = icmp eq i32 [[L1]], 0
+; LE-NEXT:    ret i1 [[C]]
+;
+; BE-LABEL: @loadCombine_4consecutive_rev_icmp0(
+; BE-NEXT:    [[P1:%.*]] = getelementptr i8, ptr [[P:%.*]], i32 1
+; BE-NEXT:    [[P2:%.*]] = getelementptr i8, ptr [[P]], i32 2
+; BE-NEXT:    [[P3:%.*]] = getelementptr i8, ptr [[P]], i32 3
+; BE-NEXT:    [[L1:%.*]] = load i8, ptr [[P]], align 1
+; BE-NEXT:    [[L2:%.*]] = load i8, ptr [[P1]], align 1
+; BE-NEXT:    [[L3:%.*]] = load i8, ptr [[P2]], align 1
+; BE-NEXT:    [[L4:%.*]] = load i8, ptr [[P3]], align 1
+; BE-NEXT:    [[E1:%.*]] = zext i8 [[L1]] to i32
+; BE-NEXT:    [[E2:%.*]] = zext i8 [[L2]] to i32
+; BE-NEXT:    [[E3:%.*]] = zext i8 [[L3]] to i32
+; BE-NEXT:    [[E4:%.*]] = zext i8 [[L4]] to i32
+; BE-NEXT:    [[S2:%.*]] = shl i32 [[E2]], 8
+; BE-NEXT:    [[S3:%.*]] = shl i32 [[E3]], 16
+; BE-NEXT:    [[S4:%.*]] = shl i32 [[E4]], 24
+; BE-NEXT:    [[O1:%.*]] = or i32 [[S4]], [[S3]]
+; BE-NEXT:    [[O2:%.*]] = or i32 [[O1]], [[S2]]
+; BE-NEXT:    [[O3:%.*]] = or i32 [[O2]], [[E1]]
+; BE-NEXT:    [[C:%.*]] = icmp eq i32 [[O3]], 0
+; BE-NEXT:    ret i1 [[C]]
+;
+  %p1 = getelementptr i8, ptr %p, i32 1
+  %p2 = getelementptr i8, ptr %p, i32 2
+  %p3 = getelementptr i8, ptr %p, i32 3
+  %l1 = load i8, ptr %p
+  %l2 = load i8, ptr %p1
+  %l3 = load i8, ptr %p2
+  %l4 = load i8, ptr %p3
+
+  %e1 = zext i8 %l1 to i32
+  %e2 = zext i8 %l2 to i32
+  %e3 = zext i8 %l3 to i32
+  %e4 = zext i8 %l4 to i32
+
+  %s2 = shl i32 %e2, 8
+  %s3 = shl i32 %e3, 16
+  %s4 = shl i32 %e4, 24
+
+  %o1 = or i32 %s4, %s3
+  %o2 = or i32 %o1, %s2
+  %o3 = or i32 %o2, %e1
+
+  %c = icmp eq i32 %o3, 0
+  ret i1 %c
+}
diff --git a/llvm/test/Transforms/AggressiveInstCombine/or-shift-chain.ll b/llvm/test/Transforms/AggressiveInstCombine/or-shift-chain.ll
new file mode 100644
index 0000000000000..b50f957c5aefe
--- /dev/null
+++ b/llvm/test/Transforms/AggressiveInstCombine/or-shift-chain.ll
@@ -0,0 +1,137 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=aggressive-instcombine -S | FileCheck %s
+
+define i1 @remove_shift_nuw_ab(i8 %a, i8 %b, i8 %s) {
+; CHECK-LABEL: @remove_shift_nuw_ab(
+; CHECK-NEXT:    [[OR:%.*]] = or i8 [[T:%.*]], [[B:%.*]]
+; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
+; CHECK-NEXT:    ret i1 [[IC]]
+;
+  %t = shl nuw i8 %a, %s
+  %or = or i8 %t, %b
+  %ic = icmp eq i8 %or, 0
+  ret i1 %ic
+}
+
+define i1 @remove_shift_nuw_ba(i8 %a, i8 %b, i8 %s) {
+; CHECK-LABEL: @remove_shift_nuw_ba(
+; CHECK-NEXT:    [[OR:%.*]] = or i8 [[B:%.*]], [[T:%.*]]
+; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
+; CHECK-NEXT:    ret i1 [[IC]]
+;
+  %t = shl nuw i8 %a, %s
+  %or = or i8 %b, %t
+  %ic = icmp eq i8 %or, 0
+  ret i1 %ic
+}
+
+define i1 @remove_shift_nsw(i8 %a, i8 %b, i8 %s) {
+; CHECK-LABEL: @remove_shift_nsw(
+; CHECK-NEXT:    [[OR:%.*]] = or i8 [[T:%.*]], [[B:%.*]]
+; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
+; CHECK-NEXT:    ret i1 [[IC]]
+;
+  %t = shl nsw i8 %a, %s
+  %or = or i8 %t, %b
+  %ic = icmp eq i8 %or, 0
+  ret i1 %ic
+}
+
+define i1 @remove_shift_nuw_ne(i8 %a, i8 %b, i8 %s) {
+; CHECK-LABEL: @remove_shift_nuw_ne(
+; CHECK-NEXT:    [[OR:%.*]] = or i8 [[T:%.*]], [[B:%.*]]
+; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
+; CHECK-NEXT:    ret i1 [[IC]]
+;
+  %t = shl nuw i8 %a, %s
+  %or = or i8 %t, %b
+  %ic = icmp eq i8 %or, 0
+  ret i1 %ic
+}
+
+define i1 @remove_shift_nsw_ne(i8 %a, i8 %b, i8 %s) {
+; CHECK-LABEL: @remove_shift_nsw_ne(
+; CHECK-NEXT:    [[OR:%.*]] = or i8 [[T:%.*]], [[B:%.*]]
+; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
+; CHECK-NEXT:    ret i1 [[IC]]
+;
+  %t = shl nsw i8 %a, %s
+  %or = or i8 %t, %b
+  %ic = icmp eq i8 %or, 0
+  ret i1 %ic
+}
+
+define i1 @remove_shift_wraps(i8 %a, i8 %b, i8 %s) {
+; CHECK-LABEL: @remove_shift_wraps(
+; CHECK-NEXT:    [[T:%.*]] = shl i8 [[A:%.*]], [[S:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i8 [[T]], [[B:%.*]]
+; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
+; CHECK-NEXT:    ret i1 [[IC]]
+;
+  %t = shl i8 %a, %s
+  %or = or i8 %t, %b
+  %ic = icmp eq i8 %or, 0
+  ret i1 %ic
+}
+
+define i1 @remove_shift_chain_d(i8 %a, i8 %b, i8 %c, i8 %d, i8 %s) {
+; CHECK-LABEL: @remove_shift_chain_d(
+; CHECK-NEXT:    [[OR1:%.*]] = or i8 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    [[OR2:%.*]] = or i8 [[C:%.*]], [[DT:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i8 [[OR1]], [[OR2]]
+; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
+; CHECK-NEXT:    ret i1 [[IC]]
+;
+  %dt = shl nuw i8 %d, %s
+  %or1 = or i8 %a, %b
+  %or2 = or i8 %c, %dt
+  %or = or i8 %or1, %or2
+  %ic = icmp eq i8 %or, 0
+  ret i1 %ic
+}
+
+define i1 @remove_shift_chain_abcd(i8 %a, i8 %b, i8 %c, i8 %d, i8 %s) {
+; CHECK-LABEL: @remove_shift_chain_abcd(
+; CHECK-NEXT:    [[OR1:%.*]] = or i8 [[AT:%.*]], [[BT:%.*]]
+; CHECK-NEXT:    [[OR2:%.*]] = or i8 [[CT:%.*]], [[DT:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i8 [[OR1]], [[OR2]]
+; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
+; CHECK-NEXT:    ret i1 [[IC]]
+;
+  %at = shl nuw i8 %a, %s
+  %bt = shl nuw i8 %b, 2
+  %ct = shl nuw i8 %c, 1
+  %dt = shl nuw i8 %d, %s
+  %or1 = or i8 %at, %bt
+  %or2 = or i8 %ct, %dt
+  %or = or i8 %or1, %or2
+  %ic = icmp eq i8 %or, 0
+  ret i1 %ic
+}
+
+define i1 @remove_shift_chain_abcd_multiuse(i8 %a, i8 %b, i8 %c, i8 %d, i8 %s) {
+; CHECK-LABEL: @remove_shift_chain_abcd_multiuse(
+; CHECK-NEXT:    [[AT:%.*]] = shl nuw i8 [[A:%.*]], [[S:%.*]]
+; CHECK-NEXT:    [[BT:%.*]] = shl nuw i8 [[B:%.*]], 2
+; CHECK-NEXT:    [[CT:%.*]] = shl nuw i8 [[C:%.*]], 1
+; CHECK-NEXT:    [[DT:%.*]] = shl nuw i8 [[D:%.*]], [[S]]
+; CHECK-NEXT:    [[OR1:%.*]] = or i8 [[AT]], [[BT]]
+; CHECK-NEXT:    [[OR2:%.*]] = or i8 [[CT]], [[DT]]
+; CHECK-NEXT:    [[OR:%.*]] = or i8 [[OR1]], [[OR2]]
+; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
+; CHECK-NEXT:    call void @use(i8 [[OR]])
+; CHECK-NEXT:    ret i1 [[IC]]
+;
+  %at = shl nuw i8 %a, %s
+  %bt = shl nuw i8 %b, 2
+  %ct = shl nuw i8 %c, 1
+  %dt = shl nuw i8 %d, %s
+  %or1 = or i8 %at, %bt
+  %or2 = or i8 %ct, %dt
+  %or = or i8 %or1, %or2
+  %ic = icmp eq i8 %or, 0
+  call void @use(i8 %or)
+  ret i1 %ic
+}
+
+declare void @use(i8)
diff --git a/llvm/test/Transforms/InstCombine/icmp-of-or-x.ll b/llvm/test/Transforms/InstCombine/icmp-of-or-x.ll
index 83fe9c8fe4bdc..993325f6ff0b0 100644
--- a/llvm/test/Transforms/InstCombine/icmp-of-or-x.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-of-or-x.ll
@@ -409,136 +409,3 @@ define i1 @PR38139(i8 %arg) {
   %r = icmp ne i8 %masked, %arg
   ret i1 %r
 }
-
-define i1 @remove_shift_nuw_ab(i8 %a, i8 %b, i8 %s) {
-; CHECK-LABEL: @remove_shift_nuw_ab(
-; CHECK-NEXT:    [[OR:%.*]] = or i8 [[T:%.*]], [[B:%.*]]
-; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
-; CHECK-NEXT:    ret i1 [[IC]]
-;
-  %t = shl nuw i8 %a, %s
-  %or = or i8 %t, %b
-  %ic = icmp eq i8 %or, 0
-  ret i1 %ic
-}
-
-define i1 @remove_shift_nuw_ba(i8 %a, i8 %b, i8 %s) {
-; CHECK-LABEL: @remove_shift_nuw_ba(
-; CHECK-NEXT:    [[OR:%.*]] = or i8 [[B:%.*]], [[T:%.*]]
-; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
-; CHECK-NEXT:    ret i1 [[IC]]
-;
-  %t = shl nuw i8 %a, %s
-  %or = or i8 %b, %t
-  %ic = icmp eq i8 %or, 0
-  ret i1 %ic
-}
-
-define i1 @remove_shift_nsw(i8 %a, i8 %b, i8 %s) {
-; CHECK-LABEL: @remove_shift_nsw(
-; CHECK-NEXT:    [[OR:%.*]] = or i8 [[T:%.*]], [[B:%.*]]
-; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
-; CHECK-NEXT:    ret i1 [[IC]]
-;
-  %t = shl nsw i8 %a, %s
-  %or = or i8 %t, %b
-  %ic = icmp eq i8 %or, 0
-  ret i1 %ic
-}
-
-define i1 @remove_shift_nuw_ne(i8 %a, i8 %b, i8 %s) {
-; CHECK-LABEL: @remove_shift_nuw_ne(
-; CHECK-NEXT:    [[OR:%.*]] = or i8 [[T:%.*]], [[B:%.*]]
-; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
-; CHECK-NEXT:    ret i1 [[IC]]
-;
-  %t = shl nuw i8 %a, %s
-  %or = or i8 %t, %b
-  %ic = icmp eq i8 %or, 0
-  ret i1 %ic
-}
-
-define i1 @remove_shift_nsw_ne(i8 %a, i8 %b, i8 %s) {
-; CHECK-LABEL: @remove_shift_nsw_ne(
-; CHECK-NEXT:    [[OR:%.*]] = or i8 [[T:%.*]], [[B:%.*]]
-; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
-; CHECK-NEXT:    ret i1 [[IC]]
-;
-  %t = shl nsw i8 %a, %s
-  %or = or i8 %t, %b
-  %ic = icmp eq i8 %or, 0
-  ret i1 %ic
-}
-
-define i1 @remove_shift_wraps(i8 %a, i8 %b, i8 %s) {
-; CHECK-LABEL: @remove_shift_wraps(
-; CHECK-NEXT:    [[T:%.*]] = shl i8 [[A:%.*]], [[S:%.*]]
-; CHECK-NEXT:    [[OR:%.*]] = or i8 [[T]], [[B:%.*]]
-; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
-; CHECK-NEXT:    ret i1 [[IC]]
-;
-  %t = shl i8 %a, %s
-  %or = or i8 %t, %b
-  %ic = icmp eq i8 %or, 0
-  ret i1 %ic
-}
-
-define i1 @remove_shift_chain_d(i8 %a, i8 %b, i8 %c, i8 %d, i8 %s) {
-; CHECK-LABEL: @remove_shift_chain_d(
-; CHECK-NEXT:    [[OR1:%.*]] = or i8 [[A:%.*]], [[B:%.*]]
-; CHECK-NEXT:    [[OR2:%.*]] = or i8 [[C:%.*]], [[DT:%.*]]
-; CHECK-NEXT:    [[OR:%.*]] = or i8 [[OR1]], [[OR2]]
-; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
-; CHECK-NEXT:    ret i1 [[IC]]
-;
-  %dt = shl nuw i8 %d, %s
-  %or1 = or i8 %a, %b
-  %or2 = or i8 %c, %dt
-  %or = or i8 %or1, %or2
-  %ic = icmp eq i8 %or, 0
-  ret i1 %ic
-}
-
-define i1 @remove_shift_chain_abcd(i8 %a, i8 %b, i8 %c, i8 %d, i8 %s) {
-; CHECK-LABEL: @remove_shift_chain_abcd(
-; CHECK-NEXT:    [[OR1:%.*]] = or i8 [[AT:%.*]], [[BT:%.*]]
-; CHECK-NEXT:    [[OR2:%.*]] = or i8 [[CT:%.*]], [[DT:%.*]]
-; CHECK-NEXT:    [[OR:%.*]] = or i8 [[OR1]], [[OR2]]
-; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
-; CHECK-NEXT:    ret i1 [[IC]]
-;
-  %at = shl nuw i8 %a, %s
-  %bt = shl nuw i8 %b, 2
-  %ct = shl nuw i8 %c, 1
-  %dt = shl nuw i8 %d, %s
-  %or1 = or i8 %at, %bt
-  %or2 = or i8 %ct, %dt
-  %or = or i8 %or1, %or2
-  %ic = icmp eq i8 %or, 0
-  ret i1 %ic
-}
-
-define i1 @remove_shift_chain_abcd_multiuse(i8 %a, i8 %b, i8 %c, i8 %d, i8 %s) {
-; CHECK-LABEL: @remove_shift_chain_abcd_multiuse(
-; CHECK-NEXT:    [[AT:%.*]] = shl nuw i8 [[A:%.*]], [[S:%.*]]
-; CHECK-NEXT:    [[BT:%.*]] = shl nuw i8 [[B:%.*]], 2
-; CHECK-NEXT:    [[CT:%.*]] = shl nuw i8 [[C:%.*]], 1
-; CHECK-NEXT:    [[DT:%.*]] = shl nuw i8 [[D:%.*]], [[S]]
-; CHECK-NEXT:    [[OR1:%.*]] = or i8 [[AT]], [[BT]]
-; CHECK-NEXT:    [[OR2:%.*]] = or i8 [[CT]], [[DT]]
-; CHECK-NEXT:    [[OR:%.*]] = or i8 [[OR1]], [[OR2]]
-; CHECK-NEXT:    [[IC:%.*]] = icmp eq i8 [[OR]], 0
-; CHECK-NEXT:    call void @use.i8(i8 [[OR]])
-; CHECK-NEXT:    ret i1 [[IC]]
-;
-  %at = shl nuw i8 %a, %s
-  %bt = shl nuw i8 %b, 2
-  %ct = shl nuw i8 %c, 1
-  %dt = shl nuw i8 %d, %s
-  %or1 = or i8 %at, %bt
-  %or2 = or i8 %ct, %dt
-  %or = or i8 %or1, %or2
-  %ic = icmp eq i8 %or, 0
-  call void @use.i8(i8 %or)
-  ret i1 %ic
-}



More information about the llvm-commits mailing list