[llvm] [InstCombine] Decompose an icmp into multiple ranges (PR #69855)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Mon Oct 23 04:57:52 PDT 2023


https://github.com/dtcxzyw updated https://github.com/llvm/llvm-project/pull/69855

>From dca01000d647c0476dd617a2c49604239174e100 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sun, 22 Oct 2023 02:51:05 +0800
Subject: [PATCH 1/4] [InstCombine] Add pre-commit tests from PR69123. NFC.

---
 .../test/Transforms/InstCombine/icmp-range.ll | 115 ++++++++++++++++++
 1 file changed, 115 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/icmp-range.ll b/llvm/test/Transforms/InstCombine/icmp-range.ll
index 7af06e03fd4b2a9..b70bad9f6fe7ee5 100644
--- a/llvm/test/Transforms/InstCombine/icmp-range.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-range.ll
@@ -1521,6 +1521,121 @@ define i1 @isFloat(i64 %0) {
   ret i1 %5
 }
 
+; tests from PR69123
+define i1 @or_slt_eq_masked(i32 %a) {
+; CHECK-LABEL: @or_slt_eq_masked(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[A:%.*]], 0
+; CHECK-NEXT:    [[AND1:%.*]] = and i32 [[A]], 2147483647
+; CHECK-NEXT:    [[TOBOOL2_NOT:%.*]] = icmp eq i32 [[AND1]], 0
+; CHECK-NEXT:    [[OR:%.*]] = or i1 [[CMP]], [[TOBOOL2_NOT]]
+; CHECK-NEXT:    ret i1 [[OR]]
+;
+  %cmp = icmp slt i32 %a, 0
+  %and1 = and i32 %a, 2147483647
+  %tobool2.not = icmp eq i32 %and1, 0
+  %or = or i1 %cmp, %tobool2.not
+  ret i1 %or
+}
+
+define i1 @and_sgt_ne_masked(i32 %d) {
+; CHECK-LABEL: @and_sgt_ne_masked(
+; CHECK-NEXT:    [[TOBOOL_NOT:%.*]] = icmp sgt i32 [[D:%.*]], -1
+; CHECK-NEXT:    [[AND1:%.*]] = and i32 [[D]], 2147483647
+; CHECK-NEXT:    [[TOBOOL2:%.*]] = icmp ne i32 [[AND1]], 0
+; CHECK-NEXT:    [[AND:%.*]] = and i1 [[TOBOOL_NOT]], [[TOBOOL2]]
+; CHECK-NEXT:    ret i1 [[AND]]
+;
+  %tobool.not = icmp sgt i32 %d, -1
+  %and1 = and i32 %d, 2147483647
+  %tobool2 = icmp ne i32 %and1, 0
+  %and = and i1 %tobool.not, %tobool2
+  ret i1 %and
+}
+
+define i1 @or_slt_ne_masked(i32 %a) {
+; CHECK-LABEL: @or_slt_ne_masked(
+; CHECK-NEXT:    [[OR:%.*]] = icmp ne i32 [[A:%.*]], 0
+; CHECK-NEXT:    ret i1 [[OR]]
+;
+  %cmp = icmp slt i32 %a, 0
+  %and1 = and i32 %a, 2147483647
+  %tobool2.not = icmp ne i32 %and1, 0
+  %or = or i1 %cmp, %tobool2.not
+  ret i1 %or
+}
+
+define i1 @and_sgt_eq_masked(i32 %d) {
+; CHECK-LABEL: @and_sgt_eq_masked(
+; CHECK-NEXT:    [[AND:%.*]] = icmp eq i32 [[D:%.*]], 0
+; CHECK-NEXT:    ret i1 [[AND]]
+;
+  %tobool.not = icmp sgt i32 %d, -1
+  %and1 = and i32 %d, 2147483647
+  %tobool2 = icmp eq i32 %and1, 0
+  %and = and i1 %tobool.not, %tobool2
+  ret i1 %and
+}
+
+define i1 @or_slt_eq_masked2(i32 %a) {
+; CHECK-LABEL: @or_slt_eq_masked2(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[A:%.*]], 8
+; CHECK-NEXT:    [[AND1:%.*]] = and i32 [[A]], -9
+; CHECK-NEXT:    [[TOBOOL2_NOT:%.*]] = icmp eq i32 [[AND1]], 0
+; CHECK-NEXT:    [[OR:%.*]] = or i1 [[CMP]], [[TOBOOL2_NOT]]
+; CHECK-NEXT:    ret i1 [[OR]]
+;
+  %cmp = icmp slt i32 %a, 8
+  %and1 = and i32 %a, -9
+  %tobool2.not = icmp eq i32 %and1, 0
+  %or = or i1 %cmp, %tobool2.not
+  ret i1 %or
+}
+
+define i1 @or_slt_eq_masked_nofold_invalid_mask(i32 %a) {
+; CHECK-LABEL: @or_slt_eq_masked_nofold_invalid_mask(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[A:%.*]], 0
+; CHECK-NEXT:    [[AND1:%.*]] = and i32 [[A]], 2147483646
+; CHECK-NEXT:    [[TOBOOL2_NOT:%.*]] = icmp eq i32 [[AND1]], 0
+; CHECK-NEXT:    [[OR:%.*]] = or i1 [[CMP]], [[TOBOOL2_NOT]]
+; CHECK-NEXT:    ret i1 [[OR]]
+;
+  %cmp = icmp slt i32 %a, 0
+  %and1 = and i32 %a, 2147483646
+  %tobool2.not = icmp eq i32 %and1, 0
+  %or = or i1 %cmp, %tobool2.not
+  ret i1 %or
+}
+
+define i1 @or_slt_eq_masked_nofold_unmergeable_ranges(i32 %a) {
+; CHECK-LABEL: @or_slt_eq_masked_nofold_unmergeable_ranges(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[A:%.*]], -1
+; CHECK-NEXT:    [[AND1:%.*]] = and i32 [[A]], 2147483647
+; CHECK-NEXT:    [[TOBOOL2_NOT:%.*]] = icmp eq i32 [[AND1]], 0
+; CHECK-NEXT:    [[OR:%.*]] = or i1 [[CMP]], [[TOBOOL2_NOT]]
+; CHECK-NEXT:    ret i1 [[OR]]
+;
+  %cmp = icmp slt i32 %a, -1
+  %and1 = and i32 %a, 2147483647
+  %tobool2.not = icmp eq i32 %and1, 0
+  %or = or i1 %cmp, %tobool2.not
+  ret i1 %or
+}
+
+define i1 @and_sgt_ne_masked_nofold_unmergeable_ranges(i32 %d) {
+; CHECK-LABEL: @and_sgt_ne_masked_nofold_unmergeable_ranges(
+; CHECK-NEXT:    [[TOBOOL_NOT:%.*]] = icmp sgt i32 [[D:%.*]], -2
+; CHECK-NEXT:    [[AND1:%.*]] = and i32 [[D]], 2147483647
+; CHECK-NEXT:    [[TOBOOL2:%.*]] = icmp ne i32 [[AND1]], 0
+; CHECK-NEXT:    [[AND:%.*]] = and i1 [[TOBOOL_NOT]], [[TOBOOL2]]
+; CHECK-NEXT:    ret i1 [[AND]]
+;
+  %tobool.not = icmp sgt i32 %d, -2
+  %and1 = and i32 %d, 2147483647
+  %tobool2 = icmp ne i32 %and1, 0
+  %and = and i1 %tobool.not, %tobool2
+  ret i1 %and
+}
+
 !0 = !{i32 1, i32 6}
 !1 = !{i32 0, i32 6}
 !2 = !{i8 0, i8 1}

>From 3efaee6cfe1032481daa8e4e98eaa041993013d2 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sun, 22 Oct 2023 02:52:48 +0800
Subject: [PATCH 2/4] [InstCombine] Decompose an icmp into multiple ranges

---
 .../InstCombine/InstCombineAndOrXor.cpp       | 99 ++++++++++++++++++-
 .../test/Transforms/InstCombine/icmp-range.ll | 15 +--
 2 files changed, 101 insertions(+), 13 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 3e0218d9b76d1f7..ace57fb6f597063 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -1334,6 +1334,101 @@ Value *InstCombinerImpl::foldAndOrOfICmpsUsingRanges(ICmpInst *ICmp1,
   return Builder.CreateICmp(NewPred, NewV, ConstantInt::get(Ty, NewC));
 }
 
+/// Decompose icmp into intersection or union of ranges.
+static bool decomposeICmpIntoRangeSet(SmallVectorImpl<ConstantRange> &Set,
+                                      ICmpInst *ICmp, Value *X, bool IsAnd) {
+  // icmp eq/ne (X & mask), 0
+  ICmpInst::Predicate Pred;
+  const APInt *Mask;
+  if (match(ICmp,
+            m_ICmp(Pred, m_And(m_Specific(X), m_APInt(Mask)), m_Zero())) &&
+      ICmp->isEquality()) {
+    if (Mask->popcount() == Mask->getBitWidth() - 1) {
+      auto Zero = APInt::getZero(Mask->getBitWidth());
+      auto Val = ~*Mask;
+      if (IsAnd) {
+        if (Pred == ICmpInst::ICMP_EQ) {
+          Set.push_back(ConstantRange(Zero, Val + 1));
+          Set.push_back(ConstantRange(Val, APInt(Mask->getBitWidth(), 1)));
+        } else {
+          Set.push_back(ConstantRange(Zero).inverse());
+          Set.push_back(ConstantRange(Val).inverse());
+        }
+      } else {
+        if (Pred == ICmpInst::ICMP_EQ) {
+          Set.push_back(Val);
+          Set.push_back(Zero);
+        } else {
+          Set.push_back(ConstantRange(APInt(Mask->getBitWidth(), 1), Val));
+          Set.push_back(ConstantRange(Val + 1, Zero));
+        }
+      }
+      return true;
+    }
+  }
+
+  return false;
+}
+
+/// Fold (icmp Pred1 V1, C1) & (icmp Pred2 V2, C2)
+/// or   (icmp Pred1 V1, C1) | (icmp Pred2 V2, C2)
+/// into a single comparison using range-based reasoning.
+/// It handles patterns which cannot be recognized by
+/// foldAndOrOfICmpsUsingRanges. Try to decompose one of icmps into two or more
+/// icmps. Example: icmp eq (X & ~signmask), 0 --> (icmp eq X, 0) | (icmp eq X,
+/// signmask)
+static Value *
+foldAndOrOfICmpsUsingDecomposedRanges(ICmpInst *ICmp1, ICmpInst *ICmp2,
+                                      bool IsAnd,
+                                      InstCombiner::BuilderTy &Builder) {
+  ICmpInst::Predicate Pred1, Pred2;
+  Value *V1, *V2;
+  const APInt *C1, *C2;
+  if (!match(ICmp1, m_ICmp(Pred1, m_Value(V1), m_APInt(C1))) ||
+      !match(ICmp2, m_ICmp(Pred2, m_Value(V2), m_APInt(C2))))
+    return nullptr;
+
+  SmallVector<ConstantRange, 3> Ranges;
+  Value *X = nullptr;
+  if (decomposeICmpIntoRangeSet(Ranges, ICmp2, V1, IsAnd)) {
+    X = V1;
+    Ranges.push_back(ConstantRange::makeExactICmpRegion(Pred1, *C1));
+  } else if (decomposeICmpIntoRangeSet(Ranges, ICmp1, V2, IsAnd)) {
+    X = V2;
+    Ranges.push_back(ConstantRange::makeExactICmpRegion(Pred2, *C2));
+  } else
+    return nullptr;
+
+  while (true) {
+    bool Merged = false;
+    for (unsigned I = 0; I < Ranges.size(); ++I) {
+      auto &CR = Ranges[I];
+      for (unsigned J = I + 1; J < Ranges.size(); ++J) {
+        if (auto NewCR = IsAnd ? CR.exactIntersectWith(Ranges[J])
+                               : CR.exactUnionWith(Ranges[J])) {
+          CR = *NewCR;
+          Ranges.erase(Ranges.begin() + J);
+          Merged = true;
+        }
+      }
+    }
+
+    if (Ranges.size() == 1)
+      break;
+
+    if (!Merged)
+      return nullptr;
+  }
+
+  ICmpInst::Predicate NewPred;
+  APInt NewRHS, NewOffset;
+  Ranges[0].getEquivalentICmp(NewPred, NewRHS, NewOffset);
+  // Similar to foldAndOrOfICmpsUsingRanges, we don't check hasOneUse here.
+  if (NewOffset != 0)
+    X = Builder.CreateAdd(X, ConstantInt::get(X->getType(), NewOffset));
+  return Builder.CreateICmp(NewPred, X, ConstantInt::get(X->getType(), NewRHS));
+}
+
 /// Ignore all operations which only change the sign of a value, returning the
 /// underlying magnitude value.
 static Value *stripSignOnlyFPOps(Value *Val) {
@@ -3280,7 +3375,9 @@ Value *InstCombinerImpl::foldAndOrOfICmps(ICmpInst *LHS, ICmpInst *RHS,
     }
   }
 
-  return foldAndOrOfICmpsUsingRanges(LHS, RHS, IsAnd);
+  if (auto *V = foldAndOrOfICmpsUsingRanges(LHS, RHS, IsAnd))
+    return V;
+  return foldAndOrOfICmpsUsingDecomposedRanges(LHS, RHS, IsAnd, Builder);
 }
 
 // FIXME: We use commutative matchers (m_c_*) for some, but not all, matches
diff --git a/llvm/test/Transforms/InstCombine/icmp-range.ll b/llvm/test/Transforms/InstCombine/icmp-range.ll
index b70bad9f6fe7ee5..1191b553c361673 100644
--- a/llvm/test/Transforms/InstCombine/icmp-range.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-range.ll
@@ -1524,10 +1524,7 @@ define i1 @isFloat(i64 %0) {
 ; tests from PR69123
 define i1 @or_slt_eq_masked(i32 %a) {
 ; CHECK-LABEL: @or_slt_eq_masked(
-; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[A:%.*]], 0
-; CHECK-NEXT:    [[AND1:%.*]] = and i32 [[A]], 2147483647
-; CHECK-NEXT:    [[TOBOOL2_NOT:%.*]] = icmp eq i32 [[AND1]], 0
-; CHECK-NEXT:    [[OR:%.*]] = or i1 [[CMP]], [[TOBOOL2_NOT]]
+; CHECK-NEXT:    [[OR:%.*]] = icmp slt i32 [[A:%.*]], 1
 ; CHECK-NEXT:    ret i1 [[OR]]
 ;
   %cmp = icmp slt i32 %a, 0
@@ -1539,10 +1536,7 @@ define i1 @or_slt_eq_masked(i32 %a) {
 
 define i1 @and_sgt_ne_masked(i32 %d) {
 ; CHECK-LABEL: @and_sgt_ne_masked(
-; CHECK-NEXT:    [[TOBOOL_NOT:%.*]] = icmp sgt i32 [[D:%.*]], -1
-; CHECK-NEXT:    [[AND1:%.*]] = and i32 [[D]], 2147483647
-; CHECK-NEXT:    [[TOBOOL2:%.*]] = icmp ne i32 [[AND1]], 0
-; CHECK-NEXT:    [[AND:%.*]] = and i1 [[TOBOOL_NOT]], [[TOBOOL2]]
+; CHECK-NEXT:    [[AND:%.*]] = icmp sgt i32 [[D:%.*]], 0
 ; CHECK-NEXT:    ret i1 [[AND]]
 ;
   %tobool.not = icmp sgt i32 %d, -1
@@ -1578,10 +1572,7 @@ define i1 @and_sgt_eq_masked(i32 %d) {
 
 define i1 @or_slt_eq_masked2(i32 %a) {
 ; CHECK-LABEL: @or_slt_eq_masked2(
-; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[A:%.*]], 8
-; CHECK-NEXT:    [[AND1:%.*]] = and i32 [[A]], -9
-; CHECK-NEXT:    [[TOBOOL2_NOT:%.*]] = icmp eq i32 [[AND1]], 0
-; CHECK-NEXT:    [[OR:%.*]] = or i1 [[CMP]], [[TOBOOL2_NOT]]
+; CHECK-NEXT:    [[OR:%.*]] = icmp slt i32 [[A:%.*]], 9
 ; CHECK-NEXT:    ret i1 [[OR]]
 ;
   %cmp = icmp slt i32 %a, 8

>From 34663d7d48c6c15e0636ccf7bdaa9841a5294cd0 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sun, 22 Oct 2023 15:34:53 +0800
Subject: [PATCH 3/4] fixup! [InstCombine] Decompose an icmp into multiple
 ranges

Improve comments
Improve performance of range merging
---
 .../InstCombine/InstCombineAndOrXor.cpp       | 85 ++++++++++++-------
 1 file changed, 53 insertions(+), 32 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index ace57fb6f597063..14618023268e509 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -1337,34 +1337,41 @@ Value *InstCombinerImpl::foldAndOrOfICmpsUsingRanges(ICmpInst *ICmp1,
 /// Decompose icmp into intersection or union of ranges.
 static bool decomposeICmpIntoRangeSet(SmallVectorImpl<ConstantRange> &Set,
                                       ICmpInst *ICmp, Value *X, bool IsAnd) {
-  // icmp eq/ne (X & mask), 0
+  // Handle "icmp eq/ne (X & mask), 0", where mask is an inverted power of 2.
   ICmpInst::Predicate Pred;
   const APInt *Mask;
   if (match(ICmp,
             m_ICmp(Pred, m_And(m_Specific(X), m_APInt(Mask)), m_Zero())) &&
-      ICmp->isEquality()) {
-    if (Mask->popcount() == Mask->getBitWidth() - 1) {
-      auto Zero = APInt::getZero(Mask->getBitWidth());
-      auto Val = ~*Mask;
-      if (IsAnd) {
-        if (Pred == ICmpInst::ICMP_EQ) {
-          Set.push_back(ConstantRange(Zero, Val + 1));
-          Set.push_back(ConstantRange(Val, APInt(Mask->getBitWidth(), 1)));
-        } else {
-          Set.push_back(ConstantRange(Zero).inverse());
-          Set.push_back(ConstantRange(Val).inverse());
-        }
+      ICmp->isEquality() && (Mask->popcount() == Mask->getBitWidth() - 1)) {
+    auto Zero = APInt::getZero(Mask->getBitWidth());
+    auto Val = ~*Mask;
+    if (IsAnd) {
+      if (Pred == ICmpInst::ICMP_EQ) {
+        // icmp eq (X & mask), 0
+        // X in {0, Val} --> X in [0, Val] and X in [Val, 0] (upper-wrapped)
+        Set.push_back(ConstantRange(Zero, Val + 1));
+        Set.push_back(ConstantRange(Val, APInt(Mask->getBitWidth(), 1)));
       } else {
-        if (Pred == ICmpInst::ICMP_EQ) {
-          Set.push_back(Val);
-          Set.push_back(Zero);
-        } else {
-          Set.push_back(ConstantRange(APInt(Mask->getBitWidth(), 1), Val));
-          Set.push_back(ConstantRange(Val + 1, Zero));
-        }
+        // icmp ne (X & mask), 0
+        // X not in {0, Val} --> X not in {0} and X not in {Val}
+        Set.push_back(ConstantRange(Zero).inverse());
+        Set.push_back(ConstantRange(Val).inverse());
+      }
+    } else {
+      if (Pred == ICmpInst::ICMP_EQ) {
+        // icmp eq (X & mask), 0
+        // X in {0, Val} --> X in {0} or X in {Val}
+        Set.push_back(Val);
+        Set.push_back(Zero);
+      } else {
+        // icmp ne (X & mask), 0
+        // X not in {0, Val} --> X in [1, Val) or X not in [Val + 1, 0)
+        // (upper-wrapped)
+        Set.push_back(ConstantRange(APInt(Mask->getBitWidth(), 1), Val));
+        Set.push_back(ConstantRange(Val + 1, Zero));
       }
-      return true;
     }
+    return true;
   }
 
   return false;
@@ -1399,30 +1406,44 @@ foldAndOrOfICmpsUsingDecomposedRanges(ICmpInst *ICmp1, ICmpInst *ICmp2,
   } else
     return nullptr;
 
+  // Try to merge ranges into single range.
+  // Since we may fail to merge ranges due to the order of merging, we cannot do
+  // merge in order (counterexample: [0, 1), [2, 3), [1, 2)) or in sorted order
+  // (due to wrapped ranges). Instead, we try to merge ranges in O(N^2) until we
+  // succeed in merging into single range or we cannot merge anymore.
+
+  // Ranges which cannot be merged with each other. We maintain this list to
+  // avoid redundant checks.
+  SmallVector<ConstantRange, 3> UnmergeableRanges;
   while (true) {
     bool Merged = false;
-    for (unsigned I = 0; I < Ranges.size(); ++I) {
-      auto &CR = Ranges[I];
-      for (unsigned J = I + 1; J < Ranges.size(); ++J) {
-        if (auto NewCR = IsAnd ? CR.exactIntersectWith(Ranges[J])
-                               : CR.exactUnionWith(Ranges[J])) {
+
+    if (!Ranges.empty()) {
+      auto &CR = Ranges.back();
+      for (unsigned I = 0; I < UnmergeableRanges.size(); ++I) {
+        if (auto NewCR = IsAnd ? CR.exactIntersectWith(UnmergeableRanges[I])
+                               : CR.exactUnionWith(UnmergeableRanges[I])) {
           CR = *NewCR;
-          Ranges.erase(Ranges.begin() + J);
+          UnmergeableRanges.erase(UnmergeableRanges.begin() + I);
           Merged = true;
         }
       }
+      if (!Merged) {
+        UnmergeableRanges.push_back(CR);
+        Ranges.pop_back();
+      }
     }
 
-    if (Ranges.size() == 1)
-      break;
-
-    if (!Merged)
+    if (Ranges.empty()) {
+      if (UnmergeableRanges.size() == 1)
+        break;
       return nullptr;
+    }
   }
 
   ICmpInst::Predicate NewPred;
   APInt NewRHS, NewOffset;
-  Ranges[0].getEquivalentICmp(NewPred, NewRHS, NewOffset);
+  UnmergeableRanges.front().getEquivalentICmp(NewPred, NewRHS, NewOffset);
   // Similar to foldAndOrOfICmpsUsingRanges, we don't check hasOneUse here.
   if (NewOffset != 0)
     X = Builder.CreateAdd(X, ConstantInt::get(X->getType(), NewOffset));

>From 01f80a1beb1763459f5300cec1e06a8831f9691c Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Mon, 23 Oct 2023 19:56:58 +0800
Subject: [PATCH 4/4] fixup! [InstCombine] Decompose an icmp into multiple
 ranges

Address comments
---
 .../InstCombine/InstCombineAndOrXor.cpp       | 41 +++++++++----------
 1 file changed, 20 insertions(+), 21 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 14618023268e509..819d48b01805442 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -1409,38 +1409,37 @@ foldAndOrOfICmpsUsingDecomposedRanges(ICmpInst *ICmp1, ICmpInst *ICmp2,
   // Try to merge ranges into single range.
   // Since we may fail to merge ranges due to the order of merging, we cannot do
   // merge in order (counterexample: [0, 1), [2, 3), [1, 2)) or in sorted order
-  // (due to wrapped ranges). Instead, we try to merge ranges in O(N^2) until we
-  // succeed in merging into single range or we cannot merge anymore.
+  // (due to wrapped ranges). Instead, we try to merge ranges in O(N^2)
+  // (typically N == 3) until we succeed in merging into single range or we
+  // cannot merge anymore.
 
   // Ranges which cannot be merged with each other. We maintain this list to
   // avoid redundant checks.
   SmallVector<ConstantRange, 3> UnmergeableRanges;
-  while (true) {
+  while (!Ranges.empty()) {
     bool Merged = false;
 
-    if (!Ranges.empty()) {
-      auto &CR = Ranges.back();
-      for (unsigned I = 0; I < UnmergeableRanges.size(); ++I) {
-        if (auto NewCR = IsAnd ? CR.exactIntersectWith(UnmergeableRanges[I])
-                               : CR.exactUnionWith(UnmergeableRanges[I])) {
-          CR = *NewCR;
-          UnmergeableRanges.erase(UnmergeableRanges.begin() + I);
-          Merged = true;
-        }
-      }
-      if (!Merged) {
-        UnmergeableRanges.push_back(CR);
-        Ranges.pop_back();
+    auto &CR = Ranges.back();
+    for (unsigned I = 0; I < UnmergeableRanges.size(); ++I) {
+      if (auto NewCR = IsAnd ? CR.exactIntersectWith(UnmergeableRanges[I])
+                             : CR.exactUnionWith(UnmergeableRanges[I])) {
+        CR = *NewCR;
+        UnmergeableRanges.erase(UnmergeableRanges.begin() + I);
+        Merged = true;
       }
     }
-
-    if (Ranges.empty()) {
-      if (UnmergeableRanges.size() == 1)
-        break;
-      return nullptr;
+    if (!Merged) {
+      UnmergeableRanges.push_back(CR);
+      Ranges.pop_back();
     }
   }
 
+  assert(!UnmergeableRanges.empty() &&
+         "UnmergeableRanges should have at least one range");
+  // We failed to merge ranges into single range.
+  if (UnmergeableRanges.size() != 1)
+    return nullptr;
+
   ICmpInst::Predicate NewPred;
   APInt NewRHS, NewOffset;
   UnmergeableRanges.front().getEquivalentICmp(NewPred, NewRHS, NewOffset);



More information about the llvm-commits mailing list