diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2023-02-11 12:38:04 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2023-02-11 12:38:11 +0000 |
commit | e3b557809604d036af6e00c60f012c2025b59a5e (patch) | |
tree | 8a11ba2269a3b669601e2fd41145b174008f4da8 /llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp | |
parent | 08e8dd7b9db7bb4a9de26d44c1cbfd24e869c014 (diff) |
Diffstat (limited to 'llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp')
-rw-r--r-- | llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp | 940 |
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; } |