15ffd83dbSDimitry Andric //===-- X86PartialReduction.cpp -------------------------------------------===// 25ffd83dbSDimitry Andric // 35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 65ffd83dbSDimitry Andric // 75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===// 85ffd83dbSDimitry Andric // 95ffd83dbSDimitry Andric // This pass looks for add instructions used by a horizontal reduction to see 105ffd83dbSDimitry Andric // if we might be able to use pmaddwd or psadbw. Some cases of this require 115ffd83dbSDimitry Andric // cross basic block knowledge and can't be done in SelectionDAG. 125ffd83dbSDimitry Andric // 135ffd83dbSDimitry Andric //===----------------------------------------------------------------------===// 145ffd83dbSDimitry Andric 155ffd83dbSDimitry Andric #include "X86.h" 1604eeddc0SDimitry Andric #include "X86TargetMachine.h" 175ffd83dbSDimitry Andric #include "llvm/Analysis/ValueTracking.h" 185ffd83dbSDimitry Andric #include "llvm/CodeGen/TargetPassConfig.h" 195ffd83dbSDimitry Andric #include "llvm/IR/Constants.h" 2004eeddc0SDimitry Andric #include "llvm/IR/IRBuilder.h" 215ffd83dbSDimitry Andric #include "llvm/IR/Instructions.h" 2281ad6265SDimitry Andric #include "llvm/IR/IntrinsicInst.h" 235ffd83dbSDimitry Andric #include "llvm/IR/IntrinsicsX86.h" 245ffd83dbSDimitry Andric #include "llvm/IR/Operator.h" 2581ad6265SDimitry Andric #include "llvm/IR/PatternMatch.h" 265ffd83dbSDimitry Andric #include "llvm/Pass.h" 2704eeddc0SDimitry Andric #include "llvm/Support/KnownBits.h" 285ffd83dbSDimitry Andric 295ffd83dbSDimitry Andric using namespace llvm; 305ffd83dbSDimitry Andric 315ffd83dbSDimitry Andric #define DEBUG_TYPE "x86-partial-reduction" 325ffd83dbSDimitry Andric 335ffd83dbSDimitry Andric namespace { 345ffd83dbSDimitry Andric 355ffd83dbSDimitry Andric class X86PartialReduction : public FunctionPass { 3606c3fb27SDimitry Andric const DataLayout *DL = nullptr; 3706c3fb27SDimitry Andric const X86Subtarget *ST = nullptr; 385ffd83dbSDimitry Andric 395ffd83dbSDimitry Andric public: 405ffd83dbSDimitry Andric static char ID; // Pass identification, replacement for typeid. 415ffd83dbSDimitry Andric 425ffd83dbSDimitry Andric X86PartialReduction() : FunctionPass(ID) { } 435ffd83dbSDimitry Andric 445ffd83dbSDimitry Andric bool runOnFunction(Function &Fn) override; 455ffd83dbSDimitry Andric 465ffd83dbSDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override { 475ffd83dbSDimitry Andric AU.setPreservesCFG(); 485ffd83dbSDimitry Andric } 495ffd83dbSDimitry Andric 505ffd83dbSDimitry Andric StringRef getPassName() const override { 515ffd83dbSDimitry Andric return "X86 Partial Reduction"; 525ffd83dbSDimitry Andric } 535ffd83dbSDimitry Andric 545ffd83dbSDimitry Andric private: 5504eeddc0SDimitry Andric bool tryMAddReplacement(Instruction *Op, bool ReduceInOneBB); 565ffd83dbSDimitry Andric bool trySADReplacement(Instruction *Op); 575ffd83dbSDimitry Andric }; 585ffd83dbSDimitry Andric } 595ffd83dbSDimitry Andric 605ffd83dbSDimitry Andric FunctionPass *llvm::createX86PartialReductionPass() { 615ffd83dbSDimitry Andric return new X86PartialReduction(); 625ffd83dbSDimitry Andric } 635ffd83dbSDimitry Andric 645ffd83dbSDimitry Andric char X86PartialReduction::ID = 0; 655ffd83dbSDimitry Andric 665ffd83dbSDimitry Andric INITIALIZE_PASS(X86PartialReduction, DEBUG_TYPE, 675ffd83dbSDimitry Andric "X86 Partial Reduction", false, false) 685ffd83dbSDimitry Andric 6904eeddc0SDimitry Andric // This function should be aligned with detectExtMul() in X86ISelLowering.cpp. 7004eeddc0SDimitry Andric static bool matchVPDPBUSDPattern(const X86Subtarget *ST, BinaryOperator *Mul, 7104eeddc0SDimitry Andric const DataLayout *DL) { 7204eeddc0SDimitry Andric if (!ST->hasVNNI() && !ST->hasAVXVNNI()) 7304eeddc0SDimitry Andric return false; 7404eeddc0SDimitry Andric 7504eeddc0SDimitry Andric Value *LHS = Mul->getOperand(0); 7604eeddc0SDimitry Andric Value *RHS = Mul->getOperand(1); 7704eeddc0SDimitry Andric 7804eeddc0SDimitry Andric if (isa<SExtInst>(LHS)) 7904eeddc0SDimitry Andric std::swap(LHS, RHS); 8004eeddc0SDimitry Andric 8104eeddc0SDimitry Andric auto IsFreeTruncation = [&](Value *Op) { 8204eeddc0SDimitry Andric if (auto *Cast = dyn_cast<CastInst>(Op)) { 8304eeddc0SDimitry Andric if (Cast->getParent() == Mul->getParent() && 8404eeddc0SDimitry Andric (Cast->getOpcode() == Instruction::SExt || 8504eeddc0SDimitry Andric Cast->getOpcode() == Instruction::ZExt) && 8604eeddc0SDimitry Andric Cast->getOperand(0)->getType()->getScalarSizeInBits() <= 8) 8704eeddc0SDimitry Andric return true; 8804eeddc0SDimitry Andric } 8904eeddc0SDimitry Andric 9004eeddc0SDimitry Andric return isa<Constant>(Op); 9104eeddc0SDimitry Andric }; 9204eeddc0SDimitry Andric 9304eeddc0SDimitry Andric // (dpbusd (zext a), (sext, b)). Since the first operand should be unsigned 9404eeddc0SDimitry Andric // value, we need to check LHS is zero extended value. RHS should be signed 9504eeddc0SDimitry Andric // value, so we just check the signed bits. 9604eeddc0SDimitry Andric if ((IsFreeTruncation(LHS) && 9704eeddc0SDimitry Andric computeKnownBits(LHS, *DL).countMaxActiveBits() <= 8) && 9804eeddc0SDimitry Andric (IsFreeTruncation(RHS) && ComputeMaxSignificantBits(RHS, *DL) <= 8)) 9904eeddc0SDimitry Andric return true; 10004eeddc0SDimitry Andric 10104eeddc0SDimitry Andric return false; 10204eeddc0SDimitry Andric } 10304eeddc0SDimitry Andric 10404eeddc0SDimitry Andric bool X86PartialReduction::tryMAddReplacement(Instruction *Op, 10504eeddc0SDimitry Andric bool ReduceInOneBB) { 1065ffd83dbSDimitry Andric if (!ST->hasSSE2()) 1075ffd83dbSDimitry Andric return false; 1085ffd83dbSDimitry Andric 1095ffd83dbSDimitry Andric // Need at least 8 elements. 1105ffd83dbSDimitry Andric if (cast<FixedVectorType>(Op->getType())->getNumElements() < 8) 1115ffd83dbSDimitry Andric return false; 1125ffd83dbSDimitry Andric 1135ffd83dbSDimitry Andric // Element type should be i32. 1145ffd83dbSDimitry Andric if (!cast<VectorType>(Op->getType())->getElementType()->isIntegerTy(32)) 1155ffd83dbSDimitry Andric return false; 1165ffd83dbSDimitry Andric 1175ffd83dbSDimitry Andric auto *Mul = dyn_cast<BinaryOperator>(Op); 1185ffd83dbSDimitry Andric if (!Mul || Mul->getOpcode() != Instruction::Mul) 1195ffd83dbSDimitry Andric return false; 1205ffd83dbSDimitry Andric 1215ffd83dbSDimitry Andric Value *LHS = Mul->getOperand(0); 1225ffd83dbSDimitry Andric Value *RHS = Mul->getOperand(1); 1235ffd83dbSDimitry Andric 12404eeddc0SDimitry Andric // If the target support VNNI, leave it to ISel to combine reduce operation 12504eeddc0SDimitry Andric // to VNNI instruction. 12604eeddc0SDimitry Andric // TODO: we can support transforming reduce to VNNI intrinsic for across block 12704eeddc0SDimitry Andric // in this pass. 12804eeddc0SDimitry Andric if (ReduceInOneBB && matchVPDPBUSDPattern(ST, Mul, DL)) 12904eeddc0SDimitry Andric return false; 13004eeddc0SDimitry Andric 1315ffd83dbSDimitry Andric // LHS and RHS should be only used once or if they are the same then only 1325ffd83dbSDimitry Andric // used twice. Only check this when SSE4.1 is enabled and we have zext/sext 1335ffd83dbSDimitry Andric // instructions, otherwise we use punpck to emulate zero extend in stages. The 1345ffd83dbSDimitry Andric // trunc/ we need to do likely won't introduce new instructions in that case. 1355ffd83dbSDimitry Andric if (ST->hasSSE41()) { 1365ffd83dbSDimitry Andric if (LHS == RHS) { 1375ffd83dbSDimitry Andric if (!isa<Constant>(LHS) && !LHS->hasNUses(2)) 1385ffd83dbSDimitry Andric return false; 1395ffd83dbSDimitry Andric } else { 1405ffd83dbSDimitry Andric if (!isa<Constant>(LHS) && !LHS->hasOneUse()) 1415ffd83dbSDimitry Andric return false; 1425ffd83dbSDimitry Andric if (!isa<Constant>(RHS) && !RHS->hasOneUse()) 1435ffd83dbSDimitry Andric return false; 1445ffd83dbSDimitry Andric } 1455ffd83dbSDimitry Andric } 1465ffd83dbSDimitry Andric 1475ffd83dbSDimitry Andric auto CanShrinkOp = [&](Value *Op) { 1485ffd83dbSDimitry Andric auto IsFreeTruncation = [&](Value *Op) { 1495ffd83dbSDimitry Andric if (auto *Cast = dyn_cast<CastInst>(Op)) { 1505ffd83dbSDimitry Andric if (Cast->getParent() == Mul->getParent() && 1515ffd83dbSDimitry Andric (Cast->getOpcode() == Instruction::SExt || 1525ffd83dbSDimitry Andric Cast->getOpcode() == Instruction::ZExt) && 1535ffd83dbSDimitry Andric Cast->getOperand(0)->getType()->getScalarSizeInBits() <= 16) 1545ffd83dbSDimitry Andric return true; 1555ffd83dbSDimitry Andric } 1565ffd83dbSDimitry Andric 1575ffd83dbSDimitry Andric return isa<Constant>(Op); 1585ffd83dbSDimitry Andric }; 1595ffd83dbSDimitry Andric 1605ffd83dbSDimitry Andric // If the operation can be freely truncated and has enough sign bits we 1615ffd83dbSDimitry Andric // can shrink. 1625ffd83dbSDimitry Andric if (IsFreeTruncation(Op) && 1635ffd83dbSDimitry Andric ComputeNumSignBits(Op, *DL, 0, nullptr, Mul) > 16) 1645ffd83dbSDimitry Andric return true; 1655ffd83dbSDimitry Andric 1665ffd83dbSDimitry Andric // SelectionDAG has limited support for truncating through an add or sub if 1675ffd83dbSDimitry Andric // the inputs are freely truncatable. 1685ffd83dbSDimitry Andric if (auto *BO = dyn_cast<BinaryOperator>(Op)) { 1695ffd83dbSDimitry Andric if (BO->getParent() == Mul->getParent() && 1705ffd83dbSDimitry Andric IsFreeTruncation(BO->getOperand(0)) && 1715ffd83dbSDimitry Andric IsFreeTruncation(BO->getOperand(1)) && 1725ffd83dbSDimitry Andric ComputeNumSignBits(Op, *DL, 0, nullptr, Mul) > 16) 1735ffd83dbSDimitry Andric return true; 1745ffd83dbSDimitry Andric } 1755ffd83dbSDimitry Andric 1765ffd83dbSDimitry Andric return false; 1775ffd83dbSDimitry Andric }; 1785ffd83dbSDimitry Andric 1795ffd83dbSDimitry Andric // Both Ops need to be shrinkable. 1805ffd83dbSDimitry Andric if (!CanShrinkOp(LHS) && !CanShrinkOp(RHS)) 1815ffd83dbSDimitry Andric return false; 1825ffd83dbSDimitry Andric 1835ffd83dbSDimitry Andric IRBuilder<> Builder(Mul); 1845ffd83dbSDimitry Andric 1855ffd83dbSDimitry Andric auto *MulTy = cast<FixedVectorType>(Op->getType()); 1865ffd83dbSDimitry Andric unsigned NumElts = MulTy->getNumElements(); 1875ffd83dbSDimitry Andric 1885ffd83dbSDimitry Andric // Extract even elements and odd elements and add them together. This will 1895ffd83dbSDimitry Andric // be pattern matched by SelectionDAG to pmaddwd. This instruction will be 1905ffd83dbSDimitry Andric // half the original width. 1915ffd83dbSDimitry Andric SmallVector<int, 16> EvenMask(NumElts / 2); 1925ffd83dbSDimitry Andric SmallVector<int, 16> OddMask(NumElts / 2); 1935ffd83dbSDimitry Andric for (int i = 0, e = NumElts / 2; i != e; ++i) { 1945ffd83dbSDimitry Andric EvenMask[i] = i * 2; 1955ffd83dbSDimitry Andric OddMask[i] = i * 2 + 1; 1965ffd83dbSDimitry Andric } 1975ffd83dbSDimitry Andric // Creating a new mul so the replaceAllUsesWith below doesn't replace the 1985ffd83dbSDimitry Andric // uses in the shuffles we're creating. 1995ffd83dbSDimitry Andric Value *NewMul = Builder.CreateMul(Mul->getOperand(0), Mul->getOperand(1)); 2005ffd83dbSDimitry Andric Value *EvenElts = Builder.CreateShuffleVector(NewMul, NewMul, EvenMask); 2015ffd83dbSDimitry Andric Value *OddElts = Builder.CreateShuffleVector(NewMul, NewMul, OddMask); 2025ffd83dbSDimitry Andric Value *MAdd = Builder.CreateAdd(EvenElts, OddElts); 2035ffd83dbSDimitry Andric 2045ffd83dbSDimitry Andric // Concatenate zeroes to extend back to the original type. 2055ffd83dbSDimitry Andric SmallVector<int, 32> ConcatMask(NumElts); 2065ffd83dbSDimitry Andric std::iota(ConcatMask.begin(), ConcatMask.end(), 0); 2075ffd83dbSDimitry Andric Value *Zero = Constant::getNullValue(MAdd->getType()); 2085ffd83dbSDimitry Andric Value *Concat = Builder.CreateShuffleVector(MAdd, Zero, ConcatMask); 2095ffd83dbSDimitry Andric 2105ffd83dbSDimitry Andric Mul->replaceAllUsesWith(Concat); 2115ffd83dbSDimitry Andric Mul->eraseFromParent(); 2125ffd83dbSDimitry Andric 2135ffd83dbSDimitry Andric return true; 2145ffd83dbSDimitry Andric } 2155ffd83dbSDimitry Andric 2165ffd83dbSDimitry Andric bool X86PartialReduction::trySADReplacement(Instruction *Op) { 2175ffd83dbSDimitry Andric if (!ST->hasSSE2()) 2185ffd83dbSDimitry Andric return false; 2195ffd83dbSDimitry Andric 2205ffd83dbSDimitry Andric // TODO: There's nothing special about i32, any integer type above i16 should 2215ffd83dbSDimitry Andric // work just as well. 2225ffd83dbSDimitry Andric if (!cast<VectorType>(Op->getType())->getElementType()->isIntegerTy(32)) 2235ffd83dbSDimitry Andric return false; 2245ffd83dbSDimitry Andric 22581ad6265SDimitry Andric Value *LHS; 22681ad6265SDimitry Andric if (match(Op, PatternMatch::m_Intrinsic<Intrinsic::abs>())) { 22781ad6265SDimitry Andric LHS = Op->getOperand(0); 22881ad6265SDimitry Andric } else { 2295ffd83dbSDimitry Andric // Operand should be a select. 2305ffd83dbSDimitry Andric auto *SI = dyn_cast<SelectInst>(Op); 2315ffd83dbSDimitry Andric if (!SI) 2325ffd83dbSDimitry Andric return false; 2335ffd83dbSDimitry Andric 23481ad6265SDimitry Andric Value *RHS; 2355ffd83dbSDimitry Andric // Select needs to implement absolute value. 2365ffd83dbSDimitry Andric auto SPR = matchSelectPattern(SI, LHS, RHS); 2375ffd83dbSDimitry Andric if (SPR.Flavor != SPF_ABS) 2385ffd83dbSDimitry Andric return false; 23981ad6265SDimitry Andric } 2405ffd83dbSDimitry Andric 2415ffd83dbSDimitry Andric // Need a subtract of two values. 2425ffd83dbSDimitry Andric auto *Sub = dyn_cast<BinaryOperator>(LHS); 2435ffd83dbSDimitry Andric if (!Sub || Sub->getOpcode() != Instruction::Sub) 2445ffd83dbSDimitry Andric return false; 2455ffd83dbSDimitry Andric 2465ffd83dbSDimitry Andric // Look for zero extend from i8. 2475ffd83dbSDimitry Andric auto getZeroExtendedVal = [](Value *Op) -> Value * { 2485ffd83dbSDimitry Andric if (auto *ZExt = dyn_cast<ZExtInst>(Op)) 2495ffd83dbSDimitry Andric if (cast<VectorType>(ZExt->getOperand(0)->getType()) 2505ffd83dbSDimitry Andric ->getElementType() 2515ffd83dbSDimitry Andric ->isIntegerTy(8)) 2525ffd83dbSDimitry Andric return ZExt->getOperand(0); 2535ffd83dbSDimitry Andric 2545ffd83dbSDimitry Andric return nullptr; 2555ffd83dbSDimitry Andric }; 2565ffd83dbSDimitry Andric 2575ffd83dbSDimitry Andric // Both operands of the subtract should be extends from vXi8. 2585ffd83dbSDimitry Andric Value *Op0 = getZeroExtendedVal(Sub->getOperand(0)); 2595ffd83dbSDimitry Andric Value *Op1 = getZeroExtendedVal(Sub->getOperand(1)); 2605ffd83dbSDimitry Andric if (!Op0 || !Op1) 2615ffd83dbSDimitry Andric return false; 2625ffd83dbSDimitry Andric 26381ad6265SDimitry Andric IRBuilder<> Builder(Op); 2645ffd83dbSDimitry Andric 2655ffd83dbSDimitry Andric auto *OpTy = cast<FixedVectorType>(Op->getType()); 2665ffd83dbSDimitry Andric unsigned NumElts = OpTy->getNumElements(); 2675ffd83dbSDimitry Andric 2685ffd83dbSDimitry Andric unsigned IntrinsicNumElts; 2695ffd83dbSDimitry Andric Intrinsic::ID IID; 2705ffd83dbSDimitry Andric if (ST->hasBWI() && NumElts >= 64) { 2715ffd83dbSDimitry Andric IID = Intrinsic::x86_avx512_psad_bw_512; 2725ffd83dbSDimitry Andric IntrinsicNumElts = 64; 2735ffd83dbSDimitry Andric } else if (ST->hasAVX2() && NumElts >= 32) { 2745ffd83dbSDimitry Andric IID = Intrinsic::x86_avx2_psad_bw; 2755ffd83dbSDimitry Andric IntrinsicNumElts = 32; 2765ffd83dbSDimitry Andric } else { 2775ffd83dbSDimitry Andric IID = Intrinsic::x86_sse2_psad_bw; 2785ffd83dbSDimitry Andric IntrinsicNumElts = 16; 2795ffd83dbSDimitry Andric } 2805ffd83dbSDimitry Andric 28181ad6265SDimitry Andric Function *PSADBWFn = Intrinsic::getDeclaration(Op->getModule(), IID); 2825ffd83dbSDimitry Andric 2835ffd83dbSDimitry Andric if (NumElts < 16) { 2845ffd83dbSDimitry Andric // Pad input with zeroes. 2855ffd83dbSDimitry Andric SmallVector<int, 32> ConcatMask(16); 2865ffd83dbSDimitry Andric for (unsigned i = 0; i != NumElts; ++i) 2875ffd83dbSDimitry Andric ConcatMask[i] = i; 2885ffd83dbSDimitry Andric for (unsigned i = NumElts; i != 16; ++i) 2895ffd83dbSDimitry Andric ConcatMask[i] = (i % NumElts) + NumElts; 2905ffd83dbSDimitry Andric 2915ffd83dbSDimitry Andric Value *Zero = Constant::getNullValue(Op0->getType()); 2925ffd83dbSDimitry Andric Op0 = Builder.CreateShuffleVector(Op0, Zero, ConcatMask); 2935ffd83dbSDimitry Andric Op1 = Builder.CreateShuffleVector(Op1, Zero, ConcatMask); 2945ffd83dbSDimitry Andric NumElts = 16; 2955ffd83dbSDimitry Andric } 2965ffd83dbSDimitry Andric 2975ffd83dbSDimitry Andric // Intrinsics produce vXi64 and need to be casted to vXi32. 2985ffd83dbSDimitry Andric auto *I32Ty = 2995ffd83dbSDimitry Andric FixedVectorType::get(Builder.getInt32Ty(), IntrinsicNumElts / 4); 3005ffd83dbSDimitry Andric 3015ffd83dbSDimitry Andric assert(NumElts % IntrinsicNumElts == 0 && "Unexpected number of elements!"); 3025ffd83dbSDimitry Andric unsigned NumSplits = NumElts / IntrinsicNumElts; 3035ffd83dbSDimitry Andric 3045ffd83dbSDimitry Andric // First collect the pieces we need. 3055ffd83dbSDimitry Andric SmallVector<Value *, 4> Ops(NumSplits); 3065ffd83dbSDimitry Andric for (unsigned i = 0; i != NumSplits; ++i) { 3075ffd83dbSDimitry Andric SmallVector<int, 64> ExtractMask(IntrinsicNumElts); 3085ffd83dbSDimitry Andric std::iota(ExtractMask.begin(), ExtractMask.end(), i * IntrinsicNumElts); 3095ffd83dbSDimitry Andric Value *ExtractOp0 = Builder.CreateShuffleVector(Op0, Op0, ExtractMask); 3105ffd83dbSDimitry Andric Value *ExtractOp1 = Builder.CreateShuffleVector(Op1, Op0, ExtractMask); 3115ffd83dbSDimitry Andric Ops[i] = Builder.CreateCall(PSADBWFn, {ExtractOp0, ExtractOp1}); 3125ffd83dbSDimitry Andric Ops[i] = Builder.CreateBitCast(Ops[i], I32Ty); 3135ffd83dbSDimitry Andric } 3145ffd83dbSDimitry Andric 3155ffd83dbSDimitry Andric assert(isPowerOf2_32(NumSplits) && "Expected power of 2 splits"); 3165ffd83dbSDimitry Andric unsigned Stages = Log2_32(NumSplits); 3175ffd83dbSDimitry Andric for (unsigned s = Stages; s > 0; --s) { 3185ffd83dbSDimitry Andric unsigned NumConcatElts = 3195ffd83dbSDimitry Andric cast<FixedVectorType>(Ops[0]->getType())->getNumElements() * 2; 3205ffd83dbSDimitry Andric for (unsigned i = 0; i != 1U << (s - 1); ++i) { 3215ffd83dbSDimitry Andric SmallVector<int, 64> ConcatMask(NumConcatElts); 3225ffd83dbSDimitry Andric std::iota(ConcatMask.begin(), ConcatMask.end(), 0); 3235ffd83dbSDimitry Andric Ops[i] = Builder.CreateShuffleVector(Ops[i*2], Ops[i*2+1], ConcatMask); 3245ffd83dbSDimitry Andric } 3255ffd83dbSDimitry Andric } 3265ffd83dbSDimitry Andric 3275ffd83dbSDimitry Andric // At this point the final value should be in Ops[0]. Now we need to adjust 3285ffd83dbSDimitry Andric // it to the final original type. 3295ffd83dbSDimitry Andric NumElts = cast<FixedVectorType>(OpTy)->getNumElements(); 3305ffd83dbSDimitry Andric if (NumElts == 2) { 3315ffd83dbSDimitry Andric // Extract down to 2 elements. 3325ffd83dbSDimitry Andric Ops[0] = Builder.CreateShuffleVector(Ops[0], Ops[0], ArrayRef<int>{0, 1}); 3335ffd83dbSDimitry Andric } else if (NumElts >= 8) { 3345ffd83dbSDimitry Andric SmallVector<int, 32> ConcatMask(NumElts); 3355ffd83dbSDimitry Andric unsigned SubElts = 3365ffd83dbSDimitry Andric cast<FixedVectorType>(Ops[0]->getType())->getNumElements(); 3375ffd83dbSDimitry Andric for (unsigned i = 0; i != SubElts; ++i) 3385ffd83dbSDimitry Andric ConcatMask[i] = i; 3395ffd83dbSDimitry Andric for (unsigned i = SubElts; i != NumElts; ++i) 3405ffd83dbSDimitry Andric ConcatMask[i] = (i % SubElts) + SubElts; 3415ffd83dbSDimitry Andric 3425ffd83dbSDimitry Andric Value *Zero = Constant::getNullValue(Ops[0]->getType()); 3435ffd83dbSDimitry Andric Ops[0] = Builder.CreateShuffleVector(Ops[0], Zero, ConcatMask); 3445ffd83dbSDimitry Andric } 3455ffd83dbSDimitry Andric 34681ad6265SDimitry Andric Op->replaceAllUsesWith(Ops[0]); 34781ad6265SDimitry Andric Op->eraseFromParent(); 3485ffd83dbSDimitry Andric 3495ffd83dbSDimitry Andric return true; 3505ffd83dbSDimitry Andric } 3515ffd83dbSDimitry Andric 3525ffd83dbSDimitry Andric // Walk backwards from the ExtractElementInst and determine if it is the end of 3535ffd83dbSDimitry Andric // a horizontal reduction. Return the input to the reduction if we find one. 35404eeddc0SDimitry Andric static Value *matchAddReduction(const ExtractElementInst &EE, 35504eeddc0SDimitry Andric bool &ReduceInOneBB) { 35604eeddc0SDimitry Andric ReduceInOneBB = true; 3575ffd83dbSDimitry Andric // Make sure we're extracting index 0. 3585ffd83dbSDimitry Andric auto *Index = dyn_cast<ConstantInt>(EE.getIndexOperand()); 3595ffd83dbSDimitry Andric if (!Index || !Index->isNullValue()) 3605ffd83dbSDimitry Andric return nullptr; 3615ffd83dbSDimitry Andric 3625ffd83dbSDimitry Andric const auto *BO = dyn_cast<BinaryOperator>(EE.getVectorOperand()); 3635ffd83dbSDimitry Andric if (!BO || BO->getOpcode() != Instruction::Add || !BO->hasOneUse()) 3645ffd83dbSDimitry Andric return nullptr; 36504eeddc0SDimitry Andric if (EE.getParent() != BO->getParent()) 36604eeddc0SDimitry Andric ReduceInOneBB = false; 3675ffd83dbSDimitry Andric 3685ffd83dbSDimitry Andric unsigned NumElems = cast<FixedVectorType>(BO->getType())->getNumElements(); 3695ffd83dbSDimitry Andric // Ensure the reduction size is a power of 2. 3705ffd83dbSDimitry Andric if (!isPowerOf2_32(NumElems)) 3715ffd83dbSDimitry Andric return nullptr; 3725ffd83dbSDimitry Andric 3735ffd83dbSDimitry Andric const Value *Op = BO; 3745ffd83dbSDimitry Andric unsigned Stages = Log2_32(NumElems); 3755ffd83dbSDimitry Andric for (unsigned i = 0; i != Stages; ++i) { 3765ffd83dbSDimitry Andric const auto *BO = dyn_cast<BinaryOperator>(Op); 3775ffd83dbSDimitry Andric if (!BO || BO->getOpcode() != Instruction::Add) 3785ffd83dbSDimitry Andric return nullptr; 37904eeddc0SDimitry Andric if (EE.getParent() != BO->getParent()) 38004eeddc0SDimitry Andric ReduceInOneBB = false; 3815ffd83dbSDimitry Andric 3825ffd83dbSDimitry Andric // If this isn't the first add, then it should only have 2 users, the 3835ffd83dbSDimitry Andric // shuffle and another add which we checked in the previous iteration. 3845ffd83dbSDimitry Andric if (i != 0 && !BO->hasNUses(2)) 3855ffd83dbSDimitry Andric return nullptr; 3865ffd83dbSDimitry Andric 3875ffd83dbSDimitry Andric Value *LHS = BO->getOperand(0); 3885ffd83dbSDimitry Andric Value *RHS = BO->getOperand(1); 3895ffd83dbSDimitry Andric 3905ffd83dbSDimitry Andric auto *Shuffle = dyn_cast<ShuffleVectorInst>(LHS); 3915ffd83dbSDimitry Andric if (Shuffle) { 3925ffd83dbSDimitry Andric Op = RHS; 3935ffd83dbSDimitry Andric } else { 3945ffd83dbSDimitry Andric Shuffle = dyn_cast<ShuffleVectorInst>(RHS); 3955ffd83dbSDimitry Andric Op = LHS; 3965ffd83dbSDimitry Andric } 3975ffd83dbSDimitry Andric 3985ffd83dbSDimitry Andric // The first operand of the shuffle should be the same as the other operand 3995ffd83dbSDimitry Andric // of the bin op. 4005ffd83dbSDimitry Andric if (!Shuffle || Shuffle->getOperand(0) != Op) 4015ffd83dbSDimitry Andric return nullptr; 4025ffd83dbSDimitry Andric 4035ffd83dbSDimitry Andric // Verify the shuffle has the expected (at this stage of the pyramid) mask. 4045ffd83dbSDimitry Andric unsigned MaskEnd = 1 << i; 4055ffd83dbSDimitry Andric for (unsigned Index = 0; Index < MaskEnd; ++Index) 4065ffd83dbSDimitry Andric if (Shuffle->getMaskValue(Index) != (int)(MaskEnd + Index)) 4075ffd83dbSDimitry Andric return nullptr; 4085ffd83dbSDimitry Andric } 4095ffd83dbSDimitry Andric 4105ffd83dbSDimitry Andric return const_cast<Value *>(Op); 4115ffd83dbSDimitry Andric } 4125ffd83dbSDimitry Andric 4135ffd83dbSDimitry Andric // See if this BO is reachable from this Phi by walking forward through single 4145ffd83dbSDimitry Andric // use BinaryOperators with the same opcode. If we get back then we know we've 4155ffd83dbSDimitry Andric // found a loop and it is safe to step through this Add to find more leaves. 4165ffd83dbSDimitry Andric static bool isReachableFromPHI(PHINode *Phi, BinaryOperator *BO) { 4175ffd83dbSDimitry Andric // The PHI itself should only have one use. 4185ffd83dbSDimitry Andric if (!Phi->hasOneUse()) 4195ffd83dbSDimitry Andric return false; 4205ffd83dbSDimitry Andric 4215ffd83dbSDimitry Andric Instruction *U = cast<Instruction>(*Phi->user_begin()); 4225ffd83dbSDimitry Andric if (U == BO) 4235ffd83dbSDimitry Andric return true; 4245ffd83dbSDimitry Andric 4255ffd83dbSDimitry Andric while (U->hasOneUse() && U->getOpcode() == BO->getOpcode()) 4265ffd83dbSDimitry Andric U = cast<Instruction>(*U->user_begin()); 4275ffd83dbSDimitry Andric 4285ffd83dbSDimitry Andric return U == BO; 4295ffd83dbSDimitry Andric } 4305ffd83dbSDimitry Andric 4315ffd83dbSDimitry Andric // Collect all the leaves of the tree of adds that feeds into the horizontal 4325ffd83dbSDimitry Andric // reduction. Root is the Value that is used by the horizontal reduction. 4335ffd83dbSDimitry Andric // We look through single use phis, single use adds, or adds that are used by 4345ffd83dbSDimitry Andric // a phi that forms a loop with the add. 4355ffd83dbSDimitry Andric static void collectLeaves(Value *Root, SmallVectorImpl<Instruction *> &Leaves) { 4365ffd83dbSDimitry Andric SmallPtrSet<Value *, 8> Visited; 4375ffd83dbSDimitry Andric SmallVector<Value *, 8> Worklist; 4385ffd83dbSDimitry Andric Worklist.push_back(Root); 4395ffd83dbSDimitry Andric 4405ffd83dbSDimitry Andric while (!Worklist.empty()) { 4415ffd83dbSDimitry Andric Value *V = Worklist.pop_back_val(); 4425ffd83dbSDimitry Andric if (!Visited.insert(V).second) 4435ffd83dbSDimitry Andric continue; 4445ffd83dbSDimitry Andric 4455ffd83dbSDimitry Andric if (auto *PN = dyn_cast<PHINode>(V)) { 4465ffd83dbSDimitry Andric // PHI node should have single use unless it is the root node, then it 4475ffd83dbSDimitry Andric // has 2 uses. 4485ffd83dbSDimitry Andric if (!PN->hasNUses(PN == Root ? 2 : 1)) 4495ffd83dbSDimitry Andric break; 4505ffd83dbSDimitry Andric 4515ffd83dbSDimitry Andric // Push incoming values to the worklist. 452e8d8bef9SDimitry Andric append_range(Worklist, PN->incoming_values()); 4535ffd83dbSDimitry Andric 4545ffd83dbSDimitry Andric continue; 4555ffd83dbSDimitry Andric } 4565ffd83dbSDimitry Andric 4575ffd83dbSDimitry Andric if (auto *BO = dyn_cast<BinaryOperator>(V)) { 4585ffd83dbSDimitry Andric if (BO->getOpcode() == Instruction::Add) { 4595ffd83dbSDimitry Andric // Simple case. Single use, just push its operands to the worklist. 4605ffd83dbSDimitry Andric if (BO->hasNUses(BO == Root ? 2 : 1)) { 461e8d8bef9SDimitry Andric append_range(Worklist, BO->operands()); 4625ffd83dbSDimitry Andric continue; 4635ffd83dbSDimitry Andric } 4645ffd83dbSDimitry Andric 4655ffd83dbSDimitry Andric // If there is additional use, make sure it is an unvisited phi that 4665ffd83dbSDimitry Andric // gets us back to this node. 4675ffd83dbSDimitry Andric if (BO->hasNUses(BO == Root ? 3 : 2)) { 4685ffd83dbSDimitry Andric PHINode *PN = nullptr; 469753f127fSDimitry Andric for (auto *U : BO->users()) 4705ffd83dbSDimitry Andric if (auto *P = dyn_cast<PHINode>(U)) 4715ffd83dbSDimitry Andric if (!Visited.count(P)) 4725ffd83dbSDimitry Andric PN = P; 4735ffd83dbSDimitry Andric 4745ffd83dbSDimitry Andric // If we didn't find a 2-input PHI then this isn't a case we can 4755ffd83dbSDimitry Andric // handle. 4765ffd83dbSDimitry Andric if (!PN || PN->getNumIncomingValues() != 2) 4775ffd83dbSDimitry Andric continue; 4785ffd83dbSDimitry Andric 4795ffd83dbSDimitry Andric // Walk forward from this phi to see if it reaches back to this add. 4805ffd83dbSDimitry Andric if (!isReachableFromPHI(PN, BO)) 4815ffd83dbSDimitry Andric continue; 4825ffd83dbSDimitry Andric 4835ffd83dbSDimitry Andric // The phi forms a loop with this Add, push its operands. 484e8d8bef9SDimitry Andric append_range(Worklist, BO->operands()); 4855ffd83dbSDimitry Andric } 4865ffd83dbSDimitry Andric } 4875ffd83dbSDimitry Andric } 4885ffd83dbSDimitry Andric 4895ffd83dbSDimitry Andric // Not an add or phi, make it a leaf. 4905ffd83dbSDimitry Andric if (auto *I = dyn_cast<Instruction>(V)) { 4915ffd83dbSDimitry Andric if (!V->hasNUses(I == Root ? 2 : 1)) 4925ffd83dbSDimitry Andric continue; 4935ffd83dbSDimitry Andric 4945ffd83dbSDimitry Andric // Add this as a leaf. 4955ffd83dbSDimitry Andric Leaves.push_back(I); 4965ffd83dbSDimitry Andric } 4975ffd83dbSDimitry Andric } 4985ffd83dbSDimitry Andric } 4995ffd83dbSDimitry Andric 5005ffd83dbSDimitry Andric bool X86PartialReduction::runOnFunction(Function &F) { 5015ffd83dbSDimitry Andric if (skipFunction(F)) 5025ffd83dbSDimitry Andric return false; 5035ffd83dbSDimitry Andric 5045ffd83dbSDimitry Andric auto *TPC = getAnalysisIfAvailable<TargetPassConfig>(); 5055ffd83dbSDimitry Andric if (!TPC) 5065ffd83dbSDimitry Andric return false; 5075ffd83dbSDimitry Andric 5085ffd83dbSDimitry Andric auto &TM = TPC->getTM<X86TargetMachine>(); 5095ffd83dbSDimitry Andric ST = TM.getSubtargetImpl(F); 5105ffd83dbSDimitry Andric 511*0fca6ea1SDimitry Andric DL = &F.getDataLayout(); 5125ffd83dbSDimitry Andric 5135ffd83dbSDimitry Andric bool MadeChange = false; 5145ffd83dbSDimitry Andric for (auto &BB : F) { 5155ffd83dbSDimitry Andric for (auto &I : BB) { 5165ffd83dbSDimitry Andric auto *EE = dyn_cast<ExtractElementInst>(&I); 5175ffd83dbSDimitry Andric if (!EE) 5185ffd83dbSDimitry Andric continue; 5195ffd83dbSDimitry Andric 52004eeddc0SDimitry Andric bool ReduceInOneBB; 5215ffd83dbSDimitry Andric // First find a reduction tree. 5225ffd83dbSDimitry Andric // FIXME: Do we need to handle other opcodes than Add? 52304eeddc0SDimitry Andric Value *Root = matchAddReduction(*EE, ReduceInOneBB); 5245ffd83dbSDimitry Andric if (!Root) 5255ffd83dbSDimitry Andric continue; 5265ffd83dbSDimitry Andric 5275ffd83dbSDimitry Andric SmallVector<Instruction *, 8> Leaves; 5285ffd83dbSDimitry Andric collectLeaves(Root, Leaves); 5295ffd83dbSDimitry Andric 5305ffd83dbSDimitry Andric for (Instruction *I : Leaves) { 53104eeddc0SDimitry Andric if (tryMAddReplacement(I, ReduceInOneBB)) { 5325ffd83dbSDimitry Andric MadeChange = true; 5335ffd83dbSDimitry Andric continue; 5345ffd83dbSDimitry Andric } 5355ffd83dbSDimitry Andric 5365ffd83dbSDimitry Andric // Don't do SAD matching on the root node. SelectionDAG already 5375ffd83dbSDimitry Andric // has support for that and currently generates better code. 5385ffd83dbSDimitry Andric if (I != Root && trySADReplacement(I)) 5395ffd83dbSDimitry Andric MadeChange = true; 5405ffd83dbSDimitry Andric } 5415ffd83dbSDimitry Andric } 5425ffd83dbSDimitry Andric } 5435ffd83dbSDimitry Andric 5445ffd83dbSDimitry Andric return MadeChange; 5455ffd83dbSDimitry Andric } 546