[llvm] [InstCombine] Decompose an icmp into multiple ranges (PR #69855)
Yingwei Zheng via llvm-commits
llvm-commits at lists.llvm.org
Sat Oct 21 12:01:41 PDT 2023
https://github.com/dtcxzyw created https://github.com/llvm/llvm-project/pull/69855
This PR decomposes an icmp into an intersection/union of ranges. It will handle patterns that cannot be captured by `foldAndOrOfICmpsUsingRanges`.
Alive2: https://alive2.llvm.org/ce/z/FMK_QA
Fixes #69123.
>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/2] [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/2] [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
More information about the llvm-commits
mailing list