[llvm] [InstCombine] Fold (X / C) < X and (X >> C) < X into X > 0 (PR #85555)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Apr 10 15:28:42 PDT 2024
https://github.com/Poseydon42 updated https://github.com/llvm/llvm-project/pull/85555
>From 31a1058e0189295b3abc3d6d15d0277a0bc3634c Mon Sep 17 00:00:00 2001
From: Poseydon42 <vvmposeydon at gmail.com>
Date: Sun, 17 Mar 2024 21:56:02 +0000
Subject: [PATCH 1/2] [InstCombine] Add test cases for folds (X / C) cmp X and
(X >> C) cmp X
---
.../InstCombine/icmp-div-constant.ll | 147 ++++++++++++++++++
1 file changed, 147 insertions(+)
diff --git a/llvm/test/Transforms/InstCombine/icmp-div-constant.ll b/llvm/test/Transforms/InstCombine/icmp-div-constant.ll
index 8dcb96284685ff..34fa4d4145a3cf 100644
--- a/llvm/test/Transforms/InstCombine/icmp-div-constant.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-div-constant.ll
@@ -375,3 +375,150 @@ define i1 @sdiv_eq_smin_use(i32 %x, i32 %y) {
%r = icmp eq i32 %d, -2147483648
ret i1 %r
}
+
+; Fold (X / C) cmp X into X ~cmp 0 (~cmp is the inverse predicate of cmp), for some C != 1
+; Alternative form of this fold is when division is replaced with logic right shift
+
+define i1 @sdiv_x_by_const_cmp_x(i32 %x) {
+; CHECK-LABEL: @sdiv_x_by_const_cmp_x(
+; CHECK-NEXT: [[V:%.*]] = sdiv i32 [[X:%.*]], 13
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[V]], [[X]]
+; CHECK-NEXT: ret i1 [[TMP1]]
+;
+ %v = sdiv i32 %x, 13
+ %r = icmp eq i32 %v, %x
+ ret i1 %r
+}
+
+define i1 @udiv_x_by_const_cmp_x(i32 %x) {
+; CHECK-LABEL: @udiv_x_by_const_cmp_x(
+; CHECK-NEXT: [[TMP2:%.*]] = udiv i32 [[X:%.*]], 123
+; CHECK-NEXT: [[TMP1:%.*]] = icmp slt i32 [[TMP2]], [[X]]
+; CHECK-NEXT: ret i1 [[TMP1]]
+;
+ %1 = udiv i32 %x, 123
+ %2 = icmp slt i32 %1, %x
+ ret i1 %2
+}
+
+; Same as above but with right shift instead of division (C != 0)
+
+define i1 @lshr_x_by_const_cmp_x(i32 %x) {
+; CHECK-LABEL: @lshr_x_by_const_cmp_x(
+; CHECK-NEXT: [[V:%.*]] = lshr i32 [[X:%.*]], 1
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[V]], [[X]]
+; CHECK-NEXT: ret i1 [[TMP1]]
+;
+ %v = lshr i32 %x, 1
+ %r = icmp eq i32 %v, %x
+ ret i1 %r
+}
+
+define <4 x i1> @lshr_by_const_cmp_sle_value(<4 x i32> %x) {
+; CHECK-LABEL: @lshr_by_const_cmp_sle_value(
+; CHECK-NEXT: [[V:%.*]] = lshr <4 x i32> [[X:%.*]], <i32 3, i32 3, i32 3, i32 3>
+; CHECK-NEXT: [[R:%.*]] = icmp sle <4 x i32> [[V]], [[X]]
+; CHECK-NEXT: ret <4 x i1> [[R]]
+;
+ %v = lshr <4 x i32> %x, <i32 3, i32 3, i32 3, i32 3>
+ %r = icmp sle <4 x i32> %v, %x
+ ret <4 x i1> %r
+}
+
+define i1 @lshr_by_const_cmp_sge_value(i32 %x) {
+; CHECK-LABEL: @lshr_by_const_cmp_sge_value(
+; CHECK-NEXT: [[V:%.*]] = lshr i32 [[X:%.*]], 3
+; CHECK-NEXT: [[R:%.*]] = icmp sge i32 [[V]], [[X]]
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = lshr i32 %x, 3
+ %r = icmp sge i32 %v, %x
+ ret i1 %r
+}
+
+define i1 @ashr_x_by_const_cmp_sge_x(i32 %x) {
+; CHECK-LABEL: @ashr_x_by_const_cmp_sge_x(
+; CHECK-NEXT: [[V:%.*]] = ashr i32 [[X:%.*]], 5
+; CHECK-NEXT: [[R:%.*]] = icmp sge i32 [[V]], [[X]]
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = ashr i32 %x, 5
+ %r = icmp sge i32 %v, %x
+ ret i1 %r
+}
+
+; Negative test - constant is 1
+
+define <2 x i1> @udiv_x_by_const_cmp_eq_value_neg(<2 x i32> %x) {
+; CHECK-LABEL: @udiv_x_by_const_cmp_eq_value_neg(
+; CHECK-NEXT: [[V:%.*]] = udiv <2 x i32> [[X:%.*]], <i32 1, i32 3>
+; CHECK-NEXT: [[R:%.*]] = icmp eq <2 x i32> [[V]], [[X]]
+; CHECK-NEXT: ret <2 x i1> [[R]]
+;
+ %v = udiv <2 x i32> %x, <i32 1, i32 3>
+ %r = icmp eq <2 x i32> %v, %x
+ ret <2 x i1> %r
+}
+
+define <2 x i1> @sdiv_x_by_const_cmp_eq_value_neg(<2 x i32> %x) {
+; CHECK-LABEL: @sdiv_x_by_const_cmp_eq_value_neg(
+; CHECK-NEXT: [[V:%.*]] = sdiv <2 x i32> [[X:%.*]], <i32 1, i32 3>
+; CHECK-NEXT: [[R:%.*]] = icmp eq <2 x i32> [[V]], [[X]]
+; CHECK-NEXT: ret <2 x i1> [[R]]
+;
+ %v = sdiv <2 x i32> %x, <i32 1, i32 3>
+ %r = icmp eq <2 x i32> %v, %x
+ ret <2 x i1> %r
+}
+
+; Negative test - constant is 0
+
+define <2 x i1> @lshr_x_by_const_cmp_slt_value_neg(<2 x i32> %x) {
+; CHECK-LABEL: @lshr_x_by_const_cmp_slt_value_neg(
+; CHECK-NEXT: [[V:%.*]] = lshr <2 x i32> [[X:%.*]], <i32 0, i32 2>
+; CHECK-NEXT: [[R:%.*]] = icmp slt <2 x i32> [[V]], [[X]]
+; CHECK-NEXT: ret <2 x i1> [[R]]
+;
+ %v = lshr <2 x i32> %x, <i32 0, i32 2>
+ %r = icmp slt <2 x i32> %v, %x
+ ret <2 x i1> %r
+}
+
+; Negative test - unsigned predicate with sdiv
+
+define i1 @sdiv_x_by_const_cmp_ult_value_neg(i32 %x) {
+; CHECK-LABEL: @sdiv_x_by_const_cmp_ult_value_neg(
+; CHECK-NEXT: [[V:%.*]] = sdiv i32 [[X:%.*]], 3
+; CHECK-NEXT: [[R:%.*]] = icmp ult i32 [[V]], [[X]]
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = sdiv i32 %x, 3
+ %r = icmp ult i32 %v, %x
+ ret i1 %r
+}
+
+; Negative case - one of the components of a vector is 1
+
+define <4 x i1> @sdiv_x_by_const_cmp_sgt_value_neg(<4 x i32> %x) {
+; CHECK-LABEL: @sdiv_x_by_const_cmp_sgt_value_neg(
+; CHECK-NEXT: [[V:%.*]] = sdiv <4 x i32> [[X:%.*]], <i32 1, i32 2, i32 3, i32 4>
+; CHECK-NEXT: [[R:%.*]] = icmp sgt <4 x i32> [[V]], [[X]]
+; CHECK-NEXT: ret <4 x i1> [[R]]
+;
+ %v = sdiv <4 x i32> %x, <i32 1, i32 2, i32 3, i32 4>
+ %r = icmp sgt <4 x i32> %v, %x
+ ret <4 x i1> %r
+}
+
+; Negative case - ashr only allows sge/slt predicates
+
+define i1 @ashr_x_by_const_cmp_sle_value_neg(i32 %x) {
+; CHECK-LABEL: @ashr_x_by_const_cmp_sle_value_neg(
+; CHECK-NEXT: [[V:%.*]] = ashr i32 [[X:%.*]], 3
+; CHECK-NEXT: [[R:%.*]] = icmp sle i32 [[V]], [[X]]
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = ashr i32 %x, 3
+ %r = icmp sle i32 %v, %x
+ ret i1 %r
+}
>From c203d1fd737f57a2b0ef5843ccbe34e9d77abbc5 Mon Sep 17 00:00:00 2001
From: Poseydon42 <vvmposeydon at gmail.com>
Date: Mon, 18 Mar 2024 20:56:57 +0000
Subject: [PATCH 2/2] [InstCombine] Fold (X / C pred X) and (X >> C pred X)
into X ~pred 0
---
.../InstCombine/InstCombineCompares.cpp | 34 +++++++++++++++++++
.../InstCombine/icmp-div-constant.ll | 18 ++++------
2 files changed, 40 insertions(+), 12 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 0dce0077bf1588..f19255d00468cc 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -7103,6 +7103,40 @@ Instruction *InstCombinerImpl::foldICmpCommutative(ICmpInst::Predicate Pred,
if (Value *V = foldICmpWithLowBitMaskedVal(Pred, Op0, Op1, Q, *this))
return replaceInstUsesWith(CxtI, V);
+ // Folding (X / Y) pred X => X swap(pred) 0 for constant Y other than 0 or 1
+ {
+ const APInt *Divisor;
+ if (match(Op0, m_UDiv(m_Specific(Op1), m_APInt(Divisor))) &&
+ Divisor->ugt(1)) {
+ return new ICmpInst(ICmpInst::getSwappedPredicate(Pred), Op1,
+ Constant::getNullValue(Op1->getType()));
+ }
+
+ if (!ICmpInst::isUnsigned(Pred) &&
+ match(Op0, m_SDiv(m_Specific(Op1), m_APInt(Divisor))) &&
+ Divisor->ugt(1)) {
+ return new ICmpInst(ICmpInst::getSwappedPredicate(Pred), Op1,
+ Constant::getNullValue(Op1->getType()));
+ }
+ }
+
+ // Another case of this fold is (X >> Y) pred X => X swap(pred) 0 if Y != 0
+ {
+ const APInt *Shift;
+ if (match(Op0, m_LShr(m_Specific(Op1), m_APInt(Shift))) &&
+ !Shift->isZero()) {
+ return new ICmpInst(ICmpInst::getSwappedPredicate(Pred), Op1,
+ Constant::getNullValue(Op1->getType()));
+ }
+
+ if ((Pred == CmpInst::ICMP_SLT || Pred == CmpInst::ICMP_SGE) &&
+ match(Op0, m_AShr(m_Specific(Op1), m_APInt(Shift))) &&
+ !Shift->isZero()) {
+ return new ICmpInst(ICmpInst::getSwappedPredicate(Pred), Op1,
+ Constant::getNullValue(Op1->getType()));
+ }
+ }
+
return nullptr;
}
diff --git a/llvm/test/Transforms/InstCombine/icmp-div-constant.ll b/llvm/test/Transforms/InstCombine/icmp-div-constant.ll
index 34fa4d4145a3cf..b047715432d779 100644
--- a/llvm/test/Transforms/InstCombine/icmp-div-constant.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-div-constant.ll
@@ -381,8 +381,7 @@ define i1 @sdiv_eq_smin_use(i32 %x, i32 %y) {
define i1 @sdiv_x_by_const_cmp_x(i32 %x) {
; CHECK-LABEL: @sdiv_x_by_const_cmp_x(
-; CHECK-NEXT: [[V:%.*]] = sdiv i32 [[X:%.*]], 13
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[V]], [[X]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[X:%.*]], 0
; CHECK-NEXT: ret i1 [[TMP1]]
;
%v = sdiv i32 %x, 13
@@ -392,8 +391,7 @@ define i1 @sdiv_x_by_const_cmp_x(i32 %x) {
define i1 @udiv_x_by_const_cmp_x(i32 %x) {
; CHECK-LABEL: @udiv_x_by_const_cmp_x(
-; CHECK-NEXT: [[TMP2:%.*]] = udiv i32 [[X:%.*]], 123
-; CHECK-NEXT: [[TMP1:%.*]] = icmp slt i32 [[TMP2]], [[X]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[X:%.*]], 0
; CHECK-NEXT: ret i1 [[TMP1]]
;
%1 = udiv i32 %x, 123
@@ -405,8 +403,7 @@ define i1 @udiv_x_by_const_cmp_x(i32 %x) {
define i1 @lshr_x_by_const_cmp_x(i32 %x) {
; CHECK-LABEL: @lshr_x_by_const_cmp_x(
-; CHECK-NEXT: [[V:%.*]] = lshr i32 [[X:%.*]], 1
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[V]], [[X]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[X:%.*]], 0
; CHECK-NEXT: ret i1 [[TMP1]]
;
%v = lshr i32 %x, 1
@@ -416,8 +413,7 @@ define i1 @lshr_x_by_const_cmp_x(i32 %x) {
define <4 x i1> @lshr_by_const_cmp_sle_value(<4 x i32> %x) {
; CHECK-LABEL: @lshr_by_const_cmp_sle_value(
-; CHECK-NEXT: [[V:%.*]] = lshr <4 x i32> [[X:%.*]], <i32 3, i32 3, i32 3, i32 3>
-; CHECK-NEXT: [[R:%.*]] = icmp sle <4 x i32> [[V]], [[X]]
+; CHECK-NEXT: [[R:%.*]] = icmp sgt <4 x i32> [[X:%.*]], <i32 -1, i32 -1, i32 -1, i32 -1>
; CHECK-NEXT: ret <4 x i1> [[R]]
;
%v = lshr <4 x i32> %x, <i32 3, i32 3, i32 3, i32 3>
@@ -427,8 +423,7 @@ define <4 x i1> @lshr_by_const_cmp_sle_value(<4 x i32> %x) {
define i1 @lshr_by_const_cmp_sge_value(i32 %x) {
; CHECK-LABEL: @lshr_by_const_cmp_sge_value(
-; CHECK-NEXT: [[V:%.*]] = lshr i32 [[X:%.*]], 3
-; CHECK-NEXT: [[R:%.*]] = icmp sge i32 [[V]], [[X]]
+; CHECK-NEXT: [[R:%.*]] = icmp slt i32 [[X:%.*]], 1
; CHECK-NEXT: ret i1 [[R]]
;
%v = lshr i32 %x, 3
@@ -438,8 +433,7 @@ define i1 @lshr_by_const_cmp_sge_value(i32 %x) {
define i1 @ashr_x_by_const_cmp_sge_x(i32 %x) {
; CHECK-LABEL: @ashr_x_by_const_cmp_sge_x(
-; CHECK-NEXT: [[V:%.*]] = ashr i32 [[X:%.*]], 5
-; CHECK-NEXT: [[R:%.*]] = icmp sge i32 [[V]], [[X]]
+; CHECK-NEXT: [[R:%.*]] = icmp slt i32 [[X:%.*]], 1
; CHECK-NEXT: ret i1 [[R]]
;
%v = ashr i32 %x, 5
More information about the llvm-commits
mailing list