HELLO·Android
系统源代码
IT资讯
技术文章
我的收藏
注册
登录
-
我收藏的文章
创建代码块
我的代码块
我的账号
Lollipop
|
5.0.1_r1
下载
查看原文件
收藏
根目录
external
llvm
lib
Transforms
Vectorize
SLPVectorizer.cpp
//===- SLPVectorizer.cpp - A bottom up SLP Vectorizer ---------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // This pass implements the Bottom Up SLP vectorizer. It detects consecutive // stores that can be put together into vector-stores. Next, it attempts to // construct vectorizable tree using the use-def chains. If a profitable tree // was found, the SLP vectorizer performs vectorization on the tree. // // The pass is inspired by the work described in the paper: // "Loop-Aware SLP in GCC" by Ira Rosen, Dorit Nuzman, Ayal Zaks. // //===----------------------------------------------------------------------===// #include "llvm/Transforms/Vectorize.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/PostOrderIterator.h" #include "llvm/ADT/SetVector.h" #include "llvm/Analysis/AliasAnalysis.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/ScalarEvolution.h" #include "llvm/Analysis/ScalarEvolutionExpressions.h" #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Module.h" #include "llvm/IR/NoFolder.h" #include "llvm/IR/Type.h" #include "llvm/IR/Value.h" #include "llvm/IR/Verifier.h" #include "llvm/Pass.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Utils/VectorUtils.h" #include
#include
using namespace llvm; #define SV_NAME "slp-vectorizer" #define DEBUG_TYPE "SLP" static cl::opt
SLPCostThreshold("slp-threshold", cl::init(0), cl::Hidden, cl::desc("Only vectorize if you gain more than this " "number ")); static cl::opt
ShouldVectorizeHor("slp-vectorize-hor", cl::init(false), cl::Hidden, cl::desc("Attempt to vectorize horizontal reductions")); static cl::opt
ShouldStartVectorizeHorAtStore( "slp-vectorize-hor-store", cl::init(false), cl::Hidden, cl::desc( "Attempt to vectorize horizontal reductions feeding into a store")); namespace { static const unsigned MinVecRegSize = 128; static const unsigned RecursionMaxDepth = 12; /// A helper class for numbering instructions in multiple blocks. /// Numbers start at zero for each basic block. struct BlockNumbering { BlockNumbering(BasicBlock *Bb) : BB(Bb), Valid(false) {} void numberInstructions() { unsigned Loc = 0; InstrIdx.clear(); InstrVec.clear(); // Number the instructions in the block. for (BasicBlock::iterator it = BB->begin(), e = BB->end(); it != e; ++it) { InstrIdx[it] = Loc++; InstrVec.push_back(it); assert(InstrVec[InstrIdx[it]] == it && "Invalid allocation"); } Valid = true; } int getIndex(Instruction *I) { assert(I->getParent() == BB && "Invalid instruction"); if (!Valid) numberInstructions(); assert(InstrIdx.count(I) && "Unknown instruction"); return InstrIdx[I]; } Instruction *getInstruction(unsigned loc) { if (!Valid) numberInstructions(); assert(InstrVec.size() > loc && "Invalid Index"); return InstrVec[loc]; } void forget() { Valid = false; } private: /// The block we are numbering. BasicBlock *BB; /// Is the block numbered. bool Valid; /// Maps instructions to numbers and back. SmallDenseMap
InstrIdx; /// Maps integers to Instructions. SmallVector
InstrVec; }; /// \returns the parent basic block if all of the instructions in \p VL /// are in the same block or null otherwise. static BasicBlock *getSameBlock(ArrayRef
VL) { Instruction *I0 = dyn_cast
(VL[0]); if (!I0) return nullptr; BasicBlock *BB = I0->getParent(); for (int i = 1, e = VL.size(); i < e; i++) { Instruction *I = dyn_cast
(VL[i]); if (!I) return nullptr; if (BB != I->getParent()) return nullptr; } return BB; } /// \returns True if all of the values in \p VL are constants. static bool allConstant(ArrayRef
VL) { for (unsigned i = 0, e = VL.size(); i < e; ++i) if (!isa
(VL[i])) return false; return true; } /// \returns True if all of the values in \p VL are identical. static bool isSplat(ArrayRef
VL) { for (unsigned i = 1, e = VL.size(); i < e; ++i) if (VL[i] != VL[0]) return false; return true; } ///\returns Opcode that can be clubbed with \p Op to create an alternate /// sequence which can later be merged as a ShuffleVector instruction. static unsigned getAltOpcode(unsigned Op) { switch (Op) { case Instruction::FAdd: return Instruction::FSub; case Instruction::FSub: return Instruction::FAdd; case Instruction::Add: return Instruction::Sub; case Instruction::Sub: return Instruction::Add; default: return 0; } } ///\returns bool representing if Opcode \p Op can be part /// of an alternate sequence which can later be merged as /// a ShuffleVector instruction. static bool canCombineAsAltInst(unsigned Op) { if (Op == Instruction::FAdd || Op == Instruction::FSub || Op == Instruction::Sub || Op == Instruction::Add) return true; return false; } /// \returns ShuffleVector instruction if intructions in \p VL have /// alternate fadd,fsub / fsub,fadd/add,sub/sub,add sequence. /// (i.e. e.g. opcodes of fadd,fsub,fadd,fsub...) static unsigned isAltInst(ArrayRef
VL) { Instruction *I0 = dyn_cast
(VL[0]); unsigned Opcode = I0->getOpcode(); unsigned AltOpcode = getAltOpcode(Opcode); for (int i = 1, e = VL.size(); i < e; i++) { Instruction *I = dyn_cast
(VL[i]); if (!I || I->getOpcode() != ((i & 1) ? AltOpcode : Opcode)) return 0; } return Instruction::ShuffleVector; } /// \returns The opcode if all of the Instructions in \p VL have the same /// opcode, or zero. static unsigned getSameOpcode(ArrayRef
VL) { Instruction *I0 = dyn_cast
(VL[0]); if (!I0) return 0; unsigned Opcode = I0->getOpcode(); for (int i = 1, e = VL.size(); i < e; i++) { Instruction *I = dyn_cast
(VL[i]); if (!I || Opcode != I->getOpcode()) { if (canCombineAsAltInst(Opcode) && i == 1) return isAltInst(VL); return 0; } } return Opcode; } /// \returns \p I after propagating metadata from \p VL. static Instruction *propagateMetadata(Instruction *I, ArrayRef
VL) { Instruction *I0 = cast
(VL[0]); SmallVector
, 4> Metadata; I0->getAllMetadataOtherThanDebugLoc(Metadata); for (unsigned i = 0, n = Metadata.size(); i != n; ++i) { unsigned Kind = Metadata[i].first; MDNode *MD = Metadata[i].second; for (int i = 1, e = VL.size(); MD && i != e; i++) { Instruction *I = cast
(VL[i]); MDNode *IMD = I->getMetadata(Kind); switch (Kind) { default: MD = nullptr; // Remove unknown metadata break; case LLVMContext::MD_tbaa: MD = MDNode::getMostGenericTBAA(MD, IMD); break; case LLVMContext::MD_fpmath: MD = MDNode::getMostGenericFPMath(MD, IMD); break; } } I->setMetadata(Kind, MD); } return I; } /// \returns The type that all of the values in \p VL have or null if there /// are different types. static Type* getSameType(ArrayRef
VL) { Type *Ty = VL[0]->getType(); for (int i = 1, e = VL.size(); i < e; i++) if (VL[i]->getType() != Ty) return nullptr; return Ty; } /// \returns True if the ExtractElement instructions in VL can be vectorized /// to use the original vector. static bool CanReuseExtract(ArrayRef
VL) { assert(Instruction::ExtractElement == getSameOpcode(VL) && "Invalid opcode"); // Check if all of the extracts come from the same vector and from the // correct offset. Value *VL0 = VL[0]; ExtractElementInst *E0 = cast
(VL0); Value *Vec = E0->getOperand(0); // We have to extract from the same vector type. unsigned NElts = Vec->getType()->getVectorNumElements(); if (NElts != VL.size()) return false; // Check that all of the indices extract from the correct offset. ConstantInt *CI = dyn_cast
(E0->getOperand(1)); if (!CI || CI->getZExtValue()) return false; for (unsigned i = 1, e = VL.size(); i < e; ++i) { ExtractElementInst *E = cast
(VL[i]); ConstantInt *CI = dyn_cast
(E->getOperand(1)); if (!CI || CI->getZExtValue() != i || E->getOperand(0) != Vec) return false; } return true; } static void reorderInputsAccordingToOpcode(ArrayRef
VL, SmallVectorImpl
&Left, SmallVectorImpl
&Right) { SmallVector
OrigLeft, OrigRight; bool AllSameOpcodeLeft = true; bool AllSameOpcodeRight = true; for (unsigned i = 0, e = VL.size(); i != e; ++i) { Instruction *I = cast
(VL[i]); Value *V0 = I->getOperand(0); Value *V1 = I->getOperand(1); OrigLeft.push_back(V0); OrigRight.push_back(V1); Instruction *I0 = dyn_cast
(V0); Instruction *I1 = dyn_cast
(V1); // Check whether all operands on one side have the same opcode. In this case // we want to preserve the original order and not make things worse by // reordering. AllSameOpcodeLeft = I0; AllSameOpcodeRight = I1; if (i && AllSameOpcodeLeft) { if(Instruction *P0 = dyn_cast
(OrigLeft[i-1])) { if(P0->getOpcode() != I0->getOpcode()) AllSameOpcodeLeft = false; } else AllSameOpcodeLeft = false; } if (i && AllSameOpcodeRight) { if(Instruction *P1 = dyn_cast
(OrigRight[i-1])) { if(P1->getOpcode() != I1->getOpcode()) AllSameOpcodeRight = false; } else AllSameOpcodeRight = false; } // Sort two opcodes. In the code below we try to preserve the ability to use // broadcast of values instead of individual inserts. // vl1 = load // vl2 = phi // vr1 = load // vr2 = vr2 // = vl1 x vr1 // = vl2 x vr2 // If we just sorted according to opcode we would leave the first line in // tact but we would swap vl2 with vr2 because opcode(phi) > opcode(load). // = vl1 x vr1 // = vr2 x vl2 // Because vr2 and vr1 are from the same load we loose the opportunity of a // broadcast for the packed right side in the backend: we have [vr1, vl2] // instead of [vr1, vr2=vr1]. if (I0 && I1) { if(!i && I0->getOpcode() > I1->getOpcode()) { Left.push_back(I1); Right.push_back(I0); } else if (i && I0->getOpcode() > I1->getOpcode() && Right[i-1] != I1) { // Try not to destroy a broad cast for no apparent benefit. Left.push_back(I1); Right.push_back(I0); } else if (i && I0->getOpcode() == I1->getOpcode() && Right[i-1] == I0) { // Try preserve broadcasts. Left.push_back(I1); Right.push_back(I0); } else if (i && I0->getOpcode() == I1->getOpcode() && Left[i-1] == I1) { // Try preserve broadcasts. Left.push_back(I1); Right.push_back(I0); } else { Left.push_back(I0); Right.push_back(I1); } continue; } // One opcode, put the instruction on the right. if (I0) { Left.push_back(V1); Right.push_back(I0); continue; } Left.push_back(V0); Right.push_back(V1); } bool LeftBroadcast = isSplat(Left); bool RightBroadcast = isSplat(Right); // Don't reorder if the operands where good to begin with. if (!(LeftBroadcast || RightBroadcast) && (AllSameOpcodeRight || AllSameOpcodeLeft)) { Left = OrigLeft; Right = OrigRight; } } /// Bottom Up SLP Vectorizer. class BoUpSLP { public: typedef SmallVector
ValueList; typedef SmallVector
InstrList; typedef SmallPtrSet
ValueSet; typedef SmallVector
StoreList; BoUpSLP(Function *Func, ScalarEvolution *Se, const DataLayout *Dl, TargetTransformInfo *Tti, TargetLibraryInfo *TLi, AliasAnalysis *Aa, LoopInfo *Li, DominatorTree *Dt) : F(Func), SE(Se), DL(Dl), TTI(Tti), TLI(TLi), AA(Aa), LI(Li), DT(Dt), Builder(Se->getContext()) {} /// \brief Vectorize the tree that starts with the elements in \p VL. /// Returns the vectorized root. Value *vectorizeTree(); /// \returns the vectorization cost of the subtree that starts at \p VL. /// A negative number means that this is profitable. int getTreeCost(); /// Construct a vectorizable tree that starts at \p Roots, ignoring users for /// the purpose of scheduling and extraction in the \p UserIgnoreLst. void buildTree(ArrayRef
Roots, ArrayRef
UserIgnoreLst = None); /// Clear the internal data structures that are created by 'buildTree'. void deleteTree() { VectorizableTree.clear(); ScalarToTreeEntry.clear(); MustGather.clear(); ExternalUses.clear(); MemBarrierIgnoreList.clear(); } /// \returns true if the memory operations A and B are consecutive. bool isConsecutiveAccess(Value *A, Value *B); /// \brief Perform LICM and CSE on the newly generated gather sequences. void optimizeGatherSequence(); private: struct TreeEntry; /// \returns the cost of the vectorizable entry. int getEntryCost(TreeEntry *E); /// This is the recursive part of buildTree. void buildTree_rec(ArrayRef
Roots, unsigned Depth); /// Vectorize a single entry in the tree. Value *vectorizeTree(TreeEntry *E); /// Vectorize a single entry in the tree, starting in \p VL. Value *vectorizeTree(ArrayRef
VL); /// \returns the pointer to the vectorized value if \p VL is already /// vectorized, or NULL. They may happen in cycles. Value *alreadyVectorized(ArrayRef
VL) const; /// \brief Take the pointer operand from the Load/Store instruction. /// \returns NULL if this is not a valid Load/Store instruction. static Value *getPointerOperand(Value *I); /// \brief Take the address space operand from the Load/Store instruction. /// \returns -1 if this is not a valid Load/Store instruction. static unsigned getAddressSpaceOperand(Value *I); /// \returns the scalarization cost for this type. Scalarization in this /// context means the creation of vectors from a group of scalars. int getGatherCost(Type *Ty); /// \returns the scalarization cost for this list of values. Assuming that /// this subtree gets vectorized, we may need to extract the values from the /// roots. This method calculates the cost of extracting the values. int getGatherCost(ArrayRef
VL); /// \returns the AA location that is being access by the instruction. AliasAnalysis::Location getLocation(Instruction *I); /// \brief Checks if it is possible to sink an instruction from /// \p Src to \p Dst. /// \returns the pointer to the barrier instruction if we can't sink. Value *getSinkBarrier(Instruction *Src, Instruction *Dst); /// \returns the index of the last instruction in the BB from \p VL. int getLastIndex(ArrayRef
VL); /// \returns the Instruction in the bundle \p VL. Instruction *getLastInstruction(ArrayRef
VL); /// \brief Set the Builder insert point to one after the last instruction in /// the bundle void setInsertPointAfterBundle(ArrayRef
VL); /// \returns a vector from a collection of scalars in \p VL. Value *Gather(ArrayRef
VL, VectorType *Ty); /// \returns whether the VectorizableTree is fully vectoriable and will /// be beneficial even the tree height is tiny. bool isFullyVectorizableTinyTree(); struct TreeEntry { TreeEntry() : Scalars(), VectorizedValue(nullptr), LastScalarIndex(0), NeedToGather(0) {} /// \returns true if the scalars in VL are equal to this entry. bool isSame(ArrayRef
VL) const { assert(VL.size() == Scalars.size() && "Invalid size"); return std::equal(VL.begin(), VL.end(), Scalars.begin()); } /// A vector of scalars. ValueList Scalars; /// The Scalars are vectorized into this value. It is initialized to Null. Value *VectorizedValue; /// The index in the basic block of the last scalar. int LastScalarIndex; /// Do we need to gather this sequence ? bool NeedToGather; }; /// Create a new VectorizableTree entry. TreeEntry *newTreeEntry(ArrayRef
VL, bool Vectorized) { VectorizableTree.push_back(TreeEntry()); int idx = VectorizableTree.size() - 1; TreeEntry *Last = &VectorizableTree[idx]; Last->Scalars.insert(Last->Scalars.begin(), VL.begin(), VL.end()); Last->NeedToGather = !Vectorized; if (Vectorized) { Last->LastScalarIndex = getLastIndex(VL); for (int i = 0, e = VL.size(); i != e; ++i) { assert(!ScalarToTreeEntry.count(VL[i]) && "Scalar already in tree!"); ScalarToTreeEntry[VL[i]] = idx; } } else { Last->LastScalarIndex = 0; MustGather.insert(VL.begin(), VL.end()); } return Last; } /// -- Vectorization State -- /// Holds all of the tree entries. std::vector
VectorizableTree; /// Maps a specific scalar to its tree entry. SmallDenseMap
ScalarToTreeEntry; /// A list of scalars that we found that we need to keep as scalars. ValueSet MustGather; /// This POD struct describes one external user in the vectorized tree. struct ExternalUser { ExternalUser (Value *S, llvm::User *U, int L) : Scalar(S), User(U), Lane(L){}; // Which scalar in our function. Value *Scalar; // Which user that uses the scalar. llvm::User *User; // Which lane does the scalar belong to. int Lane; }; typedef SmallVector
UserList; /// A list of values that need to extracted out of the tree. /// This list holds pairs of (Internal Scalar : External User). UserList ExternalUses; /// A list of instructions to ignore while sinking /// memory instructions. This map must be reset between runs of getCost. ValueSet MemBarrierIgnoreList; /// Holds all of the instructions that we gathered. SetVector
GatherSeq; /// A list of blocks that we are going to CSE. SetVector
CSEBlocks; /// Numbers instructions in different blocks. DenseMap
BlocksNumbers; /// \brief Get the corresponding instruction numbering list for a given /// BasicBlock. The list is allocated lazily. BlockNumbering &getBlockNumbering(BasicBlock *BB) { auto I = BlocksNumbers.insert(std::make_pair(BB, BlockNumbering(BB))); return I.first->second; } /// List of users to ignore during scheduling and that don't need extracting. ArrayRef
UserIgnoreList; // Analysis and block reference. Function *F; ScalarEvolution *SE; const DataLayout *DL; TargetTransformInfo *TTI; TargetLibraryInfo *TLI; AliasAnalysis *AA; LoopInfo *LI; DominatorTree *DT; /// Instruction builder to construct the vectorized tree. IRBuilder<> Builder; }; void BoUpSLP::buildTree(ArrayRef
Roots, ArrayRef
UserIgnoreLst) { deleteTree(); UserIgnoreList = UserIgnoreLst; if (!getSameType(Roots)) return; buildTree_rec(Roots, 0); // Collect the values that we need to extract from the tree. for (int EIdx = 0, EE = VectorizableTree.size(); EIdx < EE; ++EIdx) { TreeEntry *Entry = &VectorizableTree[EIdx]; // For each lane: for (int Lane = 0, LE = Entry->Scalars.size(); Lane != LE; ++Lane) { Value *Scalar = Entry->Scalars[Lane]; // No need to handle users of gathered values. if (Entry->NeedToGather) continue; for (User *U : Scalar->users()) { DEBUG(dbgs() << "SLP: Checking user:" << *U << ".\n"); // Skip in-tree scalars that become vectors. if (ScalarToTreeEntry.count(U)) { DEBUG(dbgs() << "SLP: \tInternal user will be removed:" << *U << ".\n"); int Idx = ScalarToTreeEntry[U]; (void) Idx; assert(!VectorizableTree[Idx].NeedToGather && "Bad state"); continue; } Instruction *UserInst = dyn_cast
(U); if (!UserInst) continue; // Ignore users in the user ignore list. if (std::find(UserIgnoreList.begin(), UserIgnoreList.end(), UserInst) != UserIgnoreList.end()) continue; DEBUG(dbgs() << "SLP: Need to extract:" << *U << " from lane " << Lane << " from " << *Scalar << ".\n"); ExternalUses.push_back(ExternalUser(Scalar, U, Lane)); } } } } void BoUpSLP::buildTree_rec(ArrayRef
VL, unsigned Depth) { bool SameTy = getSameType(VL); (void)SameTy; bool isAltShuffle = false; assert(SameTy && "Invalid types!"); if (Depth == RecursionMaxDepth) { DEBUG(dbgs() << "SLP: Gathering due to max recursion depth.\n"); newTreeEntry(VL, false); return; } // Don't handle vectors. if (VL[0]->getType()->isVectorTy()) { DEBUG(dbgs() << "SLP: Gathering due to vector type.\n"); newTreeEntry(VL, false); return; } if (StoreInst *SI = dyn_cast
(VL[0])) if (SI->getValueOperand()->getType()->isVectorTy()) { DEBUG(dbgs() << "SLP: Gathering due to store vector type.\n"); newTreeEntry(VL, false); return; } unsigned Opcode = getSameOpcode(VL); // Check that this shuffle vector refers to the alternate // sequence of opcodes. if (Opcode == Instruction::ShuffleVector) { Instruction *I0 = dyn_cast
(VL[0]); unsigned Op = I0->getOpcode(); if (Op != Instruction::ShuffleVector) isAltShuffle = true; } // If all of the operands are identical or constant we have a simple solution. if (allConstant(VL) || isSplat(VL) || !getSameBlock(VL) || !Opcode) { DEBUG(dbgs() << "SLP: Gathering due to C,S,B,O. \n"); newTreeEntry(VL, false); return; } // We now know that this is a vector of instructions of the same type from // the same block. // Check if this is a duplicate of another entry. if (ScalarToTreeEntry.count(VL[0])) { int Idx = ScalarToTreeEntry[VL[0]]; TreeEntry *E = &VectorizableTree[Idx]; for (unsigned i = 0, e = VL.size(); i != e; ++i) { DEBUG(dbgs() << "SLP: \tChecking bundle: " << *VL[i] << ".\n"); if (E->Scalars[i] != VL[i]) { DEBUG(dbgs() << "SLP: Gathering due to partial overlap.\n"); newTreeEntry(VL, false); return; } } DEBUG(dbgs() << "SLP: Perfect diamond merge at " << *VL[0] << ".\n"); return; } // Check that none of the instructions in the bundle are already in the tree. for (unsigned i = 0, e = VL.size(); i != e; ++i) { if (ScalarToTreeEntry.count(VL[i])) { DEBUG(dbgs() << "SLP: The instruction (" << *VL[i] << ") is already in tree.\n"); newTreeEntry(VL, false); return; } } // If any of the scalars appears in the table OR it is marked as a value that // needs to stat scalar then we need to gather the scalars. for (unsigned i = 0, e = VL.size(); i != e; ++i) { if (ScalarToTreeEntry.count(VL[i]) || MustGather.count(VL[i])) { DEBUG(dbgs() << "SLP: Gathering due to gathered scalar. \n"); newTreeEntry(VL, false); return; } } // Check that all of the users of the scalars that we want to vectorize are // schedulable. Instruction *VL0 = cast
(VL[0]); int MyLastIndex = getLastIndex(VL); BasicBlock *BB = cast
(VL0)->getParent(); for (unsigned i = 0, e = VL.size(); i != e; ++i) { Instruction *Scalar = cast
(VL[i]); DEBUG(dbgs() << "SLP: Checking users of " << *Scalar << ". \n"); for (User *U : Scalar->users()) { DEBUG(dbgs() << "SLP: \tUser " << *U << ". \n"); Instruction *UI = dyn_cast
(U); if (!UI) { DEBUG(dbgs() << "SLP: Gathering due unknown user. \n"); newTreeEntry(VL, false); return; } // We don't care if the user is in a different basic block. BasicBlock *UserBlock = UI->getParent(); if (UserBlock != BB) { DEBUG(dbgs() << "SLP: User from a different basic block " << *UI << ". \n"); continue; } // If this is a PHINode within this basic block then we can place the // extract wherever we want. if (isa
(*UI)) { DEBUG(dbgs() << "SLP: \tWe can schedule PHIs:" << *UI << ". \n"); continue; } // Check if this is a safe in-tree user. if (ScalarToTreeEntry.count(UI)) { int Idx = ScalarToTreeEntry[UI]; int VecLocation = VectorizableTree[Idx].LastScalarIndex; if (VecLocation <= MyLastIndex) { DEBUG(dbgs() << "SLP: Gathering due to unschedulable vector. \n"); newTreeEntry(VL, false); return; } DEBUG(dbgs() << "SLP: In-tree user (" << *UI << ") at #" << VecLocation << " vector value (" << *Scalar << ") at #" << MyLastIndex << ".\n"); continue; } // Ignore users in the user ignore list. if (std::find(UserIgnoreList.begin(), UserIgnoreList.end(), UI) != UserIgnoreList.end()) continue; // Make sure that we can schedule this unknown user. BlockNumbering &BN = getBlockNumbering(BB); int UserIndex = BN.getIndex(UI); if (UserIndex < MyLastIndex) { DEBUG(dbgs() << "SLP: Can't schedule extractelement for " << *UI << ". \n"); newTreeEntry(VL, false); return; } } } // Check that every instructions appears once in this bundle. for (unsigned i = 0, e = VL.size(); i < e; ++i) for (unsigned j = i+1; j < e; ++j) if (VL[i] == VL[j]) { DEBUG(dbgs() << "SLP: Scalar used twice in bundle.\n"); newTreeEntry(VL, false); return; } // Check that instructions in this bundle don't reference other instructions. // The runtime of this check is O(N * N-1 * uses(N)) and a typical N is 4. for (unsigned i = 0, e = VL.size(); i < e; ++i) { for (User *U : VL[i]->users()) { for (unsigned j = 0; j < e; ++j) { if (i != j && U == VL[j]) { DEBUG(dbgs() << "SLP: Intra-bundle dependencies!" << *U << ". \n"); newTreeEntry(VL, false); return; } } } } DEBUG(dbgs() << "SLP: We are able to schedule this bundle.\n"); // Check if it is safe to sink the loads or the stores. if (Opcode == Instruction::Load || Opcode == Instruction::Store) { Instruction *Last = getLastInstruction(VL); for (unsigned i = 0, e = VL.size(); i < e; ++i) { if (VL[i] == Last) continue; Value *Barrier = getSinkBarrier(cast
(VL[i]), Last); if (Barrier) { DEBUG(dbgs() << "SLP: Can't sink " << *VL[i] << "\n down to " << *Last << "\n because of " << *Barrier << ". Gathering.\n"); newTreeEntry(VL, false); return; } } } switch (Opcode) { case Instruction::PHI: { PHINode *PH = dyn_cast
(VL0); // Check for terminator values (e.g. invoke). for (unsigned j = 0; j < VL.size(); ++j) for (unsigned i = 0, e = PH->getNumIncomingValues(); i < e; ++i) { TerminatorInst *Term = dyn_cast
( cast
(VL[j])->getIncomingValueForBlock(PH->getIncomingBlock(i))); if (Term) { DEBUG(dbgs() << "SLP: Need to swizzle PHINodes (TerminatorInst use).\n"); newTreeEntry(VL, false); return; } } newTreeEntry(VL, true); DEBUG(dbgs() << "SLP: added a vector of PHINodes.\n"); for (unsigned i = 0, e = PH->getNumIncomingValues(); i < e; ++i) { ValueList Operands; // Prepare the operand vector. for (unsigned j = 0; j < VL.size(); ++j) Operands.push_back(cast
(VL[j])->getIncomingValueForBlock( PH->getIncomingBlock(i))); buildTree_rec(Operands, Depth + 1); } return; } case Instruction::ExtractElement: { bool Reuse = CanReuseExtract(VL); if (Reuse) { DEBUG(dbgs() << "SLP: Reusing extract sequence.\n"); } newTreeEntry(VL, Reuse); return; } case Instruction::Load: { // Check if the loads are consecutive or of we need to swizzle them. for (unsigned i = 0, e = VL.size() - 1; i < e; ++i) { LoadInst *L = cast
(VL[i]); if (!L->isSimple() || !isConsecutiveAccess(VL[i], VL[i + 1])) { newTreeEntry(VL, false); DEBUG(dbgs() << "SLP: Need to swizzle loads.\n"); return; } } newTreeEntry(VL, true); DEBUG(dbgs() << "SLP: added a vector of loads.\n"); return; } case Instruction::ZExt: case Instruction::SExt: case Instruction::FPToUI: case Instruction::FPToSI: case Instruction::FPExt: case Instruction::PtrToInt: case Instruction::IntToPtr: case Instruction::SIToFP: case Instruction::UIToFP: case Instruction::Trunc: case Instruction::FPTrunc: case Instruction::BitCast: { Type *SrcTy = VL0->getOperand(0)->getType(); for (unsigned i = 0; i < VL.size(); ++i) { Type *Ty = cast
(VL[i])->getOperand(0)->getType(); if (Ty != SrcTy || Ty->isAggregateType() || Ty->isVectorTy()) { newTreeEntry(VL, false); DEBUG(dbgs() << "SLP: Gathering casts with different src types.\n"); return; } } newTreeEntry(VL, true); DEBUG(dbgs() << "SLP: added a vector of casts.\n"); for (unsigned i = 0, e = VL0->getNumOperands(); i < e; ++i) { ValueList Operands; // Prepare the operand vector. for (unsigned j = 0; j < VL.size(); ++j) Operands.push_back(cast
(VL[j])->getOperand(i)); buildTree_rec(Operands, Depth+1); } return; } case Instruction::ICmp: case Instruction::FCmp: { // Check that all of the compares have the same predicate. CmpInst::Predicate P0 = dyn_cast
(VL0)->getPredicate(); Type *ComparedTy = cast
(VL[0])->getOperand(0)->getType(); for (unsigned i = 1, e = VL.size(); i < e; ++i) { CmpInst *Cmp = cast
(VL[i]); if (Cmp->getPredicate() != P0 || Cmp->getOperand(0)->getType() != ComparedTy) { newTreeEntry(VL, false); DEBUG(dbgs() << "SLP: Gathering cmp with different predicate.\n"); return; } } newTreeEntry(VL, true); DEBUG(dbgs() << "SLP: added a vector of compares.\n"); for (unsigned i = 0, e = VL0->getNumOperands(); i < e; ++i) { ValueList Operands; // Prepare the operand vector. for (unsigned j = 0; j < VL.size(); ++j) Operands.push_back(cast
(VL[j])->getOperand(i)); buildTree_rec(Operands, Depth+1); } return; } case Instruction::Select: case Instruction::Add: case Instruction::FAdd: case Instruction::Sub: case Instruction::FSub: case Instruction::Mul: case Instruction::FMul: case Instruction::UDiv: case Instruction::SDiv: case Instruction::FDiv: case Instruction::URem: case Instruction::SRem: case Instruction::FRem: case Instruction::Shl: case Instruction::LShr: case Instruction::AShr: case Instruction::And: case Instruction::Or: case Instruction::Xor: { newTreeEntry(VL, true); DEBUG(dbgs() << "SLP: added a vector of bin op.\n"); // Sort operands of the instructions so that each side is more likely to // have the same opcode. if (isa
(VL0) && VL0->isCommutative()) { ValueList Left, Right; reorderInputsAccordingToOpcode(VL, Left, Right); BasicBlock *LeftBB = getSameBlock(Left); BasicBlock *RightBB = getSameBlock(Right); // If we have common uses on separate paths in the tree make sure we // process the one with greater common depth first. // We can use block numbering to determine the subtree traversal as // earler user has to come in between the common use and the later user. if (LeftBB && RightBB && LeftBB == RightBB && getLastIndex(Right) > getLastIndex(Left)) { buildTree_rec(Right, Depth + 1); buildTree_rec(Left, Depth + 1); } else { buildTree_rec(Left, Depth + 1); buildTree_rec(Right, Depth + 1); } return; } for (unsigned i = 0, e = VL0->getNumOperands(); i < e; ++i) { ValueList Operands; // Prepare the operand vector. for (unsigned j = 0; j < VL.size(); ++j) Operands.push_back(cast
(VL[j])->getOperand(i)); buildTree_rec(Operands, Depth+1); } return; } case Instruction::GetElementPtr: { // We don't combine GEPs with complicated (nested) indexing. for (unsigned j = 0; j < VL.size(); ++j) { if (cast
(VL[j])->getNumOperands() != 2) { DEBUG(dbgs() << "SLP: not-vectorizable GEP (nested indexes).\n"); newTreeEntry(VL, false); return; } } // We can't combine several GEPs into one vector if they operate on // different types. Type *Ty0 = cast
(VL0)->getOperand(0)->getType(); for (unsigned j = 0; j < VL.size(); ++j) { Type *CurTy = cast
(VL[j])->getOperand(0)->getType(); if (Ty0 != CurTy) { DEBUG(dbgs() << "SLP: not-vectorizable GEP (different types).\n"); newTreeEntry(VL, false); return; } } // We don't combine GEPs with non-constant indexes. for (unsigned j = 0; j < VL.size(); ++j) { auto Op = cast
(VL[j])->getOperand(1); if (!isa
(Op)) { DEBUG( dbgs() << "SLP: not-vectorizable GEP (non-constant indexes).\n"); newTreeEntry(VL, false); return; } } newTreeEntry(VL, true); DEBUG(dbgs() << "SLP: added a vector of GEPs.\n"); for (unsigned i = 0, e = 2; i < e; ++i) { ValueList Operands; // Prepare the operand vector. for (unsigned j = 0; j < VL.size(); ++j) Operands.push_back(cast
(VL[j])->getOperand(i)); buildTree_rec(Operands, Depth + 1); } return; } case Instruction::Store: { // Check if the stores are consecutive or of we need to swizzle them. for (unsigned i = 0, e = VL.size() - 1; i < e; ++i) if (!isConsecutiveAccess(VL[i], VL[i + 1])) { newTreeEntry(VL, false); DEBUG(dbgs() << "SLP: Non-consecutive store.\n"); return; } newTreeEntry(VL, true); DEBUG(dbgs() << "SLP: added a vector of stores.\n"); ValueList Operands; for (unsigned j = 0; j < VL.size(); ++j) Operands.push_back(cast
(VL[j])->getOperand(0)); // We can ignore these values because we are sinking them down. MemBarrierIgnoreList.insert(VL.begin(), VL.end()); buildTree_rec(Operands, Depth + 1); return; } case Instruction::Call: { // Check if the calls are all to the same vectorizable intrinsic. CallInst *CI = cast
(VL[0]); // Check if this is an Intrinsic call or something that can be // represented by an intrinsic call Intrinsic::ID ID = getIntrinsicIDForCall(CI, TLI); if (!isTriviallyVectorizable(ID)) { newTreeEntry(VL, false); DEBUG(dbgs() << "SLP: Non-vectorizable call.\n"); return; } Function *Int = CI->getCalledFunction(); Value *A1I = nullptr; if (hasVectorInstrinsicScalarOpd(ID, 1)) A1I = CI->getArgOperand(1); for (unsigned i = 1, e = VL.size(); i != e; ++i) { CallInst *CI2 = dyn_cast
(VL[i]); if (!CI2 || CI2->getCalledFunction() != Int || getIntrinsicIDForCall(CI2, TLI) != ID) { newTreeEntry(VL, false); DEBUG(dbgs() << "SLP: mismatched calls:" << *CI << "!=" << *VL[i] << "\n"); return; } // ctlz,cttz and powi are special intrinsics whose second argument // should be same in order for them to be vectorized. if (hasVectorInstrinsicScalarOpd(ID, 1)) { Value *A1J = CI2->getArgOperand(1); if (A1I != A1J) { newTreeEntry(VL, false); DEBUG(dbgs() << "SLP: mismatched arguments in call:" << *CI << " argument "<< A1I<<"!=" << A1J << "\n"); return; } } } newTreeEntry(VL, true); for (unsigned i = 0, e = CI->getNumArgOperands(); i != e; ++i) { ValueList Operands; // Prepare the operand vector. for (unsigned j = 0; j < VL.size(); ++j) { CallInst *CI2 = dyn_cast
(VL[j]); Operands.push_back(CI2->getArgOperand(i)); } buildTree_rec(Operands, Depth + 1); } return; } case Instruction::ShuffleVector: { // If this is not an alternate sequence of opcode like add-sub // then do not vectorize this instruction. if (!isAltShuffle) { newTreeEntry(VL, false); DEBUG(dbgs() << "SLP: ShuffleVector are not vectorized.\n"); return; } newTreeEntry(VL, true); DEBUG(dbgs() << "SLP: added a ShuffleVector op.\n"); for (unsigned i = 0, e = VL0->getNumOperands(); i < e; ++i) { ValueList Operands; // Prepare the operand vector. for (unsigned j = 0; j < VL.size(); ++j) Operands.push_back(cast
(VL[j])->getOperand(i)); buildTree_rec(Operands, Depth + 1); } return; } default: newTreeEntry(VL, false); DEBUG(dbgs() << "SLP: Gathering unknown instruction.\n"); return; } } int BoUpSLP::getEntryCost(TreeEntry *E) { ArrayRef
VL = E->Scalars; Type *ScalarTy = VL[0]->getType(); if (StoreInst *SI = dyn_cast
(VL[0])) ScalarTy = SI->getValueOperand()->getType(); VectorType *VecTy = VectorType::get(ScalarTy, VL.size()); if (E->NeedToGather) { if (allConstant(VL)) return 0; if (isSplat(VL)) { return TTI->getShuffleCost(TargetTransformInfo::SK_Broadcast, VecTy, 0); } return getGatherCost(E->Scalars); } unsigned Opcode = getSameOpcode(VL); assert(Opcode && getSameType(VL) && getSameBlock(VL) && "Invalid VL"); Instruction *VL0 = cast
(VL[0]); switch (Opcode) { case Instruction::PHI: { return 0; } case Instruction::ExtractElement: { if (CanReuseExtract(VL)) { int DeadCost = 0; for (unsigned i = 0, e = VL.size(); i < e; ++i) { ExtractElementInst *E = cast
(VL[i]); if (E->hasOneUse()) // Take credit for instruction that will become dead. DeadCost += TTI->getVectorInstrCost(Instruction::ExtractElement, VecTy, i); } return -DeadCost; } return getGatherCost(VecTy); } case Instruction::ZExt: case Instruction::SExt: case Instruction::FPToUI: case Instruction::FPToSI: case Instruction::FPExt: case Instruction::PtrToInt: case Instruction::IntToPtr: case Instruction::SIToFP: case Instruction::UIToFP: case Instruction::Trunc: case Instruction::FPTrunc: case Instruction::BitCast: { Type *SrcTy = VL0->getOperand(0)->getType(); // Calculate the cost of this instruction. int ScalarCost = VL.size() * TTI->getCastInstrCost(VL0->getOpcode(), VL0->getType(), SrcTy); VectorType *SrcVecTy = VectorType::get(SrcTy, VL.size()); int VecCost = TTI->getCastInstrCost(VL0->getOpcode(), VecTy, SrcVecTy); return VecCost - ScalarCost; } case Instruction::FCmp: case Instruction::ICmp: case Instruction::Select: case Instruction::Add: case Instruction::FAdd: case Instruction::Sub: case Instruction::FSub: case Instruction::Mul: case Instruction::FMul: case Instruction::UDiv: case Instruction::SDiv: case Instruction::FDiv: case Instruction::URem: case Instruction::SRem: case Instruction::FRem: case Instruction::Shl: case Instruction::LShr: case Instruction::AShr: case Instruction::And: case Instruction::Or: case Instruction::Xor: { // Calculate the cost of this instruction. int ScalarCost = 0; int VecCost = 0; if (Opcode == Instruction::FCmp || Opcode == Instruction::ICmp || Opcode == Instruction::Select) { VectorType *MaskTy = VectorType::get(Builder.getInt1Ty(), VL.size()); ScalarCost = VecTy->getNumElements() * TTI->getCmpSelInstrCost(Opcode, ScalarTy, Builder.getInt1Ty()); VecCost = TTI->getCmpSelInstrCost(Opcode, VecTy, MaskTy); } else { // Certain instructions can be cheaper to vectorize if they have a // constant second vector operand. TargetTransformInfo::OperandValueKind Op1VK = TargetTransformInfo::OK_AnyValue; TargetTransformInfo::OperandValueKind Op2VK = TargetTransformInfo::OK_UniformConstantValue; // If all operands are exactly the same ConstantInt then set the // operand kind to OK_UniformConstantValue. // If instead not all operands are constants, then set the operand kind // to OK_AnyValue. If all operands are constants but not the same, // then set the operand kind to OK_NonUniformConstantValue. ConstantInt *CInt = nullptr; for (unsigned i = 0; i < VL.size(); ++i) { const Instruction *I = cast
(VL[i]); if (!isa
(I->getOperand(1))) { Op2VK = TargetTransformInfo::OK_AnyValue; break; } if (i == 0) { CInt = cast
(I->getOperand(1)); continue; } if (Op2VK == TargetTransformInfo::OK_UniformConstantValue && CInt != cast
(I->getOperand(1))) Op2VK = TargetTransformInfo::OK_NonUniformConstantValue; } ScalarCost = VecTy->getNumElements() * TTI->getArithmeticInstrCost(Opcode, ScalarTy, Op1VK, Op2VK); VecCost = TTI->getArithmeticInstrCost(Opcode, VecTy, Op1VK, Op2VK); } return VecCost - ScalarCost; } case Instruction::GetElementPtr: { TargetTransformInfo::OperandValueKind Op1VK = TargetTransformInfo::OK_AnyValue; TargetTransformInfo::OperandValueKind Op2VK = TargetTransformInfo::OK_UniformConstantValue; int ScalarCost = VecTy->getNumElements() * TTI->getArithmeticInstrCost(Instruction::Add, ScalarTy, Op1VK, Op2VK); int VecCost = TTI->getArithmeticInstrCost(Instruction::Add, VecTy, Op1VK, Op2VK); return VecCost - ScalarCost; } case Instruction::Load: { // Cost of wide load - cost of scalar loads. int ScalarLdCost = VecTy->getNumElements() * TTI->getMemoryOpCost(Instruction::Load, ScalarTy, 1, 0); int VecLdCost = TTI->getMemoryOpCost(Instruction::Load, VecTy, 1, 0); return VecLdCost - ScalarLdCost; } case Instruction::Store: { // We know that we can merge the stores. Calculate the cost. int ScalarStCost = VecTy->getNumElements() * TTI->getMemoryOpCost(Instruction::Store, ScalarTy, 1, 0); int VecStCost = TTI->getMemoryOpCost(Instruction::Store, VecTy, 1, 0); return VecStCost - ScalarStCost; } case Instruction::Call: { CallInst *CI = cast
(VL0); Intrinsic::ID ID = getIntrinsicIDForCall(CI, TLI); // Calculate the cost of the scalar and vector calls. SmallVector
ScalarTys, VecTys; for (unsigned op = 0, opc = CI->getNumArgOperands(); op!= opc; ++op) { ScalarTys.push_back(CI->getArgOperand(op)->getType()); VecTys.push_back(VectorType::get(CI->getArgOperand(op)->getType(), VecTy->getNumElements())); } int ScalarCallCost = VecTy->getNumElements() * TTI->getIntrinsicInstrCost(ID, ScalarTy, ScalarTys); int VecCallCost = TTI->getIntrinsicInstrCost(ID, VecTy, VecTys); DEBUG(dbgs() << "SLP: Call cost "<< VecCallCost - ScalarCallCost << " (" << VecCallCost << "-" << ScalarCallCost << ")" << " for " << *CI << "\n"); return VecCallCost - ScalarCallCost; } case Instruction::ShuffleVector: { TargetTransformInfo::OperandValueKind Op1VK = TargetTransformInfo::OK_AnyValue; TargetTransformInfo::OperandValueKind Op2VK = TargetTransformInfo::OK_AnyValue; int ScalarCost = 0; int VecCost = 0; for (unsigned i = 0; i < VL.size(); ++i) { Instruction *I = cast
(VL[i]); if (!I) break; ScalarCost += TTI->getArithmeticInstrCost(I->getOpcode(), ScalarTy, Op1VK, Op2VK); } // VecCost is equal to sum of the cost of creating 2 vectors // and the cost of creating shuffle. Instruction *I0 = cast
(VL[0]); VecCost = TTI->getArithmeticInstrCost(I0->getOpcode(), VecTy, Op1VK, Op2VK); Instruction *I1 = cast
(VL[1]); VecCost += TTI->getArithmeticInstrCost(I1->getOpcode(), VecTy, Op1VK, Op2VK); VecCost += TTI->getShuffleCost(TargetTransformInfo::SK_Alternate, VecTy, 0); return VecCost - ScalarCost; } default: llvm_unreachable("Unknown instruction"); } } bool BoUpSLP::isFullyVectorizableTinyTree() { DEBUG(dbgs() << "SLP: Check whether the tree with height " << VectorizableTree.size() << " is fully vectorizable .\n"); // We only handle trees of height 2. if (VectorizableTree.size() != 2) return false; // Handle splat stores. if (!VectorizableTree[0].NeedToGather && isSplat(VectorizableTree[1].Scalars)) return true; // Gathering cost would be too much for tiny trees. if (VectorizableTree[0].NeedToGather || VectorizableTree[1].NeedToGather) return false; return true; } int BoUpSLP::getTreeCost() { int Cost = 0; DEBUG(dbgs() << "SLP: Calculating cost for tree of size " << VectorizableTree.size() << ".\n"); // We only vectorize tiny trees if it is fully vectorizable. if (VectorizableTree.size() < 3 && !isFullyVectorizableTinyTree()) { if (!VectorizableTree.size()) { assert(!ExternalUses.size() && "We should not have any external users"); } return INT_MAX; } unsigned BundleWidth = VectorizableTree[0].Scalars.size(); for (unsigned i = 0, e = VectorizableTree.size(); i != e; ++i) { int C = getEntryCost(&VectorizableTree[i]); DEBUG(dbgs() << "SLP: Adding cost " << C << " for bundle that starts with " << *VectorizableTree[i].Scalars[0] << " .\n"); Cost += C; } SmallSet
ExtractCostCalculated; int ExtractCost = 0; for (UserList::iterator I = ExternalUses.begin(), E = ExternalUses.end(); I != E; ++I) { // We only add extract cost once for the same scalar. if (!ExtractCostCalculated.insert(I->Scalar)) continue; VectorType *VecTy = VectorType::get(I->Scalar->getType(), BundleWidth); ExtractCost += TTI->getVectorInstrCost(Instruction::ExtractElement, VecTy, I->Lane); } DEBUG(dbgs() << "SLP: Total Cost " << Cost + ExtractCost<< ".\n"); return Cost + ExtractCost; } int BoUpSLP::getGatherCost(Type *Ty) { int Cost = 0; for (unsigned i = 0, e = cast
(Ty)->getNumElements(); i < e; ++i) Cost += TTI->getVectorInstrCost(Instruction::InsertElement, Ty, i); return Cost; } int BoUpSLP::getGatherCost(ArrayRef
VL) { // Find the type of the operands in VL. Type *ScalarTy = VL[0]->getType(); if (StoreInst *SI = dyn_cast
(VL[0])) ScalarTy = SI->getValueOperand()->getType(); VectorType *VecTy = VectorType::get(ScalarTy, VL.size()); // Find the cost of inserting/extracting values from the vector. return getGatherCost(VecTy); } AliasAnalysis::Location BoUpSLP::getLocation(Instruction *I) { if (StoreInst *SI = dyn_cast
(I)) return AA->getLocation(SI); if (LoadInst *LI = dyn_cast
(I)) return AA->getLocation(LI); return AliasAnalysis::Location(); } Value *BoUpSLP::getPointerOperand(Value *I) { if (LoadInst *LI = dyn_cast
(I)) return LI->getPointerOperand(); if (StoreInst *SI = dyn_cast
(I)) return SI->getPointerOperand(); return nullptr; } unsigned BoUpSLP::getAddressSpaceOperand(Value *I) { if (LoadInst *L = dyn_cast
(I)) return L->getPointerAddressSpace(); if (StoreInst *S = dyn_cast
(I)) return S->getPointerAddressSpace(); return -1; } bool BoUpSLP::isConsecutiveAccess(Value *A, Value *B) { Value *PtrA = getPointerOperand(A); Value *PtrB = getPointerOperand(B); unsigned ASA = getAddressSpaceOperand(A); unsigned ASB = getAddressSpaceOperand(B); // Check that the address spaces match and that the pointers are valid. if (!PtrA || !PtrB || (ASA != ASB)) return false; // Make sure that A and B are different pointers of the same type. if (PtrA == PtrB || PtrA->getType() != PtrB->getType()) return false; unsigned PtrBitWidth = DL->getPointerSizeInBits(ASA); Type *Ty = cast
(PtrA->getType())->getElementType(); APInt Size(PtrBitWidth, DL->getTypeStoreSize(Ty)); APInt OffsetA(PtrBitWidth, 0), OffsetB(PtrBitWidth, 0); PtrA = PtrA->stripAndAccumulateInBoundsConstantOffsets(*DL, OffsetA); PtrB = PtrB->stripAndAccumulateInBoundsConstantOffsets(*DL, OffsetB); APInt OffsetDelta = OffsetB - OffsetA; // Check if they are based on the same pointer. That makes the offsets // sufficient. if (PtrA == PtrB) return OffsetDelta == Size; // Compute the necessary base pointer delta to have the necessary final delta // equal to the size. APInt BaseDelta = Size - OffsetDelta; // Otherwise compute the distance with SCEV between the base pointers. const SCEV *PtrSCEVA = SE->getSCEV(PtrA); const SCEV *PtrSCEVB = SE->getSCEV(PtrB); const SCEV *C = SE->getConstant(BaseDelta); const SCEV *X = SE->getAddExpr(PtrSCEVA, C); return X == PtrSCEVB; } Value *BoUpSLP::getSinkBarrier(Instruction *Src, Instruction *Dst) { assert(Src->getParent() == Dst->getParent() && "Not the same BB"); BasicBlock::iterator I = Src, E = Dst; /// Scan all of the instruction from SRC to DST and check if /// the source may alias. for (++I; I != E; ++I) { // Ignore store instructions that are marked as 'ignore'. if (MemBarrierIgnoreList.count(I)) continue; if (Src->mayWriteToMemory()) /* Write */ { if (!I->mayReadOrWriteMemory()) continue; } else /* Read */ { if (!I->mayWriteToMemory()) continue; } AliasAnalysis::Location A = getLocation(&*I); AliasAnalysis::Location B = getLocation(Src); if (!A.Ptr || !B.Ptr || AA->alias(A, B)) return I; } return nullptr; } int BoUpSLP::getLastIndex(ArrayRef
VL) { BasicBlock *BB = cast
(VL[0])->getParent(); assert(BB == getSameBlock(VL) && "Invalid block"); BlockNumbering &BN = getBlockNumbering(BB); int MaxIdx = BN.getIndex(BB->getFirstNonPHI()); for (unsigned i = 0, e = VL.size(); i < e; ++i) MaxIdx = std::max(MaxIdx, BN.getIndex(cast
(VL[i]))); return MaxIdx; } Instruction *BoUpSLP::getLastInstruction(ArrayRef
VL) { BasicBlock *BB = cast
(VL[0])->getParent(); assert(BB == getSameBlock(VL) && "Invalid block"); BlockNumbering &BN = getBlockNumbering(BB); int MaxIdx = BN.getIndex(cast
(VL[0])); for (unsigned i = 1, e = VL.size(); i < e; ++i) MaxIdx = std::max(MaxIdx, BN.getIndex(cast
(VL[i]))); Instruction *I = BN.getInstruction(MaxIdx); assert(I && "bad location"); return I; } void BoUpSLP::setInsertPointAfterBundle(ArrayRef
VL) { Instruction *VL0 = cast
(VL[0]); Instruction *LastInst = getLastInstruction(VL); BasicBlock::iterator NextInst = LastInst; ++NextInst; Builder.SetInsertPoint(VL0->getParent(), NextInst); Builder.SetCurrentDebugLocation(VL0->getDebugLoc()); } Value *BoUpSLP::Gather(ArrayRef
VL, VectorType *Ty) { Value *Vec = UndefValue::get(Ty); // Generate the 'InsertElement' instruction. for (unsigned i = 0; i < Ty->getNumElements(); ++i) { Vec = Builder.CreateInsertElement(Vec, VL[i], Builder.getInt32(i)); if (Instruction *Insrt = dyn_cast
(Vec)) { GatherSeq.insert(Insrt); CSEBlocks.insert(Insrt->getParent()); // Add to our 'need-to-extract' list. if (ScalarToTreeEntry.count(VL[i])) { int Idx = ScalarToTreeEntry[VL[i]]; TreeEntry *E = &VectorizableTree[Idx]; // Find which lane we need to extract. int FoundLane = -1; for (unsigned Lane = 0, LE = VL.size(); Lane != LE; ++Lane) { // Is this the lane of the scalar that we are looking for ? if (E->Scalars[Lane] == VL[i]) { FoundLane = Lane; break; } } assert(FoundLane >= 0 && "Could not find the correct lane"); ExternalUses.push_back(ExternalUser(VL[i], Insrt, FoundLane)); } } } return Vec; } Value *BoUpSLP::alreadyVectorized(ArrayRef
VL) const { SmallDenseMap
::const_iterator Entry = ScalarToTreeEntry.find(VL[0]); if (Entry != ScalarToTreeEntry.end()) { int Idx = Entry->second; const TreeEntry *En = &VectorizableTree[Idx]; if (En->isSame(VL) && En->VectorizedValue) return En->VectorizedValue; } return nullptr; } Value *BoUpSLP::vectorizeTree(ArrayRef
VL) { if (ScalarToTreeEntry.count(VL[0])) { int Idx = ScalarToTreeEntry[VL[0]]; TreeEntry *E = &VectorizableTree[Idx]; if (E->isSame(VL)) return vectorizeTree(E); } Type *ScalarTy = VL[0]->getType(); if (StoreInst *SI = dyn_cast
(VL[0])) ScalarTy = SI->getValueOperand()->getType(); VectorType *VecTy = VectorType::get(ScalarTy, VL.size()); return Gather(VL, VecTy); } Value *BoUpSLP::vectorizeTree(TreeEntry *E) { IRBuilder<>::InsertPointGuard Guard(Builder); if (E->VectorizedValue) { DEBUG(dbgs() << "SLP: Diamond merged for " << *E->Scalars[0] << ".\n"); return E->VectorizedValue; } Instruction *VL0 = cast
(E->Scalars[0]); Type *ScalarTy = VL0->getType(); if (StoreInst *SI = dyn_cast
(VL0)) ScalarTy = SI->getValueOperand()->getType(); VectorType *VecTy = VectorType::get(ScalarTy, E->Scalars.size()); if (E->NeedToGather) { setInsertPointAfterBundle(E->Scalars); return Gather(E->Scalars, VecTy); } unsigned Opcode = getSameOpcode(E->Scalars); switch (Opcode) { case Instruction::PHI: { PHINode *PH = dyn_cast
(VL0); Builder.SetInsertPoint(PH->getParent()->getFirstNonPHI()); Builder.SetCurrentDebugLocation(PH->getDebugLoc()); PHINode *NewPhi = Builder.CreatePHI(VecTy, PH->getNumIncomingValues()); E->VectorizedValue = NewPhi; // PHINodes may have multiple entries from the same block. We want to // visit every block once. SmallSet
VisitedBBs; for (unsigned i = 0, e = PH->getNumIncomingValues(); i < e; ++i) { ValueList Operands; BasicBlock *IBB = PH->getIncomingBlock(i); if (!VisitedBBs.insert(IBB)) { NewPhi->addIncoming(NewPhi->getIncomingValueForBlock(IBB), IBB); continue; } // Prepare the operand vector. for (unsigned j = 0; j < E->Scalars.size(); ++j) Operands.push_back(cast
(E->Scalars[j])-> getIncomingValueForBlock(IBB)); Builder.SetInsertPoint(IBB->getTerminator()); Builder.SetCurrentDebugLocation(PH->getDebugLoc()); Value *Vec = vectorizeTree(Operands); NewPhi->addIncoming(Vec, IBB); } assert(NewPhi->getNumIncomingValues() == PH->getNumIncomingValues() && "Invalid number of incoming values"); return NewPhi; } case Instruction::ExtractElement: { if (CanReuseExtract(E->Scalars)) { Value *V = VL0->getOperand(0); E->VectorizedValue = V; return V; } return Gather(E->Scalars, VecTy); } case Instruction::ZExt: case Instruction::SExt: case Instruction::FPToUI: case Instruction::FPToSI: case Instruction::FPExt: case Instruction::PtrToInt: case Instruction::IntToPtr: case Instruction::SIToFP: case Instruction::UIToFP: case Instruction::Trunc: case Instruction::FPTrunc: case Instruction::BitCast: { ValueList INVL; for (int i = 0, e = E->Scalars.size(); i < e; ++i) INVL.push_back(cast
(E->Scalars[i])->getOperand(0)); setInsertPointAfterBundle(E->Scalars); Value *InVec = vectorizeTree(INVL); if (Value *V = alreadyVectorized(E->Scalars)) return V; CastInst *CI = dyn_cast
(VL0); Value *V = Builder.CreateCast(CI->getOpcode(), InVec, VecTy); E->VectorizedValue = V; return V; } case Instruction::FCmp: case Instruction::ICmp: { ValueList LHSV, RHSV; for (int i = 0, e = E->Scalars.size(); i < e; ++i) { LHSV.push_back(cast
(E->Scalars[i])->getOperand(0)); RHSV.push_back(cast
(E->Scalars[i])->getOperand(1)); } setInsertPointAfterBundle(E->Scalars); Value *L = vectorizeTree(LHSV); Value *R = vectorizeTree(RHSV); if (Value *V = alreadyVectorized(E->Scalars)) return V; CmpInst::Predicate P0 = dyn_cast
(VL0)->getPredicate(); Value *V; if (Opcode == Instruction::FCmp) V = Builder.CreateFCmp(P0, L, R); else V = Builder.CreateICmp(P0, L, R); E->VectorizedValue = V; return V; } case Instruction::Select: { ValueList TrueVec, FalseVec, CondVec; for (int i = 0, e = E->Scalars.size(); i < e; ++i) { CondVec.push_back(cast
(E->Scalars[i])->getOperand(0)); TrueVec.push_back(cast
(E->Scalars[i])->getOperand(1)); FalseVec.push_back(cast
(E->Scalars[i])->getOperand(2)); } setInsertPointAfterBundle(E->Scalars); Value *Cond = vectorizeTree(CondVec); Value *True = vectorizeTree(TrueVec); Value *False = vectorizeTree(FalseVec); if (Value *V = alreadyVectorized(E->Scalars)) return V; Value *V = Builder.CreateSelect(Cond, True, False); E->VectorizedValue = V; return V; } case Instruction::Add: case Instruction::FAdd: case Instruction::Sub: case Instruction::FSub: case Instruction::Mul: case Instruction::FMul: case Instruction::UDiv: case Instruction::SDiv: case Instruction::FDiv: case Instruction::URem: case Instruction::SRem: case Instruction::FRem: case Instruction::Shl: case Instruction::LShr: case Instruction::AShr: case Instruction::And: case Instruction::Or: case Instruction::Xor: { ValueList LHSVL, RHSVL; if (isa
(VL0) && VL0->isCommutative()) reorderInputsAccordingToOpcode(E->Scalars, LHSVL, RHSVL); else for (int i = 0, e = E->Scalars.size(); i < e; ++i) { LHSVL.push_back(cast
(E->Scalars[i])->getOperand(0)); RHSVL.push_back(cast
(E->Scalars[i])->getOperand(1)); } setInsertPointAfterBundle(E->Scalars); Value *LHS = vectorizeTree(LHSVL); Value *RHS = vectorizeTree(RHSVL); if (LHS == RHS && isa
(LHS)) { assert((VL0->getOperand(0) == VL0->getOperand(1)) && "Invalid order"); } if (Value *V = alreadyVectorized(E->Scalars)) return V; BinaryOperator *BinOp = cast
(VL0); Value *V = Builder.CreateBinOp(BinOp->getOpcode(), LHS, RHS); E->VectorizedValue = V; if (Instruction *I = dyn_cast
(V)) return propagateMetadata(I, E->Scalars); return V; } case Instruction::Load: { // Loads are inserted at the head of the tree because we don't want to // sink them all the way down past store instructions. setInsertPointAfterBundle(E->Scalars); LoadInst *LI = cast
(VL0); unsigned AS = LI->getPointerAddressSpace(); Value *VecPtr = Builder.CreateBitCast(LI->getPointerOperand(), VecTy->getPointerTo(AS)); unsigned Alignment = LI->getAlignment(); LI = Builder.CreateLoad(VecPtr); if (!Alignment) Alignment = DL->getABITypeAlignment(LI->getPointerOperand()->getType()); LI->setAlignment(Alignment); E->VectorizedValue = LI; return propagateMetadata(LI, E->Scalars); } case Instruction::Store: { StoreInst *SI = cast
(VL0); unsigned Alignment = SI->getAlignment(); unsigned AS = SI->getPointerAddressSpace(); ValueList ValueOp; for (int i = 0, e = E->Scalars.size(); i < e; ++i) ValueOp.push_back(cast
(E->Scalars[i])->getValueOperand()); setInsertPointAfterBundle(E->Scalars); Value *VecValue = vectorizeTree(ValueOp); Value *VecPtr = Builder.CreateBitCast(SI->getPointerOperand(), VecTy->getPointerTo(AS)); StoreInst *S = Builder.CreateStore(VecValue, VecPtr); if (!Alignment) Alignment = DL->getABITypeAlignment(SI->getPointerOperand()->getType()); S->setAlignment(Alignment); E->VectorizedValue = S; return propagateMetadata(S, E->Scalars); } case Instruction::GetElementPtr: { setInsertPointAfterBundle(E->Scalars); ValueList Op0VL; for (int i = 0, e = E->Scalars.size(); i < e; ++i) Op0VL.push_back(cast
(E->Scalars[i])->getOperand(0)); Value *Op0 = vectorizeTree(Op0VL); std::vector
OpVecs; for (int j = 1, e = cast
(VL0)->getNumOperands(); j < e; ++j) { ValueList OpVL; for (int i = 0, e = E->Scalars.size(); i < e; ++i) OpVL.push_back(cast
(E->Scalars[i])->getOperand(j)); Value *OpVec = vectorizeTree(OpVL); OpVecs.push_back(OpVec); } Value *V = Builder.CreateGEP(Op0, OpVecs); E->VectorizedValue = V; if (Instruction *I = dyn_cast
(V)) return propagateMetadata(I, E->Scalars); return V; } case Instruction::Call: { CallInst *CI = cast
(VL0); setInsertPointAfterBundle(E->Scalars); Function *FI; Intrinsic::ID IID = Intrinsic::not_intrinsic; if (CI && (FI = CI->getCalledFunction())) { IID = (Intrinsic::ID) FI->getIntrinsicID(); } std::vector
OpVecs; for (int j = 0, e = CI->getNumArgOperands(); j < e; ++j) { ValueList OpVL; // ctlz,cttz and powi are special intrinsics whose second argument is // a scalar. This argument should not be vectorized. if (hasVectorInstrinsicScalarOpd(IID, 1) && j == 1) { CallInst *CEI = cast
(E->Scalars[0]); OpVecs.push_back(CEI->getArgOperand(j)); continue; } for (int i = 0, e = E->Scalars.size(); i < e; ++i) { CallInst *CEI = cast
(E->Scalars[i]); OpVL.push_back(CEI->getArgOperand(j)); } Value *OpVec = vectorizeTree(OpVL); DEBUG(dbgs() << "SLP: OpVec[" << j << "]: " << *OpVec << "\n"); OpVecs.push_back(OpVec); } Module *M = F->getParent(); Intrinsic::ID ID = getIntrinsicIDForCall(CI, TLI); Type *Tys[] = { VectorType::get(CI->getType(), E->Scalars.size()) }; Function *CF = Intrinsic::getDeclaration(M, ID, Tys); Value *V = Builder.CreateCall(CF, OpVecs); E->VectorizedValue = V; return V; } case Instruction::ShuffleVector: { ValueList LHSVL, RHSVL; for (int i = 0, e = E->Scalars.size(); i < e; ++i) { LHSVL.push_back(cast
(E->Scalars[i])->getOperand(0)); RHSVL.push_back(cast
(E->Scalars[i])->getOperand(1)); } setInsertPointAfterBundle(E->Scalars); Value *LHS = vectorizeTree(LHSVL); Value *RHS = vectorizeTree(RHSVL); if (Value *V = alreadyVectorized(E->Scalars)) return V; // Create a vector of LHS op1 RHS BinaryOperator *BinOp0 = cast
(VL0); Value *V0 = Builder.CreateBinOp(BinOp0->getOpcode(), LHS, RHS); // Create a vector of LHS op2 RHS Instruction *VL1 = cast
(E->Scalars[1]); BinaryOperator *BinOp1 = cast
(VL1); Value *V1 = Builder.CreateBinOp(BinOp1->getOpcode(), LHS, RHS); // Create appropriate shuffle to take alternative operations from // the vector. std::vector
Mask(E->Scalars.size()); unsigned e = E->Scalars.size(); for (unsigned i = 0; i < e; ++i) { if (i & 1) Mask[i] = Builder.getInt32(e + i); else Mask[i] = Builder.getInt32(i); } Value *ShuffleMask = ConstantVector::get(Mask); Value *V = Builder.CreateShuffleVector(V0, V1, ShuffleMask); E->VectorizedValue = V; if (Instruction *I = dyn_cast
(V)) return propagateMetadata(I, E->Scalars); return V; } default: llvm_unreachable("unknown inst"); } return nullptr; } Value *BoUpSLP::vectorizeTree() { Builder.SetInsertPoint(F->getEntryBlock().begin()); vectorizeTree(&VectorizableTree[0]); DEBUG(dbgs() << "SLP: Extracting " << ExternalUses.size() << " values .\n"); // Extract all of the elements with the external uses. for (UserList::iterator it = ExternalUses.begin(), e = ExternalUses.end(); it != e; ++it) { Value *Scalar = it->Scalar; llvm::User *User = it->User; // Skip users that we already RAUW. This happens when one instruction // has multiple uses of the same value. if (std::find(Scalar->user_begin(), Scalar->user_end(), User) == Scalar->user_end()) continue; assert(ScalarToTreeEntry.count(Scalar) && "Invalid scalar"); int Idx = ScalarToTreeEntry[Scalar]; TreeEntry *E = &VectorizableTree[Idx]; assert(!E->NeedToGather && "Extracting from a gather list"); Value *Vec = E->VectorizedValue; assert(Vec && "Can't find vectorizable value"); Value *Lane = Builder.getInt32(it->Lane); // Generate extracts for out-of-tree users. // Find the insertion point for the extractelement lane. if (isa
(Vec)){ if (PHINode *PH = dyn_cast
(User)) { for (int i = 0, e = PH->getNumIncomingValues(); i != e; ++i) { if (PH->getIncomingValue(i) == Scalar) { Builder.SetInsertPoint(PH->getIncomingBlock(i)->getTerminator()); Value *Ex = Builder.CreateExtractElement(Vec, Lane); CSEBlocks.insert(PH->getIncomingBlock(i)); PH->setOperand(i, Ex); } } } else { Builder.SetInsertPoint(cast
(User)); Value *Ex = Builder.CreateExtractElement(Vec, Lane); CSEBlocks.insert(cast
(User)->getParent()); User->replaceUsesOfWith(Scalar, Ex); } } else { Builder.SetInsertPoint(F->getEntryBlock().begin()); Value *Ex = Builder.CreateExtractElement(Vec, Lane); CSEBlocks.insert(&F->getEntryBlock()); User->replaceUsesOfWith(Scalar, Ex); } DEBUG(dbgs() << "SLP: Replaced:" << *User << ".\n"); } // For each vectorized value: for (int EIdx = 0, EE = VectorizableTree.size(); EIdx < EE; ++EIdx) { TreeEntry *Entry = &VectorizableTree[EIdx]; // For each lane: for (int Lane = 0, LE = Entry->Scalars.size(); Lane != LE; ++Lane) { Value *Scalar = Entry->Scalars[Lane]; // No need to handle users of gathered values. if (Entry->NeedToGather) continue; assert(Entry->VectorizedValue && "Can't find vectorizable value"); Type *Ty = Scalar->getType(); if (!Ty->isVoidTy()) { #ifndef NDEBUG for (User *U : Scalar->users()) { DEBUG(dbgs() << "SLP: \tvalidating user:" << *U << ".\n"); assert((ScalarToTreeEntry.count(U) || // It is legal to replace users in the ignorelist by undef. (std::find(UserIgnoreList.begin(), UserIgnoreList.end(), U) != UserIgnoreList.end())) && "Replacing out-of-tree value with undef"); } #endif Value *Undef = UndefValue::get(Ty); Scalar->replaceAllUsesWith(Undef); } DEBUG(dbgs() << "SLP: \tErasing scalar:" << *Scalar << ".\n"); cast
(Scalar)->eraseFromParent(); } } for (auto &BN : BlocksNumbers) BN.second.forget(); Builder.ClearInsertionPoint(); return VectorizableTree[0].VectorizedValue; } void BoUpSLP::optimizeGatherSequence() { DEBUG(dbgs() << "SLP: Optimizing " << GatherSeq.size() << " gather sequences instructions.\n"); // LICM InsertElementInst sequences. for (SetVector
::iterator it = GatherSeq.begin(), e = GatherSeq.end(); it != e; ++it) { InsertElementInst *Insert = dyn_cast
(*it); if (!Insert) continue; // Check if this block is inside a loop. Loop *L = LI->getLoopFor(Insert->getParent()); if (!L) continue; // Check if it has a preheader. BasicBlock *PreHeader = L->getLoopPreheader(); if (!PreHeader) continue; // If the vector or the element that we insert into it are // instructions that are defined in this basic block then we can't // hoist this instruction. Instruction *CurrVec = dyn_cast
(Insert->getOperand(0)); Instruction *NewElem = dyn_cast
(Insert->getOperand(1)); if (CurrVec && L->contains(CurrVec)) continue; if (NewElem && L->contains(NewElem)) continue; // We can hoist this instruction. Move it to the pre-header. Insert->moveBefore(PreHeader->getTerminator()); } // Make a list of all reachable blocks in our CSE queue. SmallVector
CSEWorkList; CSEWorkList.reserve(CSEBlocks.size()); for (BasicBlock *BB : CSEBlocks) if (DomTreeNode *N = DT->getNode(BB)) { assert(DT->isReachableFromEntry(N)); CSEWorkList.push_back(N); } // Sort blocks by domination. This ensures we visit a block after all blocks // dominating it are visited. std::stable_sort(CSEWorkList.begin(), CSEWorkList.end(), [this](const DomTreeNode *A, const DomTreeNode *B) { return DT->properlyDominates(A, B); }); // Perform O(N^2) search over the gather sequences and merge identical // instructions. TODO: We can further optimize this scan if we split the // instructions into different buckets based on the insert lane. SmallVector
Visited; for (auto I = CSEWorkList.begin(), E = CSEWorkList.end(); I != E; ++I) { assert((I == CSEWorkList.begin() || !DT->dominates(*I, *std::prev(I))) && "Worklist not sorted properly!"); BasicBlock *BB = (*I)->getBlock(); // For all instructions in blocks containing gather sequences: for (BasicBlock::iterator it = BB->begin(), e = BB->end(); it != e;) { Instruction *In = it++; if (!isa
(In) && !isa
(In)) continue; // Check if we can replace this instruction with any of the // visited instructions. for (SmallVectorImpl
::iterator v = Visited.begin(), ve = Visited.end(); v != ve; ++v) { if (In->isIdenticalTo(*v) && DT->dominates((*v)->getParent(), In->getParent())) { In->replaceAllUsesWith(*v); In->eraseFromParent(); In = nullptr; break; } } if (In) { assert(std::find(Visited.begin(), Visited.end(), In) == Visited.end()); Visited.push_back(In); } } } CSEBlocks.clear(); GatherSeq.clear(); } /// The SLPVectorizer Pass. struct SLPVectorizer : public FunctionPass { typedef SmallVector
StoreList; typedef MapVector
StoreListMap; /// Pass identification, replacement for typeid static char ID; explicit SLPVectorizer() : FunctionPass(ID) { initializeSLPVectorizerPass(*PassRegistry::getPassRegistry()); } ScalarEvolution *SE; const DataLayout *DL; TargetTransformInfo *TTI; TargetLibraryInfo *TLI; AliasAnalysis *AA; LoopInfo *LI; DominatorTree *DT; bool runOnFunction(Function &F) override { if (skipOptnoneFunction(F)) return false; SE = &getAnalysis
(); DataLayoutPass *DLP = getAnalysisIfAvailable
(); DL = DLP ? &DLP->getDataLayout() : nullptr; TTI = &getAnalysis
(); TLI = getAnalysisIfAvailable
(); AA = &getAnalysis
(); LI = &getAnalysis
(); DT = &getAnalysis
().getDomTree(); StoreRefs.clear(); bool Changed = false; // If the target claims to have no vector registers don't attempt // vectorization. if (!TTI->getNumberOfRegisters(true)) return false; // Must have DataLayout. We can't require it because some tests run w/o // triple. if (!DL) return false; // Don't vectorize when the attribute NoImplicitFloat is used. if (F.hasFnAttribute(Attribute::NoImplicitFloat)) return false; DEBUG(dbgs() << "SLP: Analyzing blocks in " << F.getName() << ".\n"); // Use the bottom up slp vectorizer to construct chains that start with // store instructions. BoUpSLP R(&F, SE, DL, TTI, TLI, AA, LI, DT); // Scan the blocks in the function in post order. for (po_iterator
it = po_begin(&F.getEntryBlock()), e = po_end(&F.getEntryBlock()); it != e; ++it) { BasicBlock *BB = *it; // Vectorize trees that end at stores. if (unsigned count = collectStores(BB, R)) { (void)count; DEBUG(dbgs() << "SLP: Found " << count << " stores to vectorize.\n"); Changed |= vectorizeStoreChains(R); } // Vectorize trees that end at reductions. Changed |= vectorizeChainsInBlock(BB, R); } if (Changed) { R.optimizeGatherSequence(); DEBUG(dbgs() << "SLP: vectorized \"" << F.getName() << "\"\n"); DEBUG(verifyFunction(F)); } return Changed; } void getAnalysisUsage(AnalysisUsage &AU) const override { FunctionPass::getAnalysisUsage(AU); AU.addRequired
(); AU.addRequired
(); AU.addRequired
(); AU.addRequired
(); AU.addRequired
(); AU.addPreserved
(); AU.addPreserved
(); AU.setPreservesCFG(); } private: /// \brief Collect memory references and sort them according to their base /// object. We sort the stores to their base objects to reduce the cost of the /// quadratic search on the stores. TODO: We can further reduce this cost /// if we flush the chain creation every time we run into a memory barrier. unsigned collectStores(BasicBlock *BB, BoUpSLP &R); /// \brief Try to vectorize a chain that starts at two arithmetic instrs. bool tryToVectorizePair(Value *A, Value *B, BoUpSLP &R); /// \brief Try to vectorize a list of operands. /// \@param BuildVector A list of users to ignore for the purpose of /// scheduling and that don't need extracting. /// \returns true if a value was vectorized. bool tryToVectorizeList(ArrayRef
VL, BoUpSLP &R, ArrayRef
BuildVector = None); /// \brief Try to vectorize a chain that may start at the operands of \V; bool tryToVectorize(BinaryOperator *V, BoUpSLP &R); /// \brief Vectorize the stores that were collected in StoreRefs. bool vectorizeStoreChains(BoUpSLP &R); /// \brief Scan the basic block and look for patterns that are likely to start /// a vectorization chain. bool vectorizeChainsInBlock(BasicBlock *BB, BoUpSLP &R); bool vectorizeStoreChain(ArrayRef
Chain, int CostThreshold, BoUpSLP &R); bool vectorizeStores(ArrayRef
Stores, int costThreshold, BoUpSLP &R); private: StoreListMap StoreRefs; }; /// \brief Check that the Values in the slice in VL array are still existent in /// the WeakVH array. /// Vectorization of part of the VL array may cause later values in the VL array /// to become invalid. We track when this has happened in the WeakVH array. static bool hasValueBeenRAUWed(ArrayRef
&VL, SmallVectorImpl
&VH, unsigned SliceBegin, unsigned SliceSize) { for (unsigned i = SliceBegin; i < SliceBegin + SliceSize; ++i) if (VH[i] != VL[i]) return true; return false; } bool SLPVectorizer::vectorizeStoreChain(ArrayRef
Chain, int CostThreshold, BoUpSLP &R) { unsigned ChainLen = Chain.size(); DEBUG(dbgs() << "SLP: Analyzing a store chain of length " << ChainLen << "\n"); Type *StoreTy = cast
(Chain[0])->getValueOperand()->getType(); unsigned Sz = DL->getTypeSizeInBits(StoreTy); unsigned VF = MinVecRegSize / Sz; if (!isPowerOf2_32(Sz) || VF < 2) return false; // Keep track of values that were deleted by vectorizing in the loop below. SmallVector
TrackValues(Chain.begin(), Chain.end()); bool Changed = false; // Look for profitable vectorizable trees at all offsets, starting at zero. for (unsigned i = 0, e = ChainLen; i < e; ++i) { if (i + VF > e) break; // Check that a previous iteration of this loop did not delete the Value. if (hasValueBeenRAUWed(Chain, TrackValues, i, VF)) continue; DEBUG(dbgs() << "SLP: Analyzing " << VF << " stores at offset " << i << "\n"); ArrayRef
Operands = Chain.slice(i, VF); R.buildTree(Operands); int Cost = R.getTreeCost(); DEBUG(dbgs() << "SLP: Found cost=" << Cost << " for VF=" << VF << "\n"); if (Cost < CostThreshold) { DEBUG(dbgs() << "SLP: Decided to vectorize cost=" << Cost << "\n"); R.vectorizeTree(); // Move to the next bundle. i += VF - 1; Changed = true; } } return Changed; } bool SLPVectorizer::vectorizeStores(ArrayRef
Stores, int costThreshold, BoUpSLP &R) { SetVector
Heads, Tails; SmallDenseMap
ConsecutiveChain; // We may run into multiple chains that merge into a single chain. We mark the // stores that we vectorized so that we don't visit the same store twice. BoUpSLP::ValueSet VectorizedStores; bool Changed = false; // Do a quadratic search on all of the given stores and find // all of the pairs of stores that follow each other. for (unsigned i = 0, e = Stores.size(); i < e; ++i) { for (unsigned j = 0; j < e; ++j) { if (i == j) continue; if (R.isConsecutiveAccess(Stores[i], Stores[j])) { Tails.insert(Stores[j]); Heads.insert(Stores[i]); ConsecutiveChain[Stores[i]] = Stores[j]; } } } // For stores that start but don't end a link in the chain: for (SetVector
::iterator it = Heads.begin(), e = Heads.end(); it != e; ++it) { if (Tails.count(*it)) continue; // We found a store instr that starts a chain. Now follow the chain and try // to vectorize it. BoUpSLP::ValueList Operands; Value *I = *it; // Collect the chain into a list. while (Tails.count(I) || Heads.count(I)) { if (VectorizedStores.count(I)) break; Operands.push_back(I); // Move to the next value in the chain. I = ConsecutiveChain[I]; } bool Vectorized = vectorizeStoreChain(Operands, costThreshold, R); // Mark the vectorized stores so that we don't vectorize them again. if (Vectorized) VectorizedStores.insert(Operands.begin(), Operands.end()); Changed |= Vectorized; } return Changed; } unsigned SLPVectorizer::collectStores(BasicBlock *BB, BoUpSLP &R) { unsigned count = 0; StoreRefs.clear(); for (BasicBlock::iterator it = BB->begin(), e = BB->end(); it != e; ++it) { StoreInst *SI = dyn_cast
(it); if (!SI) continue; // Don't touch volatile stores. if (!SI->isSimple()) continue; // Check that the pointer points to scalars. Type *Ty = SI->getValueOperand()->getType(); if (Ty->isAggregateType() || Ty->isVectorTy()) continue; // Find the base pointer. Value *Ptr = GetUnderlyingObject(SI->getPointerOperand(), DL); // Save the store locations. StoreRefs[Ptr].push_back(SI); count++; } return count; } bool SLPVectorizer::tryToVectorizePair(Value *A, Value *B, BoUpSLP &R) { if (!A || !B) return false; Value *VL[] = { A, B }; return tryToVectorizeList(VL, R); } bool SLPVectorizer::tryToVectorizeList(ArrayRef
VL, BoUpSLP &R, ArrayRef
BuildVector) { if (VL.size() < 2) return false; DEBUG(dbgs() << "SLP: Vectorizing a list of length = " << VL.size() << ".\n"); // Check that all of the parts are scalar instructions of the same type. Instruction *I0 = dyn_cast
(VL[0]); if (!I0) return false; unsigned Opcode0 = I0->getOpcode(); Type *Ty0 = I0->getType(); unsigned Sz = DL->getTypeSizeInBits(Ty0); unsigned VF = MinVecRegSize / Sz; for (int i = 0, e = VL.size(); i < e; ++i) { Type *Ty = VL[i]->getType(); if (Ty->isAggregateType() || Ty->isVectorTy()) return false; Instruction *Inst = dyn_cast
(VL[i]); if (!Inst || Inst->getOpcode() != Opcode0) return false; } bool Changed = false; // Keep track of values that were deleted by vectorizing in the loop below. SmallVector
TrackValues(VL.begin(), VL.end()); for (unsigned i = 0, e = VL.size(); i < e; ++i) { unsigned OpsWidth = 0; if (i + VF > e) OpsWidth = e - i; else OpsWidth = VF; if (!isPowerOf2_32(OpsWidth) || OpsWidth < 2) break; // Check that a previous iteration of this loop did not delete the Value. if (hasValueBeenRAUWed(VL, TrackValues, i, OpsWidth)) continue; DEBUG(dbgs() << "SLP: Analyzing " << OpsWidth << " operations " << "\n"); ArrayRef
Ops = VL.slice(i, OpsWidth); ArrayRef
BuildVectorSlice; if (!BuildVector.empty()) BuildVectorSlice = BuildVector.slice(i, OpsWidth); R.buildTree(Ops, BuildVectorSlice); int Cost = R.getTreeCost(); if (Cost < -SLPCostThreshold) { DEBUG(dbgs() << "SLP: Vectorizing list at cost:" << Cost << ".\n"); Value *VectorizedRoot = R.vectorizeTree(); // Reconstruct the build vector by extracting the vectorized root. This // way we handle the case where some elements of the vector are undefined. // (return (inserelt <4 xi32> (insertelt undef (opd0) 0) (opd1) 2)) if (!BuildVectorSlice.empty()) { // The insert point is the last build vector instruction. The vectorized // root will precede it. This guarantees that we get an instruction. The // vectorized tree could have been constant folded. Instruction *InsertAfter = cast
(BuildVectorSlice.back()); unsigned VecIdx = 0; for (auto &V : BuildVectorSlice) { IRBuilder
Builder( ++BasicBlock::iterator(InsertAfter)); InsertElementInst *IE = cast
(V); Instruction *Extract = cast
(Builder.CreateExtractElement( VectorizedRoot, Builder.getInt32(VecIdx++))); IE->setOperand(1, Extract); IE->removeFromParent(); IE->insertAfter(Extract); InsertAfter = IE; } } // Move to the next bundle. i += VF - 1; Changed = true; } } return Changed; } bool SLPVectorizer::tryToVectorize(BinaryOperator *V, BoUpSLP &R) { if (!V) return false; // Try to vectorize V. if (tryToVectorizePair(V->getOperand(0), V->getOperand(1), R)) return true; BinaryOperator *A = dyn_cast
(V->getOperand(0)); BinaryOperator *B = dyn_cast
(V->getOperand(1)); // Try to skip B. if (B && B->hasOneUse()) { BinaryOperator *B0 = dyn_cast
(B->getOperand(0)); BinaryOperator *B1 = dyn_cast
(B->getOperand(1)); if (tryToVectorizePair(A, B0, R)) { B->moveBefore(V); return true; } if (tryToVectorizePair(A, B1, R)) { B->moveBefore(V); return true; } } // Try to skip A. if (A && A->hasOneUse()) { BinaryOperator *A0 = dyn_cast
(A->getOperand(0)); BinaryOperator *A1 = dyn_cast
(A->getOperand(1)); if (tryToVectorizePair(A0, B, R)) { A->moveBefore(V); return true; } if (tryToVectorizePair(A1, B, R)) { A->moveBefore(V); return true; } } return 0; } /// \brief Generate a shuffle mask to be used in a reduction tree. /// /// \param VecLen The length of the vector to be reduced. /// \param NumEltsToRdx The number of elements that should be reduced in the /// vector. /// \param IsPairwise Whether the reduction is a pairwise or splitting /// reduction. A pairwise reduction will generate a mask of /// <0,2,...> or <1,3,..> while a splitting reduction will generate /// <2,3, undef,undef> for a vector of 4 and NumElts = 2. /// \param IsLeft True will generate a mask of even elements, odd otherwise. static Value *createRdxShuffleMask(unsigned VecLen, unsigned NumEltsToRdx, bool IsPairwise, bool IsLeft, IRBuilder<> &Builder) { assert((IsPairwise || !IsLeft) && "Don't support a <0,1,undef,...> mask"); SmallVector
ShuffleMask( VecLen, UndefValue::get(Builder.getInt32Ty())); if (IsPairwise) // Build a mask of 0, 2, ... (left) or 1, 3, ... (right). for (unsigned i = 0; i != NumEltsToRdx; ++i) ShuffleMask[i] = Builder.getInt32(2 * i + !IsLeft); else // Move the upper half of the vector to the lower half. for (unsigned i = 0; i != NumEltsToRdx; ++i) ShuffleMask[i] = Builder.getInt32(NumEltsToRdx + i); return ConstantVector::get(ShuffleMask); } /// Model horizontal reductions. /// /// A horizontal reduction is a tree of reduction operations (currently add and /// fadd) that has operations that can be put into a vector as its leaf. /// For example, this tree: /// /// mul mul mul mul /// \ / \ / /// + + /// \ / /// + /// This tree has "mul" as its reduced values and "+" as its reduction /// operations. A reduction might be feeding into a store or a binary operation /// feeding a phi. /// ... /// \ / /// + /// | /// phi += /// /// Or: /// ... /// \ / /// + /// | /// *p = /// class HorizontalReduction { SmallVector
ReductionOps; SmallVector
ReducedVals; BinaryOperator *ReductionRoot; PHINode *ReductionPHI; /// The opcode of the reduction. unsigned ReductionOpcode; /// The opcode of the values we perform a reduction on. unsigned ReducedValueOpcode; /// The width of one full horizontal reduction operation. unsigned ReduxWidth; /// Should we model this reduction as a pairwise reduction tree or a tree that /// splits the vector in halves and adds those halves. bool IsPairwiseReduction; public: HorizontalReduction() : ReductionRoot(nullptr), ReductionPHI(nullptr), ReductionOpcode(0), ReducedValueOpcode(0), ReduxWidth(0), IsPairwiseReduction(false) {} /// \brief Try to find a reduction tree. bool matchAssociativeReduction(PHINode *Phi, BinaryOperator *B, const DataLayout *DL) { assert((!Phi || std::find(Phi->op_begin(), Phi->op_end(), B) != Phi->op_end()) && "Thi phi needs to use the binary operator"); // We could have a initial reductions that is not an add. // r *= v1 + v2 + v3 + v4 // In such a case start looking for a tree rooted in the first '+'. if (Phi) { if (B->getOperand(0) == Phi) { Phi = nullptr; B = dyn_cast
(B->getOperand(1)); } else if (B->getOperand(1) == Phi) { Phi = nullptr; B = dyn_cast
(B->getOperand(0)); } } if (!B) return false; Type *Ty = B->getType(); if (Ty->isVectorTy()) return false; ReductionOpcode = B->getOpcode(); ReducedValueOpcode = 0; ReduxWidth = MinVecRegSize / DL->getTypeSizeInBits(Ty); ReductionRoot = B; ReductionPHI = Phi; if (ReduxWidth < 4) return false; // We currently only support adds. if (ReductionOpcode != Instruction::Add && ReductionOpcode != Instruction::FAdd) return false; // Post order traverse the reduction tree starting at B. We only handle true // trees containing only binary operators. SmallVector
, 32> Stack; Stack.push_back(std::make_pair(B, 0)); while (!Stack.empty()) { BinaryOperator *TreeN = Stack.back().first; unsigned EdgeToVist = Stack.back().second++; bool IsReducedValue = TreeN->getOpcode() != ReductionOpcode; // Only handle trees in the current basic block. if (TreeN->getParent() != B->getParent()) return false; // Each tree node needs to have one user except for the ultimate // reduction. if (!TreeN->hasOneUse() && TreeN != B) return false; // Postorder vist. if (EdgeToVist == 2 || IsReducedValue) { if (IsReducedValue) { // Make sure that the opcodes of the operations that we are going to // reduce match. if (!ReducedValueOpcode) ReducedValueOpcode = TreeN->getOpcode(); else if (ReducedValueOpcode != TreeN->getOpcode()) return false; ReducedVals.push_back(TreeN); } else { // We need to be able to reassociate the adds. if (!TreeN->isAssociative()) return false; ReductionOps.push_back(TreeN); } // Retract. Stack.pop_back(); continue; } // Visit left or right. Value *NextV = TreeN->getOperand(EdgeToVist); BinaryOperator *Next = dyn_cast
(NextV); if (Next) Stack.push_back(std::make_pair(Next, 0)); else if (NextV != Phi) return false; } return true; } /// \brief Attempt to vectorize the tree found by /// matchAssociativeReduction. bool tryToReduce(BoUpSLP &V, TargetTransformInfo *TTI) { if (ReducedVals.empty()) return false; unsigned NumReducedVals = ReducedVals.size(); if (NumReducedVals < ReduxWidth) return false; Value *VectorizedTree = nullptr; IRBuilder<> Builder(ReductionRoot); FastMathFlags Unsafe; Unsafe.setUnsafeAlgebra(); Builder.SetFastMathFlags(Unsafe); unsigned i = 0; for (; i < NumReducedVals - ReduxWidth + 1; i += ReduxWidth) { ArrayRef
ValsToReduce(&ReducedVals[i], ReduxWidth); V.buildTree(ValsToReduce, ReductionOps); // Estimate cost. int Cost = V.getTreeCost() + getReductionCost(TTI, ReducedVals[i]); if (Cost >= -SLPCostThreshold) break; DEBUG(dbgs() << "SLP: Vectorizing horizontal reduction at cost:" << Cost << ". (HorRdx)\n"); // Vectorize a tree. DebugLoc Loc = cast
(ReducedVals[i])->getDebugLoc(); Value *VectorizedRoot = V.vectorizeTree(); // Emit a reduction. Value *ReducedSubTree = emitReduction(VectorizedRoot, Builder); if (VectorizedTree) { Builder.SetCurrentDebugLocation(Loc); VectorizedTree = createBinOp(Builder, ReductionOpcode, VectorizedTree, ReducedSubTree, "bin.rdx"); } else VectorizedTree = ReducedSubTree; } if (VectorizedTree) { // Finish the reduction. for (; i < NumReducedVals; ++i) { Builder.SetCurrentDebugLocation( cast
(ReducedVals[i])->getDebugLoc()); VectorizedTree = createBinOp(Builder, ReductionOpcode, VectorizedTree, ReducedVals[i]); } // Update users. if (ReductionPHI) { assert(ReductionRoot && "Need a reduction operation"); ReductionRoot->setOperand(0, VectorizedTree); ReductionRoot->setOperand(1, ReductionPHI); } else ReductionRoot->replaceAllUsesWith(VectorizedTree); } return VectorizedTree != nullptr; } private: /// \brief Calcuate the cost of a reduction. int getReductionCost(TargetTransformInfo *TTI, Value *FirstReducedVal) { Type *ScalarTy = FirstReducedVal->getType(); Type *VecTy = VectorType::get(ScalarTy, ReduxWidth); int PairwiseRdxCost = TTI->getReductionCost(ReductionOpcode, VecTy, true); int SplittingRdxCost = TTI->getReductionCost(ReductionOpcode, VecTy, false); IsPairwiseReduction = PairwiseRdxCost < SplittingRdxCost; int VecReduxCost = IsPairwiseReduction ? PairwiseRdxCost : SplittingRdxCost; int ScalarReduxCost = ReduxWidth * TTI->getArithmeticInstrCost(ReductionOpcode, VecTy); DEBUG(dbgs() << "SLP: Adding cost " << VecReduxCost - ScalarReduxCost << " for reduction that starts with " << *FirstReducedVal << " (It is a " << (IsPairwiseReduction ? "pairwise" : "splitting") << " reduction)\n"); return VecReduxCost - ScalarReduxCost; } static Value *createBinOp(IRBuilder<> &Builder, unsigned Opcode, Value *L, Value *R, const Twine &Name = "") { if (Opcode == Instruction::FAdd) return Builder.CreateFAdd(L, R, Name); return Builder.CreateBinOp((Instruction::BinaryOps)Opcode, L, R, Name); } /// \brief Emit a horizontal reduction of the vectorized value. Value *emitReduction(Value *VectorizedValue, IRBuilder<> &Builder) { assert(VectorizedValue && "Need to have a vectorized tree node"); Instruction *ValToReduce = dyn_cast
(VectorizedValue); assert(isPowerOf2_32(ReduxWidth) && "We only handle power-of-two reductions for now"); Value *TmpVec = ValToReduce; for (unsigned i = ReduxWidth / 2; i != 0; i >>= 1) { if (IsPairwiseReduction) { Value *LeftMask = createRdxShuffleMask(ReduxWidth, i, true, true, Builder); Value *RightMask = createRdxShuffleMask(ReduxWidth, i, true, false, Builder); Value *LeftShuf = Builder.CreateShuffleVector( TmpVec, UndefValue::get(TmpVec->getType()), LeftMask, "rdx.shuf.l"); Value *RightShuf = Builder.CreateShuffleVector( TmpVec, UndefValue::get(TmpVec->getType()), (RightMask), "rdx.shuf.r"); TmpVec = createBinOp(Builder, ReductionOpcode, LeftShuf, RightShuf, "bin.rdx"); } else { Value *UpperHalf = createRdxShuffleMask(ReduxWidth, i, false, false, Builder); Value *Shuf = Builder.CreateShuffleVector( TmpVec, UndefValue::get(TmpVec->getType()), UpperHalf, "rdx.shuf"); TmpVec = createBinOp(Builder, ReductionOpcode, TmpVec, Shuf, "bin.rdx"); } } // The result is in the first element of the vector. return Builder.CreateExtractElement(TmpVec, Builder.getInt32(0)); } }; /// \brief Recognize construction of vectors like /// %ra = insertelement <4 x float> undef, float %s0, i32 0 /// %rb = insertelement <4 x float> %ra, float %s1, i32 1 /// %rc = insertelement <4 x float> %rb, float %s2, i32 2 /// %rd = insertelement <4 x float> %rc, float %s3, i32 3 /// /// Returns true if it matches /// static bool findBuildVector(InsertElementInst *FirstInsertElem, SmallVectorImpl
&BuildVector, SmallVectorImpl
&BuildVectorOpds) { if (!isa
(FirstInsertElem->getOperand(0))) return false; InsertElementInst *IE = FirstInsertElem; while (true) { BuildVector.push_back(IE); BuildVectorOpds.push_back(IE->getOperand(1)); if (IE->use_empty()) return false; InsertElementInst *NextUse = dyn_cast
(IE->user_back()); if (!NextUse) return true; // If this isn't the final use, make sure the next insertelement is the only // use. It's OK if the final constructed vector is used multiple times if (!IE->hasOneUse()) return false; IE = NextUse; } return false; } static bool PhiTypeSorterFunc(Value *V, Value *V2) { return V->getType() < V2->getType(); } bool SLPVectorizer::vectorizeChainsInBlock(BasicBlock *BB, BoUpSLP &R) { bool Changed = false; SmallVector
Incoming; SmallSet