diff options
Diffstat (limited to 'llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp')
-rw-r--r-- | llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp | 327 |
1 files changed, 278 insertions, 49 deletions
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp index f26c194d31b9..ce2b913dba61 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp @@ -18,6 +18,7 @@ #include "llvm/Analysis/AssumptionCache.h" #include "llvm/Analysis/CmpInstAnalysis.h" #include "llvm/Analysis/InstructionSimplify.h" +#include "llvm/Analysis/OverflowInstAnalysis.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Constant.h" @@ -47,11 +48,6 @@ using namespace PatternMatch; #define DEBUG_TYPE "instcombine" -/// FIXME: Enabled by default until the pattern is supported well. -static cl::opt<bool> EnableUnsafeSelectTransform( - "instcombine-unsafe-select-transform", cl::init(true), - cl::desc("Enable poison-unsafe select to and/or transform")); - static Value *createMinMax(InstCombiner::BuilderTy &Builder, SelectPatternFlavor SPF, Value *A, Value *B) { CmpInst::Predicate Pred = getMinMaxPred(SPF); @@ -327,6 +323,35 @@ Instruction *InstCombinerImpl::foldSelectOpOp(SelectInst &SI, Instruction *TI, return UnaryOperator::CreateFNegFMF(NewSel, TI); } + // 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}); + } + if (T1 == F0) { + Value *NewSel = Builder.CreateSelect(Cond, T0, F1, "minmaxop", &SI); + return CallInst::Create(TII->getCalledFunction(), {NewSel, T1}); + } + if (T1 == F1) { + Value *NewSel = Builder.CreateSelect(Cond, T0, F0, "minmaxop", &SI); + return CallInst::Create(TII->getCalledFunction(), {NewSel, T1}); + } + } + } + // Only handle binary operators (including two-operand getelementptr) with // one-use here. As with the cast case above, it may be possible to relax the // one-use constraint, but that needs be examined carefully since it may not @@ -1095,7 +1120,10 @@ static Instruction *canonicalizeAbsNabs(SelectInst &Sel, ICmpInst &Cmp, /// TODO: Wrapping flags could be preserved in some cases with better analysis. Instruction *InstCombinerImpl::foldSelectValueEquivalence(SelectInst &Sel, ICmpInst &Cmp) { - if (!Cmp.isEquality()) + // 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()) return nullptr; // Canonicalize the pattern to ICMP_EQ by swapping the select operands. @@ -1114,7 +1142,7 @@ Instruction *InstCombinerImpl::foldSelectValueEquivalence(SelectInst &Sel, Value *CmpLHS = Cmp.getOperand(0), *CmpRHS = Cmp.getOperand(1); if (TrueVal != CmpLHS && isGuaranteedNotToBeUndefOrPoison(CmpRHS, SQ.AC, &Sel, &DT)) { - if (Value *V = SimplifyWithOpReplaced(TrueVal, CmpLHS, CmpRHS, SQ, + if (Value *V = simplifyWithOpReplaced(TrueVal, CmpLHS, CmpRHS, SQ, /* AllowRefinement */ true)) return replaceOperand(Sel, Swapped ? 2 : 1, V); @@ -1136,7 +1164,7 @@ Instruction *InstCombinerImpl::foldSelectValueEquivalence(SelectInst &Sel, } if (TrueVal != CmpRHS && isGuaranteedNotToBeUndefOrPoison(CmpLHS, SQ.AC, &Sel, &DT)) - if (Value *V = SimplifyWithOpReplaced(TrueVal, CmpRHS, CmpLHS, SQ, + if (Value *V = simplifyWithOpReplaced(TrueVal, CmpRHS, CmpLHS, SQ, /* AllowRefinement */ true)) return replaceOperand(Sel, Swapped ? 2 : 1, V); @@ -1167,9 +1195,9 @@ Instruction *InstCombinerImpl::foldSelectValueEquivalence(SelectInst &Sel, // We have an 'EQ' comparison, so the select's false value will propagate. // Example: // (X == 42) ? 43 : (X + 1) --> (X == 42) ? (X + 1) : (X + 1) --> X + 1 - if (SimplifyWithOpReplaced(FalseVal, CmpLHS, CmpRHS, SQ, + if (simplifyWithOpReplaced(FalseVal, CmpLHS, CmpRHS, SQ, /* AllowRefinement */ false) == TrueVal || - SimplifyWithOpReplaced(FalseVal, CmpRHS, CmpLHS, SQ, + simplifyWithOpReplaced(FalseVal, CmpRHS, CmpLHS, SQ, /* AllowRefinement */ false) == TrueVal) { return replaceInstUsesWith(Sel, FalseVal); } @@ -2351,7 +2379,7 @@ static Instruction *foldSelectFunnelShift(SelectInst &Sel, Intrinsic::ID IID = IsFshl ? Intrinsic::fshl : Intrinsic::fshr; Function *F = Intrinsic::getDeclaration(Sel.getModule(), IID, Sel.getType()); ShAmt = Builder.CreateZExt(ShAmt, Sel.getType()); - return IntrinsicInst::Create(F, { SV0, SV1, ShAmt }); + return CallInst::Create(F, { SV0, SV1, ShAmt }); } static Instruction *foldSelectToCopysign(SelectInst &Sel, @@ -2392,7 +2420,7 @@ static Instruction *foldSelectToCopysign(SelectInst &Sel, Value *MagArg = TC->isNegative() ? FVal : TVal; Function *F = Intrinsic::getDeclaration(Sel.getModule(), Intrinsic::copysign, Sel.getType()); - Instruction *CopySign = IntrinsicInst::Create(F, { MagArg, X }); + Instruction *CopySign = CallInst::Create(F, { MagArg, X }); CopySign->setFastMathFlags(Sel.getFastMathFlags()); return CopySign; } @@ -2553,6 +2581,48 @@ static Value *foldSelectWithFrozenICmp(SelectInst &Sel, InstCombiner::BuilderTy return nullptr; } +Instruction *InstCombinerImpl::foldAndOrOfSelectUsingImpliedCond(Value *Op, + SelectInst &SI, + bool IsAnd) { + Value *CondVal = SI.getCondition(); + Value *A = SI.getTrueValue(); + Value *B = SI.getFalseValue(); + + assert(Op->getType()->isIntOrIntVectorTy(1) && + "Op must be either i1 or vector of i1."); + + Optional<bool> Res = isImpliedCondition(Op, CondVal, DL, IsAnd); + if (!Res) + return nullptr; + + Value *Zero = Constant::getNullValue(A->getType()); + Value *One = Constant::getAllOnesValue(A->getType()); + + if (*Res == true) { + if (IsAnd) + // select op, (select cond, A, B), false => select op, A, false + // and op, (select cond, A, B) => select op, A, false + // if op = true implies condval = true. + return SelectInst::Create(Op, A, Zero); + else + // select op, true, (select cond, A, B) => select op, true, A + // or op, (select cond, A, B) => select op, true, A + // if op = false implies condval = true. + return SelectInst::Create(Op, One, A); + } else { + if (IsAnd) + // select op, (select cond, A, B), false => select op, B, false + // and op, (select cond, A, B) => select op, B, false + // if op = true implies condval = false. + return SelectInst::Create(Op, B, Zero); + else + // select op, true, (select cond, A, B) => select op, true, B + // or op, (select cond, A, B) => select op, true, B + // if op = false implies condval = false. + return SelectInst::Create(Op, One, B); + } +} + Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) { Value *CondVal = SI.getCondition(); Value *TrueVal = SI.getTrueValue(); @@ -2564,7 +2634,7 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) { // don't simplify it so loop unswitch can know the equality comparison // may have an undef operand. This is a workaround for PR31652 caused by // descrepancy about branch on undef between LoopUnswitch and GVN. - if (isa<UndefValue>(TrueVal) || isa<UndefValue>(FalseVal)) { + if (match(TrueVal, m_Undef()) || match(FalseVal, m_Undef())) { if (llvm::any_of(SI.users(), [&](User *U) { ICmpInst *CI = dyn_cast<ICmpInst>(U); if (CI && CI->isEquality()) @@ -2587,47 +2657,152 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) { CmpInst::Predicate Pred; - if (SelType->isIntOrIntVectorTy(1) && + // 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()) { - if (match(TrueVal, m_One()) && - (EnableUnsafeSelectTransform || impliesPoison(FalseVal, CondVal))) { + // 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()) && impliesPoison(FalseVal, CondVal)) { // Change: A = select B, true, C --> A = or B, C return BinaryOperator::CreateOr(CondVal, FalseVal); } - if (match(FalseVal, m_Zero()) && - (EnableUnsafeSelectTransform || impliesPoison(TrueVal, CondVal))) { + if (match(FalseVal, m_Zero()) && impliesPoison(TrueVal, CondVal)) { // Change: A = select B, C, false --> A = and B, C return BinaryOperator::CreateAnd(CondVal, TrueVal); } + auto *One = ConstantInt::getTrue(SelType); + auto *Zero = ConstantInt::getFalse(SelType); + + // 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_Zero())) { + if (match(TrueVal, m_Specific(Zero))) { Value *NotCond = Builder.CreateNot(CondVal, "not." + CondVal->getName()); - return SelectInst::Create(NotCond, FalseVal, - ConstantInt::getFalse(SelType)); + return SelectInst::Create(NotCond, FalseVal, Zero); } // select a, b, true -> select !a, true, b - if (match(FalseVal, m_One())) { + if (match(FalseVal, m_Specific(One))) { Value *NotCond = Builder.CreateNot(CondVal, "not." + CondVal->getName()); - return SelectInst::Create(NotCond, ConstantInt::getTrue(SelType), - TrueVal); + return SelectInst::Create(NotCond, One, TrueVal); } // select a, a, b -> select a, true, b if (CondVal == TrueVal) - return replaceOperand(SI, 1, ConstantInt::getTrue(SelType)); + return replaceOperand(SI, 1, One); // select a, b, a -> select a, b, false if (CondVal == FalseVal) - return replaceOperand(SI, 2, ConstantInt::getFalse(SelType)); + 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, - ConstantInt::getFalse(SelType)); + 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, ConstantInt::getTrue(SelType), - TrueVal); + 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))) + 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); + + 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); + } + + 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); + } + + 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 = foldAndOrOfICmpsOfAndWithPow2(ICmp0, ICmp1, &SI, IsAnd, + /* IsLogical */ true)) + return replaceInstUsesWith(SI, V); + } + + // 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); + } + + // 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); + } } // Selecting between two integer or vector splat integer constants? @@ -2662,9 +2837,9 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) { } } - // See if we are selecting two values based on a comparison of the two values. - if (FCmpInst *FCI = dyn_cast<FCmpInst>(CondVal)) { - Value *Cmp0 = FCI->getOperand(0), *Cmp1 = FCI->getOperand(1); + if (auto *FCmp = dyn_cast<FCmpInst>(CondVal)) { + Value *Cmp0 = FCmp->getOperand(0), *Cmp1 = FCmp->getOperand(1); + // Are we selecting a value based on a comparison of the two values? if ((Cmp0 == TrueVal && Cmp1 == FalseVal) || (Cmp0 == FalseVal && Cmp1 == TrueVal)) { // Canonicalize to use ordered comparisons by swapping the select @@ -2672,13 +2847,13 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) { // // e.g. // (X ugt Y) ? X : Y -> (X ole Y) ? Y : X - if (FCI->hasOneUse() && FCmpInst::isUnordered(FCI->getPredicate())) { - FCmpInst::Predicate InvPred = FCI->getInversePredicate(); + if (FCmp->hasOneUse() && FCmpInst::isUnordered(FCmp->getPredicate())) { + FCmpInst::Predicate InvPred = FCmp->getInversePredicate(); IRBuilder<>::FastMathFlagGuard FMFG(Builder); // FIXME: The FMF should propagate from the select, not the fcmp. - Builder.setFastMathFlags(FCI->getFastMathFlags()); + Builder.setFastMathFlags(FCmp->getFastMathFlags()); Value *NewCond = Builder.CreateFCmp(InvPred, Cmp0, Cmp1, - FCI->getName() + ".inv"); + FCmp->getName() + ".inv"); Value *NewSel = Builder.CreateSelect(NewCond, FalseVal, TrueVal); return replaceInstUsesWith(SI, NewSel); } @@ -2691,15 +2866,13 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) { // fast-math-flags (nsz) or fsub with +0.0 (not fneg) for this to work. We // also require nnan because we do not want to unintentionally change the // sign of a NaN value. - // FIXME: These folds should test/propagate FMF from the select, not the - // fsub or fneg. // (X <= +/-0.0) ? (0.0 - X) : X --> fabs(X) Instruction *FSub; if (match(CondVal, m_FCmp(Pred, m_Specific(FalseVal), m_AnyZeroFP())) && match(TrueVal, m_FSub(m_PosZeroFP(), m_Specific(FalseVal))) && match(TrueVal, m_Instruction(FSub)) && FSub->hasNoNaNs() && (Pred == FCmpInst::FCMP_OLE || Pred == FCmpInst::FCMP_ULE)) { - Value *Fabs = Builder.CreateUnaryIntrinsic(Intrinsic::fabs, FalseVal, FSub); + Value *Fabs = Builder.CreateUnaryIntrinsic(Intrinsic::fabs, FalseVal, &SI); return replaceInstUsesWith(SI, Fabs); } // (X > +/-0.0) ? X : (0.0 - X) --> fabs(X) @@ -2707,7 +2880,7 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) { match(FalseVal, m_FSub(m_PosZeroFP(), m_Specific(TrueVal))) && match(FalseVal, m_Instruction(FSub)) && FSub->hasNoNaNs() && (Pred == FCmpInst::FCMP_OGT || Pred == FCmpInst::FCMP_UGT)) { - Value *Fabs = Builder.CreateUnaryIntrinsic(Intrinsic::fabs, TrueVal, FSub); + Value *Fabs = Builder.CreateUnaryIntrinsic(Intrinsic::fabs, TrueVal, &SI); return replaceInstUsesWith(SI, Fabs); } // With nnan and nsz: @@ -2716,11 +2889,11 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) { Instruction *FNeg; if (match(CondVal, m_FCmp(Pred, m_Specific(FalseVal), m_AnyZeroFP())) && match(TrueVal, m_FNeg(m_Specific(FalseVal))) && - match(TrueVal, m_Instruction(FNeg)) && - FNeg->hasNoNaNs() && FNeg->hasNoSignedZeros() && + match(TrueVal, m_Instruction(FNeg)) && FNeg->hasNoNaNs() && + FNeg->hasNoSignedZeros() && SI.hasNoSignedZeros() && (Pred == FCmpInst::FCMP_OLT || Pred == FCmpInst::FCMP_OLE || Pred == FCmpInst::FCMP_ULT || Pred == FCmpInst::FCMP_ULE)) { - Value *Fabs = Builder.CreateUnaryIntrinsic(Intrinsic::fabs, FalseVal, FNeg); + Value *Fabs = Builder.CreateUnaryIntrinsic(Intrinsic::fabs, FalseVal, &SI); return replaceInstUsesWith(SI, Fabs); } // With nnan and nsz: @@ -2728,11 +2901,11 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) { // (X >= +/-0.0) ? X : -X --> fabs(X) if (match(CondVal, m_FCmp(Pred, m_Specific(TrueVal), m_AnyZeroFP())) && match(FalseVal, m_FNeg(m_Specific(TrueVal))) && - match(FalseVal, m_Instruction(FNeg)) && - FNeg->hasNoNaNs() && FNeg->hasNoSignedZeros() && + match(FalseVal, m_Instruction(FNeg)) && FNeg->hasNoNaNs() && + FNeg->hasNoSignedZeros() && SI.hasNoSignedZeros() && (Pred == FCmpInst::FCMP_OGT || Pred == FCmpInst::FCMP_OGE || Pred == FCmpInst::FCMP_UGT || Pred == FCmpInst::FCMP_UGE)) { - Value *Fabs = Builder.CreateUnaryIntrinsic(Intrinsic::fabs, TrueVal, FNeg); + Value *Fabs = Builder.CreateUnaryIntrinsic(Intrinsic::fabs, TrueVal, &SI); return replaceInstUsesWith(SI, Fabs); } @@ -2758,6 +2931,31 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) { if (Instruction *I = foldSelectExtConst(SI)) return I; + // Fold (select C, (gep Ptr, Idx), Ptr) -> (gep Ptr, (select C, Idx, 0)) + // Fold (select C, Ptr, (gep Ptr, Idx)) -> (gep Ptr, (select C, 0, Idx)) + auto SelectGepWithBase = [&](GetElementPtrInst *Gep, Value *Base, + bool Swap) -> GetElementPtrInst * { + Value *Ptr = Gep->getPointerOperand(); + if (Gep->getNumOperands() != 2 || Gep->getPointerOperand() != Base || + !Gep->hasOneUse()) + return nullptr; + Type *ElementType = Gep->getResultElementType(); + Value *Idx = Gep->getOperand(1); + Value *NewT = Idx; + Value *NewF = Constant::getNullValue(Idx->getType()); + if (Swap) + std::swap(NewT, NewF); + Value *NewSI = + Builder.CreateSelect(CondVal, NewT, NewF, SI.getName() + ".idx", &SI); + return GetElementPtrInst::Create(ElementType, Ptr, {NewSI}); + }; + if (auto *TrueGep = dyn_cast<GetElementPtrInst>(TrueVal)) + if (auto *NewGep = SelectGepWithBase(TrueGep, FalseVal, false)) + return NewGep; + if (auto *FalseGep = dyn_cast<GetElementPtrInst>(FalseVal)) + if (auto *NewGep = SelectGepWithBase(FalseGep, TrueVal, true)) + return NewGep; + // See if we can fold the select into one of our operands. if (SelType->isIntOrIntVectorTy() || SelType->isFPOrFPVectorTy()) { if (Instruction *FoldI = foldSelectIntoOp(SI, TrueVal, FalseVal)) @@ -2890,7 +3088,7 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) { // shortening paths for the values (this helps getUnderlyingObjects() for // example). if (TrueSI->getFalseValue() == FalseVal && TrueSI->hasOneUse()) { - Value *And = Builder.CreateAnd(CondVal, TrueSI->getCondition()); + Value *And = Builder.CreateLogicalAnd(CondVal, TrueSI->getCondition()); replaceOperand(SI, 0, And); replaceOperand(SI, 1, TrueSI->getTrueValue()); return &SI; @@ -2907,7 +3105,7 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) { } // select(C0, a, select(C1, a, b)) -> select(C0|C1, a, b) if (FalseSI->getTrueValue() == TrueVal && FalseSI->hasOneUse()) { - Value *Or = Builder.CreateOr(CondVal, FalseSI->getCondition()); + Value *Or = Builder.CreateLogicalOr(CondVal, FalseSI->getCondition()); replaceOperand(SI, 0, Or); replaceOperand(SI, 2, FalseSI->getFalseValue()); return &SI; @@ -3018,5 +3216,36 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) { if (Value *Fr = foldSelectWithFrozenICmp(SI, Builder)) return replaceInstUsesWith(SI, Fr); + // select(mask, mload(,,mask,0), 0) -> mload(,,mask,0) + // Load inst is intentionally not checked for hasOneUse() + if (match(FalseVal, m_Zero()) && + match(TrueVal, m_MaskedLoad(m_Value(), m_Value(), m_Specific(CondVal), + m_CombineOr(m_Undef(), m_Zero())))) { + auto *MaskedLoad = cast<IntrinsicInst>(TrueVal); + if (isa<UndefValue>(MaskedLoad->getArgOperand(3))) + MaskedLoad->setArgOperand(3, FalseVal /* Zero */); + return replaceInstUsesWith(SI, MaskedLoad); + } + + Value *Mask; + if (match(TrueVal, m_Zero()) && + match(FalseVal, m_MaskedLoad(m_Value(), m_Value(), m_Value(Mask), + m_CombineOr(m_Undef(), m_Zero())))) { + // We can remove the select by ensuring the load zeros all lanes the + // select would have. We determine this by proving there is no overlap + // between the load and select masks. + // (i.e (load_mask & select_mask) == 0 == no overlap) + bool CanMergeSelectIntoLoad = false; + if (Value *V = SimplifyAndInst(CondVal, Mask, SQ.getWithInstruction(&SI))) + CanMergeSelectIntoLoad = match(V, m_Zero()); + + if (CanMergeSelectIntoLoad) { + auto *MaskedLoad = cast<IntrinsicInst>(FalseVal); + if (isa<UndefValue>(MaskedLoad->getArgOperand(3))) + MaskedLoad->setArgOperand(3, TrueVal /* Zero */); + return replaceInstUsesWith(SI, MaskedLoad); + } + } + return nullptr; } |