Skip to content

Commit 979a035

Browse files
[InstCombine] Fold X Pred C2 ? X BOp C1 : C2 BOp C1 to min/max(X, C2) BOp C1 (#116888)
Fixes #82414. General Proof: https://alive2.llvm.org/ce/z/ERjNs4 Proof for Tests: https://alive2.llvm.org/ce/z/K-934G This PR transforms `select` instructions of the form `select (Cmp X C1) (BOp X C2) C3` to `BOp (min/max X C1) C2` iff `C3 == BOp C1 C2`. This helps in eliminating a noop loop in rust-lang/rust#123845 but does not improve optimizations.
1 parent eaa6cc5 commit 979a035

File tree

9 files changed

+557
-93
lines changed

9 files changed

+557
-93
lines changed

clang/test/CodeGen/attr-counted-by.c

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,8 @@ void test1(struct annotated *p, int index, int val) {
119119
// SANITIZE-WITH-ATTR: cont3:
120120
// SANITIZE-WITH-ATTR-NEXT: [[ARRAY:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 12
121121
// SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds nuw [0 x i32], ptr [[ARRAY]], i64 0, i64 [[INDEX]]
122-
// SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = shl i32 [[DOT_COUNTED_BY_LOAD]], 2
123-
// SANITIZE-WITH-ATTR-NEXT: [[DOTINV:%.*]] = icmp slt i32 [[DOT_COUNTED_BY_LOAD]], 0
124-
// SANITIZE-WITH-ATTR-NEXT: [[CONV:%.*]] = select i1 [[DOTINV]], i32 0, i32 [[TMP2]]
122+
// SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = tail call i32 @llvm.smax.i32(i32 [[DOT_COUNTED_BY_LOAD]], i32 0)
123+
// SANITIZE-WITH-ATTR-NEXT: [[CONV:%.*]] = shl i32 [[TMP2]], 2
125124
// SANITIZE-WITH-ATTR-NEXT: store i32 [[CONV]], ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA4]]
126125
// SANITIZE-WITH-ATTR-NEXT: ret void
127126
//
@@ -130,9 +129,8 @@ void test1(struct annotated *p, int index, int val) {
130129
// NO-SANITIZE-WITH-ATTR-NEXT: entry:
131130
// NO-SANITIZE-WITH-ATTR-NEXT: [[DOT_COUNTED_BY_GEP:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8
132131
// NO-SANITIZE-WITH-ATTR-NEXT: [[DOT_COUNTED_BY_LOAD:%.*]] = load i32, ptr [[DOT_COUNTED_BY_GEP]], align 4
133-
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = shl i32 [[DOT_COUNTED_BY_LOAD]], 2
134-
// NO-SANITIZE-WITH-ATTR-NEXT: [[DOTINV:%.*]] = icmp slt i32 [[DOT_COUNTED_BY_LOAD]], 0
135-
// NO-SANITIZE-WITH-ATTR-NEXT: [[CONV:%.*]] = select i1 [[DOTINV]], i32 0, i32 [[TMP0]]
132+
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = tail call i32 @llvm.smax.i32(i32 [[DOT_COUNTED_BY_LOAD]], i32 0)
133+
// NO-SANITIZE-WITH-ATTR-NEXT: [[CONV:%.*]] = shl i32 [[TMP0]], 2
136134
// NO-SANITIZE-WITH-ATTR-NEXT: [[ARRAY:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 12
137135
// NO-SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds nuw [0 x i32], ptr [[ARRAY]], i64 0, i64 [[INDEX]]
138136
// NO-SANITIZE-WITH-ATTR-NEXT: store i32 [[CONV]], ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA2]]
@@ -539,10 +537,9 @@ size_t test5_bdos(struct anon_struct *p) {
539537
// SANITIZE-WITH-ATTR: cont3:
540538
// SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
541539
// SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [0 x i32], ptr [[TMP1]], i64 0, i64 [[IDXPROM]]
542-
// SANITIZE-WITH-ATTR-NEXT: [[DOTINV:%.*]] = icmp slt i64 [[DOT_COUNTED_BY_LOAD]], 0
543-
// SANITIZE-WITH-ATTR-NEXT: [[DOT_COUNTED_BY_LOAD_TR:%.*]] = trunc i64 [[DOT_COUNTED_BY_LOAD]] to i32
544-
// SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = shl i32 [[DOT_COUNTED_BY_LOAD_TR]], 2
545-
// SANITIZE-WITH-ATTR-NEXT: [[CONV:%.*]] = select i1 [[DOTINV]], i32 0, i32 [[TMP2]]
540+
// SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = tail call i64 @llvm.smax.i64(i64 [[DOT_COUNTED_BY_LOAD]], i64 0)
541+
// SANITIZE-WITH-ATTR-NEXT: [[DOTTR:%.*]] = trunc i64 [[TMP2]] to i32
542+
// SANITIZE-WITH-ATTR-NEXT: [[CONV:%.*]] = shl i32 [[DOTTR]], 2
546543
// SANITIZE-WITH-ATTR-NEXT: store i32 [[CONV]], ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA4]]
547544
// SANITIZE-WITH-ATTR-NEXT: ret void
548545
//
@@ -551,10 +548,9 @@ size_t test5_bdos(struct anon_struct *p) {
551548
// NO-SANITIZE-WITH-ATTR-NEXT: entry:
552549
// NO-SANITIZE-WITH-ATTR-NEXT: [[DOT_COUNTED_BY_GEP:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8
553550
// NO-SANITIZE-WITH-ATTR-NEXT: [[DOT_COUNTED_BY_LOAD:%.*]] = load i64, ptr [[DOT_COUNTED_BY_GEP]], align 4
554-
// NO-SANITIZE-WITH-ATTR-NEXT: [[DOTINV:%.*]] = icmp slt i64 [[DOT_COUNTED_BY_LOAD]], 0
555-
// NO-SANITIZE-WITH-ATTR-NEXT: [[DOT_COUNTED_BY_LOAD_TR:%.*]] = trunc i64 [[DOT_COUNTED_BY_LOAD]] to i32
556-
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = shl i32 [[DOT_COUNTED_BY_LOAD_TR]], 2
557-
// NO-SANITIZE-WITH-ATTR-NEXT: [[CONV:%.*]] = select i1 [[DOTINV]], i32 0, i32 [[TMP0]]
551+
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = tail call i64 @llvm.smax.i64(i64 [[DOT_COUNTED_BY_LOAD]], i64 0)
552+
// NO-SANITIZE-WITH-ATTR-NEXT: [[DOTTR:%.*]] = trunc i64 [[TMP0]] to i32
553+
// NO-SANITIZE-WITH-ATTR-NEXT: [[CONV:%.*]] = shl i32 [[DOTTR]], 2
558554
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
559555
// NO-SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
560556
// NO-SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [0 x i32], ptr [[TMP1]], i64 0, i64 [[IDXPROM]]
@@ -588,19 +584,17 @@ void test6(struct anon_struct *p, int index) {
588584
// SANITIZE-WITH-ATTR-NEXT: entry:
589585
// SANITIZE-WITH-ATTR-NEXT: [[DOT_COUNTED_BY_GEP:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8
590586
// SANITIZE-WITH-ATTR-NEXT: [[DOT_COUNTED_BY_LOAD:%.*]] = load i64, ptr [[DOT_COUNTED_BY_GEP]], align 4
591-
// SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = shl nuw i64 [[DOT_COUNTED_BY_LOAD]], 2
592-
// SANITIZE-WITH-ATTR-NEXT: [[DOTINV:%.*]] = icmp slt i64 [[DOT_COUNTED_BY_LOAD]], 0
593-
// SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = select i1 [[DOTINV]], i64 0, i64 [[TMP0]]
587+
// SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = tail call i64 @llvm.smax.i64(i64 [[DOT_COUNTED_BY_LOAD]], i64 0)
588+
// SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = shl i64 [[TMP0]], 2
594589
// SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP1]]
595590
//
596591
// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, -3) i64 @test6_bdos(
597592
// NO-SANITIZE-WITH-ATTR-SAME: ptr nocapture noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR2]] {
598593
// NO-SANITIZE-WITH-ATTR-NEXT: entry:
599594
// NO-SANITIZE-WITH-ATTR-NEXT: [[DOT_COUNTED_BY_GEP:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8
600595
// NO-SANITIZE-WITH-ATTR-NEXT: [[DOT_COUNTED_BY_LOAD:%.*]] = load i64, ptr [[DOT_COUNTED_BY_GEP]], align 4
601-
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = shl nuw i64 [[DOT_COUNTED_BY_LOAD]], 2
602-
// NO-SANITIZE-WITH-ATTR-NEXT: [[DOTINV:%.*]] = icmp slt i64 [[DOT_COUNTED_BY_LOAD]], 0
603-
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = select i1 [[DOTINV]], i64 0, i64 [[TMP0]]
596+
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = tail call i64 @llvm.smax.i64(i64 [[DOT_COUNTED_BY_LOAD]], i64 0)
597+
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = shl i64 [[TMP0]], 2
604598
// NO-SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP1]]
605599
//
606600
// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test6_bdos(
@@ -1740,9 +1734,8 @@ struct annotated_struct_array {
17401734
// SANITIZE-WITH-ATTR: cont20:
17411735
// SANITIZE-WITH-ATTR-NEXT: [[ARRAY:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP2]], i64 12
17421736
// SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX18:%.*]] = getelementptr inbounds [0 x i32], ptr [[ARRAY]], i64 0, i64 [[IDXPROM15]]
1743-
// SANITIZE-WITH-ATTR-NEXT: [[TMP5:%.*]] = shl i32 [[DOT_COUNTED_BY_LOAD]], 2
1744-
// SANITIZE-WITH-ATTR-NEXT: [[DOTINV:%.*]] = icmp slt i32 [[DOT_COUNTED_BY_LOAD]], 0
1745-
// SANITIZE-WITH-ATTR-NEXT: [[CONV:%.*]] = select i1 [[DOTINV]], i32 0, i32 [[TMP5]]
1737+
// SANITIZE-WITH-ATTR-NEXT: [[TMP5:%.*]] = tail call i32 @llvm.smax.i32(i32 [[DOT_COUNTED_BY_LOAD]], i32 0)
1738+
// SANITIZE-WITH-ATTR-NEXT: [[CONV:%.*]] = shl i32 [[TMP5]], 2
17461739
// SANITIZE-WITH-ATTR-NEXT: store i32 [[CONV]], ptr [[ARRAYIDX18]], align 4, !tbaa [[TBAA4]]
17471740
// SANITIZE-WITH-ATTR-NEXT: ret void
17481741
//
@@ -1754,9 +1747,8 @@ struct annotated_struct_array {
17541747
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA11]]
17551748
// NO-SANITIZE-WITH-ATTR-NEXT: [[DOT_COUNTED_BY_GEP:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 8
17561749
// NO-SANITIZE-WITH-ATTR-NEXT: [[DOT_COUNTED_BY_LOAD:%.*]] = load i32, ptr [[DOT_COUNTED_BY_GEP]], align 4
1757-
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = shl i32 [[DOT_COUNTED_BY_LOAD]], 2
1758-
// NO-SANITIZE-WITH-ATTR-NEXT: [[DOTINV:%.*]] = icmp slt i32 [[DOT_COUNTED_BY_LOAD]], 0
1759-
// NO-SANITIZE-WITH-ATTR-NEXT: [[CONV:%.*]] = select i1 [[DOTINV]], i32 0, i32 [[TMP1]]
1750+
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = tail call i32 @llvm.smax.i32(i32 [[DOT_COUNTED_BY_LOAD]], i32 0)
1751+
// NO-SANITIZE-WITH-ATTR-NEXT: [[CONV:%.*]] = shl i32 [[TMP1]], 2
17601752
// NO-SANITIZE-WITH-ATTR-NEXT: [[ARRAY:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP0]], i64 12
17611753
// NO-SANITIZE-WITH-ATTR-NEXT: [[IDXPROM4:%.*]] = sext i32 [[IDX2]] to i64
17621754
// NO-SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX5:%.*]] = getelementptr inbounds [0 x i32], ptr [[ARRAY]], i64 0, i64 [[IDXPROM4]]

llvm/include/llvm/Analysis/ValueTracking.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,10 +1178,20 @@ SelectPatternResult matchDecomposedSelectPattern(
11781178
CmpInst *CmpI, Value *TrueVal, Value *FalseVal, Value *&LHS, Value *&RHS,
11791179
Instruction::CastOps *CastOp = nullptr, unsigned Depth = 0);
11801180

1181+
/// Determine the pattern for predicate `X Pred Y ? X : Y`.
1182+
SelectPatternResult
1183+
getSelectPattern(CmpInst::Predicate Pred,
1184+
SelectPatternNaNBehavior NaNBehavior = SPNB_NA,
1185+
bool Ordered = false);
1186+
11811187
/// Return the canonical comparison predicate for the specified
11821188
/// minimum/maximum flavor.
11831189
CmpInst::Predicate getMinMaxPred(SelectPatternFlavor SPF, bool Ordered = false);
11841190

1191+
/// Convert given `SPF` to equivalent min/max intrinsic.
1192+
/// Caller must ensure `SPF` is an integer min or max pattern.
1193+
Intrinsic::ID getMinMaxIntrinsic(SelectPatternFlavor SPF);
1194+
11851195
/// Return the inverse minimum/maximum flavor of the specified flavor.
11861196
/// For example, signed minimum is the inverse of signed maximum.
11871197
SelectPatternFlavor getInverseMinMaxFlavor(SelectPatternFlavor SPF);

llvm/lib/Analysis/ValueTracking.cpp

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8589,6 +8589,37 @@ bool llvm::isKnownInversion(const Value *X, const Value *Y) {
85898589
return CR1.inverse() == CR2;
85908590
}
85918591

8592+
SelectPatternResult llvm::getSelectPattern(CmpInst::Predicate Pred,
8593+
SelectPatternNaNBehavior NaNBehavior,
8594+
bool Ordered) {
8595+
switch (Pred) {
8596+
default:
8597+
return {SPF_UNKNOWN, SPNB_NA, false}; // Equality.
8598+
case ICmpInst::ICMP_UGT:
8599+
case ICmpInst::ICMP_UGE:
8600+
return {SPF_UMAX, SPNB_NA, false};
8601+
case ICmpInst::ICMP_SGT:
8602+
case ICmpInst::ICMP_SGE:
8603+
return {SPF_SMAX, SPNB_NA, false};
8604+
case ICmpInst::ICMP_ULT:
8605+
case ICmpInst::ICMP_ULE:
8606+
return {SPF_UMIN, SPNB_NA, false};
8607+
case ICmpInst::ICMP_SLT:
8608+
case ICmpInst::ICMP_SLE:
8609+
return {SPF_SMIN, SPNB_NA, false};
8610+
case FCmpInst::FCMP_UGT:
8611+
case FCmpInst::FCMP_UGE:
8612+
case FCmpInst::FCMP_OGT:
8613+
case FCmpInst::FCMP_OGE:
8614+
return {SPF_FMAXNUM, NaNBehavior, Ordered};
8615+
case FCmpInst::FCMP_ULT:
8616+
case FCmpInst::FCMP_ULE:
8617+
case FCmpInst::FCMP_OLT:
8618+
case FCmpInst::FCMP_OLE:
8619+
return {SPF_FMINNUM, NaNBehavior, Ordered};
8620+
}
8621+
}
8622+
85928623
static SelectPatternResult matchSelectPattern(CmpInst::Predicate Pred,
85938624
FastMathFlags FMF,
85948625
Value *CmpLHS, Value *CmpRHS,
@@ -8696,27 +8727,8 @@ static SelectPatternResult matchSelectPattern(CmpInst::Predicate Pred,
86968727
}
86978728

86988729
// ([if]cmp X, Y) ? X : Y
8699-
if (TrueVal == CmpLHS && FalseVal == CmpRHS) {
8700-
switch (Pred) {
8701-
default: return {SPF_UNKNOWN, SPNB_NA, false}; // Equality.
8702-
case ICmpInst::ICMP_UGT:
8703-
case ICmpInst::ICMP_UGE: return {SPF_UMAX, SPNB_NA, false};
8704-
case ICmpInst::ICMP_SGT:
8705-
case ICmpInst::ICMP_SGE: return {SPF_SMAX, SPNB_NA, false};
8706-
case ICmpInst::ICMP_ULT:
8707-
case ICmpInst::ICMP_ULE: return {SPF_UMIN, SPNB_NA, false};
8708-
case ICmpInst::ICMP_SLT:
8709-
case ICmpInst::ICMP_SLE: return {SPF_SMIN, SPNB_NA, false};
8710-
case FCmpInst::FCMP_UGT:
8711-
case FCmpInst::FCMP_UGE:
8712-
case FCmpInst::FCMP_OGT:
8713-
case FCmpInst::FCMP_OGE: return {SPF_FMAXNUM, NaNBehavior, Ordered};
8714-
case FCmpInst::FCMP_ULT:
8715-
case FCmpInst::FCMP_ULE:
8716-
case FCmpInst::FCMP_OLT:
8717-
case FCmpInst::FCMP_OLE: return {SPF_FMINNUM, NaNBehavior, Ordered};
8718-
}
8719-
}
8730+
if (TrueVal == CmpLHS && FalseVal == CmpRHS)
8731+
return getSelectPattern(Pred, NaNBehavior, Ordered);
87208732

87218733
if (isKnownNegation(TrueVal, FalseVal)) {
87228734
// Sign-extending LHS does not change its sign, so TrueVal/FalseVal can
@@ -8960,6 +8972,21 @@ CmpInst::Predicate llvm::getMinMaxPred(SelectPatternFlavor SPF, bool Ordered) {
89608972
llvm_unreachable("unhandled!");
89618973
}
89628974

8975+
Intrinsic::ID llvm::getMinMaxIntrinsic(SelectPatternFlavor SPF) {
8976+
switch (SPF) {
8977+
case SelectPatternFlavor::SPF_UMIN:
8978+
return Intrinsic::umin;
8979+
case SelectPatternFlavor::SPF_UMAX:
8980+
return Intrinsic::umax;
8981+
case SelectPatternFlavor::SPF_SMIN:
8982+
return Intrinsic::smin;
8983+
case SelectPatternFlavor::SPF_SMAX:
8984+
return Intrinsic::smax;
8985+
default:
8986+
llvm_unreachable("Unexpected SPF");
8987+
}
8988+
}
8989+
89638990
SelectPatternFlavor llvm::getInverseMinMaxFlavor(SelectPatternFlavor SPF) {
89648991
if (SPF == SPF_SMIN) return SPF_SMAX;
89658992
if (SPF == SPF_UMIN) return SPF_UMAX;

llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp

Lines changed: 61 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,23 +1257,7 @@ static Value *canonicalizeSPF(ICmpInst &Cmp, Value *TrueVal, Value *FalseVal,
12571257
}
12581258

12591259
if (SelectPatternResult::isMinOrMax(SPF)) {
1260-
Intrinsic::ID IntrinsicID;
1261-
switch (SPF) {
1262-
case SelectPatternFlavor::SPF_UMIN:
1263-
IntrinsicID = Intrinsic::umin;
1264-
break;
1265-
case SelectPatternFlavor::SPF_UMAX:
1266-
IntrinsicID = Intrinsic::umax;
1267-
break;
1268-
case SelectPatternFlavor::SPF_SMIN:
1269-
IntrinsicID = Intrinsic::smin;
1270-
break;
1271-
case SelectPatternFlavor::SPF_SMAX:
1272-
IntrinsicID = Intrinsic::smax;
1273-
break;
1274-
default:
1275-
llvm_unreachable("Unexpected SPF");
1276-
}
1260+
Intrinsic::ID IntrinsicID = getMinMaxIntrinsic(SPF);
12771261
return IC.Builder.CreateBinaryIntrinsic(IntrinsicID, LHS, RHS);
12781262
}
12791263

@@ -1898,6 +1882,63 @@ static Instruction *foldSelectICmpEq(SelectInst &SI, ICmpInst *ICI,
18981882
return nullptr;
18991883
}
19001884

1885+
/// Fold `X Pred C1 ? X BOp C2 : C1 BOp C2` to `min/max(X, C1) BOp C2`.
1886+
/// This allows for better canonicalization.
1887+
static Value *foldSelectWithConstOpToBinOp(ICmpInst *Cmp, Value *TrueVal,
1888+
Value *FalseVal,
1889+
IRBuilderBase &Builder) {
1890+
BinaryOperator *BOp;
1891+
Constant *C1, *C2, *C3;
1892+
Value *X;
1893+
ICmpInst::Predicate Predicate;
1894+
1895+
if (!match(Cmp, m_ICmp(Predicate, m_Value(X), m_Constant(C1))))
1896+
return nullptr;
1897+
1898+
if (!ICmpInst::isRelational(Predicate))
1899+
return nullptr;
1900+
1901+
if (match(TrueVal, m_Constant())) {
1902+
std::swap(FalseVal, TrueVal);
1903+
Predicate = ICmpInst::getInversePredicate(Predicate);
1904+
}
1905+
1906+
if (!match(TrueVal, m_BinOp(BOp)) || !match(FalseVal, m_Constant(C3)))
1907+
return nullptr;
1908+
1909+
unsigned Opcode = BOp->getOpcode();
1910+
1911+
// This fold causes some regressions and is primarily intended for
1912+
// add and sub. So we early exit for div and rem to minimize the
1913+
// regressions.
1914+
if (Instruction::isIntDivRem(Opcode))
1915+
return nullptr;
1916+
1917+
if (!match(BOp, m_OneUse(m_BinOp(m_Specific(X), m_Constant(C2)))))
1918+
return nullptr;
1919+
1920+
Value *RHS;
1921+
SelectPatternFlavor SPF;
1922+
const DataLayout &DL = BOp->getDataLayout();
1923+
auto Flipped =
1924+
InstCombiner::getFlippedStrictnessPredicateAndConstant(Predicate, C1);
1925+
1926+
if (C3 == ConstantFoldBinaryOpOperands(Opcode, C1, C2, DL)) {
1927+
SPF = getSelectPattern(Predicate).Flavor;
1928+
RHS = C1;
1929+
} else if (Flipped && C3 == ConstantFoldBinaryOpOperands(
1930+
Opcode, Flipped->second, C2, DL)) {
1931+
SPF = getSelectPattern(Flipped->first).Flavor;
1932+
RHS = Flipped->second;
1933+
} else {
1934+
return nullptr;
1935+
}
1936+
1937+
Intrinsic::ID IntrinsicID = getMinMaxIntrinsic(SPF);
1938+
Value *Intrinsic = Builder.CreateBinaryIntrinsic(IntrinsicID, X, RHS);
1939+
return Builder.CreateBinOp(BOp->getOpcode(), Intrinsic, C2);
1940+
}
1941+
19011942
/// Visit a SelectInst that has an ICmpInst as its first operand.
19021943
Instruction *InstCombinerImpl::foldSelectInstWithICmp(SelectInst &SI,
19031944
ICmpInst *ICI) {
@@ -1987,6 +2028,9 @@ Instruction *InstCombinerImpl::foldSelectInstWithICmp(SelectInst &SI,
19872028
if (Value *V = foldAbsDiff(ICI, TrueVal, FalseVal, Builder))
19882029
return replaceInstUsesWith(SI, V);
19892030

2031+
if (Value *V = foldSelectWithConstOpToBinOp(ICI, TrueVal, FalseVal, Builder))
2032+
return replaceInstUsesWith(SI, V);
2033+
19902034
return Changed ? &SI : nullptr;
19912035
}
19922036

0 commit comments

Comments
 (0)