[PatternMatch] allow intrinsic form of min/max with existing matchers

I skimmed the existing users of these matchers and don't see any problems
(eg, the caller assumes the matched value was a select instruction without checking).

So I think we can generalize the matching to allow the new intrinsics or the cmp+select idioms.

I did not find any unit tests for the matchers, so added some basics there. The instsimplify
tests are adapted from existing tests for the cmp+select pattern and cover the folds in
simplifyICmpWithMinMax().

Differential Revision: https://reviews.llvm.org/D85230
This commit is contained in:
Sanjay Patel 2020-08-06 10:49:26 -04:00
parent dcf3ffb0a8
commit 60f2c6a94c
3 changed files with 81 additions and 80 deletions

View File

@ -1590,6 +1590,17 @@ struct MaxMin_match {
MaxMin_match(const LHS_t &LHS, const RHS_t &RHS) : L(LHS), R(RHS) {}
template <typename OpTy> bool match(OpTy *V) {
if (auto *II = dyn_cast<IntrinsicInst>(V)) {
Intrinsic::ID IID = II->getIntrinsicID();
if ((IID == Intrinsic::smax && Pred_t::match(ICmpInst::ICMP_SGT)) ||
(IID == Intrinsic::smin && Pred_t::match(ICmpInst::ICMP_SLT)) ||
(IID == Intrinsic::umax && Pred_t::match(ICmpInst::ICMP_UGT)) ||
(IID == Intrinsic::umin && Pred_t::match(ICmpInst::ICMP_ULT))) {
Value *LHS = II->getOperand(0), *RHS = II->getOperand(1);
return (L.match(LHS) && R.match(RHS)) ||
(Commutable && L.match(RHS) && R.match(LHS));
}
}
// Look for "(x pred y) ? x : y" or "(x pred y) ? y : x".
auto *SI = dyn_cast<SelectInst>(V);
if (!SI)

View File

@ -812,9 +812,7 @@ define <2 x i8> @smax_smax_constants_partial_undef(<2 x i8> %x) {
define i1 @smax_slt(i8 %x, i8 %y) {
; CHECK-LABEL: @smax_slt(
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smax.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
; CHECK-NEXT: [[R:%.*]] = icmp slt i8 [[M]], [[X]]
; CHECK-NEXT: ret i1 [[R]]
; CHECK-NEXT: ret i1 false
;
%m = call i8 @llvm.smax.i8(i8 %x, i8 %y)
%r = icmp slt i8 %m, %x
@ -823,9 +821,7 @@ define i1 @smax_slt(i8 %x, i8 %y) {
define i1 @smax_sge(i8 %x, i8 %y) {
; CHECK-LABEL: @smax_sge(
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smax.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
; CHECK-NEXT: [[R:%.*]] = icmp sge i8 [[M]], [[X]]
; CHECK-NEXT: ret i1 [[R]]
; CHECK-NEXT: ret i1 true
;
%m = call i8 @llvm.smax.i8(i8 %x, i8 %y)
%r = icmp sge i8 %m, %x
@ -834,9 +830,7 @@ define i1 @smax_sge(i8 %x, i8 %y) {
define i1 @umax_ult(i8 %x, i8 %y) {
; CHECK-LABEL: @umax_ult(
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umax.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
; CHECK-NEXT: [[R:%.*]] = icmp ult i8 [[M]], [[X]]
; CHECK-NEXT: ret i1 [[R]]
; CHECK-NEXT: ret i1 false
;
%m = call i8 @llvm.umax.i8(i8 %x, i8 %y)
%r = icmp ult i8 %m, %x
@ -845,9 +839,7 @@ define i1 @umax_ult(i8 %x, i8 %y) {
define i1 @umax_uge(i8 %x, i8 %y) {
; CHECK-LABEL: @umax_uge(
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umax.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
; CHECK-NEXT: [[R:%.*]] = icmp uge i8 [[M]], [[X]]
; CHECK-NEXT: ret i1 [[R]]
; CHECK-NEXT: ret i1 true
;
%m = call i8 @llvm.umax.i8(i8 %x, i8 %y)
%r = icmp uge i8 %m, %x
@ -856,9 +848,7 @@ define i1 @umax_uge(i8 %x, i8 %y) {
define i1 @smax_sgt(i8 %x, i8 %y) {
; CHECK-LABEL: @smax_sgt(
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smax.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
; CHECK-NEXT: [[R:%.*]] = icmp sgt i8 [[X]], [[M]]
; CHECK-NEXT: ret i1 [[R]]
; CHECK-NEXT: ret i1 false
;
%m = call i8 @llvm.smax.i8(i8 %x, i8 %y)
%r = icmp sgt i8 %x, %m
@ -867,9 +857,7 @@ define i1 @smax_sgt(i8 %x, i8 %y) {
define i1 @smax_sle(i8 %x, i8 %y) {
; CHECK-LABEL: @smax_sle(
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smax.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
; CHECK-NEXT: [[R:%.*]] = icmp sle i8 [[X]], [[M]]
; CHECK-NEXT: ret i1 [[R]]
; CHECK-NEXT: ret i1 true
;
%m = call i8 @llvm.smax.i8(i8 %x, i8 %y)
%r = icmp sle i8 %x, %m
@ -878,9 +866,7 @@ define i1 @smax_sle(i8 %x, i8 %y) {
define i1 @umax_ugt(i8 %x, i8 %y) {
; CHECK-LABEL: @umax_ugt(
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umax.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
; CHECK-NEXT: [[R:%.*]] = icmp ugt i8 [[X]], [[M]]
; CHECK-NEXT: ret i1 [[R]]
; CHECK-NEXT: ret i1 false
;
%m = call i8 @llvm.umax.i8(i8 %x, i8 %y)
%r = icmp ugt i8 %x, %m
@ -889,9 +875,7 @@ define i1 @umax_ugt(i8 %x, i8 %y) {
define i1 @umax_ule(i8 %x, i8 %y) {
; CHECK-LABEL: @umax_ule(
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umax.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
; CHECK-NEXT: [[R:%.*]] = icmp ule i8 [[X]], [[M]]
; CHECK-NEXT: ret i1 [[R]]
; CHECK-NEXT: ret i1 true
;
%m = call i8 @llvm.umax.i8(i8 %x, i8 %y)
%r = icmp ule i8 %x, %m
@ -900,9 +884,7 @@ define i1 @umax_ule(i8 %x, i8 %y) {
define i1 @smin_sgt(i8 %x, i8 %y) {
; CHECK-LABEL: @smin_sgt(
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smin.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
; CHECK-NEXT: [[R:%.*]] = icmp sgt i8 [[M]], [[X]]
; CHECK-NEXT: ret i1 [[R]]
; CHECK-NEXT: ret i1 false
;
%m = call i8 @llvm.smin.i8(i8 %x, i8 %y)
%r = icmp sgt i8 %m, %x
@ -911,9 +893,7 @@ define i1 @smin_sgt(i8 %x, i8 %y) {
define i1 @smin_sle(i8 %x, i8 %y) {
; CHECK-LABEL: @smin_sle(
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smin.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
; CHECK-NEXT: [[R:%.*]] = icmp sle i8 [[M]], [[X]]
; CHECK-NEXT: ret i1 [[R]]
; CHECK-NEXT: ret i1 true
;
%m = call i8 @llvm.smin.i8(i8 %x, i8 %y)
%r = icmp sle i8 %m, %x
@ -922,9 +902,7 @@ define i1 @smin_sle(i8 %x, i8 %y) {
define i1 @umin_ugt(i8 %x, i8 %y) {
; CHECK-LABEL: @umin_ugt(
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umin.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
; CHECK-NEXT: [[R:%.*]] = icmp ugt i8 [[M]], [[X]]
; CHECK-NEXT: ret i1 [[R]]
; CHECK-NEXT: ret i1 false
;
%m = call i8 @llvm.umin.i8(i8 %x, i8 %y)
%r = icmp ugt i8 %m, %x
@ -933,9 +911,7 @@ define i1 @umin_ugt(i8 %x, i8 %y) {
define i1 @umin_ule(i8 %x, i8 %y) {
; CHECK-LABEL: @umin_ule(
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umin.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
; CHECK-NEXT: [[R:%.*]] = icmp ule i8 [[M]], [[X]]
; CHECK-NEXT: ret i1 [[R]]
; CHECK-NEXT: ret i1 true
;
%m = call i8 @llvm.umin.i8(i8 %x, i8 %y)
%r = icmp ule i8 %m, %x
@ -944,9 +920,7 @@ define i1 @umin_ule(i8 %x, i8 %y) {
define i1 @smin_slt(i8 %x, i8 %y) {
; CHECK-LABEL: @smin_slt(
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smin.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
; CHECK-NEXT: [[R:%.*]] = icmp slt i8 [[X]], [[M]]
; CHECK-NEXT: ret i1 [[R]]
; CHECK-NEXT: ret i1 false
;
%m = call i8 @llvm.smin.i8(i8 %x, i8 %y)
%r = icmp slt i8 %x, %m
@ -955,9 +929,7 @@ define i1 @smin_slt(i8 %x, i8 %y) {
define i1 @smin_sge(i8 %x, i8 %y) {
; CHECK-LABEL: @smin_sge(
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smin.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
; CHECK-NEXT: [[R:%.*]] = icmp sge i8 [[X]], [[M]]
; CHECK-NEXT: ret i1 [[R]]
; CHECK-NEXT: ret i1 true
;
%m = call i8 @llvm.smin.i8(i8 %x, i8 %y)
%r = icmp sge i8 %x, %m
@ -966,9 +938,7 @@ define i1 @smin_sge(i8 %x, i8 %y) {
define i1 @umin_ult(i8 %x, i8 %y) {
; CHECK-LABEL: @umin_ult(
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umin.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
; CHECK-NEXT: [[R:%.*]] = icmp ult i8 [[X]], [[M]]
; CHECK-NEXT: ret i1 [[R]]
; CHECK-NEXT: ret i1 false
;
%m = call i8 @llvm.umin.i8(i8 %x, i8 %y)
%r = icmp ult i8 %x, %m
@ -977,9 +947,7 @@ define i1 @umin_ult(i8 %x, i8 %y) {
define i1 @umin_uge(i8 %x, i8 %y) {
; CHECK-LABEL: @umin_uge(
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umin.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
; CHECK-NEXT: [[R:%.*]] = icmp uge i8 [[X]], [[M]]
; CHECK-NEXT: ret i1 [[R]]
; CHECK-NEXT: ret i1 true
;
%m = call i8 @llvm.umin.i8(i8 %x, i8 %y)
%r = icmp uge i8 %x, %m
@ -988,10 +956,7 @@ define i1 @umin_uge(i8 %x, i8 %y) {
define i1 @smaxmin_sge(i8 %x, i8 %y, i8 %z) {
; CHECK-LABEL: @smaxmin_sge(
; CHECK-NEXT: [[MAX:%.*]] = call i8 @llvm.smax.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
; CHECK-NEXT: [[MIN:%.*]] = call i8 @llvm.smin.i8(i8 [[Z:%.*]], i8 [[X]])
; CHECK-NEXT: [[C:%.*]] = icmp sge i8 [[MAX]], [[MIN]]
; CHECK-NEXT: ret i1 [[C]]
; CHECK-NEXT: ret i1 true
;
%max = call i8 @llvm.smax.i8(i8 %x, i8 %y)
%min = call i8 @llvm.smin.i8(i8 %z, i8 %x)
@ -1001,10 +966,7 @@ define i1 @smaxmin_sge(i8 %x, i8 %y, i8 %z) {
define i1 @smaxmin_sgt(i8 %x, i8 %y, i8 %z) {
; CHECK-LABEL: @smaxmin_sgt(
; CHECK-NEXT: [[MAX:%.*]] = call i8 @llvm.smax.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
; CHECK-NEXT: [[MIN:%.*]] = call i8 @llvm.smin.i8(i8 [[Z:%.*]], i8 [[X]])
; CHECK-NEXT: [[C:%.*]] = icmp sgt i8 [[MIN]], [[MAX]]
; CHECK-NEXT: ret i1 [[C]]
; CHECK-NEXT: ret i1 false
;
%max = call i8 @llvm.smax.i8(i8 %x, i8 %y)
%min = call i8 @llvm.smin.i8(i8 %z, i8 %x)
@ -1014,10 +976,7 @@ define i1 @smaxmin_sgt(i8 %x, i8 %y, i8 %z) {
define i1 @smaxmin_sle(i8 %x, i8 %y, i8 %z) {
; CHECK-LABEL: @smaxmin_sle(
; CHECK-NEXT: [[MAX:%.*]] = call i8 @llvm.smax.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
; CHECK-NEXT: [[MIN:%.*]] = call i8 @llvm.smin.i8(i8 [[Z:%.*]], i8 [[X]])
; CHECK-NEXT: [[C:%.*]] = icmp sle i8 [[MIN]], [[MAX]]
; CHECK-NEXT: ret i1 [[C]]
; CHECK-NEXT: ret i1 true
;
%max = call i8 @llvm.smax.i8(i8 %x, i8 %y)
%min = call i8 @llvm.smin.i8(i8 %z, i8 %x)
@ -1027,10 +986,7 @@ define i1 @smaxmin_sle(i8 %x, i8 %y, i8 %z) {
define i1 @smaxmin_slt(i8 %x, i8 %y, i8 %z) {
; CHECK-LABEL: @smaxmin_slt(
; CHECK-NEXT: [[MAX:%.*]] = call i8 @llvm.smax.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
; CHECK-NEXT: [[MIN:%.*]] = call i8 @llvm.smin.i8(i8 [[Z:%.*]], i8 [[X]])
; CHECK-NEXT: [[C:%.*]] = icmp slt i8 [[MAX]], [[MIN]]
; CHECK-NEXT: ret i1 [[C]]
; CHECK-NEXT: ret i1 false
;
%max = call i8 @llvm.smax.i8(i8 %x, i8 %y)
%min = call i8 @llvm.smin.i8(i8 %z, i8 %x)
@ -1040,10 +996,7 @@ define i1 @smaxmin_slt(i8 %x, i8 %y, i8 %z) {
define i1 @umaxmin_uge(i8 %x, i8 %y, i8 %z) {
; CHECK-LABEL: @umaxmin_uge(
; CHECK-NEXT: [[MAX:%.*]] = call i8 @llvm.umax.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
; CHECK-NEXT: [[MIN:%.*]] = call i8 @llvm.umin.i8(i8 [[Z:%.*]], i8 [[X]])
; CHECK-NEXT: [[C:%.*]] = icmp uge i8 [[MAX]], [[MIN]]
; CHECK-NEXT: ret i1 [[C]]
; CHECK-NEXT: ret i1 true
;
%max = call i8 @llvm.umax.i8(i8 %x, i8 %y)
%min = call i8 @llvm.umin.i8(i8 %z, i8 %x)
@ -1053,10 +1006,7 @@ define i1 @umaxmin_uge(i8 %x, i8 %y, i8 %z) {
define i1 @umaxmin_ugt(i8 %x, i8 %y, i8 %z) {
; CHECK-LABEL: @umaxmin_ugt(
; CHECK-NEXT: [[MAX:%.*]] = call i8 @llvm.umax.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
; CHECK-NEXT: [[MIN:%.*]] = call i8 @llvm.umin.i8(i8 [[Z:%.*]], i8 [[X]])
; CHECK-NEXT: [[C:%.*]] = icmp ugt i8 [[MIN]], [[MAX]]
; CHECK-NEXT: ret i1 [[C]]
; CHECK-NEXT: ret i1 false
;
%max = call i8 @llvm.umax.i8(i8 %x, i8 %y)
%min = call i8 @llvm.umin.i8(i8 %z, i8 %x)
@ -1066,10 +1016,7 @@ define i1 @umaxmin_ugt(i8 %x, i8 %y, i8 %z) {
define i1 @umaxmin_ule(i8 %x, i8 %y, i8 %z) {
; CHECK-LABEL: @umaxmin_ule(
; CHECK-NEXT: [[MAX:%.*]] = call i8 @llvm.umax.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
; CHECK-NEXT: [[MIN:%.*]] = call i8 @llvm.umin.i8(i8 [[Z:%.*]], i8 [[X]])
; CHECK-NEXT: [[C:%.*]] = icmp ule i8 [[MIN]], [[MAX]]
; CHECK-NEXT: ret i1 [[C]]
; CHECK-NEXT: ret i1 true
;
%max = call i8 @llvm.umax.i8(i8 %x, i8 %y)
%min = call i8 @llvm.umin.i8(i8 %z, i8 %x)
@ -1079,10 +1026,7 @@ define i1 @umaxmin_ule(i8 %x, i8 %y, i8 %z) {
define i1 @umaxmin_ult(i8 %x, i8 %y, i8 %z) {
; CHECK-LABEL: @umaxmin_ult(
; CHECK-NEXT: [[MAX:%.*]] = call i8 @llvm.umax.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
; CHECK-NEXT: [[MIN:%.*]] = call i8 @llvm.umin.i8(i8 [[Z:%.*]], i8 [[X]])
; CHECK-NEXT: [[C:%.*]] = icmp ult i8 [[MAX]], [[MIN]]
; CHECK-NEXT: ret i1 [[C]]
; CHECK-NEXT: ret i1 false
;
%max = call i8 @llvm.umax.i8(i8 %x, i8 %y)
%min = call i8 @llvm.umin.i8(i8 %z, i8 %x)
@ -1090,6 +1034,8 @@ define i1 @umaxmin_ult(i8 %x, i8 %y, i8 %z) {
ret i1 %c
}
; Negative test - should reduce via instcombine, but not here.
define i1 @smax_eq(i8 %x, i8 %y) {
; CHECK-LABEL: @smax_eq(
; CHECK-NEXT: [[MAX:%.*]] = call i8 @llvm.smax.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
@ -1101,6 +1047,8 @@ define i1 @smax_eq(i8 %x, i8 %y) {
ret i1 %r
}
; Negative test - should reduce via instcombine, but not here.
define i1 @smax_eq_commute(i8 %x, i8 %y) {
; CHECK-LABEL: @smax_eq_commute(
; CHECK-NEXT: [[MAX:%.*]] = call i8 @llvm.smax.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
@ -1112,6 +1060,8 @@ define i1 @smax_eq_commute(i8 %x, i8 %y) {
ret i1 %r
}
; Negative test - should reduce via instcombine, but not here.
define i1 @umax_eq(i8 %x, i8 %y) {
; CHECK-LABEL: @umax_eq(
; CHECK-NEXT: [[MAX:%.*]] = call i8 @llvm.umax.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
@ -1123,6 +1073,8 @@ define i1 @umax_eq(i8 %x, i8 %y) {
ret i1 %r
}
; Negative test - should reduce via instcombine, but not here.
define i1 @umax_eq_commute(i8 %x, i8 %y) {
; CHECK-LABEL: @umax_eq_commute(
; CHECK-NEXT: [[MAX:%.*]] = call i8 @llvm.umax.i8(i8 [[X:%.*]], i8 [[Y:%.*]])

View File

@ -1246,6 +1246,44 @@ TEST_F(PatternMatchTest, WithOverflowInst) {
EXPECT_EQ(Add, WOI);
}
TEST_F(PatternMatchTest, MinMaxIntrinsics) {
Type *Ty = IRB.getInt32Ty();
Value *L = ConstantInt::get(Ty, 1);
Value *R = ConstantInt::get(Ty, 2);
Value *MatchL, *MatchR;
// Check for intrinsic ID match and capture of operands.
EXPECT_TRUE(m_SMax(m_Value(MatchL), m_Value(MatchR))
.match(IRB.CreateBinaryIntrinsic(Intrinsic::smax, L, R)));
EXPECT_EQ(L, MatchL);
EXPECT_EQ(R, MatchR);
EXPECT_TRUE(m_SMin(m_Value(MatchL), m_Value(MatchR))
.match(IRB.CreateBinaryIntrinsic(Intrinsic::smin, L, R)));
EXPECT_EQ(L, MatchL);
EXPECT_EQ(R, MatchR);
EXPECT_TRUE(m_UMax(m_Value(MatchL), m_Value(MatchR))
.match(IRB.CreateBinaryIntrinsic(Intrinsic::umax, L, R)));
EXPECT_EQ(L, MatchL);
EXPECT_EQ(R, MatchR);
EXPECT_TRUE(m_UMin(m_Value(MatchL), m_Value(MatchR))
.match(IRB.CreateBinaryIntrinsic(Intrinsic::umin, L, R)));
EXPECT_EQ(L, MatchL);
EXPECT_EQ(R, MatchR);
// Check for intrinsic ID mismatch.
EXPECT_FALSE(m_SMax(m_Value(MatchL), m_Value(MatchR))
.match(IRB.CreateBinaryIntrinsic(Intrinsic::smin, L, R)));
EXPECT_FALSE(m_SMin(m_Value(MatchL), m_Value(MatchR))
.match(IRB.CreateBinaryIntrinsic(Intrinsic::umax, L, R)));
EXPECT_FALSE(m_UMax(m_Value(MatchL), m_Value(MatchR))
.match(IRB.CreateBinaryIntrinsic(Intrinsic::umin, L, R)));
EXPECT_FALSE(m_UMin(m_Value(MatchL), m_Value(MatchR))
.match(IRB.CreateBinaryIntrinsic(Intrinsic::smax, L, R)));
}
TEST_F(PatternMatchTest, IntrinsicMatcher) {
Value *Name = IRB.CreateAlloca(IRB.getInt8Ty());
Value *Hash = IRB.getInt64(0);