Index: lib/Transforms/Vectorize/SLPVectorizer.cpp =================================================================== --- lib/Transforms/Vectorize/SLPVectorizer.cpp +++ lib/Transforms/Vectorize/SLPVectorizer.cpp @@ -115,8 +115,17 @@ "number ")); static cl::opt -ShouldVectorizeHor("slp-vectorize-hor", cl::init(true), cl::Hidden, - cl::desc("Attempt to vectorize horizontal reductions")); + ShouldVectorizeHor("slp-vectorize-hor", cl::init(true), cl::Hidden, + cl::desc("Attempt to vectorize horizontal reductions")); + +static cl::opt + SLPThrottling("slp-throttling", cl::init(true), cl::Hidden, + cl::desc("Enable tree partial vectorize with throttling")); + +static cl::opt + MaxCostsRecalculations("slp-throttling-budget", cl::init(128), cl::Hidden, + cl::desc("Limit the total number of nodes for cost " + "recalculations during throttling")); static cl::opt ShouldStartVectorizeHorAtStore( "slp-vectorize-hor-store", cl::init(false), cl::Hidden, @@ -546,9 +555,29 @@ /// holding live values over call sites. int getSpillCost() const; + /// \returns the cost extracting vectorized elements. + int getExtractCost() const; + + /// \returns the cost of gathering canceled elements to be used + /// by vectorized operations during throttling. + int getInsertCost() const; + + /// Explore SLP graph non-gathering nodes structure. + void treeTraversal(); + + /// Cut given path until it might be good to vectorize. + int cutPath(int &Cost, ArrayRef Path); + + /// Find a non-gathering leaf node from current node C and record the path + /// on the way. + TreeEntry *findLeaf(TreeEntry *C, SmallVectorImpl &Path) const; + + /// Find a subtree of the whole tree suitable to be vectorized. + int findSubTree(int Cost); + /// \returns the vectorization cost of the subtree that starts at \p VL. /// A negative number means that this is profitable. - int getTreeCost(); + int getTreeCost(bool CutTree = false); /// Construct a vectorizable tree that starts at \p Roots, ignoring users for /// the purpose of scheduling and extraction in the \p UserIgnoreLst. @@ -569,6 +598,8 @@ ScalarToTreeEntry.clear(); MustGather.clear(); ExternalUses.clear(); + InternalTreeUses.clear(); + RemovedOperations.clear(); NumOpsWantToKeepOrder.clear(); NumOpsWantToKeepOriginalOrder = 0; for (auto &Iter : BlocksSchedules) { @@ -576,6 +607,9 @@ BS->clear(); } MinBWs.clear(); + ScalarsToVec.clear(); + VecToScalars.clear(); + CostsRecalculations = 0; } unsigned getTreeSize() const { return VectorizableTree.size(); } @@ -619,6 +653,9 @@ return MinVecRegSize; } + /// Save seed instructions to try partially vectorize later. + void recordSeeds(ArrayRef Ops); + /// Check if ArrayType or StructType is isomorphic to some VectorType. /// /// \returns number of elements in vector if isomorphism exists, 0 otherwise. @@ -628,6 +665,12 @@ /// vectorizable. We do not vectorize such trees. bool isTreeTinyAndNotFullyVectorizable() const; + /// Try to cut the tree to make it partially vectorizable. + bool cutTree(); + + /// Try partially vectorize the tree via throttling. + bool tryPartialVectorization(); + OptimizationRemarkEmitter *getORE() { return ORE; } /// This structure holds any data we need about the edges being traversed @@ -1399,6 +1442,16 @@ /// Does this entry require reordering? ArrayRef ReorderIndices; + /// Cost of this tree entry. + int Cost = 0; + + /// True if this node has more than one non-gathering child. + bool IsBranch = false; + + /// True if this node is a gathering node or proposed to be a gathering + /// node during throttling. + bool ProposedToGather = false; + /// Points back to the VectorizableTree. /// /// Only used for Graphviz right now. Unfortunately GraphTrait::NodeRef has @@ -1411,6 +1464,9 @@ /// have multiple users so the data structure is not truly a tree. SmallVector UserTreeIndices; + /// The index containing the use of this entry. + SmallVector UseTreeIndices; + /// The index of this treeEntry in VectorizableTree. int Idx = -1; @@ -1516,8 +1572,10 @@ MustGather.insert(VL.begin(), VL.end()); } - if (UserTreeIdx.UserTE) + if (UserTreeIdx.UserTE) { Last->UserTreeIndices.push_back(UserTreeIdx); + VectorizableTree[UserTreeIdx.UserTE->Idx]->UseTreeIndices.push_back(Last); + } Last->trySetUserTEOperand(UserTreeIdx, VL, ReuseShuffleIndices); return Last; @@ -1554,6 +1612,16 @@ /// Maps a specific scalar to its tree entry. SmallDenseMap ScalarToTreeEntry; + /// Tree entries that should not be vectorized due to throttling. + SmallVector RemovedOperations; + + /// Tree values proposed to be vectorized. + ValueSet ScalarsToVec; + + /// Tree values once considered to be vectorized, but later with throttling + /// decided to stay in a scalar form. + ValueSet VecToScalars; + /// A list of scalars that we found that we need to keep as scalars. ValueSet MustGather; @@ -1573,6 +1641,9 @@ }; using UserList = SmallVector; + /// \returns the cost of extracting the vectorized elements. + int getExtractOperationCost(const ExternalUser &EU) const; + /// Checks if two instructions may access the same memory. /// /// \p Loc1 is the location of \p Inst1. It is passed explicitly because it @@ -1624,6 +1695,17 @@ /// after vectorization. UserList ExternalUses; + /// List of all seeds instructions, we could try to vectorize those seed + /// instructions with partial vectorization. + SmallVector>, 2> Seeds; + + /// Number of times in nodes that we already recalulated cost of + /// the subtree during throtteling. + int CostsRecalculations = 0; + + /// Internal tree oprations proposed to be vectorized values use. + SmallDenseMap InternalTreeUses; + /// Values used only by @llvm.assume calls. SmallPtrSet EphValues; @@ -1982,6 +2064,9 @@ /// Attaches the BlockScheduling structures to basic blocks. MapVector> BlocksSchedules; + /// Remove operations from the list of proposed to schedule. + void removeFromScheduling(BlockScheduling *BS); + /// Performs the "real" scheduling. Done before vectorization is actually /// performed in a basic block. void scheduleBlock(BlockScheduling *BS); @@ -2203,6 +2288,7 @@ LLVM_DEBUG(dbgs() << "SLP: \tInternal user will be removed:" << *U << ".\n"); assert(!UseEntry->NeedToGather && "Bad state"); + InternalTreeUses[U].emplace_back(ExternalUser(Scalar, U, FoundLane)); continue; } } @@ -2809,6 +2895,112 @@ } } +bool BoUpSLP::cutTree() { + SmallPtrSet Removed; + SmallVector VecNodes; + bool FoundRealOp = false; + for (unsigned I = 0, E = VectorizableTree.size(); I < E; ++I) { + TreeEntry *Entry = VectorizableTree[I].get(); + if (!Entry->ProposedToGather) + VecNodes.push_back(I); + } + if (VecNodes.size() <= 2) + return false; + for (unsigned N : VecNodes) { + InstructionsState S = getSameOpcode(VectorizableTree[N]->Scalars); + Instruction *Inst = S.MainOp; + if (Inst && (isa(Inst) || isa(Inst) || + isa(Inst))) { + FoundRealOp = true; + break; + } + } + if (!FoundRealOp) + return false; + // Canceling unprofitable elements. + for (unsigned I = 0, E = VectorizableTree.size(); I < E; ++I) { + TreeEntry *Entry = VectorizableTree[I].get(); + if (Entry->NeedToGather) + continue; + if (Entry->ProposedToGather) { + Entry->NeedToGather = true; + for (Value *V : Entry->Scalars) { + LLVM_DEBUG(dbgs() << "SLP: Remove scalar " << *V + << " out of proposed to vectorize.\n"); + ScalarToTreeEntry.erase(V); + Removed.insert(V); + RemovedOperations.push_back(Entry); + MustGather.insert(V); + ExternalUses.erase( + std::remove_if(ExternalUses.begin(), ExternalUses.end(), + [&V](ExternalUser &EU) { return EU.Scalar == V; }), + ExternalUses.end()); + } + } + } + // For all canceled operations we should consider the possibility of + // use by with non-canceled operations and for that, it requires + // to populate ExternalUser list with canceled elements. + for (unsigned N : VecNodes) { + TreeEntry *Entry = VectorizableTree[N].get(); + for (int Lane = 0, LE = Entry->Scalars.size(); Lane != LE; ++Lane) { + Value *Scalar = Entry->Scalars[Lane]; + for (User *U : Scalar->users()) { + LLVM_DEBUG(dbgs() << "SLP: Checking user:" << *U << ".\n"); + auto *UserInst = dyn_cast(U); + if (!UserInst) + continue; + if (!Removed.count(U)) + continue; + // Ignore users in the user ignore list. + if (is_contained(UserIgnoreList, UserInst)) + continue; + LLVM_DEBUG(dbgs() << "SLP: Need to extract canceled operation :" << *U + << " from lane " << Lane << " from " << *Scalar + << ".\n"); + ExternalUses.emplace_back(ExternalUser(Scalar, U, Lane)); + } + } + } + return true; +} + +bool BoUpSLP::tryPartialVectorization() { + bool Changed = false; + for (auto &S1 : Seeds) { + SmallVector &S = *S1.get(); + // Check those seed instructions are still alive. + if (llvm::any_of(S, [](Value *V) { + return (!(cast(V))->getOperand(0)); + })) + continue; + + // Stop if we are over our budget of maximum cost calculations. + if (CostsRecalculations >= MaxCostsRecalculations) + break; + + buildTree(S); + + // If other part BB were vectorized the tree might not be + // enough interest to look. + if (isTreeTinyAndNotFullyVectorizable()) + continue; + + int Cost = getTreeCost(true); + if (Cost < -SLPCostThreshold) { + vectorizeTree(); + Changed = true; + } + } + Seeds.clear(); + return Changed; +} + +void BoUpSLP::recordSeeds(ArrayRef Ops) { + Seeds.push_back( + llvm::make_unique>(Ops.begin(), Ops.end())); +} + unsigned BoUpSLP::canMapToVector(Type *T, const DataLayout &DL) const { unsigned N; Type *EltTy; @@ -3342,7 +3534,7 @@ // Update LiveValues. LiveValues.erase(PrevInst); for (auto &J : PrevInst->operands()) { - if (isa(&*J) && getTreeEntry(&*J)) + if (isa(&*J) && ScalarsToVec.count(&*J)) LiveValues.insert(cast(&*J)); } @@ -3383,13 +3575,217 @@ return Cost; } -int BoUpSLP::getTreeCost() { - int Cost = 0; +int BoUpSLP::getExtractOperationCost(const ExternalUser &EU) const { + unsigned BundleWidth = VectorizableTree.front()->Scalars.size(); + + // Uses by ephemeral values are free (because the ephemeral value will be + // removed prior to code generation, and so the extraction will be + // removed as well). + if (EphValues.count(EU.User)) + return 0; + + // If we plan to rewrite the tree in a smaller type, we will need to sign + // extend the extracted value back to the original type. Here, we account + // for the extract and the added cost of the sign extend if needed. + auto *VecTy = VectorType::get(EU.Scalar->getType(), BundleWidth); + Value *ScalarRoot = VectorizableTree[0]->Scalars[0]; + + auto It = MinBWs.find(ScalarRoot); + if (It != MinBWs.end()) { + uint64_t Width = It->second.first; + bool Signed = It->second.second; + IntegerType *MinTy = IntegerType::get(F->getContext(), Width); + unsigned ExtOp = Signed ? Instruction::SExt : Instruction::ZExt; + VecTy = VectorType::get(MinTy, BundleWidth); + return (TTI->getExtractWithExtendCost(ExtOp, EU.Scalar->getType(), VecTy, + EU.Lane)); + } + return TTI->getVectorInstrCost(Instruction::ExtractElement, VecTy, EU.Lane); +} + +int BoUpSLP::getExtractCost() const { + int ExtractCost = 0; + SmallPtrSet ExtractCostCalculated; + for (const ExternalUser &EU : ExternalUses) { + // We only add extract cost once for the same scalar. + if (!ExtractCostCalculated.insert(EU.Scalar).second) + continue; + + // Avoid non-vectorized scalars. + if (!ScalarsToVec.count(EU.Scalar)) { + // Consider the possibility of extracting vectorized + // values for canceled elements use. + auto It = InternalTreeUses.find(EU.Scalar); + if (It != InternalTreeUses.end()) { + const UserList &UL = It->second; + for (const ExternalUser &IU : UL) + ExtractCost += getExtractOperationCost(IU); + } + continue; + } + + ExtractCost += getExtractOperationCost(EU); + } + return ExtractCost; +} + +int BoUpSLP::getInsertCost() const { + int InsertCost = 0; + for (unsigned I = 0; I < VectorizableTree.size(); I++) { + TreeEntry *Entry = VectorizableTree[I].get(); + if (Entry->NeedToGather) + continue; + for (Value *V : Entry->Scalars) { + auto *Inst = cast(V); + for (Use &U : Inst->operands()) { + Value *Op = U.get(); + if (VecToScalars.count(Op)) + InsertCost += getGatherCost(Op); + } + } + } + return InsertCost; +} + +int BoUpSLP::cutPath(int &Cost, ArrayRef Path) { + // Decrement nodes one by one until Path is empty or we find a suitable set + // of nodes for partial tree vectorization + for (TreeEntry *N : Path) { + CostsRecalculations++; + + // We are no longer propose to vectorize this node and we substitute + // cost of this node from the cost of all vectorizable nodes. + assert(!N->ProposedToGather && "Incorrect node state, visiting twice."); + N->ProposedToGather = true; + Cost -= N->Cost; + for (Value *V : N->Scalars) { + ScalarsToVec.erase(V); + VecToScalars.insert(V); + } + int PartialCost = Cost; + PartialCost += getExtractCost() + getSpillCost() + getInsertCost(); + if (PartialCost < -SLPCostThreshold && cutTree()) + return PartialCost; + } + return INT_MAX; +} + +BoUpSLP::TreeEntry * +BoUpSLP::findLeaf(TreeEntry *C, SmallVectorImpl &Path) const { + int NonGatherUse; + if (!is_contained(Path, C)) + Path.push_back(C); + do { + NonGatherUse = 0; + for (auto I = C->UseTreeIndices.rbegin(), E = C->UseTreeIndices.rend(); + I != E; ++I) { + TreeEntry *Next = *I; + // Ignore any processed nodes to avoid cycles. + if (Next->ProposedToGather || is_contained(Path, Next) || Next == C) + continue; + C = Next; + Path.push_back(C); + NonGatherUse++; + break; + } + } while (NonGatherUse != 0); + return C; +} + +void BoUpSLP::treeTraversal() { + // Find nodes with more than one use and it might include cycles because + // we don't know our routes yet. + for (int N = 0, E = VectorizableTree.size(); N < E; ++N) { + TreeEntry *TE = VectorizableTree[N].get(); + int NonGatherUse = 0; + if (TE->NeedToGather) { + TE->ProposedToGather = true; + continue; + } + for (TreeEntry *Next : TE->UseTreeIndices) { + if (Next->Idx == N || Next->NeedToGather) + continue; + NonGatherUse++; + if (NonGatherUse > 1) + TE->IsBranch = true; + } + } +} + +int BoUpSLP::findSubTree(int Cost) { + SmallVector Path; + SmallVector SubPath; + TreeEntry *Node = nullptr; + treeTraversal(); + + // To start we can find just one leaf node that happens to be not the root + // node of the graph i.e. with non-zero index. Then, Path is route from the + // root node to our leaf node. + if (!findLeaf(VectorizableTree[0].get(), Path)->Idx) + return INT_MAX; + do { + Node = Path.back(); + if (Node->Idx && Node->ProposedToGather) + continue; + // If we found a branch node i.e. node with more than one non-gathering + // child, we could try to find set of profitable nodes in SubPath to + // vectorize and if there is no such set of profitable nodes then we could + // consider another leaf that is reachable from this branch node. + if (Node->IsBranch) { + int PartialCost = cutPath(Cost, SubPath); + if (CostsRecalculations >= MaxCostsRecalculations) { + SubPath.clear(); + break; + } + if (PartialCost != INT_MAX) + return PartialCost; + TreeEntry *NextFromBranch = nullptr; + for (auto I = Node->UseTreeIndices.rbegin(), + E = Node->UseTreeIndices.rend(); + I != E; ++I) { + TreeEntry *Next = *I; + if (Next == Node || Next->ProposedToGather || is_contained(Path, Next)) + continue; + NextFromBranch = Next; + break; + } + SubPath.clear(); + if (NextFromBranch && NextFromBranch != Node) { + Node = findLeaf(NextFromBranch, Path); + } else { + Node->IsBranch = false; + } + } else { + // If this node is not a branch node then we could move to another node + // below until we reach the root node of the graph or encounter another + // branch node. + SubPath.push_back(Node); + Path.pop_back(); + } + } while (Node->Idx); + + // We don't have any branches now and reduce single remaining path now. + if (!SubPath.empty()) { + int PartialCost = cutPath(Cost, SubPath); + if (PartialCost != INT_MAX) + return PartialCost; + } + +#ifndef NDEBUG + // Make sure that we have processed all nodes. + if (CostsRecalculations < MaxCostsRecalculations) { + for (unsigned I = 1, E = VectorizableTree.size(); I < E; ++I) + assert(VectorizableTree[I]->ProposedToGather && "Incorrect node state"); + } +#endif + return INT_MAX; +} + +int BoUpSLP::getTreeCost(bool CutTree) { + int CostSum = 0; LLVM_DEBUG(dbgs() << "SLP: Calculating cost for tree of size " << VectorizableTree.size() << ".\n"); - unsigned BundleWidth = VectorizableTree[0]->Scalars.size(); - for (unsigned I = 0, E = VectorizableTree.size(); I < E; ++I) { TreeEntry &TE = *VectorizableTree[I].get(); @@ -3413,49 +3809,47 @@ })) continue; - int C = getEntryCost(&TE); - LLVM_DEBUG(dbgs() << "SLP: Adding cost " << C + if (!TE.NeedToGather) { + for (Value *V : TE.Scalars) + ScalarsToVec.insert(V); + } + + VectorizableTree[I]->Cost = getEntryCost(&TE); + LLVM_DEBUG(dbgs() << "SLP: Adding cost " << VectorizableTree[I]->Cost << " for bundle that starts with " << *TE.Scalars[0] << ".\n"); - Cost += C; + CostSum += VectorizableTree[I]->Cost; } - SmallPtrSet ExtractCostCalculated; int ExtractCost = 0; - for (ExternalUser &EU : ExternalUses) { - // We only add extract cost once for the same scalar. - if (!ExtractCostCalculated.insert(EU.Scalar).second) - continue; + int SpillCost = 0; + int Cost = CostSum; - // Uses by ephemeral values are free (because the ephemeral value will be - // removed prior to code generation, and so the extraction will be - // removed as well). - if (EphValues.count(EU.User)) - continue; - - // If we plan to rewrite the tree in a smaller type, we will need to sign - // extend the extracted value back to the original type. Here, we account - // for the extract and the added cost of the sign extend if needed. - auto *VecTy = VectorType::get(EU.Scalar->getType(), BundleWidth); - auto *ScalarRoot = VectorizableTree[0]->Scalars[0]; - if (MinBWs.count(ScalarRoot)) { - auto *MinTy = IntegerType::get(F->getContext(), MinBWs[ScalarRoot].first); - auto Extend = - MinBWs[ScalarRoot].second ? Instruction::SExt : Instruction::ZExt; - VecTy = VectorType::get(MinTy, BundleWidth); - ExtractCost += TTI->getExtractWithExtendCost(Extend, EU.Scalar->getType(), - VecTy, EU.Lane); - } else { - ExtractCost += - TTI->getVectorInstrCost(Instruction::ExtractElement, VecTy, EU.Lane); - } - } - - int SpillCost = getSpillCost(); - Cost += SpillCost + ExtractCost; + ExtractCost = getExtractCost(); + Cost += ExtractCost; + SpillCost = getSpillCost(); + Cost += SpillCost; std::string Str; - { + if (CutTree && Cost >= -SLPCostThreshold) { + for (unsigned I = 0, E = VectorizableTree.size(); I < E; ++I) { + TreeEntry &TE = *VectorizableTree[I].get(); + if (TE.NeedToGather) + continue; + int GatherCost = 0; + for (TreeEntry *Gather : TE.UseTreeIndices) + if (Gather->NeedToGather) + GatherCost += Gather->Cost; + TE.Cost = TE.Cost + GatherCost; + } + int PartialCost = findSubTree(CostSum); + if (PartialCost != INT_MAX) { + raw_string_ostream OS(Str); + OS << "SLP: Partial vectorization with Total Cost = " << PartialCost + << ".\n"; + Cost = PartialCost; + } + } else { raw_string_ostream OS(Str); OS << "SLP: Spill Cost = " << SpillCost << ".\n" << "SLP: Extract Cost = " << ExtractCost << ".\n" @@ -4208,7 +4602,12 @@ BoUpSLP::vectorizeTree(ExtraValueToDebugLocsMap &ExternallyUsedValues) { // All blocks must be scheduled before any instructions are inserted. for (auto &BSIter : BlocksSchedules) { - scheduleBlock(BSIter.second.get()); + BlockScheduling *BS = BSIter.second.get(); + // Remove all Schedule Data from all nodes that we have changed + // vectorization decision. + if (RemovedOperations.size()) + removeFromScheduling(BS); + scheduleBlock(BS); } Builder.SetInsertPoint(&F->getEntryBlock().front()); @@ -4337,13 +4736,16 @@ Type *Ty = Scalar->getType(); if (!Ty->isVoidTy()) { #ifndef NDEBUG - for (User *U : Scalar->users()) { - LLVM_DEBUG(dbgs() << "SLP: \tvalidating user:" << *U << ".\n"); - - // It is legal to replace users in the ignorelist by undef. - assert((getTreeEntry(U) || is_contained(UserIgnoreList, U)) && - "Replacing out-of-tree value with undef"); - } + // The tree might not be fully vectorized, so we don't have to + // check every user. + if (RemovedOperations.empty()) + for (User *U : Scalar->users()) { + LLVM_DEBUG(dbgs() << "SLP: \tvalidating user:" << *U << ".\n"); + + // It is legal to replace users in the ignorelist by undef. + assert((getTreeEntry(U) || is_contained(UserIgnoreList, U)) && + "Replacing out-of-tree value with undef"); + } #endif Value *Undef = UndefValue::get(Ty); Scalar->replaceAllUsesWith(Undef); @@ -4820,6 +5222,31 @@ ReadyInsts.clear(); } +void BoUpSLP::removeFromScheduling(BlockScheduling *BS) { + bool Removed = false; + for (TreeEntry *Entry : RemovedOperations) { + ScheduleData *SD = BS->getScheduleData(Entry->Scalars[0]); + if (SD && SD->isPartOfBundle()) { + if (!Removed) { + Removed = true; + BS->resetSchedule(); + } + BS->cancelScheduling(Entry->Scalars, SD->OpValue); + } + } + if (!Removed) + return; + BS->resetSchedule(); + BS->initialFillReadyList(BS->ReadyInsts); + for (Instruction *I = BS->ScheduleStart; I != BS->ScheduleEnd; + I = I->getNextNode()) { + if (BS->ScheduleDataMap.find(I) == BS->ScheduleDataMap.end()) + continue; + BS->doForAllOpcodes(I, + [&](ScheduleData *SD) { SD->clearDependencies(); }); + } +} + void BoUpSLP::scheduleBlock(BlockScheduling *BS) { if (!BS->ScheduleStart) return; @@ -5288,6 +5715,12 @@ << " underlying objects.\n"); Changed |= vectorizeGEPIndices(BB, R); } + + // Partially vectorize trees after all full vectorization is done, + // otherwise, we could prevent more profitable full vectorization with + // smaller vector sizes. + if (SLPThrottling) + Changed |= R.tryPartialVectorization(); } if (Changed) { @@ -5363,6 +5796,8 @@ // Move to the next bundle. i += VF - 1; Changed = true; + } else { + R.recordSeeds(Operands); } } @@ -5592,6 +6027,8 @@ I += VF - 1; NextInst = I + 1; Changed = true; + } else { + R.recordSeeds(Ops); } } } Index: test/Transforms/SLPVectorizer/AArch64/ext-trunc.ll =================================================================== --- test/Transforms/SLPVectorizer/AArch64/ext-trunc.ll +++ test/Transforms/SLPVectorizer/AArch64/ext-trunc.ll @@ -12,21 +12,18 @@ ; CHECK-NEXT: [[Z0:%.*]] = zext <4 x i16> [[A:%.*]] to <4 x i32> ; CHECK-NEXT: [[Z1:%.*]] = zext <4 x i16> [[B:%.*]] to <4 x i32> ; CHECK-NEXT: [[SUB0:%.*]] = sub <4 x i32> [[Z0]], [[Z1]] -; CHECK-NEXT: [[E0:%.*]] = extractelement <4 x i32> [[SUB0]], i32 0 -; CHECK-NEXT: [[S0:%.*]] = sext i32 [[E0]] to i64 -; CHECK-NEXT: [[GEP0:%.*]] = getelementptr inbounds i64, i64* [[P:%.*]], i64 [[S0]] +; CHECK-NEXT: [[TMP0:%.*]] = sext <4 x i32> [[SUB0]] to <4 x i64> +; CHECK-NEXT: [[TMP1:%.*]] = extractelement <4 x i64> [[TMP0]], i32 0 +; CHECK-NEXT: [[GEP0:%.*]] = getelementptr inbounds i64, i64* [[P:%.*]], i64 [[TMP1]] ; CHECK-NEXT: [[LOAD0:%.*]] = load i64, i64* [[GEP0]] -; CHECK-NEXT: [[E1:%.*]] = extractelement <4 x i32> [[SUB0]], i32 1 -; CHECK-NEXT: [[S1:%.*]] = sext i32 [[E1]] to i64 -; CHECK-NEXT: [[GEP1:%.*]] = getelementptr inbounds i64, i64* [[P]], i64 [[S1]] +; CHECK-NEXT: [[TMP2:%.*]] = extractelement <4 x i64> [[TMP0]], i32 1 +; CHECK-NEXT: [[GEP1:%.*]] = getelementptr inbounds i64, i64* [[P]], i64 [[TMP2]] ; CHECK-NEXT: [[LOAD1:%.*]] = load i64, i64* [[GEP1]] -; CHECK-NEXT: [[E2:%.*]] = extractelement <4 x i32> [[SUB0]], i32 2 -; CHECK-NEXT: [[S2:%.*]] = sext i32 [[E2]] to i64 -; CHECK-NEXT: [[GEP2:%.*]] = getelementptr inbounds i64, i64* [[P]], i64 [[S2]] +; CHECK-NEXT: [[TMP3:%.*]] = extractelement <4 x i64> [[TMP0]], i32 2 +; CHECK-NEXT: [[GEP2:%.*]] = getelementptr inbounds i64, i64* [[P]], i64 [[TMP3]] ; CHECK-NEXT: [[LOAD2:%.*]] = load i64, i64* [[GEP2]] -; CHECK-NEXT: [[E3:%.*]] = extractelement <4 x i32> [[SUB0]], i32 3 -; CHECK-NEXT: [[S3:%.*]] = sext i32 [[E3]] to i64 -; CHECK-NEXT: [[GEP3:%.*]] = getelementptr inbounds i64, i64* [[P]], i64 [[S3]] +; CHECK-NEXT: [[TMP4:%.*]] = extractelement <4 x i64> [[TMP0]], i32 3 +; CHECK-NEXT: [[GEP3:%.*]] = getelementptr inbounds i64, i64* [[P]], i64 [[TMP4]] ; CHECK-NEXT: [[LOAD3:%.*]] = load i64, i64* [[GEP3]] ; CHECK-NEXT: call void @foo(i64 [[LOAD0]], i64 [[LOAD1]], i64 [[LOAD2]], i64 [[LOAD3]]) ; CHECK-NEXT: ret void Index: test/Transforms/SLPVectorizer/AArch64/horizontal.ll =================================================================== --- test/Transforms/SLPVectorizer/AArch64/horizontal.ll +++ test/Transforms/SLPVectorizer/AArch64/horizontal.ll @@ -28,39 +28,42 @@ ; CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[LX:%.*]] to i64 ; CHECK-NEXT: br label [[FOR_BODY:%.*]] ; CHECK: for.body: -; CHECK-NEXT: [[S_026:%.*]] = phi i32 [ 0, [[FOR_BODY_LR_PH]] ], [ [[OP_EXTRA:%.*]], [[FOR_BODY]] ] -; CHECK-NEXT: [[J_025:%.*]] = phi i32 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INC:%.*]], [[FOR_BODY]] ] ; CHECK-NEXT: [[P2_024:%.*]] = phi i32* [ [[BLK2:%.*]], [[FOR_BODY_LR_PH]] ], [ [[ADD_PTR29:%.*]], [[FOR_BODY]] ] ; CHECK-NEXT: [[P1_023:%.*]] = phi i32* [ [[BLK1:%.*]], [[FOR_BODY_LR_PH]] ], [ [[ADD_PTR:%.*]], [[FOR_BODY]] ] +; CHECK-NEXT: [[TMP0:%.*]] = phi <2 x i32> [ zeroinitializer, [[FOR_BODY_LR_PH]] ], [ [[TMP13:%.*]], [[FOR_BODY]] ] ; CHECK-NEXT: [[ARRAYIDX4:%.*]] = getelementptr inbounds i32, i32* [[P1_023]], i64 1 ; CHECK-NEXT: [[ARRAYIDX5:%.*]] = getelementptr inbounds i32, i32* [[P2_024]], i64 1 ; CHECK-NEXT: [[ARRAYIDX12:%.*]] = getelementptr inbounds i32, i32* [[P1_023]], i64 2 ; CHECK-NEXT: [[ARRAYIDX13:%.*]] = getelementptr inbounds i32, i32* [[P2_024]], i64 2 ; CHECK-NEXT: [[ARRAYIDX20:%.*]] = getelementptr inbounds i32, i32* [[P1_023]], i64 3 -; CHECK-NEXT: [[TMP0:%.*]] = bitcast i32* [[P1_023]] to <4 x i32>* -; CHECK-NEXT: [[TMP1:%.*]] = load <4 x i32>, <4 x i32>* [[TMP0]], align 4 +; CHECK-NEXT: [[TMP1:%.*]] = bitcast i32* [[P1_023]] to <4 x i32>* +; CHECK-NEXT: [[TMP2:%.*]] = load <4 x i32>, <4 x i32>* [[TMP1]], align 4 ; CHECK-NEXT: [[ARRAYIDX21:%.*]] = getelementptr inbounds i32, i32* [[P2_024]], i64 3 -; CHECK-NEXT: [[TMP2:%.*]] = bitcast i32* [[P2_024]] to <4 x i32>* -; CHECK-NEXT: [[TMP3:%.*]] = load <4 x i32>, <4 x i32>* [[TMP2]], align 4 -; CHECK-NEXT: [[TMP4:%.*]] = sub nsw <4 x i32> [[TMP1]], [[TMP3]] -; CHECK-NEXT: [[TMP5:%.*]] = icmp slt <4 x i32> [[TMP4]], zeroinitializer -; CHECK-NEXT: [[TMP6:%.*]] = sub nsw <4 x i32> zeroinitializer, [[TMP4]] -; CHECK-NEXT: [[TMP7:%.*]] = select <4 x i1> [[TMP5]], <4 x i32> [[TMP6]], <4 x i32> [[TMP4]] -; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 undef, [[S_026]] +; CHECK-NEXT: [[TMP3:%.*]] = bitcast i32* [[P2_024]] to <4 x i32>* +; CHECK-NEXT: [[TMP4:%.*]] = load <4 x i32>, <4 x i32>* [[TMP3]], align 4 +; CHECK-NEXT: [[TMP5:%.*]] = sub nsw <4 x i32> [[TMP2]], [[TMP4]] +; CHECK-NEXT: [[TMP6:%.*]] = icmp slt <4 x i32> [[TMP5]], zeroinitializer +; CHECK-NEXT: [[TMP7:%.*]] = sub nsw <4 x i32> zeroinitializer, [[TMP5]] +; CHECK-NEXT: [[TMP8:%.*]] = select <4 x i1> [[TMP6]], <4 x i32> [[TMP7]], <4 x i32> [[TMP5]] +; CHECK-NEXT: [[TMP9:%.*]] = extractelement <2 x i32> [[TMP0]], i32 0 +; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 undef, [[TMP9]] ; CHECK-NEXT: [[ADD11:%.*]] = add nsw i32 [[ADD]], undef ; CHECK-NEXT: [[ADD19:%.*]] = add nsw i32 [[ADD11]], undef -; CHECK-NEXT: [[TMP8:%.*]] = call i32 @llvm.experimental.vector.reduce.add.v4i32(<4 x i32> [[TMP7]]) -; CHECK-NEXT: [[OP_EXTRA]] = add nsw i32 [[TMP8]], [[S_026]] +; CHECK-NEXT: [[TMP10:%.*]] = call i32 @llvm.experimental.vector.reduce.add.v4i32(<4 x i32> [[TMP8]]) ; CHECK-NEXT: [[ADD27:%.*]] = add nsw i32 [[ADD19]], undef ; CHECK-NEXT: [[ADD_PTR]] = getelementptr inbounds i32, i32* [[P1_023]], i64 [[IDX_EXT]] ; CHECK-NEXT: [[ADD_PTR29]] = getelementptr inbounds i32, i32* [[P2_024]], i64 [[IDX_EXT]] -; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[J_025]], 1 -; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i32 [[INC]], [[H]] +; CHECK-NEXT: [[TMP11:%.*]] = insertelement <2 x i32> undef, i32 [[TMP10]], i32 0 +; CHECK-NEXT: [[TMP12:%.*]] = insertelement <2 x i32> [[TMP11]], i32 1, i32 1 +; CHECK-NEXT: [[TMP13]] = add nsw <2 x i32> [[TMP12]], [[TMP0]] +; CHECK-NEXT: [[TMP14:%.*]] = extractelement <2 x i32> [[TMP13]], i32 1 +; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i32 [[TMP14]], [[H]] ; CHECK-NEXT: br i1 [[EXITCOND]], label [[FOR_END_LOOPEXIT:%.*]], label [[FOR_BODY]] ; CHECK: for.end.loopexit: +; CHECK-NEXT: [[TMP15:%.*]] = extractelement <2 x i32> [[TMP13]], i32 0 ; CHECK-NEXT: br label [[FOR_END]] ; CHECK: for.end: -; CHECK-NEXT: [[S_0_LCSSA:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[OP_EXTRA]], [[FOR_END_LOOPEXIT]] ] +; CHECK-NEXT: [[S_0_LCSSA:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[TMP15]], [[FOR_END_LOOPEXIT]] ] ; CHECK-NEXT: ret i32 [[S_0_LCSSA]] ; entry: Index: test/Transforms/SLPVectorizer/AArch64/transpose.ll =================================================================== --- test/Transforms/SLPVectorizer/AArch64/transpose.ll +++ test/Transforms/SLPVectorizer/AArch64/transpose.ll @@ -37,21 +37,24 @@ define void @store_chain_v2i64(i64* %a, i64* %b, i64* %c) { ; CHECK-LABEL: @store_chain_v2i64( -; CHECK-NEXT: [[A_1:%.*]] = getelementptr i64, i64* [[A:%.*]], i64 1 -; CHECK-NEXT: [[B_1:%.*]] = getelementptr i64, i64* [[B:%.*]], i64 1 -; CHECK-NEXT: [[C_1:%.*]] = getelementptr i64, i64* [[C:%.*]], i64 1 -; CHECK-NEXT: [[V0_0:%.*]] = load i64, i64* [[A]], align 8 -; CHECK-NEXT: [[V0_1:%.*]] = load i64, i64* [[A_1]], align 8 -; CHECK-NEXT: [[V1_0:%.*]] = load i64, i64* [[B]], align 8 -; CHECK-NEXT: [[V1_1:%.*]] = load i64, i64* [[B_1]], align 8 -; CHECK-NEXT: [[TMP0_0:%.*]] = add i64 [[V0_0]], [[V1_0]] -; CHECK-NEXT: [[TMP0_1:%.*]] = add i64 [[V0_1]], [[V1_1]] -; CHECK-NEXT: [[TMP1_0:%.*]] = sub i64 [[V0_0]], [[V1_0]] -; CHECK-NEXT: [[TMP1_1:%.*]] = sub i64 [[V0_1]], [[V1_1]] -; CHECK-NEXT: [[TMP2_0:%.*]] = add i64 [[TMP0_0]], [[TMP0_1]] -; CHECK-NEXT: [[TMP2_1:%.*]] = add i64 [[TMP1_0]], [[TMP1_1]] -; CHECK-NEXT: store i64 [[TMP2_0]], i64* [[C]], align 8 -; CHECK-NEXT: store i64 [[TMP2_1]], i64* [[C_1]], align 8 +; CHECK-NEXT: [[TMP1:%.*]] = bitcast i64* [[A:%.*]] to <2 x i64>* +; CHECK-NEXT: [[TMP2:%.*]] = load <2 x i64>, <2 x i64>* [[TMP1]], align 8 +; CHECK-NEXT: [[TMP3:%.*]] = bitcast i64* [[B:%.*]] to <2 x i64>* +; CHECK-NEXT: [[TMP4:%.*]] = load <2 x i64>, <2 x i64>* [[TMP3]], align 8 +; CHECK-NEXT: [[TMP5:%.*]] = extractelement <2 x i64> [[TMP2]], i32 1 +; CHECK-NEXT: [[TMP6:%.*]] = extractelement <2 x i64> [[TMP4]], i32 1 +; CHECK-NEXT: [[TMP0_1:%.*]] = add i64 [[TMP5]], [[TMP6]] +; CHECK-NEXT: [[TMP7:%.*]] = extractelement <2 x i64> [[TMP2]], i32 0 +; CHECK-NEXT: [[TMP8:%.*]] = extractelement <2 x i64> [[TMP4]], i32 0 +; CHECK-NEXT: [[TMP1_0:%.*]] = sub i64 [[TMP7]], [[TMP8]] +; CHECK-NEXT: [[TMP9:%.*]] = add <2 x i64> [[TMP2]], [[TMP4]] +; CHECK-NEXT: [[TMP10:%.*]] = sub <2 x i64> [[TMP2]], [[TMP4]] +; CHECK-NEXT: [[TMP11:%.*]] = shufflevector <2 x i64> [[TMP9]], <2 x i64> [[TMP10]], <2 x i32> +; CHECK-NEXT: [[TMP12:%.*]] = insertelement <2 x i64> undef, i64 [[TMP0_1]], i32 0 +; CHECK-NEXT: [[TMP13:%.*]] = insertelement <2 x i64> [[TMP12]], i64 [[TMP1_0]], i32 1 +; CHECK-NEXT: [[TMP14:%.*]] = add <2 x i64> [[TMP11]], [[TMP13]] +; CHECK-NEXT: [[TMP15:%.*]] = bitcast i64* [[C:%.*]] to <2 x i64>* +; CHECK-NEXT: store <2 x i64> [[TMP14]], <2 x i64>* [[TMP15]], align 8 ; CHECK-NEXT: ret void ; %a.0 = getelementptr i64, i64* %a, i64 0 Index: test/Transforms/SLPVectorizer/X86/crash_cmpop.ll =================================================================== --- test/Transforms/SLPVectorizer/X86/crash_cmpop.ll +++ test/Transforms/SLPVectorizer/X86/crash_cmpop.ll @@ -12,39 +12,44 @@ ; SSE: for.body: ; SSE-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_BODY]] ] ; SSE-NEXT: [[ACC1_056:%.*]] = phi float [ 0.000000e+00, [[ENTRY]] ], [ [[ADD13:%.*]], [[FOR_BODY]] ] -; SSE-NEXT: [[S1_055:%.*]] = phi float [ 0.000000e+00, [[ENTRY]] ], [ [[COND_I40:%.*]], [[FOR_BODY]] ] -; SSE-NEXT: [[S0_054:%.*]] = phi float [ 0.000000e+00, [[ENTRY]] ], [ [[COND_I44:%.*]], [[FOR_BODY]] ] +; SSE-NEXT: [[TMP0:%.*]] = phi <2 x float> [ zeroinitializer, [[ENTRY]] ], [ [[TMP19:%.*]], [[FOR_BODY]] ] ; SSE-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds float, float* [[SRC:%.*]], i64 [[INDVARS_IV]] -; SSE-NEXT: [[TMP0:%.*]] = load float, float* [[ARRAYIDX]], align 4 +; SSE-NEXT: [[TMP1:%.*]] = load float, float* [[ARRAYIDX]], align 4 ; SSE-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 ; SSE-NEXT: [[ARRAYIDX2:%.*]] = getelementptr inbounds float, float* [[DEST:%.*]], i64 [[INDVARS_IV]] ; SSE-NEXT: store float [[ACC1_056]], float* [[ARRAYIDX2]], align 4 -; SSE-NEXT: [[ADD:%.*]] = fadd float [[S0_054]], [[TMP0]] -; SSE-NEXT: [[ADD3:%.*]] = fadd float [[S1_055]], [[TMP0]] -; SSE-NEXT: [[MUL:%.*]] = fmul float [[S0_054]], 0.000000e+00 -; SSE-NEXT: [[ADD4:%.*]] = fadd float [[MUL]], [[ADD3]] -; SSE-NEXT: [[MUL5:%.*]] = fmul float [[S1_055]], 0.000000e+00 -; SSE-NEXT: [[ADD6:%.*]] = fadd float [[MUL5]], [[ADD]] -; SSE-NEXT: [[CMP_I:%.*]] = fcmp olt float [[ADD6]], 1.000000e+00 -; SSE-NEXT: [[COND_I:%.*]] = select i1 [[CMP_I]], float [[ADD6]], float 1.000000e+00 -; SSE-NEXT: [[CMP_I51:%.*]] = fcmp olt float [[COND_I]], -1.000000e+00 -; SSE-NEXT: [[CMP_I49:%.*]] = fcmp olt float [[ADD4]], 1.000000e+00 -; SSE-NEXT: [[COND_I50:%.*]] = select i1 [[CMP_I49]], float [[ADD4]], float 1.000000e+00 -; SSE-NEXT: [[CMP_I47:%.*]] = fcmp olt float [[COND_I50]], -1.000000e+00 -; SSE-NEXT: [[COND_I_OP:%.*]] = fmul float [[COND_I]], 0.000000e+00 -; SSE-NEXT: [[MUL10:%.*]] = select i1 [[CMP_I51]], float -0.000000e+00, float [[COND_I_OP]] -; SSE-NEXT: [[COND_I50_OP:%.*]] = fmul float [[COND_I50]], 0.000000e+00 -; SSE-NEXT: [[MUL11:%.*]] = select i1 [[CMP_I47]], float -0.000000e+00, float [[COND_I50_OP]] -; SSE-NEXT: [[ADD13]] = fadd float [[MUL10]], [[MUL11]] +; SSE-NEXT: [[TMP2:%.*]] = extractelement <2 x float> [[TMP0]], i32 1 +; SSE-NEXT: [[ADD:%.*]] = fadd float [[TMP2]], [[TMP1]] +; SSE-NEXT: [[TMP3:%.*]] = extractelement <2 x float> [[TMP0]], i32 0 +; SSE-NEXT: [[ADD3:%.*]] = fadd float [[TMP3]], [[TMP1]] +; SSE-NEXT: [[TMP4:%.*]] = fmul <2 x float> [[TMP0]], zeroinitializer +; SSE-NEXT: [[TMP5:%.*]] = insertelement <2 x float> undef, float [[ADD]], i32 0 +; SSE-NEXT: [[TMP6:%.*]] = insertelement <2 x float> [[TMP5]], float [[ADD3]], i32 1 +; SSE-NEXT: [[TMP7:%.*]] = fadd <2 x float> [[TMP4]], [[TMP6]] +; SSE-NEXT: [[TMP8:%.*]] = fcmp olt <2 x float> [[TMP7]], +; SSE-NEXT: [[TMP9:%.*]] = select <2 x i1> [[TMP8]], <2 x float> [[TMP7]], <2 x float> +; SSE-NEXT: [[TMP10:%.*]] = fcmp olt <2 x float> [[TMP9]], +; SSE-NEXT: [[TMP11:%.*]] = extractelement <2 x float> [[TMP9]], i32 0 +; SSE-NEXT: [[COND_I_OP:%.*]] = fmul float [[TMP11]], 0.000000e+00 +; SSE-NEXT: [[TMP12:%.*]] = extractelement <2 x float> [[TMP9]], i32 1 +; SSE-NEXT: [[COND_I50_OP:%.*]] = fmul float [[TMP12]], 0.000000e+00 +; SSE-NEXT: [[TMP13:%.*]] = insertelement <2 x float> undef, float [[COND_I_OP]], i32 0 +; SSE-NEXT: [[TMP14:%.*]] = insertelement <2 x float> [[TMP13]], float [[COND_I50_OP]], i32 1 +; SSE-NEXT: [[TMP15:%.*]] = select <2 x i1> [[TMP10]], <2 x float> , <2 x float> [[TMP14]] +; SSE-NEXT: [[TMP16:%.*]] = extractelement <2 x float> [[TMP15]], i32 0 +; SSE-NEXT: [[TMP17:%.*]] = extractelement <2 x float> [[TMP15]], i32 1 +; SSE-NEXT: [[ADD13]] = fadd float [[TMP16]], [[TMP17]] ; SSE-NEXT: [[CMP_I45:%.*]] = fcmp olt float [[ADD13]], 1.000000e+00 ; SSE-NEXT: [[COND_I46:%.*]] = select i1 [[CMP_I45]], float [[ADD13]], float 1.000000e+00 ; SSE-NEXT: [[CMP_I43:%.*]] = fcmp olt float [[COND_I46]], -1.000000e+00 -; SSE-NEXT: [[COND_I44]] = select i1 [[CMP_I43]], float -1.000000e+00, float [[COND_I46]] -; SSE-NEXT: [[CMP_I41:%.*]] = fcmp olt float [[MUL11]], 1.000000e+00 -; SSE-NEXT: [[COND_I42:%.*]] = select i1 [[CMP_I41]], float [[MUL11]], float 1.000000e+00 +; SSE-NEXT: [[COND_I44:%.*]] = select i1 [[CMP_I43]], float -1.000000e+00, float [[COND_I46]] +; SSE-NEXT: [[CMP_I41:%.*]] = fcmp olt float [[TMP17]], 1.000000e+00 +; SSE-NEXT: [[COND_I42:%.*]] = select i1 [[CMP_I41]], float [[TMP17]], float 1.000000e+00 ; SSE-NEXT: [[CMP_I39:%.*]] = fcmp olt float [[COND_I42]], -1.000000e+00 -; SSE-NEXT: [[COND_I40]] = select i1 [[CMP_I39]], float -1.000000e+00, float [[COND_I42]] +; SSE-NEXT: [[COND_I40:%.*]] = select i1 [[CMP_I39]], float -1.000000e+00, float [[COND_I42]] ; SSE-NEXT: [[EXITCOND:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], 32 +; SSE-NEXT: [[TMP18:%.*]] = insertelement <2 x float> undef, float [[COND_I40]], i32 0 +; SSE-NEXT: [[TMP19]] = insertelement <2 x float> [[TMP18]], float [[COND_I44]], i32 1 ; SSE-NEXT: br i1 [[EXITCOND]], label [[FOR_END:%.*]], label [[FOR_BODY]] ; SSE: for.end: ; SSE-NEXT: ret void Index: test/Transforms/SLPVectorizer/X86/crash_mandeltext.ll =================================================================== --- test/Transforms/SLPVectorizer/X86/crash_mandeltext.ll +++ test/Transforms/SLPVectorizer/X86/crash_mandeltext.ll @@ -15,18 +15,20 @@ ; CHECK: for.body6: ; CHECK-NEXT: br label [[FOR_BODY12:%.*]] ; CHECK: for.body12: -; CHECK-NEXT: [[FZIMG_069:%.*]] = phi double [ undef, [[FOR_BODY6]] ], [ [[ADD19:%.*]], [[IF_END:%.*]] ] -; CHECK-NEXT: [[FZREAL_068:%.*]] = phi double [ undef, [[FOR_BODY6]] ], [ [[ADD20:%.*]], [[IF_END]] ] -; CHECK-NEXT: [[MUL13:%.*]] = fmul double [[FZREAL_068]], [[FZREAL_068]] -; CHECK-NEXT: [[MUL14:%.*]] = fmul double [[FZIMG_069]], [[FZIMG_069]] -; CHECK-NEXT: [[ADD15:%.*]] = fadd double [[MUL13]], [[MUL14]] +; CHECK-NEXT: [[TMP0:%.*]] = phi <2 x double> [ undef, [[FOR_BODY6]] ], [ [[TMP7:%.*]], [[IF_END:%.*]] ] +; CHECK-NEXT: [[TMP1:%.*]] = fmul <2 x double> [[TMP0]], [[TMP0]] +; CHECK-NEXT: [[TMP2:%.*]] = extractelement <2 x double> [[TMP1]], i32 0 +; CHECK-NEXT: [[TMP3:%.*]] = extractelement <2 x double> [[TMP1]], i32 1 +; CHECK-NEXT: [[ADD15:%.*]] = fadd double [[TMP2]], [[TMP3]] ; CHECK-NEXT: [[CMP16:%.*]] = fcmp ogt double [[ADD15]], 4.000000e+00 ; CHECK-NEXT: br i1 [[CMP16]], label [[FOR_INC21:%.*]], label [[IF_END]] ; CHECK: if.end: -; CHECK-NEXT: [[MUL18:%.*]] = fmul double undef, [[FZIMG_069]] -; CHECK-NEXT: [[ADD19]] = fadd double undef, [[MUL18]] -; CHECK-NEXT: [[SUB:%.*]] = fsub double [[MUL13]], [[MUL14]] -; CHECK-NEXT: [[ADD20]] = fadd double undef, [[SUB]] +; CHECK-NEXT: [[TMP4:%.*]] = extractelement <2 x double> [[TMP0]], i32 1 +; CHECK-NEXT: [[MUL18:%.*]] = fmul double undef, [[TMP4]] +; CHECK-NEXT: [[SUB:%.*]] = fsub double [[TMP2]], [[TMP3]] +; CHECK-NEXT: [[TMP5:%.*]] = insertelement <2 x double> undef, double [[SUB]], i32 0 +; CHECK-NEXT: [[TMP6:%.*]] = insertelement <2 x double> [[TMP5]], double [[MUL18]], i32 1 +; CHECK-NEXT: [[TMP7]] = fadd <2 x double> undef, [[TMP6]] ; CHECK-NEXT: br i1 undef, label [[FOR_BODY12]], label [[FOR_INC21]] ; CHECK: for.inc21: ; CHECK-NEXT: br i1 undef, label [[FOR_END23:%.*]], label [[FOR_BODY6]] Index: test/Transforms/SLPVectorizer/X86/slp-throttle.ll =================================================================== --- test/Transforms/SLPVectorizer/X86/slp-throttle.ll +++ test/Transforms/SLPVectorizer/X86/slp-throttle.ll @@ -5,18 +5,20 @@ ; CHECK-LABEL: @rftbsub( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[ARRAYIDX6:%.*]] = getelementptr inbounds double, double* [[A:%.*]], i64 2 -; CHECK-NEXT: [[TMP0:%.*]] = load double, double* [[ARRAYIDX6]], align 8 -; CHECK-NEXT: [[TMP1:%.*]] = or i64 2, 1 -; CHECK-NEXT: [[ARRAYIDX12:%.*]] = getelementptr inbounds double, double* [[A]], i64 [[TMP1]] -; CHECK-NEXT: [[TMP2:%.*]] = load double, double* [[ARRAYIDX12]], align 8 -; CHECK-NEXT: [[ADD16:%.*]] = fadd double [[TMP2]], undef +; CHECK-NEXT: [[TMP0:%.*]] = or i64 2, 1 +; CHECK-NEXT: [[ARRAYIDX12:%.*]] = getelementptr inbounds double, double* [[A]], i64 [[TMP0]] +; CHECK-NEXT: [[TMP1:%.*]] = bitcast double* [[ARRAYIDX6]] to <2 x double>* +; CHECK-NEXT: [[TMP2:%.*]] = load <2 x double>, <2 x double>* [[TMP1]], align 8 +; CHECK-NEXT: [[TMP3:%.*]] = extractelement <2 x double> [[TMP2]], i32 1 +; CHECK-NEXT: [[ADD16:%.*]] = fadd double [[TMP3]], undef ; CHECK-NEXT: [[MUL18:%.*]] = fmul double undef, [[ADD16]] ; CHECK-NEXT: [[ADD19:%.*]] = fadd double undef, [[MUL18]] ; CHECK-NEXT: [[SUB22:%.*]] = fsub double undef, undef -; CHECK-NEXT: [[SUB25:%.*]] = fsub double [[TMP0]], [[ADD19]] -; CHECK-NEXT: store double [[SUB25]], double* [[ARRAYIDX6]], align 8 -; CHECK-NEXT: [[SUB29:%.*]] = fsub double [[TMP2]], [[SUB22]] -; CHECK-NEXT: store double [[SUB29]], double* [[ARRAYIDX12]], align 8 +; CHECK-NEXT: [[TMP4:%.*]] = insertelement <2 x double> undef, double [[ADD19]], i32 0 +; CHECK-NEXT: [[TMP5:%.*]] = insertelement <2 x double> [[TMP4]], double [[SUB22]], i32 1 +; CHECK-NEXT: [[TMP6:%.*]] = fsub <2 x double> [[TMP2]], [[TMP5]] +; CHECK-NEXT: [[TMP7:%.*]] = bitcast double* [[ARRAYIDX6]] to <2 x double>* +; CHECK-NEXT: store <2 x double> [[TMP6]], <2 x double>* [[TMP7]], align 8 ; CHECK-NEXT: unreachable ; entry: