aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2023-02-11 12:38:04 +0000
committerDimitry Andric <dim@FreeBSD.org>2023-02-11 12:38:11 +0000
commite3b557809604d036af6e00c60f012c2025b59a5e (patch)
tree8a11ba2269a3b669601e2fd41145b174008f4da8 /llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
parent08e8dd7b9db7bb4a9de26d44c1cbfd24e869c014 (diff)
Diffstat (limited to 'llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp')
-rw-r--r--llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp940
1 files changed, 629 insertions, 311 deletions
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index ad96a5f475f1..e7d8208f94fd 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -12,7 +12,6 @@
#include "InstCombineInternal.h"
#include "llvm/ADT/APInt.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Analysis/AssumptionCache.h"
@@ -20,6 +19,7 @@
#include "llvm/Analysis/InstructionSimplify.h"
#include "llvm/Analysis/OverflowInstAnalysis.h"
#include "llvm/Analysis/ValueTracking.h"
+#include "llvm/Analysis/VectorUtils.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constant.h"
#include "llvm/IR/ConstantRange.h"
@@ -314,47 +314,95 @@ Instruction *InstCombinerImpl::foldSelectOpOp(SelectInst &SI, Instruction *TI,
TI->getType());
}
- // Cond ? -X : -Y --> -(Cond ? X : Y)
- Value *X, *Y;
- if (match(TI, m_FNeg(m_Value(X))) && match(FI, m_FNeg(m_Value(Y))) &&
- (TI->hasOneUse() || FI->hasOneUse())) {
- // Intersect FMF from the fneg instructions and union those with the select.
- FastMathFlags FMF = TI->getFastMathFlags();
- FMF &= FI->getFastMathFlags();
- FMF |= SI.getFastMathFlags();
- Value *NewSel = Builder.CreateSelect(Cond, X, Y, SI.getName() + ".v", &SI);
- if (auto *NewSelI = dyn_cast<Instruction>(NewSel))
- NewSelI->setFastMathFlags(FMF);
- Instruction *NewFNeg = UnaryOperator::CreateFNeg(NewSel);
- NewFNeg->setFastMathFlags(FMF);
- return NewFNeg;
- }
-
- // Min/max intrinsic with a common operand can have the common operand pulled
- // after the select. This is the same transform as below for binops, but
- // specialized for intrinsic matching and without the restrictive uses clause.
- auto *TII = dyn_cast<IntrinsicInst>(TI);
- auto *FII = dyn_cast<IntrinsicInst>(FI);
- if (TII && FII && TII->getIntrinsicID() == FII->getIntrinsicID() &&
- (TII->hasOneUse() || FII->hasOneUse())) {
- Value *T0, *T1, *F0, *F1;
- if (match(TII, m_MaxOrMin(m_Value(T0), m_Value(T1))) &&
- match(FII, m_MaxOrMin(m_Value(F0), m_Value(F1)))) {
- if (T0 == F0) {
- Value *NewSel = Builder.CreateSelect(Cond, T1, F1, "minmaxop", &SI);
- return CallInst::Create(TII->getCalledFunction(), {NewSel, T0});
- }
- if (T0 == F1) {
- Value *NewSel = Builder.CreateSelect(Cond, T1, F0, "minmaxop", &SI);
- return CallInst::Create(TII->getCalledFunction(), {NewSel, T0});
+ Value *OtherOpT, *OtherOpF;
+ bool MatchIsOpZero;
+ auto getCommonOp = [&](Instruction *TI, Instruction *FI, bool Commute,
+ bool Swapped = false) -> Value * {
+ assert(!(Commute && Swapped) &&
+ "Commute and Swapped can't set at the same time");
+ if (!Swapped) {
+ if (TI->getOperand(0) == FI->getOperand(0)) {
+ OtherOpT = TI->getOperand(1);
+ OtherOpF = FI->getOperand(1);
+ MatchIsOpZero = true;
+ return TI->getOperand(0);
+ } else if (TI->getOperand(1) == FI->getOperand(1)) {
+ OtherOpT = TI->getOperand(0);
+ OtherOpF = FI->getOperand(0);
+ MatchIsOpZero = false;
+ return TI->getOperand(1);
}
- if (T1 == F0) {
- Value *NewSel = Builder.CreateSelect(Cond, T0, F1, "minmaxop", &SI);
- return CallInst::Create(TII->getCalledFunction(), {NewSel, T1});
+ }
+
+ if (!Commute && !Swapped)
+ return nullptr;
+
+ // If we are allowing commute or swap of operands, then
+ // allow a cross-operand match. In that case, MatchIsOpZero
+ // means that TI's operand 0 (FI's operand 1) is the common op.
+ if (TI->getOperand(0) == FI->getOperand(1)) {
+ OtherOpT = TI->getOperand(1);
+ OtherOpF = FI->getOperand(0);
+ MatchIsOpZero = true;
+ return TI->getOperand(0);
+ } else if (TI->getOperand(1) == FI->getOperand(0)) {
+ OtherOpT = TI->getOperand(0);
+ OtherOpF = FI->getOperand(1);
+ MatchIsOpZero = false;
+ return TI->getOperand(1);
+ }
+ return nullptr;
+ };
+
+ if (TI->hasOneUse() || FI->hasOneUse()) {
+ // Cond ? -X : -Y --> -(Cond ? X : Y)
+ Value *X, *Y;
+ if (match(TI, m_FNeg(m_Value(X))) && match(FI, m_FNeg(m_Value(Y)))) {
+ // Intersect FMF from the fneg instructions and union those with the
+ // select.
+ FastMathFlags FMF = TI->getFastMathFlags();
+ FMF &= FI->getFastMathFlags();
+ FMF |= SI.getFastMathFlags();
+ Value *NewSel =
+ Builder.CreateSelect(Cond, X, Y, SI.getName() + ".v", &SI);
+ if (auto *NewSelI = dyn_cast<Instruction>(NewSel))
+ NewSelI->setFastMathFlags(FMF);
+ Instruction *NewFNeg = UnaryOperator::CreateFNeg(NewSel);
+ NewFNeg->setFastMathFlags(FMF);
+ return NewFNeg;
+ }
+
+ // Min/max intrinsic with a common operand can have the common operand
+ // pulled after the select. This is the same transform as below for binops,
+ // but specialized for intrinsic matching and without the restrictive uses
+ // clause.
+ auto *TII = dyn_cast<IntrinsicInst>(TI);
+ auto *FII = dyn_cast<IntrinsicInst>(FI);
+ if (TII && FII && TII->getIntrinsicID() == FII->getIntrinsicID()) {
+ if (match(TII, m_MaxOrMin(m_Value(), m_Value()))) {
+ if (Value *MatchOp = getCommonOp(TI, FI, true)) {
+ Value *NewSel =
+ Builder.CreateSelect(Cond, OtherOpT, OtherOpF, "minmaxop", &SI);
+ return CallInst::Create(TII->getCalledFunction(), {NewSel, MatchOp});
+ }
}
- if (T1 == F1) {
- Value *NewSel = Builder.CreateSelect(Cond, T0, F0, "minmaxop", &SI);
- return CallInst::Create(TII->getCalledFunction(), {NewSel, T1});
+ }
+
+ // icmp with a common operand also can have the common operand
+ // pulled after the select.
+ ICmpInst::Predicate TPred, FPred;
+ if (match(TI, m_ICmp(TPred, m_Value(), m_Value())) &&
+ match(FI, m_ICmp(FPred, m_Value(), m_Value()))) {
+ if (TPred == FPred || TPred == CmpInst::getSwappedPredicate(FPred)) {
+ bool Swapped = TPred != FPred;
+ if (Value *MatchOp =
+ getCommonOp(TI, FI, ICmpInst::isEquality(TPred), Swapped)) {
+ Value *NewSel = Builder.CreateSelect(Cond, OtherOpT, OtherOpF,
+ SI.getName() + ".v", &SI);
+ return new ICmpInst(
+ MatchIsOpZero ? TPred : CmpInst::getSwappedPredicate(TPred),
+ MatchOp, NewSel);
+ }
}
}
}
@@ -370,33 +418,9 @@ Instruction *InstCombinerImpl::foldSelectOpOp(SelectInst &SI, Instruction *TI,
return nullptr;
// Figure out if the operations have any operands in common.
- Value *MatchOp, *OtherOpT, *OtherOpF;
- bool MatchIsOpZero;
- if (TI->getOperand(0) == FI->getOperand(0)) {
- MatchOp = TI->getOperand(0);
- OtherOpT = TI->getOperand(1);
- OtherOpF = FI->getOperand(1);
- MatchIsOpZero = true;
- } else if (TI->getOperand(1) == FI->getOperand(1)) {
- MatchOp = TI->getOperand(1);
- OtherOpT = TI->getOperand(0);
- OtherOpF = FI->getOperand(0);
- MatchIsOpZero = false;
- } else if (!TI->isCommutative()) {
- return nullptr;
- } else if (TI->getOperand(0) == FI->getOperand(1)) {
- MatchOp = TI->getOperand(0);
- OtherOpT = TI->getOperand(1);
- OtherOpF = FI->getOperand(0);
- MatchIsOpZero = true;
- } else if (TI->getOperand(1) == FI->getOperand(0)) {
- MatchOp = TI->getOperand(1);
- OtherOpT = TI->getOperand(0);
- OtherOpF = FI->getOperand(1);
- MatchIsOpZero = true;
- } else {
+ Value *MatchOp = getCommonOp(TI, FI, TI->isCommutative());
+ if (!MatchOp)
return nullptr;
- }
// If the select condition is a vector, the operands of the original select's
// operands also must be vectors. This may not be the case for getelementptr
@@ -442,44 +466,44 @@ Instruction *InstCombinerImpl::foldSelectIntoOp(SelectInst &SI, Value *TrueVal,
auto TryFoldSelectIntoOp = [&](SelectInst &SI, Value *TrueVal,
Value *FalseVal,
bool Swapped) -> Instruction * {
- if (auto *TVI = dyn_cast<BinaryOperator>(TrueVal)) {
- if (TVI->hasOneUse() && !isa<Constant>(FalseVal)) {
- if (unsigned SFO = getSelectFoldableOperands(TVI)) {
- unsigned OpToFold = 0;
- if ((SFO & 1) && FalseVal == TVI->getOperand(0))
- OpToFold = 1;
- else if ((SFO & 2) && FalseVal == TVI->getOperand(1))
- OpToFold = 2;
-
- if (OpToFold) {
- FastMathFlags FMF;
- // TODO: We probably ought to revisit cases where the select and FP
- // instructions have different flags and add tests to ensure the
- // behaviour is correct.
- if (isa<FPMathOperator>(&SI))
- FMF = SI.getFastMathFlags();
- Constant *C = ConstantExpr::getBinOpIdentity(
- TVI->getOpcode(), TVI->getType(), true, FMF.noSignedZeros());
- Value *OOp = TVI->getOperand(2 - OpToFold);
- // Avoid creating select between 2 constants unless it's selecting
- // between 0, 1 and -1.
- const APInt *OOpC;
- bool OOpIsAPInt = match(OOp, m_APInt(OOpC));
- if (!isa<Constant>(OOp) ||
- (OOpIsAPInt && isSelect01(C->getUniqueInteger(), *OOpC))) {
- Value *NewSel = Builder.CreateSelect(
- SI.getCondition(), Swapped ? C : OOp, Swapped ? OOp : C);
- if (isa<FPMathOperator>(&SI))
- cast<Instruction>(NewSel)->setFastMathFlags(FMF);
- NewSel->takeName(TVI);
- BinaryOperator *BO =
- BinaryOperator::Create(TVI->getOpcode(), FalseVal, NewSel);
- BO->copyIRFlags(TVI);
- return BO;
- }
- }
- }
- }
+ auto *TVI = dyn_cast<BinaryOperator>(TrueVal);
+ if (!TVI || !TVI->hasOneUse() || isa<Constant>(FalseVal))
+ return nullptr;
+
+ unsigned SFO = getSelectFoldableOperands(TVI);
+ unsigned OpToFold = 0;
+ if ((SFO & 1) && FalseVal == TVI->getOperand(0))
+ OpToFold = 1;
+ else if ((SFO & 2) && FalseVal == TVI->getOperand(1))
+ OpToFold = 2;
+
+ if (!OpToFold)
+ return nullptr;
+
+ // TODO: We probably ought to revisit cases where the select and FP
+ // instructions have different flags and add tests to ensure the
+ // behaviour is correct.
+ FastMathFlags FMF;
+ if (isa<FPMathOperator>(&SI))
+ FMF = SI.getFastMathFlags();
+ Constant *C = ConstantExpr::getBinOpIdentity(
+ TVI->getOpcode(), TVI->getType(), true, FMF.noSignedZeros());
+ Value *OOp = TVI->getOperand(2 - OpToFold);
+ // Avoid creating select between 2 constants unless it's selecting
+ // between 0, 1 and -1.
+ const APInt *OOpC;
+ bool OOpIsAPInt = match(OOp, m_APInt(OOpC));
+ if (!isa<Constant>(OOp) ||
+ (OOpIsAPInt && isSelect01(C->getUniqueInteger(), *OOpC))) {
+ Value *NewSel = Builder.CreateSelect(SI.getCondition(), Swapped ? C : OOp,
+ Swapped ? OOp : C);
+ if (isa<FPMathOperator>(&SI))
+ cast<Instruction>(NewSel)->setFastMathFlags(FMF);
+ NewSel->takeName(TVI);
+ BinaryOperator *BO =
+ BinaryOperator::Create(TVI->getOpcode(), FalseVal, NewSel);
+ BO->copyIRFlags(TVI);
+ return BO;
}
return nullptr;
};
@@ -779,19 +803,31 @@ static Value *canonicalizeSaturatedSubtract(const ICmpInst *ICI,
const Value *FalseVal,
InstCombiner::BuilderTy &Builder) {
ICmpInst::Predicate Pred = ICI->getPredicate();
- if (!ICmpInst::isUnsigned(Pred))
- return nullptr;
+ Value *A = ICI->getOperand(0);
+ Value *B = ICI->getOperand(1);
// (b > a) ? 0 : a - b -> (b <= a) ? a - b : 0
+ // (a == 0) ? 0 : a - 1 -> (a != 0) ? a - 1 : 0
if (match(TrueVal, m_Zero())) {
Pred = ICmpInst::getInversePredicate(Pred);
std::swap(TrueVal, FalseVal);
}
+
if (!match(FalseVal, m_Zero()))
return nullptr;
- Value *A = ICI->getOperand(0);
- Value *B = ICI->getOperand(1);
+ // ugt 0 is canonicalized to ne 0 and requires special handling
+ // (a != 0) ? a + -1 : 0 -> usub.sat(a, 1)
+ if (Pred == ICmpInst::ICMP_NE) {
+ if (match(B, m_Zero()) && match(TrueVal, m_Add(m_Specific(A), m_AllOnes())))
+ return Builder.CreateBinaryIntrinsic(Intrinsic::usub_sat, A,
+ ConstantInt::get(A->getType(), 1));
+ return nullptr;
+ }
+
+ if (!ICmpInst::isUnsigned(Pred))
+ return nullptr;
+
if (Pred == ICmpInst::ICMP_ULE || Pred == ICmpInst::ICMP_ULT) {
// (b < a) ? a - b : 0 -> (a > b) ? a - b : 0
std::swap(A, B);
@@ -952,8 +988,8 @@ static Value *foldSelectCttzCtlz(ICmpInst *ICI, Value *TrueVal, Value *FalseVal,
Value *CmpLHS = ICI->getOperand(0);
Value *CmpRHS = ICI->getOperand(1);
- // Check if the condition value compares a value for equality against zero.
- if (!ICI->isEquality() || !match(CmpRHS, m_Zero()))
+ // Check if the select condition compares a value for equality.
+ if (!ICI->isEquality())
return nullptr;
Value *SelectArg = FalseVal;
@@ -969,8 +1005,15 @@ static Value *foldSelectCttzCtlz(ICmpInst *ICI, Value *TrueVal, Value *FalseVal,
// Check that 'Count' is a call to intrinsic cttz/ctlz. Also check that the
// input to the cttz/ctlz is used as LHS for the compare instruction.
- if (!match(Count, m_Intrinsic<Intrinsic::cttz>(m_Specific(CmpLHS))) &&
- !match(Count, m_Intrinsic<Intrinsic::ctlz>(m_Specific(CmpLHS))))
+ Value *X;
+ if (!match(Count, m_Intrinsic<Intrinsic::cttz>(m_Value(X))) &&
+ !match(Count, m_Intrinsic<Intrinsic::ctlz>(m_Value(X))))
+ return nullptr;
+
+ // (X == 0) ? BitWidth : ctz(X)
+ // (X == -1) ? BitWidth : ctz(~X)
+ if ((X != CmpLHS || !match(CmpRHS, m_Zero())) &&
+ (!match(X, m_Not(m_Specific(CmpLHS))) || !match(CmpRHS, m_AllOnes())))
return nullptr;
IntrinsicInst *II = cast<IntrinsicInst>(Count);
@@ -1139,6 +1182,28 @@ static Instruction *canonicalizeSPF(SelectInst &Sel, ICmpInst &Cmp,
return nullptr;
}
+static bool replaceInInstruction(Value *V, Value *Old, Value *New,
+ InstCombiner &IC, unsigned Depth = 0) {
+ // Conservatively limit replacement to two instructions upwards.
+ if (Depth == 2)
+ return false;
+
+ auto *I = dyn_cast<Instruction>(V);
+ if (!I || !I->hasOneUse() || !isSafeToSpeculativelyExecute(I))
+ return false;
+
+ bool Changed = false;
+ for (Use &U : I->operands()) {
+ if (U == Old) {
+ IC.replaceUse(U, New);
+ Changed = true;
+ } else {
+ Changed |= replaceInInstruction(U, Old, New, IC, Depth + 1);
+ }
+ }
+ return Changed;
+}
+
/// If we have a select with an equality comparison, then we know the value in
/// one of the arms of the select. See if substituting this value into an arm
/// and simplifying the result yields the same value as the other arm.
@@ -1157,10 +1222,7 @@ static Instruction *canonicalizeSPF(SelectInst &Sel, ICmpInst &Cmp,
/// TODO: Wrapping flags could be preserved in some cases with better analysis.
Instruction *InstCombinerImpl::foldSelectValueEquivalence(SelectInst &Sel,
ICmpInst &Cmp) {
- // Value equivalence substitution requires an all-or-nothing replacement.
- // It does not make sense for a vector compare where each lane is chosen
- // independently.
- if (!Cmp.isEquality() || Cmp.getType()->isVectorTy())
+ if (!Cmp.isEquality())
return nullptr;
// Canonicalize the pattern to ICMP_EQ by swapping the select operands.
@@ -1189,15 +1251,11 @@ Instruction *InstCombinerImpl::foldSelectValueEquivalence(SelectInst &Sel,
// with different operands, which should not cause side-effects or trigger
// undefined behavior). Only do this if CmpRHS is a constant, as
// profitability is not clear for other cases.
- // FIXME: The replacement could be performed recursively.
- if (match(CmpRHS, m_ImmConstant()) && !match(CmpLHS, m_ImmConstant()))
- if (auto *I = dyn_cast<Instruction>(TrueVal))
- if (I->hasOneUse() && isSafeToSpeculativelyExecute(I))
- for (Use &U : I->operands())
- if (U == CmpLHS) {
- replaceUse(U, CmpRHS);
- return &Sel;
- }
+ // FIXME: Support vectors.
+ if (match(CmpRHS, m_ImmConstant()) && !match(CmpLHS, m_ImmConstant()) &&
+ !Cmp.getType()->isVectorTy())
+ if (replaceInInstruction(TrueVal, CmpLHS, CmpRHS, *this))
+ return &Sel;
}
if (TrueVal != CmpRHS &&
isGuaranteedNotToBeUndefOrPoison(CmpLHS, SQ.AC, &Sel, &DT))
@@ -1371,7 +1429,7 @@ static Value *canonicalizeClampLike(SelectInst &Sel0, ICmpInst &Cmp0,
C2->getType()->getScalarSizeInBits()))))
return nullptr; // Can't do, have signed max element[s].
C2 = InstCombiner::AddOne(C2);
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case ICmpInst::Predicate::ICMP_SGE:
// Also non-canonical, but here we don't need to change C2,
// so we don't have any restrictions on C2, so we can just handle it.
@@ -2307,6 +2365,41 @@ static Instruction *foldSelectToCopysign(SelectInst &Sel,
}
Instruction *InstCombinerImpl::foldVectorSelect(SelectInst &Sel) {
+ if (!isa<VectorType>(Sel.getType()))
+ return nullptr;
+
+ Value *Cond = Sel.getCondition();
+ Value *TVal = Sel.getTrueValue();
+ Value *FVal = Sel.getFalseValue();
+ Value *C, *X, *Y;
+
+ if (match(Cond, m_VecReverse(m_Value(C)))) {
+ auto createSelReverse = [&](Value *C, Value *X, Value *Y) {
+ Value *V = Builder.CreateSelect(C, X, Y, Sel.getName(), &Sel);
+ if (auto *I = dyn_cast<Instruction>(V))
+ I->copyIRFlags(&Sel);
+ Module *M = Sel.getModule();
+ Function *F = Intrinsic::getDeclaration(
+ M, Intrinsic::experimental_vector_reverse, V->getType());
+ return CallInst::Create(F, V);
+ };
+
+ if (match(TVal, m_VecReverse(m_Value(X)))) {
+ // select rev(C), rev(X), rev(Y) --> rev(select C, X, Y)
+ if (match(FVal, m_VecReverse(m_Value(Y))) &&
+ (Cond->hasOneUse() || TVal->hasOneUse() || FVal->hasOneUse()))
+ return createSelReverse(C, X, Y);
+
+ // select rev(C), rev(X), FValSplat --> rev(select C, X, FValSplat)
+ if ((Cond->hasOneUse() || TVal->hasOneUse()) && isSplatValue(FVal))
+ return createSelReverse(C, X, FVal);
+ }
+ // select rev(C), TValSplat, rev(Y) --> rev(select C, TValSplat, Y)
+ else if (isSplatValue(TVal) && match(FVal, m_VecReverse(m_Value(Y))) &&
+ (Cond->hasOneUse() || FVal->hasOneUse()))
+ return createSelReverse(C, TVal, Y);
+ }
+
auto *VecTy = dyn_cast<FixedVectorType>(Sel.getType());
if (!VecTy)
return nullptr;
@@ -2323,10 +2416,6 @@ Instruction *InstCombinerImpl::foldVectorSelect(SelectInst &Sel) {
// A select of a "select shuffle" with a common operand can be rearranged
// to select followed by "select shuffle". Because of poison, this only works
// in the case of a shuffle with no undefined mask elements.
- Value *Cond = Sel.getCondition();
- Value *TVal = Sel.getTrueValue();
- Value *FVal = Sel.getFalseValue();
- Value *X, *Y;
ArrayRef<int> Mask;
if (match(TVal, m_OneUse(m_Shuffle(m_Value(X), m_Value(Y), m_Mask(Mask)))) &&
!is_contained(Mask, UndefMaskElem) &&
@@ -2472,7 +2561,7 @@ Instruction *InstCombinerImpl::foldAndOrOfSelectUsingImpliedCond(Value *Op,
assert(Op->getType()->isIntOrIntVectorTy(1) &&
"Op must be either i1 or vector of i1.");
- Optional<bool> Res = isImpliedCondition(Op, CondVal, DL, IsAnd);
+ std::optional<bool> Res = isImpliedCondition(Op, CondVal, DL, IsAnd);
if (!Res)
return nullptr;
@@ -2510,6 +2599,7 @@ static Instruction *foldSelectWithFCmpToFabs(SelectInst &SI,
InstCombinerImpl &IC) {
Value *CondVal = SI.getCondition();
+ bool ChangedFMF = false;
for (bool Swap : {false, true}) {
Value *TrueVal = SI.getTrueValue();
Value *X = SI.getFalseValue();
@@ -2534,13 +2624,33 @@ static Instruction *foldSelectWithFCmpToFabs(SelectInst &SI,
}
}
+ if (!match(TrueVal, m_FNeg(m_Specific(X))))
+ return nullptr;
+
+ // Forward-propagate nnan and ninf from the fneg to the select.
+ // If all inputs are not those values, then the select is not either.
+ // Note: nsz is defined differently, so it may not be correct to propagate.
+ FastMathFlags FMF = cast<FPMathOperator>(TrueVal)->getFastMathFlags();
+ if (FMF.noNaNs() && !SI.hasNoNaNs()) {
+ SI.setHasNoNaNs(true);
+ ChangedFMF = true;
+ }
+ if (FMF.noInfs() && !SI.hasNoInfs()) {
+ SI.setHasNoInfs(true);
+ ChangedFMF = true;
+ }
+
// With nsz, when 'Swap' is false:
// fold (X < +/-0.0) ? -X : X or (X <= +/-0.0) ? -X : X to fabs(X)
// fold (X > +/-0.0) ? -X : X or (X >= +/-0.0) ? -X : X to -fabs(x)
// when 'Swap' is true:
// fold (X > +/-0.0) ? X : -X or (X >= +/-0.0) ? X : -X to fabs(X)
// fold (X < +/-0.0) ? X : -X or (X <= +/-0.0) ? X : -X to -fabs(X)
- if (!match(TrueVal, m_FNeg(m_Specific(X))) || !SI.hasNoSignedZeros())
+ //
+ // Note: We require "nnan" for this fold because fcmp ignores the signbit
+ // of NAN, but IEEE-754 specifies the signbit of NAN values with
+ // fneg/fabs operations.
+ if (!SI.hasNoSignedZeros() || !SI.hasNoNaNs())
return nullptr;
if (Swap)
@@ -2563,7 +2673,7 @@ static Instruction *foldSelectWithFCmpToFabs(SelectInst &SI,
}
}
- return nullptr;
+ return ChangedFMF ? &SI : nullptr;
}
// Match the following IR pattern:
@@ -2602,10 +2712,14 @@ foldRoundUpIntegerWithPow2Alignment(SelectInst &SI,
if (!match(XLowBits, m_And(m_Specific(X), m_APIntAllowUndef(LowBitMaskCst))))
return nullptr;
+ // Match even if the AND and ADD are swapped.
const APInt *BiasCst, *HighBitMaskCst;
if (!match(XBiasedHighBits,
m_And(m_Add(m_Specific(X), m_APIntAllowUndef(BiasCst)),
- m_APIntAllowUndef(HighBitMaskCst))))
+ m_APIntAllowUndef(HighBitMaskCst))) &&
+ !match(XBiasedHighBits,
+ m_Add(m_And(m_Specific(X), m_APIntAllowUndef(HighBitMaskCst)),
+ m_APIntAllowUndef(BiasCst))))
return nullptr;
if (!LowBitMaskCst->isMask())
@@ -2635,200 +2749,392 @@ foldRoundUpIntegerWithPow2Alignment(SelectInst &SI,
return R;
}
-Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) {
+namespace {
+struct DecomposedSelect {
+ Value *Cond = nullptr;
+ Value *TrueVal = nullptr;
+ Value *FalseVal = nullptr;
+};
+} // namespace
+
+/// Look for patterns like
+/// %outer.cond = select i1 %inner.cond, i1 %alt.cond, i1 false
+/// %inner.sel = select i1 %inner.cond, i8 %inner.sel.t, i8 %inner.sel.f
+/// %outer.sel = select i1 %outer.cond, i8 %outer.sel.t, i8 %inner.sel
+/// and rewrite it as
+/// %inner.sel = select i1 %cond.alternative, i8 %sel.outer.t, i8 %sel.inner.t
+/// %sel.outer = select i1 %cond.inner, i8 %inner.sel, i8 %sel.inner.f
+static Instruction *foldNestedSelects(SelectInst &OuterSelVal,
+ InstCombiner::BuilderTy &Builder) {
+ // We must start with a `select`.
+ DecomposedSelect OuterSel;
+ match(&OuterSelVal,
+ m_Select(m_Value(OuterSel.Cond), m_Value(OuterSel.TrueVal),
+ m_Value(OuterSel.FalseVal)));
+
+ // Canonicalize inversion of the outermost `select`'s condition.
+ if (match(OuterSel.Cond, m_Not(m_Value(OuterSel.Cond))))
+ std::swap(OuterSel.TrueVal, OuterSel.FalseVal);
+
+ // The condition of the outermost select must be an `and`/`or`.
+ if (!match(OuterSel.Cond, m_c_LogicalOp(m_Value(), m_Value())))
+ return nullptr;
+
+ // Depending on the logical op, inner select might be in different hand.
+ bool IsAndVariant = match(OuterSel.Cond, m_LogicalAnd());
+ Value *InnerSelVal = IsAndVariant ? OuterSel.FalseVal : OuterSel.TrueVal;
+
+ // Profitability check - avoid increasing instruction count.
+ if (none_of(ArrayRef<Value *>({OuterSelVal.getCondition(), InnerSelVal}),
+ [](Value *V) { return V->hasOneUse(); }))
+ return nullptr;
+
+ // The appropriate hand of the outermost `select` must be a select itself.
+ DecomposedSelect InnerSel;
+ if (!match(InnerSelVal,
+ m_Select(m_Value(InnerSel.Cond), m_Value(InnerSel.TrueVal),
+ m_Value(InnerSel.FalseVal))))
+ return nullptr;
+
+ // Canonicalize inversion of the innermost `select`'s condition.
+ if (match(InnerSel.Cond, m_Not(m_Value(InnerSel.Cond))))
+ std::swap(InnerSel.TrueVal, InnerSel.FalseVal);
+
+ Value *AltCond = nullptr;
+ auto matchOuterCond = [OuterSel, &AltCond](auto m_InnerCond) {
+ return match(OuterSel.Cond, m_c_LogicalOp(m_InnerCond, m_Value(AltCond)));
+ };
+
+ // Finally, match the condition that was driving the outermost `select`,
+ // it should be a logical operation between the condition that was driving
+ // the innermost `select` (after accounting for the possible inversions
+ // of the condition), and some other condition.
+ if (matchOuterCond(m_Specific(InnerSel.Cond))) {
+ // Done!
+ } else if (Value * NotInnerCond; matchOuterCond(m_CombineAnd(
+ m_Not(m_Specific(InnerSel.Cond)), m_Value(NotInnerCond)))) {
+ // Done!
+ std::swap(InnerSel.TrueVal, InnerSel.FalseVal);
+ InnerSel.Cond = NotInnerCond;
+ } else // Not the pattern we were looking for.
+ return nullptr;
+
+ Value *SelInner = Builder.CreateSelect(
+ AltCond, IsAndVariant ? OuterSel.TrueVal : InnerSel.FalseVal,
+ IsAndVariant ? InnerSel.TrueVal : OuterSel.FalseVal);
+ SelInner->takeName(InnerSelVal);
+ return SelectInst::Create(InnerSel.Cond,
+ IsAndVariant ? SelInner : InnerSel.TrueVal,
+ !IsAndVariant ? SelInner : InnerSel.FalseVal);
+}
+
+Instruction *InstCombinerImpl::foldSelectOfBools(SelectInst &SI) {
Value *CondVal = SI.getCondition();
Value *TrueVal = SI.getTrueValue();
Value *FalseVal = SI.getFalseValue();
Type *SelType = SI.getType();
- if (Value *V = simplifySelectInst(CondVal, TrueVal, FalseVal,
- SQ.getWithInstruction(&SI)))
- return replaceInstUsesWith(SI, V);
-
- if (Instruction *I = canonicalizeSelectToShuffle(SI))
- return I;
-
- if (Instruction *I = canonicalizeScalarSelectOfVecs(SI, *this))
- return I;
-
// Avoid potential infinite loops by checking for non-constant condition.
// TODO: Can we assert instead by improving canonicalizeSelectToShuffle()?
// Scalar select must have simplified?
- if (SelType->isIntOrIntVectorTy(1) && !isa<Constant>(CondVal) &&
- TrueVal->getType() == CondVal->getType()) {
- // Folding select to and/or i1 isn't poison safe in general. impliesPoison
- // checks whether folding it does not convert a well-defined value into
- // poison.
- if (match(TrueVal, m_One())) {
- if (impliesPoison(FalseVal, CondVal)) {
- // Change: A = select B, true, C --> A = or B, C
- return BinaryOperator::CreateOr(CondVal, FalseVal);
- }
+ if (!SelType->isIntOrIntVectorTy(1) || isa<Constant>(CondVal) ||
+ TrueVal->getType() != CondVal->getType())
+ return nullptr;
+
+ auto *One = ConstantInt::getTrue(SelType);
+ auto *Zero = ConstantInt::getFalse(SelType);
+ Value *A, *B, *C, *D;
- if (auto *LHS = dyn_cast<FCmpInst>(CondVal))
- if (auto *RHS = dyn_cast<FCmpInst>(FalseVal))
- if (Value *V = foldLogicOfFCmps(LHS, RHS, /*IsAnd*/ false,
- /*IsSelectLogical*/ true))
- return replaceInstUsesWith(SI, V);
+ // Folding select to and/or i1 isn't poison safe in general. impliesPoison
+ // checks whether folding it does not convert a well-defined value into
+ // poison.
+ if (match(TrueVal, m_One())) {
+ if (impliesPoison(FalseVal, CondVal)) {
+ // Change: A = select B, true, C --> A = or B, C
+ return BinaryOperator::CreateOr(CondVal, FalseVal);
}
- if (match(FalseVal, m_Zero())) {
- if (impliesPoison(TrueVal, CondVal)) {
- // Change: A = select B, C, false --> A = and B, C
- return BinaryOperator::CreateAnd(CondVal, TrueVal);
- }
- if (auto *LHS = dyn_cast<FCmpInst>(CondVal))
- if (auto *RHS = dyn_cast<FCmpInst>(TrueVal))
- if (Value *V = foldLogicOfFCmps(LHS, RHS, /*IsAnd*/ true,
- /*IsSelectLogical*/ true))
- return replaceInstUsesWith(SI, V);
+ if (auto *LHS = dyn_cast<FCmpInst>(CondVal))
+ if (auto *RHS = dyn_cast<FCmpInst>(FalseVal))
+ if (Value *V = foldLogicOfFCmps(LHS, RHS, /*IsAnd*/ false,
+ /*IsSelectLogical*/ true))
+ return replaceInstUsesWith(SI, V);
+
+ // (A && B) || (C && B) --> (A || C) && B
+ if (match(CondVal, m_LogicalAnd(m_Value(A), m_Value(B))) &&
+ match(FalseVal, m_LogicalAnd(m_Value(C), m_Value(D))) &&
+ (CondVal->hasOneUse() || FalseVal->hasOneUse())) {
+ bool CondLogicAnd = isa<SelectInst>(CondVal);
+ bool FalseLogicAnd = isa<SelectInst>(FalseVal);
+ auto AndFactorization = [&](Value *Common, Value *InnerCond,
+ Value *InnerVal,
+ bool SelFirst = false) -> Instruction * {
+ Value *InnerSel = Builder.CreateSelect(InnerCond, One, InnerVal);
+ if (SelFirst)
+ std::swap(Common, InnerSel);
+ if (FalseLogicAnd || (CondLogicAnd && Common == A))
+ return SelectInst::Create(Common, InnerSel, Zero);
+ else
+ return BinaryOperator::CreateAnd(Common, InnerSel);
+ };
+
+ if (A == C)
+ return AndFactorization(A, B, D);
+ if (A == D)
+ return AndFactorization(A, B, C);
+ if (B == C)
+ return AndFactorization(B, A, D);
+ if (B == D)
+ return AndFactorization(B, A, C, CondLogicAnd && FalseLogicAnd);
}
+ }
- auto *One = ConstantInt::getTrue(SelType);
- auto *Zero = ConstantInt::getFalse(SelType);
+ if (match(FalseVal, m_Zero())) {
+ if (impliesPoison(TrueVal, CondVal)) {
+ // Change: A = select B, C, false --> A = and B, C
+ return BinaryOperator::CreateAnd(CondVal, TrueVal);
+ }
- // We match the "full" 0 or 1 constant here to avoid a potential infinite
- // loop with vectors that may have undefined/poison elements.
- // select a, false, b -> select !a, b, false
- if (match(TrueVal, m_Specific(Zero))) {
- Value *NotCond = Builder.CreateNot(CondVal, "not." + CondVal->getName());
- return SelectInst::Create(NotCond, FalseVal, Zero);
+ if (auto *LHS = dyn_cast<FCmpInst>(CondVal))
+ if (auto *RHS = dyn_cast<FCmpInst>(TrueVal))
+ if (Value *V = foldLogicOfFCmps(LHS, RHS, /*IsAnd*/ true,
+ /*IsSelectLogical*/ true))
+ return replaceInstUsesWith(SI, V);
+
+ // (A || B) && (C || B) --> (A && C) || B
+ if (match(CondVal, m_LogicalOr(m_Value(A), m_Value(B))) &&
+ match(TrueVal, m_LogicalOr(m_Value(C), m_Value(D))) &&
+ (CondVal->hasOneUse() || TrueVal->hasOneUse())) {
+ bool CondLogicOr = isa<SelectInst>(CondVal);
+ bool TrueLogicOr = isa<SelectInst>(TrueVal);
+ auto OrFactorization = [&](Value *Common, Value *InnerCond,
+ Value *InnerVal,
+ bool SelFirst = false) -> Instruction * {
+ Value *InnerSel = Builder.CreateSelect(InnerCond, InnerVal, Zero);
+ if (SelFirst)
+ std::swap(Common, InnerSel);
+ if (TrueLogicOr || (CondLogicOr && Common == A))
+ return SelectInst::Create(Common, One, InnerSel);
+ else
+ return BinaryOperator::CreateOr(Common, InnerSel);
+ };
+
+ if (A == C)
+ return OrFactorization(A, B, D);
+ if (A == D)
+ return OrFactorization(A, B, C);
+ if (B == C)
+ return OrFactorization(B, A, D);
+ if (B == D)
+ return OrFactorization(B, A, C, CondLogicOr && TrueLogicOr);
}
- // select a, b, true -> select !a, true, b
- if (match(FalseVal, m_Specific(One))) {
- Value *NotCond = Builder.CreateNot(CondVal, "not." + CondVal->getName());
- return SelectInst::Create(NotCond, One, TrueVal);
+ }
+
+ // We match the "full" 0 or 1 constant here to avoid a potential infinite
+ // loop with vectors that may have undefined/poison elements.
+ // select a, false, b -> select !a, b, false
+ if (match(TrueVal, m_Specific(Zero))) {
+ Value *NotCond = Builder.CreateNot(CondVal, "not." + CondVal->getName());
+ return SelectInst::Create(NotCond, FalseVal, Zero);
+ }
+ // select a, b, true -> select !a, true, b
+ if (match(FalseVal, m_Specific(One))) {
+ Value *NotCond = Builder.CreateNot(CondVal, "not." + CondVal->getName());
+ return SelectInst::Create(NotCond, One, TrueVal);
+ }
+
+ // DeMorgan in select form: !a && !b --> !(a || b)
+ // select !a, !b, false --> not (select a, true, b)
+ if (match(&SI, m_LogicalAnd(m_Not(m_Value(A)), m_Not(m_Value(B)))) &&
+ (CondVal->hasOneUse() || TrueVal->hasOneUse()) &&
+ !match(A, m_ConstantExpr()) && !match(B, m_ConstantExpr()))
+ return BinaryOperator::CreateNot(Builder.CreateSelect(A, One, B));
+
+ // DeMorgan in select form: !a || !b --> !(a && b)
+ // select !a, true, !b --> not (select a, b, false)
+ if (match(&SI, m_LogicalOr(m_Not(m_Value(A)), m_Not(m_Value(B)))) &&
+ (CondVal->hasOneUse() || FalseVal->hasOneUse()) &&
+ !match(A, m_ConstantExpr()) && !match(B, m_ConstantExpr()))
+ return BinaryOperator::CreateNot(Builder.CreateSelect(A, B, Zero));
+
+ // select (select a, true, b), true, b -> select a, true, b
+ if (match(CondVal, m_Select(m_Value(A), m_One(), m_Value(B))) &&
+ match(TrueVal, m_One()) && match(FalseVal, m_Specific(B)))
+ return replaceOperand(SI, 0, A);
+ // select (select a, b, false), b, false -> select a, b, false
+ if (match(CondVal, m_Select(m_Value(A), m_Value(B), m_Zero())) &&
+ match(TrueVal, m_Specific(B)) && match(FalseVal, m_Zero()))
+ return replaceOperand(SI, 0, A);
+
+ // ~(A & B) & (A | B) --> A ^ B
+ if (match(&SI, m_c_LogicalAnd(m_Not(m_LogicalAnd(m_Value(A), m_Value(B))),
+ m_c_LogicalOr(m_Deferred(A), m_Deferred(B)))))
+ return BinaryOperator::CreateXor(A, B);
+
+ // select (~a | c), a, b -> and a, (or c, freeze(b))
+ if (match(CondVal, m_c_Or(m_Not(m_Specific(TrueVal)), m_Value(C))) &&
+ CondVal->hasOneUse()) {
+ FalseVal = Builder.CreateFreeze(FalseVal);
+ return BinaryOperator::CreateAnd(TrueVal, Builder.CreateOr(C, FalseVal));
+ }
+ // select (~c & b), a, b -> and b, (or freeze(a), c)
+ if (match(CondVal, m_c_And(m_Not(m_Value(C)), m_Specific(FalseVal))) &&
+ CondVal->hasOneUse()) {
+ TrueVal = Builder.CreateFreeze(TrueVal);
+ return BinaryOperator::CreateAnd(FalseVal, Builder.CreateOr(C, TrueVal));
+ }
+
+ if (match(FalseVal, m_Zero()) || match(TrueVal, m_One())) {
+ Use *Y = nullptr;
+ bool IsAnd = match(FalseVal, m_Zero()) ? true : false;
+ Value *Op1 = IsAnd ? TrueVal : FalseVal;
+ if (isCheckForZeroAndMulWithOverflow(CondVal, Op1, IsAnd, Y)) {
+ auto *FI = new FreezeInst(*Y, (*Y)->getName() + ".fr");
+ InsertNewInstBefore(FI, *cast<Instruction>(Y->getUser()));
+ replaceUse(*Y, FI);
+ return replaceInstUsesWith(SI, Op1);
}
- // select a, a, b -> select a, true, b
- if (CondVal == TrueVal)
- return replaceOperand(SI, 1, One);
- // select a, b, a -> select a, b, false
- if (CondVal == FalseVal)
- return replaceOperand(SI, 2, Zero);
-
- // select a, !a, b -> select !a, b, false
- if (match(TrueVal, m_Not(m_Specific(CondVal))))
- return SelectInst::Create(TrueVal, FalseVal, Zero);
- // select a, b, !a -> select !a, true, b
- if (match(FalseVal, m_Not(m_Specific(CondVal))))
- return SelectInst::Create(FalseVal, One, TrueVal);
-
- Value *A, *B;
-
- // DeMorgan in select form: !a && !b --> !(a || b)
- // select !a, !b, false --> not (select a, true, b)
- if (match(&SI, m_LogicalAnd(m_Not(m_Value(A)), m_Not(m_Value(B)))) &&
- (CondVal->hasOneUse() || TrueVal->hasOneUse()) &&
- !match(A, m_ConstantExpr()) && !match(B, m_ConstantExpr()))
- return BinaryOperator::CreateNot(Builder.CreateSelect(A, One, B));
-
- // DeMorgan in select form: !a || !b --> !(a && b)
- // select !a, true, !b --> not (select a, b, false)
- if (match(&SI, m_LogicalOr(m_Not(m_Value(A)), m_Not(m_Value(B)))) &&
- (CondVal->hasOneUse() || FalseVal->hasOneUse()) &&
- !match(A, m_ConstantExpr()) && !match(B, m_ConstantExpr()))
- return BinaryOperator::CreateNot(Builder.CreateSelect(A, B, Zero));
-
- // select (select a, true, b), true, b -> select a, true, b
- if (match(CondVal, m_Select(m_Value(A), m_One(), m_Value(B))) &&
- match(TrueVal, m_One()) && match(FalseVal, m_Specific(B)))
+ if (auto *Op1SI = dyn_cast<SelectInst>(Op1))
+ if (auto *I = foldAndOrOfSelectUsingImpliedCond(CondVal, *Op1SI,
+ /* IsAnd */ IsAnd))
+ return I;
+
+ if (auto *ICmp0 = dyn_cast<ICmpInst>(CondVal))
+ if (auto *ICmp1 = dyn_cast<ICmpInst>(Op1))
+ if (auto *V = foldAndOrOfICmps(ICmp0, ICmp1, SI, IsAnd,
+ /* IsLogical */ true))
+ return replaceInstUsesWith(SI, V);
+ }
+
+ // select (a || b), c, false -> select a, c, false
+ // select c, (a || b), false -> select c, a, false
+ // if c implies that b is false.
+ if (match(CondVal, m_LogicalOr(m_Value(A), m_Value(B))) &&
+ match(FalseVal, m_Zero())) {
+ std::optional<bool> Res = isImpliedCondition(TrueVal, B, DL);
+ if (Res && *Res == false)
return replaceOperand(SI, 0, A);
- // select (select a, b, false), b, false -> select a, b, false
- if (match(CondVal, m_Select(m_Value(A), m_Value(B), m_Zero())) &&
- match(TrueVal, m_Specific(B)) && match(FalseVal, m_Zero()))
+ }
+ if (match(TrueVal, m_LogicalOr(m_Value(A), m_Value(B))) &&
+ match(FalseVal, m_Zero())) {
+ std::optional<bool> Res = isImpliedCondition(CondVal, B, DL);
+ if (Res && *Res == false)
+ return replaceOperand(SI, 1, A);
+ }
+ // select c, true, (a && b) -> select c, true, a
+ // select (a && b), true, c -> select a, true, c
+ // if c = false implies that b = true
+ if (match(TrueVal, m_One()) &&
+ match(FalseVal, m_LogicalAnd(m_Value(A), m_Value(B)))) {
+ std::optional<bool> Res = isImpliedCondition(CondVal, B, DL, false);
+ if (Res && *Res == true)
+ return replaceOperand(SI, 2, A);
+ }
+ if (match(CondVal, m_LogicalAnd(m_Value(A), m_Value(B))) &&
+ match(TrueVal, m_One())) {
+ std::optional<bool> Res = isImpliedCondition(FalseVal, B, DL, false);
+ if (Res && *Res == true)
return replaceOperand(SI, 0, A);
+ }
+ if (match(TrueVal, m_One())) {
Value *C;
- // select (~a | c), a, b -> and a, (or c, freeze(b))
- if (match(CondVal, m_c_Or(m_Not(m_Specific(TrueVal)), m_Value(C))) &&
- CondVal->hasOneUse()) {
- FalseVal = Builder.CreateFreeze(FalseVal);
- return BinaryOperator::CreateAnd(TrueVal, Builder.CreateOr(C, FalseVal));
- }
- // select (~c & b), a, b -> and b, (or freeze(a), c)
- if (match(CondVal, m_c_And(m_Not(m_Value(C)), m_Specific(FalseVal))) &&
- CondVal->hasOneUse()) {
- TrueVal = Builder.CreateFreeze(TrueVal);
- return BinaryOperator::CreateAnd(FalseVal, Builder.CreateOr(C, TrueVal));
+
+ // (C && A) || (!C && B) --> sel C, A, B
+ // (A && C) || (!C && B) --> sel C, A, B
+ // (C && A) || (B && !C) --> sel C, A, B
+ // (A && C) || (B && !C) --> sel C, A, B (may require freeze)
+ if (match(FalseVal, m_c_LogicalAnd(m_Not(m_Value(C)), m_Value(B))) &&
+ match(CondVal, m_c_LogicalAnd(m_Specific(C), m_Value(A)))) {
+ auto *SelCond = dyn_cast<SelectInst>(CondVal);
+ auto *SelFVal = dyn_cast<SelectInst>(FalseVal);
+ bool MayNeedFreeze = SelCond && SelFVal &&
+ match(SelFVal->getTrueValue(),
+ m_Not(m_Specific(SelCond->getTrueValue())));
+ if (MayNeedFreeze)
+ C = Builder.CreateFreeze(C);
+ return SelectInst::Create(C, A, B);
}
- if (!SelType->isVectorTy()) {
- if (Value *S = simplifyWithOpReplaced(TrueVal, CondVal, One, SQ,
- /* AllowRefinement */ true))
- return replaceOperand(SI, 1, S);
- if (Value *S = simplifyWithOpReplaced(FalseVal, CondVal, Zero, SQ,
- /* AllowRefinement */ true))
- return replaceOperand(SI, 2, S);
+ // (!C && A) || (C && B) --> sel C, B, A
+ // (A && !C) || (C && B) --> sel C, B, A
+ // (!C && A) || (B && C) --> sel C, B, A
+ // (A && !C) || (B && C) --> sel C, B, A (may require freeze)
+ if (match(CondVal, m_c_LogicalAnd(m_Not(m_Value(C)), m_Value(A))) &&
+ match(FalseVal, m_c_LogicalAnd(m_Specific(C), m_Value(B)))) {
+ auto *SelCond = dyn_cast<SelectInst>(CondVal);
+ auto *SelFVal = dyn_cast<SelectInst>(FalseVal);
+ bool MayNeedFreeze = SelCond && SelFVal &&
+ match(SelCond->getTrueValue(),
+ m_Not(m_Specific(SelFVal->getTrueValue())));
+ if (MayNeedFreeze)
+ C = Builder.CreateFreeze(C);
+ return SelectInst::Create(C, B, A);
}
+ }
- if (match(FalseVal, m_Zero()) || match(TrueVal, m_One())) {
- Use *Y = nullptr;
- bool IsAnd = match(FalseVal, m_Zero()) ? true : false;
- Value *Op1 = IsAnd ? TrueVal : FalseVal;
- if (isCheckForZeroAndMulWithOverflow(CondVal, Op1, IsAnd, Y)) {
- auto *FI = new FreezeInst(*Y, (*Y)->getName() + ".fr");
- InsertNewInstBefore(FI, *cast<Instruction>(Y->getUser()));
- replaceUse(*Y, FI);
- return replaceInstUsesWith(SI, Op1);
- }
+ return nullptr;
+}
+
+Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) {
+ Value *CondVal = SI.getCondition();
+ Value *TrueVal = SI.getTrueValue();
+ Value *FalseVal = SI.getFalseValue();
+ Type *SelType = SI.getType();
- if (auto *Op1SI = dyn_cast<SelectInst>(Op1))
- if (auto *I = foldAndOrOfSelectUsingImpliedCond(CondVal, *Op1SI,
- /* IsAnd */ IsAnd))
- return I;
+ if (Value *V = simplifySelectInst(CondVal, TrueVal, FalseVal,
+ SQ.getWithInstruction(&SI)))
+ return replaceInstUsesWith(SI, V);
- if (auto *ICmp0 = dyn_cast<ICmpInst>(CondVal))
- if (auto *ICmp1 = dyn_cast<ICmpInst>(Op1))
- if (auto *V = foldAndOrOfICmps(ICmp0, ICmp1, SI, IsAnd,
- /* IsLogical */ true))
- return replaceInstUsesWith(SI, V);
- }
+ if (Instruction *I = canonicalizeSelectToShuffle(SI))
+ return I;
- // select (select a, true, b), c, false -> select a, c, false
- // select c, (select a, true, b), false -> select c, a, false
- // if c implies that b is false.
- if (match(CondVal, m_Select(m_Value(A), m_One(), m_Value(B))) &&
- match(FalseVal, m_Zero())) {
- Optional<bool> Res = isImpliedCondition(TrueVal, B, DL);
- if (Res && *Res == false)
- return replaceOperand(SI, 0, A);
- }
- if (match(TrueVal, m_Select(m_Value(A), m_One(), m_Value(B))) &&
- match(FalseVal, m_Zero())) {
- Optional<bool> Res = isImpliedCondition(CondVal, B, DL);
- if (Res && *Res == false)
- return replaceOperand(SI, 1, A);
- }
- // select c, true, (select a, b, false) -> select c, true, a
- // select (select a, b, false), true, c -> select a, true, c
- // if c = false implies that b = true
- if (match(TrueVal, m_One()) &&
- match(FalseVal, m_Select(m_Value(A), m_Value(B), m_Zero()))) {
- Optional<bool> Res = isImpliedCondition(CondVal, B, DL, false);
- if (Res && *Res == true)
- return replaceOperand(SI, 2, A);
- }
- if (match(CondVal, m_Select(m_Value(A), m_Value(B), m_Zero())) &&
- match(TrueVal, m_One())) {
- Optional<bool> Res = isImpliedCondition(FalseVal, B, DL, false);
- if (Res && *Res == true)
- return replaceOperand(SI, 0, A);
- }
+ if (Instruction *I = canonicalizeScalarSelectOfVecs(SI, *this))
+ return I;
- // sel (sel c, a, false), true, (sel !c, b, false) -> sel c, a, b
- // sel (sel !c, a, false), true, (sel c, b, false) -> sel c, b, a
- Value *C1, *C2;
- if (match(CondVal, m_Select(m_Value(C1), m_Value(A), m_Zero())) &&
- match(TrueVal, m_One()) &&
- match(FalseVal, m_Select(m_Value(C2), m_Value(B), m_Zero()))) {
- if (match(C2, m_Not(m_Specific(C1)))) // first case
- return SelectInst::Create(C1, A, B);
- else if (match(C1, m_Not(m_Specific(C2)))) // second case
- return SelectInst::Create(C2, B, A);
- }
+ // If the type of select is not an integer type or if the condition and
+ // the selection type are not both scalar nor both vector types, there is no
+ // point in attempting to match these patterns.
+ Type *CondType = CondVal->getType();
+ if (!isa<Constant>(CondVal) && SelType->isIntOrIntVectorTy() &&
+ CondType->isVectorTy() == SelType->isVectorTy()) {
+ if (Value *S = simplifyWithOpReplaced(TrueVal, CondVal,
+ ConstantInt::getTrue(CondType), SQ,
+ /* AllowRefinement */ true))
+ return replaceOperand(SI, 1, S);
+
+ if (Value *S = simplifyWithOpReplaced(FalseVal, CondVal,
+ ConstantInt::getFalse(CondType), SQ,
+ /* AllowRefinement */ true))
+ return replaceOperand(SI, 2, S);
+
+ // Handle patterns involving sext/zext + not explicitly,
+ // as simplifyWithOpReplaced() only looks past one instruction.
+ Value *NotCond;
+
+ // select a, sext(!a), b -> select !a, b, 0
+ // select a, zext(!a), b -> select !a, b, 0
+ if (match(TrueVal, m_ZExtOrSExt(m_CombineAnd(m_Value(NotCond),
+ m_Not(m_Specific(CondVal))))))
+ return SelectInst::Create(NotCond, FalseVal,
+ Constant::getNullValue(SelType));
+
+ // select a, b, zext(!a) -> select !a, 1, b
+ if (match(FalseVal, m_ZExt(m_CombineAnd(m_Value(NotCond),
+ m_Not(m_Specific(CondVal))))))
+ return SelectInst::Create(NotCond, ConstantInt::get(SelType, 1), TrueVal);
+
+ // select a, b, sext(!a) -> select !a, -1, b
+ if (match(FalseVal, m_SExt(m_CombineAnd(m_Value(NotCond),
+ m_Not(m_Specific(CondVal))))))
+ return SelectInst::Create(NotCond, Constant::getAllOnesValue(SelType),
+ TrueVal);
}
+ if (Instruction *R = foldSelectOfBools(SI))
+ return R;
+
// Selecting between two integer or vector splat integer constants?
//
// Note that we don't handle a scalar select of vectors:
@@ -2881,8 +3187,23 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) {
Value *NewSel = Builder.CreateSelect(NewCond, FalseVal, TrueVal);
return replaceInstUsesWith(SI, NewSel);
}
+ }
+ }
+
+ if (isa<FPMathOperator>(SI)) {
+ // TODO: Try to forward-propagate FMF from select arms to the select.
- // NOTE: if we wanted to, this is where to detect MIN/MAX
+ // Canonicalize select of FP values where NaN and -0.0 are not valid as
+ // minnum/maxnum intrinsics.
+ if (SI.hasNoNaNs() && SI.hasNoSignedZeros()) {
+ Value *X, *Y;
+ if (match(&SI, m_OrdFMax(m_Value(X), m_Value(Y))))
+ return replaceInstUsesWith(
+ SI, Builder.CreateBinaryIntrinsic(Intrinsic::maxnum, X, Y, &SI));
+
+ if (match(&SI, m_OrdFMin(m_Value(X), m_Value(Y))))
+ return replaceInstUsesWith(
+ SI, Builder.CreateBinaryIntrinsic(Intrinsic::minnum, X, Y, &SI));
}
}
@@ -2997,19 +3318,6 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) {
}
}
- // Canonicalize select of FP values where NaN and -0.0 are not valid as
- // minnum/maxnum intrinsics.
- if (isa<FPMathOperator>(SI) && SI.hasNoNaNs() && SI.hasNoSignedZeros()) {
- Value *X, *Y;
- if (match(&SI, m_OrdFMax(m_Value(X), m_Value(Y))))
- return replaceInstUsesWith(
- SI, Builder.CreateBinaryIntrinsic(Intrinsic::maxnum, X, Y, &SI));
-
- if (match(&SI, m_OrdFMin(m_Value(X), m_Value(Y))))
- return replaceInstUsesWith(
- SI, Builder.CreateBinaryIntrinsic(Intrinsic::minnum, X, Y, &SI));
- }
-
// See if we can fold the select into a phi node if the condition is a select.
if (auto *PN = dyn_cast<PHINode>(SI.getCondition()))
// The true/false values have to be live in the PHI predecessor's blocks.
@@ -3198,5 +3506,15 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) {
}
}
+ if (Instruction *I = foldNestedSelects(SI, Builder))
+ return I;
+
+ // Match logical variants of the pattern,
+ // and transform them iff that gets rid of inversions.
+ // (~x) | y --> ~(x & (~y))
+ // (~x) & y --> ~(x | (~y))
+ if (sinkNotIntoOtherHandOfLogicalOp(SI))
+ return &SI;
+
return nullptr;
}