| //===- polly/ScopBuilder.h --------------------------------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Create a polyhedral description for a static control flow region. |
| // |
| // The pass creates a polyhedral description of the Scops detected by the SCoP |
| // detection derived from their LLVM-IR code. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef POLLY_SCOPBUILDER_H |
| #define POLLY_SCOPBUILDER_H |
| |
| #include "polly/ScopInfo.h" |
| #include "polly/Support/ScopHelper.h" |
| #include "llvm/ADT/ArrayRef.h" |
| #include "llvm/ADT/SetVector.h" |
| |
| namespace polly { |
| using llvm::SmallSetVector; |
| |
| class ScopDetection; |
| |
| /// Command line switch whether to model read-only accesses. |
| extern bool ModelReadOnlyScalars; |
| |
| /// Build the Polly IR (Scop and ScopStmt) on a Region. |
| class ScopBuilder { |
| |
| /// The AAResults to build AliasSetTracker. |
| AAResults &AA; |
| |
| /// Target data for element size computing. |
| const DataLayout &DL; |
| |
| /// DominatorTree to reason about guaranteed execution. |
| DominatorTree &DT; |
| |
| /// LoopInfo for information about loops. |
| LoopInfo &LI; |
| |
| /// Valid Regions for Scop |
| ScopDetection &SD; |
| |
| /// The ScalarEvolution to help building Scop. |
| ScalarEvolution &SE; |
| |
| /// An optimization diagnostic interface to add optimization remarks. |
| OptimizationRemarkEmitter &ORE; |
| |
| /// Set of instructions that might read any memory location. |
| SmallVector<std::pair<ScopStmt *, Instruction *>, 16> GlobalReads; |
| |
| /// Set of all accessed array base pointers. |
| SmallSetVector<Value *, 16> ArrayBasePointers; |
| |
| // The Scop |
| std::unique_ptr<Scop> scop; |
| |
| /// Collection to hold taken assumptions. |
| /// |
| /// There are two reasons why we want to record assumptions first before we |
| /// add them to the assumed/invalid context: |
| /// 1) If the SCoP is not profitable or otherwise invalid without the |
| /// assumed/invalid context we do not have to compute it. |
| /// 2) Information about the context are gathered rather late in the SCoP |
| /// construction (basically after we know all parameters), thus the user |
| /// might see overly complicated assumptions to be taken while they will |
| /// only be simplified later on. |
| RecordedAssumptionsTy RecordedAssumptions; |
| |
| // Build the SCoP for Region @p R. |
| void buildScop(Region &R, AssumptionCache &AC); |
| |
| /// Adjust the dimensions of @p Dom that was constructed for @p OldL |
| /// to be compatible to domains constructed for loop @p NewL. |
| /// |
| /// This function assumes @p NewL and @p OldL are equal or there is a CFG |
| /// edge from @p OldL to @p NewL. |
| isl::set adjustDomainDimensions(isl::set Dom, Loop *OldL, Loop *NewL); |
| |
| /// Compute the domain for each basic block in @p R. |
| /// |
| /// @param R The region we currently traverse. |
| /// @param InvalidDomainMap BB to InvalidDomain map for the BB of current |
| /// region. |
| /// |
| /// @returns True if there was no problem and false otherwise. |
| bool buildDomains(Region *R, |
| DenseMap<BasicBlock *, isl::set> &InvalidDomainMap); |
| |
| /// Compute the branching constraints for each basic block in @p R. |
| /// |
| /// @param R The region we currently build branching conditions |
| /// for. |
| /// @param InvalidDomainMap BB to InvalidDomain map for the BB of current |
| /// region. |
| /// |
| /// @returns True if there was no problem and false otherwise. |
| bool buildDomainsWithBranchConstraints( |
| Region *R, DenseMap<BasicBlock *, isl::set> &InvalidDomainMap); |
| |
| /// Build the conditions sets for the terminator @p TI in the @p Domain. |
| /// |
| /// This will fill @p ConditionSets with the conditions under which control |
| /// will be moved from @p TI to its successors. Hence, @p ConditionSets will |
| /// have as many elements as @p TI has successors. |
| bool buildConditionSets(BasicBlock *BB, Instruction *TI, Loop *L, |
| __isl_keep isl_set *Domain, |
| DenseMap<BasicBlock *, isl::set> &InvalidDomainMap, |
| SmallVectorImpl<__isl_give isl_set *> &ConditionSets); |
| |
| /// Build the conditions sets for the branch condition @p Condition in |
| /// the @p Domain. |
| /// |
| /// This will fill @p ConditionSets with the conditions under which control |
| /// will be moved from @p TI to its successors. Hence, @p ConditionSets will |
| /// have as many elements as @p TI has successors. If @p TI is nullptr the |
| /// context under which @p Condition is true/false will be returned as the |
| /// new elements of @p ConditionSets. |
| bool buildConditionSets(BasicBlock *BB, Value *Condition, Instruction *TI, |
| Loop *L, __isl_keep isl_set *Domain, |
| DenseMap<BasicBlock *, isl::set> &InvalidDomainMap, |
| SmallVectorImpl<__isl_give isl_set *> &ConditionSets); |
| |
| /// Build the conditions sets for the switch @p SI in the @p Domain. |
| /// |
| /// This will fill @p ConditionSets with the conditions under which control |
| /// will be moved from @p SI to its successors. Hence, @p ConditionSets will |
| /// have as many elements as @p SI has successors. |
| bool buildConditionSets(BasicBlock *BB, SwitchInst *SI, Loop *L, |
| __isl_keep isl_set *Domain, |
| DenseMap<BasicBlock *, isl::set> &InvalidDomainMap, |
| SmallVectorImpl<__isl_give isl_set *> &ConditionSets); |
| |
| /// Build condition sets for unsigned ICmpInst(s). |
| /// Special handling is required for unsigned operands to ensure that if |
| /// MSB (aka the Sign bit) is set for an operands in an unsigned ICmpInst |
| /// it should wrap around. |
| /// |
| /// @param IsStrictUpperBound holds information on the predicate relation |
| /// between TestVal and UpperBound, i.e, |
| /// TestVal < UpperBound OR TestVal <= UpperBound |
| __isl_give isl_set *buildUnsignedConditionSets( |
| BasicBlock *BB, Value *Condition, __isl_keep isl_set *Domain, |
| const SCEV *SCEV_TestVal, const SCEV *SCEV_UpperBound, |
| DenseMap<BasicBlock *, isl::set> &InvalidDomainMap, |
| bool IsStrictUpperBound); |
| |
| /// Propagate the domain constraints through the region @p R. |
| /// |
| /// @param R The region we currently build branching |
| /// conditions for. |
| /// @param InvalidDomainMap BB to InvalidDomain map for the BB of current |
| /// region. |
| /// |
| /// @returns True if there was no problem and false otherwise. |
| bool propagateDomainConstraints( |
| Region *R, DenseMap<BasicBlock *, isl::set> &InvalidDomainMap); |
| |
| /// Propagate domains that are known due to graph properties. |
| /// |
| /// As a CFG is mostly structured we use the graph properties to propagate |
| /// domains without the need to compute all path conditions. In particular, |
| /// if a block A dominates a block B and B post-dominates A we know that the |
| /// domain of B is a superset of the domain of A. As we do not have |
| /// post-dominator information available here we use the less precise region |
| /// information. Given a region R, we know that the exit is always executed |
| /// if the entry was executed, thus the domain of the exit is a superset of |
| /// the domain of the entry. In case the exit can only be reached from |
| /// within the region the domains are in fact equal. This function will use |
| /// this property to avoid the generation of condition constraints that |
| /// determine when a branch is taken. If @p BB is a region entry block we |
| /// will propagate its domain to the region exit block. Additionally, we put |
| /// the region exit block in the @p FinishedExitBlocks set so we can later |
| /// skip edges from within the region to that block. |
| /// |
| /// @param BB The block for which the domain is currently |
| /// propagated. |
| /// @param BBLoop The innermost affine loop surrounding @p BB. |
| /// @param FinishedExitBlocks Set of region exits the domain was set for. |
| /// @param InvalidDomainMap BB to InvalidDomain map for the BB of current |
| /// region. |
| void propagateDomainConstraintsToRegionExit( |
| BasicBlock *BB, Loop *BBLoop, |
| SmallPtrSetImpl<BasicBlock *> &FinishedExitBlocks, |
| DenseMap<BasicBlock *, isl::set> &InvalidDomainMap); |
| |
| /// Propagate invalid domains of statements through @p R. |
| /// |
| /// This method will propagate invalid statement domains through @p R and at |
| /// the same time add error block domains to them. Additionally, the domains |
| /// of error statements and those only reachable via error statements will |
| /// be replaced by an empty set. Later those will be removed completely. |
| /// |
| /// @param R The currently traversed region. |
| /// @param InvalidDomainMap BB to InvalidDomain map for the BB of current |
| /// region. |
| // |
| /// @returns True if there was no problem and false otherwise. |
| bool propagateInvalidStmtDomains( |
| Region *R, DenseMap<BasicBlock *, isl::set> &InvalidDomainMap); |
| |
| /// Compute the union of predecessor domains for @p BB. |
| /// |
| /// To compute the union of all domains of predecessors of @p BB this |
| /// function applies similar reasoning on the CFG structure as described for |
| /// @see propagateDomainConstraintsToRegionExit |
| /// |
| /// @param BB The block for which the predecessor domains are collected. |
| /// @param Domain The domain under which BB is executed. |
| /// |
| /// @returns The domain under which @p BB is executed. |
| isl::set getPredecessorDomainConstraints(BasicBlock *BB, isl::set Domain); |
| |
| /// Add loop carried constraints to the header block of the loop @p L. |
| /// |
| /// @param L The loop to process. |
| /// @param InvalidDomainMap BB to InvalidDomain map for the BB of current |
| /// region. |
| /// |
| /// @returns True if there was no problem and false otherwise. |
| bool addLoopBoundsToHeaderDomain( |
| Loop *L, DenseMap<BasicBlock *, isl::set> &InvalidDomainMap); |
| |
| /// Compute the isl representation for the SCEV @p E in this BB. |
| /// |
| /// @param BB The BB for which isl representation is to be |
| /// computed. |
| /// @param InvalidDomainMap A map of BB to their invalid domains. |
| /// @param E The SCEV that should be translated. |
| /// @param NonNegative Flag to indicate the @p E has to be |
| /// non-negative. |
| /// |
| /// Note that this function will also adjust the invalid context |
| /// accordingly. |
| __isl_give isl_pw_aff * |
| getPwAff(BasicBlock *BB, DenseMap<BasicBlock *, isl::set> &InvalidDomainMap, |
| const SCEV *E, bool NonNegative = false); |
| |
| /// Create equivalence classes for required invariant accesses. |
| /// |
| /// These classes will consolidate multiple required invariant loads from the |
| /// same address in order to keep the number of dimensions in the SCoP |
| /// description small. For each such class equivalence class only one |
| /// representing element, hence one required invariant load, will be chosen |
| /// and modeled as parameter. The method |
| /// Scop::getRepresentingInvariantLoadSCEV() will replace each element from an |
| /// equivalence class with the representing element that is modeled. As a |
| /// consequence Scop::getIdForParam() will only return an id for the |
| /// representing element of each equivalence class, thus for each required |
| /// invariant location. |
| void buildInvariantEquivalenceClasses(); |
| |
| /// Try to build a multi-dimensional fixed sized MemoryAccess from the |
| /// Load/Store instruction. |
| /// |
| /// @param Inst The Load/Store instruction that access the memory |
| /// @param Stmt The parent statement of the instruction |
| /// |
| /// @returns True if the access could be built, False otherwise. |
| bool buildAccessMultiDimFixed(MemAccInst Inst, ScopStmt *Stmt); |
| |
| /// Try to build a multi-dimensional parametric sized MemoryAccess. |
| /// from the Load/Store instruction. |
| /// |
| /// @param Inst The Load/Store instruction that access the memory |
| /// @param Stmt The parent statement of the instruction |
| /// |
| /// @returns True if the access could be built, False otherwise. |
| bool buildAccessMultiDimParam(MemAccInst Inst, ScopStmt *Stmt); |
| |
| /// Try to build a MemoryAccess for a memory intrinsic. |
| /// |
| /// @param Inst The instruction that access the memory |
| /// @param Stmt The parent statement of the instruction |
| /// |
| /// @returns True if the access could be built, False otherwise. |
| bool buildAccessMemIntrinsic(MemAccInst Inst, ScopStmt *Stmt); |
| |
| /// Try to build a MemoryAccess for a call instruction. |
| /// |
| /// @param Inst The call instruction that access the memory |
| /// @param Stmt The parent statement of the instruction |
| /// |
| /// @returns True if the access could be built, False otherwise. |
| bool buildAccessCallInst(MemAccInst Inst, ScopStmt *Stmt); |
| |
| /// Build a single-dimensional parametric sized MemoryAccess |
| /// from the Load/Store instruction. |
| /// |
| /// @param Inst The Load/Store instruction that access the memory |
| /// @param Stmt The parent statement of the instruction |
| void buildAccessSingleDim(MemAccInst Inst, ScopStmt *Stmt); |
| |
| /// Finalize all access relations. |
| /// |
| /// When building up access relations, temporary access relations that |
| /// correctly represent each individual access are constructed. However, these |
| /// access relations can be inconsistent or non-optimal when looking at the |
| /// set of accesses as a whole. This function finalizes the memory accesses |
| /// and constructs a globally consistent state. |
| void finalizeAccesses(); |
| |
| /// Update access dimensionalities. |
| /// |
| /// When detecting memory accesses different accesses to the same array may |
| /// have built with different dimensionality, as outer zero-values dimensions |
| /// may not have been recognized as separate dimensions. This function goes |
| /// again over all memory accesses and updates their dimensionality to match |
| /// the dimensionality of the underlying ScopArrayInfo object. |
| void updateAccessDimensionality(); |
| |
| /// Fold size constants to the right. |
| /// |
| /// In case all memory accesses in a given dimension are multiplied with a |
| /// common constant, we can remove this constant from the individual access |
| /// functions and move it to the size of the memory access. We do this as this |
| /// increases the size of the innermost dimension, consequently widens the |
| /// valid range the array subscript in this dimension can evaluate to, and |
| /// as a result increases the likelihood that our delinearization is |
| /// correct. |
| /// |
| /// Example: |
| /// |
| /// A[][n] |
| /// S[i,j] -> A[2i][2j+1] |
| /// S[i,j] -> A[2i][2j] |
| /// |
| /// => |
| /// |
| /// A[][2n] |
| /// S[i,j] -> A[i][2j+1] |
| /// S[i,j] -> A[i][2j] |
| /// |
| /// Constants in outer dimensions can arise when the elements of a parametric |
| /// multi-dimensional array are not elementary data types, but e.g., |
| /// structures. |
| void foldSizeConstantsToRight(); |
| |
| /// Fold memory accesses to handle parametric offset. |
| /// |
| /// As a post-processing step, we 'fold' memory accesses to parametric |
| /// offsets in the access functions. @see MemoryAccess::foldAccess for |
| /// details. |
| void foldAccessRelations(); |
| |
| /// Assume that all memory accesses are within bounds. |
| /// |
| /// After we have built a model of all memory accesses, we need to assume |
| /// that the model we built matches reality -- aka. all modeled memory |
| /// accesses always remain within bounds. We do this as last step, after |
| /// all memory accesses have been modeled and canonicalized. |
| void assumeNoOutOfBounds(); |
| |
| /// Build the alias checks for this SCoP. |
| bool buildAliasChecks(); |
| |
| /// A vector of memory accesses that belong to an alias group. |
| using AliasGroupTy = SmallVector<MemoryAccess *, 4>; |
| |
| /// A vector of alias groups. |
| using AliasGroupVectorTy = SmallVector<AliasGroupTy, 4>; |
| |
| /// Build a given alias group and its access data. |
| /// |
| /// @param AliasGroup The alias group to build. |
| /// @param HasWriteAccess A set of arrays through which memory is not only |
| /// read, but also written. |
| // |
| /// @returns True if __no__ error occurred, false otherwise. |
| bool buildAliasGroup(AliasGroupTy &AliasGroup, |
| DenseSet<const ScopArrayInfo *> HasWriteAccess); |
| |
| /// Build all alias groups for this SCoP. |
| /// |
| /// @returns True if __no__ error occurred, false otherwise. |
| bool buildAliasGroups(); |
| |
| /// Build alias groups for all memory accesses in the Scop. |
| /// |
| /// Using the alias analysis and an alias set tracker we build alias sets |
| /// for all memory accesses inside the Scop. For each alias set we then map |
| /// the aliasing pointers back to the memory accesses we know, thus obtain |
| /// groups of memory accesses which might alias. We also collect the set of |
| /// arrays through which memory is written. |
| /// |
| /// @returns A pair consistent of a vector of alias groups and a set of arrays |
| /// through which memory is written. |
| std::tuple<AliasGroupVectorTy, DenseSet<const ScopArrayInfo *>> |
| buildAliasGroupsForAccesses(); |
| |
| /// Split alias groups by iteration domains. |
| /// |
| /// We split each group based on the domains of the minimal/maximal accesses. |
| /// That means two minimal/maximal accesses are only in a group if their |
| /// access domains intersect. Otherwise, they are in different groups. |
| /// |
| /// @param AliasGroups The alias groups to split |
| void splitAliasGroupsByDomain(AliasGroupVectorTy &AliasGroups); |
| |
| /// Build an instance of MemoryAccess from the Load/Store instruction. |
| /// |
| /// @param Inst The Load/Store instruction that access the memory |
| /// @param Stmt The parent statement of the instruction |
| void buildMemoryAccess(MemAccInst Inst, ScopStmt *Stmt); |
| |
| /// Analyze and extract the cross-BB scalar dependences (or, dataflow |
| /// dependencies) of an instruction. |
| /// |
| /// @param UserStmt The statement @p Inst resides in. |
| /// @param Inst The instruction to be analyzed. |
| void buildScalarDependences(ScopStmt *UserStmt, Instruction *Inst); |
| |
| /// Build the escaping dependences for @p Inst. |
| /// |
| /// Search for uses of the llvm::Value defined by @p Inst that are not |
| /// within the SCoP. If there is such use, add a SCALAR WRITE such that |
| /// it is available after the SCoP as escaping value. |
| /// |
| /// @param Inst The instruction to be analyzed. |
| void buildEscapingDependences(Instruction *Inst); |
| |
| /// Create MemoryAccesses for the given PHI node in the given region. |
| /// |
| /// @param PHIStmt The statement @p PHI resides in. |
| /// @param PHI The PHI node to be handled |
| /// @param NonAffineSubRegion The non affine sub-region @p PHI is in. |
| /// @param IsExitBlock Flag to indicate that @p PHI is in the exit BB. |
| void buildPHIAccesses(ScopStmt *PHIStmt, PHINode *PHI, |
| Region *NonAffineSubRegion, bool IsExitBlock = false); |
| |
| /// Build the access functions for the subregion @p SR. |
| void buildAccessFunctions(); |
| |
| /// Should an instruction be modeled in a ScopStmt. |
| /// |
| /// @param Inst The instruction to check. |
| /// @param L The loop in which context the instruction is looked at. |
| /// |
| /// @returns True if the instruction should be modeled. |
| bool shouldModelInst(Instruction *Inst, Loop *L); |
| |
| /// Create one or more ScopStmts for @p BB. |
| /// |
| /// Consecutive instructions are associated to the same statement until a |
| /// separator is found. |
| void buildSequentialBlockStmts(BasicBlock *BB, bool SplitOnStore = false); |
| |
| /// Create one or more ScopStmts for @p BB using equivalence classes. |
| /// |
| /// Instructions of a basic block that belong to the same equivalence class |
| /// are added to the same statement. |
| void buildEqivClassBlockStmts(BasicBlock *BB); |
| |
| /// Create ScopStmt for all BBs and non-affine subregions of @p SR. |
| /// |
| /// @param SR A subregion of @p R. |
| /// |
| /// Some of the statements might be optimized away later when they do not |
| /// access any memory and thus have no effect. |
| void buildStmts(Region &SR); |
| |
| /// Build the access functions for the statement @p Stmt in or represented by |
| /// @p BB. |
| /// |
| /// @param Stmt Statement to add MemoryAccesses to. |
| /// @param BB A basic block in @p R. |
| /// @param NonAffineSubRegion The non affine sub-region @p BB is in. |
| void buildAccessFunctions(ScopStmt *Stmt, BasicBlock &BB, |
| Region *NonAffineSubRegion = nullptr); |
| |
| /// Create a new MemoryAccess object and add it to #AccFuncMap. |
| /// |
| /// @param Stmt The statement where the access takes place. |
| /// @param Inst The instruction doing the access. It is not necessarily |
| /// inside @p BB. |
| /// @param AccType The kind of access. |
| /// @param BaseAddress The accessed array's base address. |
| /// @param ElemType The type of the accessed array elements. |
| /// @param Affine Whether all subscripts are affine expressions. |
| /// @param AccessValue Value read or written. |
| /// @param Subscripts Access subscripts per dimension. |
| /// @param Sizes The array dimension's sizes. |
| /// @param Kind The kind of memory accessed. |
| /// |
| /// @return The created MemoryAccess, or nullptr if the access is not within |
| /// the SCoP. |
| MemoryAccess *addMemoryAccess(ScopStmt *Stmt, Instruction *Inst, |
| MemoryAccess::AccessType AccType, |
| Value *BaseAddress, Type *ElemType, bool Affine, |
| Value *AccessValue, |
| ArrayRef<const SCEV *> Subscripts, |
| ArrayRef<const SCEV *> Sizes, MemoryKind Kind); |
| |
| /// Create a MemoryAccess that represents either a LoadInst or |
| /// StoreInst. |
| /// |
| /// @param Stmt The statement to add the MemoryAccess to. |
| /// @param MemAccInst The LoadInst or StoreInst. |
| /// @param AccType The kind of access. |
| /// @param BaseAddress The accessed array's base address. |
| /// @param ElemType The type of the accessed array elements. |
| /// @param IsAffine Whether all subscripts are affine expressions. |
| /// @param Subscripts Access subscripts per dimension. |
| /// @param Sizes The array dimension's sizes. |
| /// @param AccessValue Value read or written. |
| /// |
| /// @see MemoryKind |
| void addArrayAccess(ScopStmt *Stmt, MemAccInst MemAccInst, |
| MemoryAccess::AccessType AccType, Value *BaseAddress, |
| Type *ElemType, bool IsAffine, |
| ArrayRef<const SCEV *> Subscripts, |
| ArrayRef<const SCEV *> Sizes, Value *AccessValue); |
| |
| /// Create a MemoryAccess for writing an llvm::Instruction. |
| /// |
| /// The access will be created at the position of @p Inst. |
| /// |
| /// @param Inst The instruction to be written. |
| /// |
| /// @see ensureValueRead() |
| /// @see MemoryKind |
| void ensureValueWrite(Instruction *Inst); |
| |
| /// Ensure an llvm::Value is available in the BB's statement, creating a |
| /// MemoryAccess for reloading it if necessary. |
| /// |
| /// @param V The value expected to be loaded. |
| /// @param UserStmt Where to reload the value. |
| /// |
| /// @see ensureValueStore() |
| /// @see MemoryKind |
| void ensureValueRead(Value *V, ScopStmt *UserStmt); |
| |
| /// Create a write MemoryAccess for the incoming block of a phi node. |
| /// |
| /// Each of the incoming blocks write their incoming value to be picked in the |
| /// phi's block. |
| /// |
| /// @param PHI PHINode under consideration. |
| /// @param IncomingStmt The statement to add the MemoryAccess to. |
| /// @param IncomingBlock Some predecessor block. |
| /// @param IncomingValue @p PHI's value when coming from @p IncomingBlock. |
| /// @param IsExitBlock When true, uses the .s2a alloca instead of the |
| /// .phiops one. Required for values escaping through a |
| /// PHINode in the SCoP region's exit block. |
| /// @see addPHIReadAccess() |
| /// @see MemoryKind |
| void ensurePHIWrite(PHINode *PHI, ScopStmt *IncomintStmt, |
| BasicBlock *IncomingBlock, Value *IncomingValue, |
| bool IsExitBlock); |
| |
| /// Add user provided parameter constraints to context (command line). |
| void addUserContext(); |
| |
| /// Add user provided parameter constraints to context (source code). |
| void addUserAssumptions(AssumptionCache &AC, |
| DenseMap<BasicBlock *, isl::set> &InvalidDomainMap); |
| |
| /// Add all recorded assumptions to the assumed context. |
| void addRecordedAssumptions(); |
| |
| /// Create a MemoryAccess for reading the value of a phi. |
| /// |
| /// The modeling assumes that all incoming blocks write their incoming value |
| /// to the same location. Thus, this access will read the incoming block's |
| /// value as instructed by this @p PHI. |
| /// |
| /// @param PHIStmt Statement @p PHI resides in. |
| /// @param PHI PHINode under consideration; the READ access will be added |
| /// here. |
| /// |
| /// @see ensurePHIWrite() |
| /// @see MemoryKind |
| void addPHIReadAccess(ScopStmt *PHIStmt, PHINode *PHI); |
| |
| /// Wrapper function to calculate minimal/maximal accesses to each array. |
| bool calculateMinMaxAccess(AliasGroupTy AliasGroup, |
| Scop::MinMaxVectorTy &MinMaxAccesses); |
| /// Build the domain of @p Stmt. |
| void buildDomain(ScopStmt &Stmt); |
| |
| /// Fill NestLoops with loops surrounding @p Stmt. |
| void collectSurroundingLoops(ScopStmt &Stmt); |
| |
| /// Check for reductions in @p Stmt. |
| /// |
| /// Iterate over all store memory accesses and check for valid binary |
| /// reduction like chains. For all candidates we check if they have the same |
| /// base address and there are no other accesses which overlap with them. The |
| /// base address check rules out impossible reductions candidates early. The |
| /// overlap check, together with the "only one user" check in |
| /// collectCandidateReductionLoads, guarantees that none of the intermediate |
| /// results will escape during execution of the loop nest. We basically check |
| /// here that no other memory access can access the same memory as the |
| /// potential reduction. |
| void checkForReductions(ScopStmt &Stmt); |
| |
| /// Verify that all required invariant loads have been hoisted. |
| /// |
| /// Invariant load hoisting is not guaranteed to hoist all loads that were |
| /// assumed to be scop invariant during scop detection. This function checks |
| /// for cases where the hoisting failed, but where it would have been |
| /// necessary for our scop modeling to be correct. In case of insufficient |
| /// hoisting the scop is marked as invalid. |
| /// |
| /// In the example below Bound[1] is required to be invariant: |
| /// |
| /// for (int i = 1; i < Bound[0]; i++) |
| /// for (int j = 1; j < Bound[1]; j++) |
| /// ... |
| void verifyInvariantLoads(); |
| |
| /// Hoist invariant memory loads and check for required ones. |
| /// |
| /// We first identify "common" invariant loads, thus loads that are invariant |
| /// and can be hoisted. Then we check if all required invariant loads have |
| /// been identified as (common) invariant. A load is a required invariant load |
| /// if it was assumed to be invariant during SCoP detection, e.g., to assume |
| /// loop bounds to be affine or runtime alias checks to be placeable. In case |
| /// a required invariant load was not identified as (common) invariant we will |
| /// drop this SCoP. An example for both "common" as well as required invariant |
| /// loads is given below: |
| /// |
| /// for (int i = 1; i < *LB[0]; i++) |
| /// for (int j = 1; j < *LB[1]; j++) |
| /// A[i][j] += A[0][0] + (*V); |
| /// |
| /// Common inv. loads: V, A[0][0], LB[0], LB[1] |
| /// Required inv. loads: LB[0], LB[1], (V, if it may alias with A or LB) |
| void hoistInvariantLoads(); |
| |
| /// Add invariant loads listed in @p InvMAs with the domain of @p Stmt. |
| void addInvariantLoads(ScopStmt &Stmt, InvariantAccessesTy &InvMAs); |
| |
| /// Check if @p MA can always be hoisted without execution context. |
| bool canAlwaysBeHoisted(MemoryAccess *MA, bool StmtInvalidCtxIsEmpty, |
| bool MAInvalidCtxIsEmpty, |
| bool NonHoistableCtxIsEmpty); |
| |
| /// Return true if and only if @p LI is a required invariant load. |
| bool isRequiredInvariantLoad(LoadInst *LI) const { |
| return scop->getRequiredInvariantLoads().count(LI); |
| } |
| |
| /// Check if the base ptr of @p MA is in the SCoP but not hoistable. |
| bool hasNonHoistableBasePtrInScop(MemoryAccess *MA, isl::union_map Writes); |
| |
| /// Return the context under which the access cannot be hoisted. |
| /// |
| /// @param Access The access to check. |
| /// @param Writes The set of all memory writes in the scop. |
| /// |
| /// @return Return the context under which the access cannot be hoisted or a |
| /// nullptr if it cannot be hoisted at all. |
| isl::set getNonHoistableCtx(MemoryAccess *Access, isl::union_map Writes); |
| |
| /// Collect loads which might form a reduction chain with @p StoreMA. |
| /// |
| /// Check if the stored value for @p StoreMA is a binary operator with one or |
| /// two loads as operands. If the binary operand is commutative & associative, |
| /// used only once (by @p StoreMA) and its load operands are also used only |
| /// once, we have found a possible reduction chain. It starts at an operand |
| /// load and includes the binary operator and @p StoreMA. |
| /// |
| /// Note: We allow only one use to ensure the load and binary operator cannot |
| /// escape this block or into any other store except @p StoreMA. |
| void collectCandidateReductionLoads(MemoryAccess *StoreMA, |
| SmallVectorImpl<MemoryAccess *> &Loads); |
| |
| /// Build the access relation of all memory accesses of @p Stmt. |
| void buildAccessRelations(ScopStmt &Stmt); |
| |
| /// Canonicalize arrays with base pointers from the same equivalence class. |
| /// |
| /// Some context: in our normal model we assume that each base pointer is |
| /// related to a single specific memory region, where memory regions |
| /// associated with different base pointers are disjoint. Consequently we do |
| /// not need to compute additional data dependences that model possible |
| /// overlaps of these memory regions. To verify our assumption we compute |
| /// alias checks that verify that modeled arrays indeed do not overlap. In |
| /// case an overlap is detected the runtime check fails and we fall back to |
| /// the original code. |
| /// |
| /// In case of arrays where the base pointers are know to be identical, |
| /// because they are dynamically loaded by accesses that are in the same |
| /// invariant load equivalence class, such run-time alias check would always |
| /// be false. |
| /// |
| /// This function makes sure that we do not generate consistently failing |
| /// run-time checks for code that contains distinct arrays with known |
| /// equivalent base pointers. It identifies for each invariant load |
| /// equivalence class a single canonical array and canonicalizes all memory |
| /// accesses that reference arrays that have base pointers that are known to |
| /// be equal to the base pointer of such a canonical array to this canonical |
| /// array. |
| /// |
| /// We currently do not canonicalize arrays for which certain memory accesses |
| /// have been hoisted as loop invariant. |
| void canonicalizeDynamicBasePtrs(); |
| |
| /// Construct the schedule of this SCoP. |
| void buildSchedule(); |
| |
| /// A loop stack element to keep track of per-loop information during |
| /// schedule construction. |
| using LoopStackElementTy = struct LoopStackElement { |
| // The loop for which we keep information. |
| Loop *L; |
| |
| // The (possibly incomplete) schedule for this loop. |
| isl::schedule Schedule; |
| |
| // The number of basic blocks in the current loop, for which a schedule has |
| // already been constructed. |
| unsigned NumBlocksProcessed; |
| |
| LoopStackElement(Loop *L, isl::schedule S, unsigned NumBlocksProcessed) |
| : L(L), Schedule(S), NumBlocksProcessed(NumBlocksProcessed) {} |
| }; |
| |
| /// The loop stack used for schedule construction. |
| /// |
| /// The loop stack keeps track of schedule information for a set of nested |
| /// loops as well as an (optional) 'nullptr' loop that models the outermost |
| /// schedule dimension. The loops in a loop stack always have a parent-child |
| /// relation where the loop at position n is the parent of the loop at |
| /// position n + 1. |
| using LoopStackTy = SmallVector<LoopStackElementTy, 4>; |
| |
| /// Construct schedule information for a given Region and add the |
| /// derived information to @p LoopStack. |
| /// |
| /// Given a Region we derive schedule information for all RegionNodes |
| /// contained in this region ensuring that the assigned execution times |
| /// correctly model the existing control flow relations. |
| /// |
| /// @param R The region which to process. |
| /// @param LoopStack A stack of loops that are currently under |
| /// construction. |
| void buildSchedule(Region *R, LoopStackTy &LoopStack); |
| |
| /// Build Schedule for the region node @p RN and add the derived |
| /// information to @p LoopStack. |
| /// |
| /// In case @p RN is a BasicBlock or a non-affine Region, we construct the |
| /// schedule for this @p RN and also finalize loop schedules in case the |
| /// current @p RN completes the loop. |
| /// |
| /// In case @p RN is a not-non-affine Region, we delegate the construction to |
| /// buildSchedule(Region *R, ...). |
| /// |
| /// @param RN The RegionNode region traversed. |
| /// @param LoopStack A stack of loops that are currently under |
| /// construction. |
| void buildSchedule(RegionNode *RN, LoopStackTy &LoopStack); |
| |
| public: |
| explicit ScopBuilder(Region *R, AssumptionCache &AC, AAResults &AA, |
| const DataLayout &DL, DominatorTree &DT, LoopInfo &LI, |
| ScopDetection &SD, ScalarEvolution &SE, |
| OptimizationRemarkEmitter &ORE); |
| ScopBuilder(const ScopBuilder &) = delete; |
| ScopBuilder &operator=(const ScopBuilder &) = delete; |
| ~ScopBuilder() = default; |
| |
| /// Try to build the Polly IR of static control part on the current |
| /// SESE-Region. |
| /// |
| /// @return Give up the ownership of the scop object or static control part |
| /// for the region |
| std::unique_ptr<Scop> getScop() { return std::move(scop); } |
| }; |
| } // end namespace polly |
| |
| #endif // POLLY_SCOPBUILDER_H |