HELLO·Android
系统源代码
IT资讯
技术文章
我的收藏
注册
登录
-
我收藏的文章
创建代码块
我的代码块
我的账号
Marshmallow
|
6.0.1_r16
下载
查看原文件
收藏
根目录
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/Optional.h" #include "llvm/ADT/PostOrderIterator.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/Statistic.h" #include "llvm/Analysis/AliasAnalysis.h" #include "llvm/Analysis/AssumptionCache.h" #include "llvm/Analysis/CodeMetrics.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
#include
using namespace llvm; #define SV_NAME "slp-vectorizer" #define DEBUG_TYPE "SLP" STATISTIC(NumVectorInstructions, "Number of vector instructions generated"); 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; // Limit the number of alias checks. The limit is chosen so that // it has no negative effect on the llvm benchmarks. static const unsigned AliasedCheckLimit = 10; // Another limit for the alias checks: The maximum distance between load/store // instructions where alias checks are done. // This limit is useful for very large basic blocks. static const unsigned MaxMemDepDistance = 160; /// \brief Predicate for the element types that the SLP vectorizer supports. /// /// The most important thing to filter here are types which are invalid in LLVM /// vectors. We also filter target specific types which have absolutely no /// meaningful vectorization path such as x86_fp80 and ppc_f128. This just /// avoids spending time checking the cost model and realizing that they will /// be inevitably scalarized. static bool isValidElementType(Type *Ty) { return VectorType::isValidElementType(Ty) && !Ty->isX86_FP80Ty() && !Ty->isPPC_FP128Ty(); } /// \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; } /// Get the intersection (logical and) of all of the potential IR flags /// of each scalar operation (VL) that will be converted into a vector (I). /// Flag set: NSW, NUW, exact, and all of fast-math. static void propagateIRFlags(Value *I, ArrayRef
VL) { if (auto *VecOp = dyn_cast
(I)) { if (auto *Intersection = dyn_cast
(VL[0])) { // Intersection is initialized to the 0th scalar, // so start counting from index '1'. for (int i = 1, e = VL.size(); i < e; ++i) { if (auto *Scalar = dyn_cast
(VL[i])) Intersection->andIRFlags(Scalar); } VecOp->copyIRFlags(Intersection); } } } /// \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_alias_scope: MD = MDNode::getMostGenericAliasScope(MD, IMD); break; case LLVMContext::MD_noalias: MD = MDNode::intersect(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; } /// \returns True if in-tree use also needs extract. This refers to /// possible scalar operand in vectorized instruction. static bool InTreeUserNeedToExtract(Value *Scalar, Instruction *UserInst, TargetLibraryInfo *TLI) { unsigned Opcode = UserInst->getOpcode(); switch (Opcode) { case Instruction::Load: { LoadInst *LI = cast
(UserInst); return (LI->getPointerOperand() == Scalar); } case Instruction::Store: { StoreInst *SI = cast
(UserInst); return (SI->getPointerOperand() == Scalar); } case Instruction::Call: { CallInst *CI = cast
(UserInst); Intrinsic::ID ID = getIntrinsicIDForCall(CI, TLI); if (hasVectorInstrinsicScalarOpd(ID, 1)) { return (CI->getArgOperand(1) == Scalar); } } default: return false; } } /// \returns the AA location that is being access by the instruction. static AliasAnalysis::Location getLocation(Instruction *I, AliasAnalysis *AA) { if (StoreInst *SI = dyn_cast
(I)) return AA->getLocation(SI); if (LoadInst *LI = dyn_cast
(I)) return AA->getLocation(LI); return AliasAnalysis::Location(); } /// \returns True if the instruction is not a volatile or atomic load/store. static bool isSimple(Instruction *I) { if (LoadInst *LI = dyn_cast
(I)) return LI->isSimple(); if (StoreInst *SI = dyn_cast
(I)) return SI->isSimple(); if (MemIntrinsic *MI = dyn_cast
(I)) return !MI->isVolatile(); return true; } /// Bottom Up SLP Vectorizer. class BoUpSLP { public: typedef SmallVector
ValueList; typedef SmallVector
InstrList; typedef SmallPtrSet
ValueSet; typedef SmallVector
StoreList; BoUpSLP(Function *Func, ScalarEvolution *Se, TargetTransformInfo *Tti, TargetLibraryInfo *TLi, AliasAnalysis *Aa, LoopInfo *Li, DominatorTree *Dt, AssumptionCache *AC) : NumLoadsWantToKeepOrder(0), NumLoadsWantToChangeOrder(0), F(Func), SE(Se), TTI(Tti), TLI(TLi), AA(Aa), LI(Li), DT(Dt), Builder(Se->getContext()) { CodeMetrics::collectEphemeralValues(F, AC, EphValues); } /// \brief Vectorize the tree that starts with the elements in \p VL. /// Returns the vectorized root. Value *vectorizeTree(); /// \returns the cost incurred by unwanted spills and fills, caused by /// holding live values over call sites. int getSpillCost(); /// \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(); NumLoadsWantToKeepOrder = 0; NumLoadsWantToChangeOrder = 0; for (auto &Iter : BlocksSchedules) { BlockScheduling *BS = Iter.second.get(); BS->clear(); } } /// \returns true if the memory operations A and B are consecutive. bool isConsecutiveAccess(Value *A, Value *B, const DataLayout &DL); /// \brief Perform LICM and CSE on the newly generated gather sequences. void optimizeGatherSequence(); /// \returns true if it is benefitial to reverse the vector order. bool shouldReorder() const { return NumLoadsWantToChangeOrder > NumLoadsWantToKeepOrder; } 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); /// \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(); /// \reorder commutative operands in alt shuffle if they result in /// vectorized code. void reorderAltShuffleOperands(ArrayRef
VL, SmallVectorImpl
&Left, SmallVectorImpl
&Right); /// \reorder commutative operands to get better probability of /// generating vectorized code. void reorderInputsAccordingToOpcode(ArrayRef
VL, SmallVectorImpl
&Left, SmallVectorImpl
&Right); struct TreeEntry { TreeEntry() : Scalars(), VectorizedValue(nullptr), 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; /// 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) { for (int i = 0, e = VL.size(); i != e; ++i) { assert(!ScalarToTreeEntry.count(VL[i]) && "Scalar already in tree!"); ScalarToTreeEntry[VL[i]] = idx; } } else { 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; /// Checks if two instructions may access the same memory. /// /// \p Loc1 is the location of \p Inst1. It is passed explicitly because it /// is invariant in the calling loop. bool isAliased(const AliasAnalysis::Location &Loc1, Instruction *Inst1, Instruction *Inst2) { // First check if the result is already in the cache. AliasCacheKey key = std::make_pair(Inst1, Inst2); Optional
&result = AliasCache[key]; if (result.hasValue()) { return result.getValue(); } AliasAnalysis::Location Loc2 = getLocation(Inst2, AA); bool aliased = true; if (Loc1.Ptr && Loc2.Ptr && isSimple(Inst1) && isSimple(Inst2)) { // Do the alias check. aliased = AA->alias(Loc1, Loc2); } // Store the result in the cache. result = aliased; return aliased; } typedef std::pair
AliasCacheKey; /// Cache for alias results. /// TODO: consider moving this to the AliasAnalysis itself. DenseMap
> AliasCache; /// Removes an instruction from its block and eventually deletes it. /// It's like Instruction::eraseFromParent() except that the actual deletion /// is delayed until BoUpSLP is destructed. /// This is required to ensure that there are no incorrect collisions in the /// AliasCache, which can happen if a new instruction is allocated at the /// same address as a previously deleted instruction. void eraseInstruction(Instruction *I) { I->removeFromParent(); I->dropAllReferences(); DeletedInstructions.push_back(std::unique_ptr
(I)); } /// Temporary store for deleted instructions. Instructions will be deleted /// eventually when the BoUpSLP is destructed. SmallVector
, 8> DeletedInstructions; /// A list of values that need to extracted out of the tree. /// This list holds pairs of (Internal Scalar : External User). UserList ExternalUses; /// Values used only by @llvm.assume calls. SmallPtrSet
EphValues; /// Holds all of the instructions that we gathered. SetVector
GatherSeq; /// A list of blocks that we are going to CSE. SetVector
CSEBlocks; /// Contains all scheduling relevant data for an instruction. /// A ScheduleData either represents a single instruction or a member of an /// instruction bundle (= a group of instructions which is combined into a /// vector instruction). struct ScheduleData { // The initial value for the dependency counters. It means that the // dependencies are not calculated yet. enum { InvalidDeps = -1 }; ScheduleData() : Inst(nullptr), FirstInBundle(nullptr), NextInBundle(nullptr), NextLoadStore(nullptr), SchedulingRegionID(0), SchedulingPriority(0), Dependencies(InvalidDeps), UnscheduledDeps(InvalidDeps), UnscheduledDepsInBundle(InvalidDeps), IsScheduled(false) {} void init(int BlockSchedulingRegionID) { FirstInBundle = this; NextInBundle = nullptr; NextLoadStore = nullptr; IsScheduled = false; SchedulingRegionID = BlockSchedulingRegionID; UnscheduledDepsInBundle = UnscheduledDeps; clearDependencies(); } /// Returns true if the dependency information has been calculated. bool hasValidDependencies() const { return Dependencies != InvalidDeps; } /// Returns true for single instructions and for bundle representatives /// (= the head of a bundle). bool isSchedulingEntity() const { return FirstInBundle == this; } /// Returns true if it represents an instruction bundle and not only a /// single instruction. bool isPartOfBundle() const { return NextInBundle != nullptr || FirstInBundle != this; } /// Returns true if it is ready for scheduling, i.e. it has no more /// unscheduled depending instructions/bundles. bool isReady() const { assert(isSchedulingEntity() && "can't consider non-scheduling entity for ready list"); return UnscheduledDepsInBundle == 0 && !IsScheduled; } /// Modifies the number of unscheduled dependencies, also updating it for /// the whole bundle. int incrementUnscheduledDeps(int Incr) { UnscheduledDeps += Incr; return FirstInBundle->UnscheduledDepsInBundle += Incr; } /// Sets the number of unscheduled dependencies to the number of /// dependencies. void resetUnscheduledDeps() { incrementUnscheduledDeps(Dependencies - UnscheduledDeps); } /// Clears all dependency information. void clearDependencies() { Dependencies = InvalidDeps; resetUnscheduledDeps(); MemoryDependencies.clear(); } void dump(raw_ostream &os) const { if (!isSchedulingEntity()) { os << "/ " << *Inst; } else if (NextInBundle) { os << '[' << *Inst; ScheduleData *SD = NextInBundle; while (SD) { os << ';' << *SD->Inst; SD = SD->NextInBundle; } os << ']'; } else { os << *Inst; } } Instruction *Inst; /// Points to the head in an instruction bundle (and always to this for /// single instructions). ScheduleData *FirstInBundle; /// Single linked list of all instructions in a bundle. Null if it is a /// single instruction. ScheduleData *NextInBundle; /// Single linked list of all memory instructions (e.g. load, store, call) /// in the block - until the end of the scheduling region. ScheduleData *NextLoadStore; /// The dependent memory instructions. /// This list is derived on demand in calculateDependencies(). SmallVector
MemoryDependencies; /// This ScheduleData is in the current scheduling region if this matches /// the current SchedulingRegionID of BlockScheduling. int SchedulingRegionID; /// Used for getting a "good" final ordering of instructions. int SchedulingPriority; /// The number of dependencies. Constitutes of the number of users of the /// instruction plus the number of dependent memory instructions (if any). /// This value is calculated on demand. /// If InvalidDeps, the number of dependencies is not calculated yet. /// int Dependencies; /// The number of dependencies minus the number of dependencies of scheduled /// instructions. As soon as this is zero, the instruction/bundle gets ready /// for scheduling. /// Note that this is negative as long as Dependencies is not calculated. int UnscheduledDeps; /// The sum of UnscheduledDeps in a bundle. Equals to UnscheduledDeps for /// single instructions. int UnscheduledDepsInBundle; /// True if this instruction is scheduled (or considered as scheduled in the /// dry-run). bool IsScheduled; }; #ifndef NDEBUG friend raw_ostream &operator<<(raw_ostream &os, const BoUpSLP::ScheduleData &SD); #endif /// Contains all scheduling data for a basic block. /// struct BlockScheduling { BlockScheduling(BasicBlock *BB) : BB(BB), ChunkSize(BB->size()), ChunkPos(ChunkSize), ScheduleStart(nullptr), ScheduleEnd(nullptr), FirstLoadStoreInRegion(nullptr), LastLoadStoreInRegion(nullptr), // Make sure that the initial SchedulingRegionID is greater than the // initial SchedulingRegionID in ScheduleData (which is 0). SchedulingRegionID(1) {} void clear() { ReadyInsts.clear(); ScheduleStart = nullptr; ScheduleEnd = nullptr; FirstLoadStoreInRegion = nullptr; LastLoadStoreInRegion = nullptr; // Make a new scheduling region, i.e. all existing ScheduleData is not // in the new region yet. ++SchedulingRegionID; } ScheduleData *getScheduleData(Value *V) { ScheduleData *SD = ScheduleDataMap[V]; if (SD && SD->SchedulingRegionID == SchedulingRegionID) return SD; return nullptr; } bool isInSchedulingRegion(ScheduleData *SD) { return SD->SchedulingRegionID == SchedulingRegionID; } /// Marks an instruction as scheduled and puts all dependent ready /// instructions into the ready-list. template
void schedule(ScheduleData *SD, ReadyListType &ReadyList) { SD->IsScheduled = true; DEBUG(dbgs() << "SLP: schedule " << *SD << "\n"); ScheduleData *BundleMember = SD; while (BundleMember) { // Handle the def-use chain dependencies. for (Use &U : BundleMember->Inst->operands()) { ScheduleData *OpDef = getScheduleData(U.get()); if (OpDef && OpDef->hasValidDependencies() && OpDef->incrementUnscheduledDeps(-1) == 0) { // There are no more unscheduled dependencies after decrementing, // so we can put the dependent instruction into the ready list. ScheduleData *DepBundle = OpDef->FirstInBundle; assert(!DepBundle->IsScheduled && "already scheduled bundle gets ready"); ReadyList.insert(DepBundle); DEBUG(dbgs() << "SLP: gets ready (def): " << *DepBundle << "\n"); } } // Handle the memory dependencies. for (ScheduleData *MemoryDepSD : BundleMember->MemoryDependencies) { if (MemoryDepSD->incrementUnscheduledDeps(-1) == 0) { // There are no more unscheduled dependencies after decrementing, // so we can put the dependent instruction into the ready list. ScheduleData *DepBundle = MemoryDepSD->FirstInBundle; assert(!DepBundle->IsScheduled && "already scheduled bundle gets ready"); ReadyList.insert(DepBundle); DEBUG(dbgs() << "SLP: gets ready (mem): " << *DepBundle << "\n"); } } BundleMember = BundleMember->NextInBundle; } } /// Put all instructions into the ReadyList which are ready for scheduling. template
void initialFillReadyList(ReadyListType &ReadyList) { for (auto *I = ScheduleStart; I != ScheduleEnd; I = I->getNextNode()) { ScheduleData *SD = getScheduleData(I); if (SD->isSchedulingEntity() && SD->isReady()) { ReadyList.insert(SD); DEBUG(dbgs() << "SLP: initially in ready list: " << *I << "\n"); } } } /// Checks if a bundle of instructions can be scheduled, i.e. has no /// cyclic dependencies. This is only a dry-run, no instructions are /// actually moved at this stage. bool tryScheduleBundle(ArrayRef
VL, BoUpSLP *SLP); /// Un-bundles a group of instructions. void cancelScheduling(ArrayRef
VL); /// Extends the scheduling region so that V is inside the region. void extendSchedulingRegion(Value *V); /// Initialize the ScheduleData structures for new instructions in the /// scheduling region. void initScheduleData(Instruction *FromI, Instruction *ToI, ScheduleData *PrevLoadStore, ScheduleData *NextLoadStore); /// Updates the dependency information of a bundle and of all instructions/ /// bundles which depend on the original bundle. void calculateDependencies(ScheduleData *SD, bool InsertInReadyList, BoUpSLP *SLP); /// Sets all instruction in the scheduling region to un-scheduled. void resetSchedule(); BasicBlock *BB; /// Simple memory allocation for ScheduleData. std::vector
> ScheduleDataChunks; /// The size of a ScheduleData array in ScheduleDataChunks. int ChunkSize; /// The allocator position in the current chunk, which is the last entry /// of ScheduleDataChunks. int ChunkPos; /// Attaches ScheduleData to Instruction. /// Note that the mapping survives during all vectorization iterations, i.e. /// ScheduleData structures are recycled. DenseMap
ScheduleDataMap; struct ReadyList : SmallVector
{ void insert(ScheduleData *SD) { push_back(SD); } }; /// The ready-list for scheduling (only used for the dry-run). ReadyList ReadyInsts; /// The first instruction of the scheduling region. Instruction *ScheduleStart; /// The first instruction _after_ the scheduling region. Instruction *ScheduleEnd; /// The first memory accessing instruction in the scheduling region /// (can be null). ScheduleData *FirstLoadStoreInRegion; /// The last memory accessing instruction in the scheduling region /// (can be null). ScheduleData *LastLoadStoreInRegion; /// The ID of the scheduling region. For a new vectorization iteration this /// is incremented which "removes" all ScheduleData from the region. int SchedulingRegionID; }; /// Attaches the BlockScheduling structures to basic blocks. MapVector
> BlocksSchedules; /// Performs the "real" scheduling. Done before vectorization is actually /// performed in a basic block. void scheduleBlock(BlockScheduling *BS); /// List of users to ignore during scheduling and that don't need extracting. ArrayRef
UserIgnoreList; // Number of load-bundles, which contain consecutive loads. int NumLoadsWantToKeepOrder; // Number of load-bundles of size 2, which are consecutive loads if reversed. int NumLoadsWantToChangeOrder; // Analysis and block reference. Function *F; ScalarEvolution *SE; TargetTransformInfo *TTI; TargetLibraryInfo *TLI; AliasAnalysis *AA; LoopInfo *LI; DominatorTree *DT; /// Instruction builder to construct the vectorized tree. IRBuilder<> Builder; }; #ifndef NDEBUG raw_ostream &operator<<(raw_ostream &os, const BoUpSLP::ScheduleData &SD) { SD.dump(os); return os; } #endif 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"); Instruction *UserInst = dyn_cast
(U); if (!UserInst) continue; // Skip in-tree scalars that become vectors if (ScalarToTreeEntry.count(U)) { int Idx = ScalarToTreeEntry[U]; TreeEntry *UseEntry = &VectorizableTree[Idx]; Value *UseScalar = UseEntry->Scalars[0]; // Some in-tree scalars will remain as scalar in vectorized // instructions. If that is the case, the one in Lane 0 will // be used. if (UseScalar != U || !InTreeUserNeedToExtract(Scalar, UserInst, TLI)) { DEBUG(dbgs() << "SLP: \tInternal user will be removed:" << *U << ".\n"); assert(!VectorizableTree[Idx].NeedToGather && "Bad state"); 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. // Don't vectorize ephemeral values. for (unsigned i = 0, e = VL.size(); i != e; ++i) { if (EphValues.count(VL[i])) { DEBUG(dbgs() << "SLP: The instruction (" << *VL[i] << ") is ephemeral.\n"); newTreeEntry(VL, false); return; } } // 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 is marked as a value that needs to stay scalar then // we need to gather the scalars. for (unsigned i = 0, e = VL.size(); i != e; ++i) { if (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]); BasicBlock *BB = cast
(VL0)->getParent(); if (!DT->isReachableFromEntry(BB)) { // Don't go into unreachable blocks. They may contain instructions with // dependency cycles which confuse the final scheduling. DEBUG(dbgs() << "SLP: bundle in unreachable block.\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; } auto &BSRef = BlocksSchedules[BB]; if (!BSRef) { BSRef = llvm::make_unique
(BB); } BlockScheduling &BS = *BSRef.get(); if (!BS.tryScheduleBundle(VL, this)) { DEBUG(dbgs() << "SLP: We are not able to schedule this bundle!\n"); BS.cancelScheduling(VL); newTreeEntry(VL, false); return; } DEBUG(dbgs() << "SLP: We are able to schedule this bundle.\n"); 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"); BS.cancelScheduling(VL); 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"); } else { BS.cancelScheduling(VL); } 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()) { BS.cancelScheduling(VL); newTreeEntry(VL, false); DEBUG(dbgs() << "SLP: Gathering non-simple loads.\n"); return; } const DataLayout &DL = F->getParent()->getDataLayout(); if (!isConsecutiveAccess(VL[i], VL[i + 1], DL)) { if (VL.size() == 2 && isConsecutiveAccess(VL[1], VL[0], DL)) { ++NumLoadsWantToChangeOrder; } BS.cancelScheduling(VL); newTreeEntry(VL, false); DEBUG(dbgs() << "SLP: Gathering non-consecutive loads.\n"); return; } } ++NumLoadsWantToKeepOrder; 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 || !isValidElementType(Ty)) { BS.cancelScheduling(VL); 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 = 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) { BS.cancelScheduling(VL); 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); 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"); BS.cancelScheduling(VL); 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"); BS.cancelScheduling(VL); 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"); BS.cancelScheduling(VL); 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: { const DataLayout &DL = F->getParent()->getDataLayout(); // 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], DL)) { BS.cancelScheduling(VL); 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)); 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)) { BS.cancelScheduling(VL); 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) { BS.cancelScheduling(VL); 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) { BS.cancelScheduling(VL); 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) { BS.cancelScheduling(VL); newTreeEntry(VL, false); DEBUG(dbgs() << "SLP: ShuffleVector are not vectorized.\n"); return; } newTreeEntry(VL, true); DEBUG(dbgs() << "SLP: added a ShuffleVector op.\n"); // Reorder operands if reordering would enable vectorization. if (isa
(VL0)) { ValueList Left, Right; reorderAltShuffleOperands(VL, Left, Right); 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; } default: BS.cancelScheduling(VL); 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; TargetTransformInfo::OperandValueProperties Op1VP = TargetTransformInfo::OP_None; TargetTransformInfo::OperandValueProperties Op2VP = TargetTransformInfo::OP_None; // 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; } // FIXME: Currently cost of model modification for division by // power of 2 is handled only for X86. Add support for other targets. if (Op2VK == TargetTransformInfo::OK_UniformConstantValue && CInt && CInt->getValue().isPowerOf2()) Op2VP = TargetTransformInfo::OP_PowerOf2; ScalarCost = VecTy->getNumElements() * TTI->getArithmeticInstrCost(Opcode, ScalarTy, Op1VK, Op2VK, Op1VP, Op2VP); VecCost = TTI->getArithmeticInstrCost(Opcode, VecTy, Op1VK, Op2VK, Op1VP, Op2VP); } 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::getSpillCost() { // Walk from the bottom of the tree to the top, tracking which values are // live. When we see a call instruction that is not part of our tree, // query TTI to see if there is a cost to keeping values live over it // (for example, if spills and fills are required). unsigned BundleWidth = VectorizableTree.front().Scalars.size(); int Cost = 0; SmallPtrSet
LiveValues; Instruction *PrevInst = nullptr; for (unsigned N = 0; N < VectorizableTree.size(); ++N) { Instruction *Inst = dyn_cast
(VectorizableTree[N].Scalars[0]); if (!Inst) continue; if (!PrevInst) { PrevInst = Inst; continue; } DEBUG( dbgs() << "SLP: #LV: " << LiveValues.size(); for (auto *X : LiveValues) dbgs() << " " << X->getName(); dbgs() << ", Looking at "; Inst->dump(); ); // Update LiveValues. LiveValues.erase(PrevInst); for (auto &J : PrevInst->operands()) { if (isa
(&*J) && ScalarToTreeEntry.count(&*J)) LiveValues.insert(cast
(&*J)); } // Now find the sequence of instructions between PrevInst and Inst. BasicBlock::reverse_iterator InstIt(Inst), PrevInstIt(PrevInst); --PrevInstIt; while (InstIt != PrevInstIt) { if (PrevInstIt == PrevInst->getParent()->rend()) { PrevInstIt = Inst->getParent()->rbegin(); continue; } if (isa
(&*PrevInstIt) && &*PrevInstIt != PrevInst) { SmallVector
V; for (auto *II : LiveValues) V.push_back(VectorType::get(II->getType(), BundleWidth)); Cost += TTI->getCostOfKeepingLiveOverCall(V); } ++PrevInstIt; } PrevInst = Inst; } DEBUG(dbgs() << "SLP: SpillCost=" << Cost << "\n"); return Cost; } 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.empty()) { 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).second) continue; // 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(I->User)) continue; VectorType *VecTy = VectorType::get(I->Scalar->getType(), BundleWidth); ExtractCost += TTI->getVectorInstrCost(Instruction::ExtractElement, VecTy, I->Lane); } Cost += getSpillCost(); 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); } 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, const DataLayout &DL) { 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; } // Reorder commutative operations in alternate shuffle if the resulting vectors // are consecutive loads. This would allow us to vectorize the tree. // If we have something like- // load a[0] - load b[0] // load b[1] + load a[1] // load a[2] - load b[2] // load a[3] + load b[3] // Reordering the second load b[1] load a[1] would allow us to vectorize this // code. void BoUpSLP::reorderAltShuffleOperands(ArrayRef
VL, SmallVectorImpl
&Left, SmallVectorImpl
&Right) { const DataLayout &DL = F->getParent()->getDataLayout(); // Push left and right operands of binary operation into Left and Right for (unsigned i = 0, e = VL.size(); i < e; ++i) { Left.push_back(cast
(VL[i])->getOperand(0)); Right.push_back(cast
(VL[i])->getOperand(1)); } // Reorder if we have a commutative operation and consecutive access // are on either side of the alternate instructions. for (unsigned j = 0; j < VL.size() - 1; ++j) { if (LoadInst *L = dyn_cast
(Left[j])) { if (LoadInst *L1 = dyn_cast
(Right[j + 1])) { Instruction *VL1 = cast
(VL[j]); Instruction *VL2 = cast
(VL[j + 1]); if (isConsecutiveAccess(L, L1, DL) && VL1->isCommutative()) { std::swap(Left[j], Right[j]); continue; } else if (isConsecutiveAccess(L, L1, DL) && VL2->isCommutative()) { std::swap(Left[j + 1], Right[j + 1]); continue; } // else unchanged } } if (LoadInst *L = dyn_cast
(Right[j])) { if (LoadInst *L1 = dyn_cast
(Left[j + 1])) { Instruction *VL1 = cast
(VL[j]); Instruction *VL2 = cast
(VL[j + 1]); if (isConsecutiveAccess(L, L1, DL) && VL1->isCommutative()) { std::swap(Left[j], Right[j]); continue; } else if (isConsecutiveAccess(L, L1, DL) && VL2->isCommutative()) { std::swap(Left[j + 1], Right[j + 1]); continue; } // else unchanged } } } } void BoUpSLP::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 *VLeft = I->getOperand(0); Value *VRight = I->getOperand(1); OrigLeft.push_back(VLeft); OrigRight.push_back(VRight); Instruction *ILeft = dyn_cast
(VLeft); Instruction *IRight = dyn_cast
(VRight); // 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. if (i && AllSameOpcodeLeft && ILeft) { if (Instruction *PLeft = dyn_cast
(OrigLeft[i - 1])) { if (PLeft->getOpcode() != ILeft->getOpcode()) AllSameOpcodeLeft = false; } else AllSameOpcodeLeft = false; } if (i && AllSameOpcodeRight && IRight) { if (Instruction *PRight = dyn_cast
(OrigRight[i - 1])) { if (PRight->getOpcode() != IRight->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 (ILeft && IRight) { if (!i && ILeft->getOpcode() > IRight->getOpcode()) { Left.push_back(IRight); Right.push_back(ILeft); } else if (i && ILeft->getOpcode() > IRight->getOpcode() && Right[i - 1] != IRight) { // Try not to destroy a broad cast for no apparent benefit. Left.push_back(IRight); Right.push_back(ILeft); } else if (i && ILeft->getOpcode() == IRight->getOpcode() && Right[i - 1] == ILeft) { // Try preserve broadcasts. Left.push_back(IRight); Right.push_back(ILeft); } else if (i && ILeft->getOpcode() == IRight->getOpcode() && Left[i - 1] == IRight) { // Try preserve broadcasts. Left.push_back(IRight); Right.push_back(ILeft); } else { Left.push_back(ILeft); Right.push_back(IRight); } continue; } // One opcode, put the instruction on the right. if (ILeft) { Left.push_back(VRight); Right.push_back(ILeft); continue; } Left.push_back(VLeft); Right.push_back(VRight); } bool LeftBroadcast = isSplat(Left); bool RightBroadcast = isSplat(Right); // If operands end up being broadcast return this operand order. if (LeftBroadcast || RightBroadcast) return; // Don't reorder if the operands where good to begin. if (AllSameOpcodeRight || AllSameOpcodeLeft) { Left = OrigLeft; Right = OrigRight; } const DataLayout &DL = F->getParent()->getDataLayout(); // Finally check if we can get longer vectorizable chain by reordering // without breaking the good operand order detected above. // E.g. If we have something like- // load a[0] load b[0] // load b[1] load a[1] // load a[2] load b[2] // load a[3] load b[3] // Reordering the second load b[1] load a[1] would allow us to vectorize // this code and we still retain AllSameOpcode property. // FIXME: This load reordering might break AllSameOpcode in some rare cases // such as- // add a[0],c[0] load b[0] // add a[1],c[2] load b[1] // b[2] load b[2] // add a[3],c[3] load b[3] for (unsigned j = 0; j < VL.size() - 1; ++j) { if (LoadInst *L = dyn_cast
(Left[j])) { if (LoadInst *L1 = dyn_cast
(Right[j + 1])) { if (isConsecutiveAccess(L, L1, DL)) { std::swap(Left[j + 1], Right[j + 1]); continue; } } } if (LoadInst *L = dyn_cast
(Right[j])) { if (LoadInst *L1 = dyn_cast
(Left[j + 1])) { if (isConsecutiveAccess(L, L1, DL)) { std::swap(Left[j + 1], Right[j + 1]); continue; } } } // else unchanged } } void BoUpSLP::setInsertPointAfterBundle(ArrayRef
VL) { Instruction *VL0 = cast
(VL[0]); BasicBlock::iterator NextInst = VL0; ++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); } const DataLayout &DL = F->getParent()->getDataLayout(); 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).second) { 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; ++NumVectorInstructions; 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 = 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; ++NumVectorInstructions; 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; ++NumVectorInstructions; 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; propagateIRFlags(E->VectorizedValue, E->Scalars); ++NumVectorInstructions; 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); Type *ScalarLoadTy = LI->getType(); unsigned AS = LI->getPointerAddressSpace(); Value *VecPtr = Builder.CreateBitCast(LI->getPointerOperand(), VecTy->getPointerTo(AS)); // The pointer operand uses an in-tree scalar so we add the new BitCast to // ExternalUses list to make sure that an extract will be generated in the // future. if (ScalarToTreeEntry.count(LI->getPointerOperand())) ExternalUses.push_back( ExternalUser(LI->getPointerOperand(), cast
(VecPtr), 0)); unsigned Alignment = LI->getAlignment(); LI = Builder.CreateLoad(VecPtr); if (!Alignment) { Alignment = DL.getABITypeAlignment(ScalarLoadTy); } LI->setAlignment(Alignment); E->VectorizedValue = LI; ++NumVectorInstructions; 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); // The pointer operand uses an in-tree scalar so we add the new BitCast to // ExternalUses list to make sure that an extract will be generated in the // future. if (ScalarToTreeEntry.count(SI->getPointerOperand())) ExternalUses.push_back( ExternalUser(SI->getPointerOperand(), cast
(VecPtr), 0)); if (!Alignment) { Alignment = DL.getABITypeAlignment(SI->getValueOperand()->getType()); } S->setAlignment(Alignment); E->VectorizedValue = S; ++NumVectorInstructions; 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( cast
(VL0)->getSourceElementType(), Op0, OpVecs); E->VectorizedValue = V; ++NumVectorInstructions; 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; Value *ScalarArg = nullptr; 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]); ScalarArg = CEI->getArgOperand(j); 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); // The scalar argument uses an in-tree scalar so we add the new vectorized // call to ExternalUses list to make sure that an extract will be // generated in the future. if (ScalarArg && ScalarToTreeEntry.count(ScalarArg)) ExternalUses.push_back(ExternalUser(ScalarArg, cast
(V), 0)); E->VectorizedValue = V; ++NumVectorInstructions; return V; } case Instruction::ShuffleVector: { ValueList LHSVL, RHSVL; assert(isa
(VL0) && "Invalid Shuffle Vector Operand"); reorderAltShuffleOperands(E->Scalars, LHSVL, RHSVL); 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 shuffle to take alternate operations from the vector. // Also, gather up odd and even scalar ops to propagate IR flags to // each vector operation. ValueList OddScalars, EvenScalars; unsigned e = E->Scalars.size(); SmallVector
Mask(e); for (unsigned i = 0; i < e; ++i) { if (i & 1) { Mask[i] = Builder.getInt32(e + i); OddScalars.push_back(E->Scalars[i]); } else { Mask[i] = Builder.getInt32(i); EvenScalars.push_back(E->Scalars[i]); } } Value *ShuffleMask = ConstantVector::get(Mask); propagateIRFlags(V0, EvenScalars); propagateIRFlags(V1, OddScalars); Value *V = Builder.CreateShuffleVector(V0, V1, ShuffleMask); E->VectorizedValue = V; ++NumVectorInstructions; if (Instruction *I = dyn_cast
(V)) return propagateMetadata(I, E->Scalars); return V; } default: llvm_unreachable("unknown inst"); } return nullptr; } Value *BoUpSLP::vectorizeTree() { // All blocks must be scheduled before any instructions are inserted. for (auto &BSIter : BlocksSchedules) { scheduleBlock(BSIter.second.get()); } 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"); eraseInstruction(cast
(Scalar)); } } 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); eraseInstruction(In); In = nullptr; break; } } if (In) { assert(std::find(Visited.begin(), Visited.end(), In) == Visited.end()); Visited.push_back(In); } } } CSEBlocks.clear(); GatherSeq.clear(); } // Groups the instructions to a bundle (which is then a single scheduling entity) // and schedules instructions until the bundle gets ready. bool BoUpSLP::BlockScheduling::tryScheduleBundle(ArrayRef
VL, BoUpSLP *SLP) { if (isa
(VL[0])) return true; // Initialize the instruction bundle. Instruction *OldScheduleEnd = ScheduleEnd; ScheduleData *PrevInBundle = nullptr; ScheduleData *Bundle = nullptr; bool ReSchedule = false; DEBUG(dbgs() << "SLP: bundle: " << *VL[0] << "\n"); for (Value *V : VL) { extendSchedulingRegion(V); ScheduleData *BundleMember = getScheduleData(V); assert(BundleMember && "no ScheduleData for bundle member (maybe not in same basic block)"); if (BundleMember->IsScheduled) { // A bundle member was scheduled as single instruction before and now // needs to be scheduled as part of the bundle. We just get rid of the // existing schedule. DEBUG(dbgs() << "SLP: reset schedule because " << *BundleMember << " was already scheduled\n"); ReSchedule = true; } assert(BundleMember->isSchedulingEntity() && "bundle member already part of other bundle"); if (PrevInBundle) { PrevInBundle->NextInBundle = BundleMember; } else { Bundle = BundleMember; } BundleMember->UnscheduledDepsInBundle = 0; Bundle->UnscheduledDepsInBundle += BundleMember->UnscheduledDeps; // Group the instructions to a bundle. BundleMember->FirstInBundle = Bundle; PrevInBundle = BundleMember; } if (ScheduleEnd != OldScheduleEnd) { // The scheduling region got new instructions at the lower end (or it is a // new region for the first bundle). This makes it necessary to // recalculate all dependencies. // It is seldom that this needs to be done a second time after adding the // initial bundle to the region. for (auto *I = ScheduleStart; I != ScheduleEnd; I = I->getNextNode()) { ScheduleData *SD = getScheduleData(I); SD->clearDependencies(); } ReSchedule = true; } if (ReSchedule) { resetSchedule(); initialFillReadyList(ReadyInsts); } DEBUG(dbgs() << "SLP: try schedule bundle " << *Bundle << " in block " << BB->getName() << "\n"); calculateDependencies(Bundle, true, SLP); // Now try to schedule the new bundle. As soon as the bundle is "ready" it // means that there are no cyclic dependencies and we can schedule it. // Note that's important that we don't "schedule" the bundle yet (see // cancelScheduling). while (!Bundle->isReady() && !ReadyInsts.empty()) { ScheduleData *pickedSD = ReadyInsts.back(); ReadyInsts.pop_back(); if (pickedSD->isSchedulingEntity() && pickedSD->isReady()) { schedule(pickedSD, ReadyInsts); } } return Bundle->isReady(); } void BoUpSLP::BlockScheduling::cancelScheduling(ArrayRef
VL) { if (isa
(VL[0])) return; ScheduleData *Bundle = getScheduleData(VL[0]); DEBUG(dbgs() << "SLP: cancel scheduling of " << *Bundle << "\n"); assert(!Bundle->IsScheduled && "Can't cancel bundle which is already scheduled"); assert(Bundle->isSchedulingEntity() && Bundle->isPartOfBundle() && "tried to unbundle something which is not a bundle"); // Un-bundle: make single instructions out of the bundle. ScheduleData *BundleMember = Bundle; while (BundleMember) { assert(BundleMember->FirstInBundle == Bundle && "corrupt bundle links"); BundleMember->FirstInBundle = BundleMember; ScheduleData *Next = BundleMember->NextInBundle; BundleMember->NextInBundle = nullptr; BundleMember->UnscheduledDepsInBundle = BundleMember->UnscheduledDeps; if (BundleMember->UnscheduledDepsInBundle == 0) { ReadyInsts.insert(BundleMember); } BundleMember = Next; } } void BoUpSLP::BlockScheduling::extendSchedulingRegion(Value *V) { if (getScheduleData(V)) return; Instruction *I = dyn_cast
(V); assert(I && "bundle member must be an instruction"); assert(!isa
(I) && "phi nodes don't need to be scheduled"); if (!ScheduleStart) { // It's the first instruction in the new region. initScheduleData(I, I->getNextNode(), nullptr, nullptr); ScheduleStart = I; ScheduleEnd = I->getNextNode(); assert(ScheduleEnd && "tried to vectorize a TerminatorInst?"); DEBUG(dbgs() << "SLP: initialize schedule region to " << *I << "\n"); return; } // Search up and down at the same time, because we don't know if the new // instruction is above or below the existing scheduling region. BasicBlock::reverse_iterator UpIter(ScheduleStart); BasicBlock::reverse_iterator UpperEnd = BB->rend(); BasicBlock::iterator DownIter(ScheduleEnd); BasicBlock::iterator LowerEnd = BB->end(); for (;;) { if (UpIter != UpperEnd) { if (&*UpIter == I) { initScheduleData(I, ScheduleStart, nullptr, FirstLoadStoreInRegion); ScheduleStart = I; DEBUG(dbgs() << "SLP: extend schedule region start to " << *I << "\n"); return; } UpIter++; } if (DownIter != LowerEnd) { if (&*DownIter == I) { initScheduleData(ScheduleEnd, I->getNextNode(), LastLoadStoreInRegion, nullptr); ScheduleEnd = I->getNextNode(); assert(ScheduleEnd && "tried to vectorize a TerminatorInst?"); DEBUG(dbgs() << "SLP: extend schedule region end to " << *I << "\n"); return; } DownIter++; } assert((UpIter != UpperEnd || DownIter != LowerEnd) && "instruction not found in block"); } } void BoUpSLP::BlockScheduling::initScheduleData(Instruction *FromI, Instruction *ToI, ScheduleData *PrevLoadStore, ScheduleData *NextLoadStore) { ScheduleData *CurrentLoadStore = PrevLoadStore; for (Instruction *I = FromI; I != ToI; I = I->getNextNode()) { ScheduleData *SD = ScheduleDataMap[I]; if (!SD) { // Allocate a new ScheduleData for the instruction. if (ChunkPos >= ChunkSize) { ScheduleDataChunks.push_back( llvm::make_unique
(ChunkSize)); ChunkPos = 0; } SD = &(ScheduleDataChunks.back()[ChunkPos++]); ScheduleDataMap[I] = SD; SD->Inst = I; } assert(!isInSchedulingRegion(SD) && "new ScheduleData already in scheduling region"); SD->init(SchedulingRegionID); if (I->mayReadOrWriteMemory()) { // Update the linked list of memory accessing instructions. if (CurrentLoadStore) { CurrentLoadStore->NextLoadStore = SD; } else { FirstLoadStoreInRegion = SD; } CurrentLoadStore = SD; } } if (NextLoadStore) { if (CurrentLoadStore) CurrentLoadStore->NextLoadStore = NextLoadStore; } else { LastLoadStoreInRegion = CurrentLoadStore; } } void BoUpSLP::BlockScheduling::calculateDependencies(ScheduleData *SD, bool InsertInReadyList, BoUpSLP *SLP) { assert(SD->isSchedulingEntity()); SmallVector
WorkList; WorkList.push_back(SD); while (!WorkList.empty()) { ScheduleData *SD = WorkList.back(); WorkList.pop_back(); ScheduleData *BundleMember = SD; while (BundleMember) { assert(isInSchedulingRegion(BundleMember)); if (!BundleMember->hasValidDependencies()) { DEBUG(dbgs() << "SLP: update deps of " << *BundleMember << "\n"); BundleMember->Dependencies = 0; BundleMember->resetUnscheduledDeps(); // Handle def-use chain dependencies. for (User *U : BundleMember->Inst->users()) { if (isa
(U)) { ScheduleData *UseSD = getScheduleData(U); if (UseSD && isInSchedulingRegion(UseSD->FirstInBundle)) { BundleMember->Dependencies++; ScheduleData *DestBundle = UseSD->FirstInBundle; if (!DestBundle->IsScheduled) { BundleMember->incrementUnscheduledDeps(1); } if (!DestBundle->hasValidDependencies()) { WorkList.push_back(DestBundle); } } } else { // I'm not sure if this can ever happen. But we need to be safe. // This lets the instruction/bundle never be scheduled and eventally // disable vectorization. BundleMember->Dependencies++; BundleMember->incrementUnscheduledDeps(1); } } // Handle the memory dependencies. ScheduleData *DepDest = BundleMember->NextLoadStore; if (DepDest) { Instruction *SrcInst = BundleMember->Inst; AliasAnalysis::Location SrcLoc = getLocation(SrcInst, SLP->AA); bool SrcMayWrite = BundleMember->Inst->mayWriteToMemory(); unsigned numAliased = 0; unsigned DistToSrc = 1; while (DepDest) { assert(isInSchedulingRegion(DepDest)); // We have two limits to reduce the complexity: // 1) AliasedCheckLimit: It's a small limit to reduce calls to // SLP->isAliased (which is the expensive part in this loop). // 2) MaxMemDepDistance: It's for very large blocks and it aborts // the whole loop (even if the loop is fast, it's quadratic). // It's important for the loop break condition (see below) to // check this limit even between two read-only instructions. if (DistToSrc >= MaxMemDepDistance || ((SrcMayWrite || DepDest->Inst->mayWriteToMemory()) && (numAliased >= AliasedCheckLimit || SLP->isAliased(SrcLoc, SrcInst, DepDest->Inst)))) { // We increment the counter only if the locations are aliased // (instead of counting all alias checks). This gives a better // balance between reduced runtime and accurate dependencies. numAliased++; DepDest->MemoryDependencies.push_back(BundleMember); BundleMember->Dependencies++; ScheduleData *DestBundle = DepDest->FirstInBundle; if (!DestBundle->IsScheduled) { BundleMember->incrementUnscheduledDeps(1); } if (!DestBundle->hasValidDependencies()) { WorkList.push_back(DestBundle); } } DepDest = DepDest->NextLoadStore; // Example, explaining the loop break condition: Let's assume our // starting instruction is i0 and MaxMemDepDistance = 3. // // +--------v--v--v // i0,i1,i2,i3,i4,i5,i6,i7,i8 // +--------^--^--^ // // MaxMemDepDistance let us stop alias-checking at i3 and we add // dependencies from i0 to i3,i4,.. (even if they are not aliased). // Previously we already added dependencies from i3 to i6,i7,i8 // (because of MaxMemDepDistance). As we added a dependency from // i0 to i3, we have transitive dependencies from i0 to i6,i7,i8 // and we can abort this loop at i6. if (DistToSrc >= 2 * MaxMemDepDistance) break; DistToSrc++; } } } BundleMember = BundleMember->NextInBundle; } if (InsertInReadyList && SD->isReady()) { ReadyInsts.push_back(SD); DEBUG(dbgs() << "SLP: gets ready on update: " << *SD->Inst << "\n"); } } } void BoUpSLP::BlockScheduling::resetSchedule() { assert(ScheduleStart && "tried to reset schedule on block which has not been scheduled"); for (Instruction *I = ScheduleStart; I != ScheduleEnd; I = I->getNextNode()) { ScheduleData *SD = getScheduleData(I); assert(isInSchedulingRegion(SD)); SD->IsScheduled = false; SD->resetUnscheduledDeps(); } ReadyInsts.clear(); } void BoUpSLP::scheduleBlock(BlockScheduling *BS) { if (!BS->ScheduleStart) return; DEBUG(dbgs() << "SLP: schedule block " << BS->BB->getName() << "\n"); BS->resetSchedule(); // For the real scheduling we use a more sophisticated ready-list: it is // sorted by the original instruction location. This lets the final schedule // be as close as possible to the original instruction order. struct ScheduleDataCompare { bool operator()(ScheduleData *SD1, ScheduleData *SD2) { return SD2->SchedulingPriority < SD1->SchedulingPriority; } }; std::set
ReadyInsts; // Ensure that all depencency data is updated and fill the ready-list with // initial instructions. int Idx = 0; int NumToSchedule = 0; for (auto *I = BS->ScheduleStart; I != BS->ScheduleEnd; I = I->getNextNode()) { ScheduleData *SD = BS->getScheduleData(I); assert( SD->isPartOfBundle() == (ScalarToTreeEntry.count(SD->Inst) != 0) && "scheduler and vectorizer have different opinion on what is a bundle"); SD->FirstInBundle->SchedulingPriority = Idx++; if (SD->isSchedulingEntity()) { BS->calculateDependencies(SD, false, this); NumToSchedule++; } } BS->initialFillReadyList(ReadyInsts); Instruction *LastScheduledInst = BS->ScheduleEnd; // Do the "real" scheduling. while (!ReadyInsts.empty()) { ScheduleData *picked = *ReadyInsts.begin(); ReadyInsts.erase(ReadyInsts.begin()); // Move the scheduled instruction(s) to their dedicated places, if not // there yet. ScheduleData *BundleMember = picked; while (BundleMember) { Instruction *pickedInst = BundleMember->Inst; if (LastScheduledInst->getNextNode() != pickedInst) { BS->BB->getInstList().remove(pickedInst); BS->BB->getInstList().insert(LastScheduledInst, pickedInst); } LastScheduledInst = pickedInst; BundleMember = BundleMember->NextInBundle; } BS->schedule(picked, ReadyInsts); NumToSchedule--; } assert(NumToSchedule == 0 && "could not schedule all instructions"); // Avoid duplicate scheduling of the block. BS->ScheduleStart = nullptr; } /// 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; TargetTransformInfo *TTI; TargetLibraryInfo *TLI; AliasAnalysis *AA; LoopInfo *LI; DominatorTree *DT; AssumptionCache *AC; bool runOnFunction(Function &F) override { if (skipOptnoneFunction(F)) return false; SE = &getAnalysis
(); TTI = &getAnalysis
().getTTI(F); auto *TLIP = getAnalysisIfAvailable
(); TLI = TLIP ? &TLIP->getTLI() : nullptr; AA = &getAnalysis
(); LI = &getAnalysis
().getLoopInfo(); DT = &getAnalysis
().getDomTree(); AC = &getAnalysis
().getAssumptionCache(F); StoreRefs.clear(); bool Changed = false; // If the target claims to have no vector registers don't attempt // vectorization. if (!TTI->getNumberOfRegisters(true)) 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, TTI, TLI, AA, LI, DT, AC); // A general note: the vectorizer must use BoUpSLP::eraseInstruction() to // delete instructions. // Scan the blocks in the function in post order. for (auto BB : post_order(&F.getEntryBlock())) { // 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