//===- llvm/Analysis/LoopAccessAnalysis.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 // //===----------------------------------------------------------------------===// // // This file defines the interface for the loop memory dependence framework that // was originally developed for the Loop Vectorizer. // //===----------------------------------------------------------------------===// #ifndef LLVM_ANALYSIS_LOOPACCESSANALYSIS_H #define LLVM_ANALYSIS_LOOPACCESSANALYSIS_H #include "llvm/ADT/EquivalenceClasses.h" #include "llvm/Analysis/ScalarEvolution.h" #include "llvm/IR/DiagnosticInfo.h" #include <optional> #include <variant> namespace llvm { class AAResults; class DataLayout; class Loop; class raw_ostream; class TargetTransformInfo; /// Collection of parameters shared beetween the Loop Vectorizer and the /// Loop Access Analysis. struct VectorizerParams { … }; /// Checks memory dependences among accesses to the same underlying /// object to determine whether there vectorization is legal or not (and at /// which vectorization factor). /// /// Note: This class will compute a conservative dependence for access to /// different underlying pointers. Clients, such as the loop vectorizer, will /// sometimes deal these potential dependencies by emitting runtime checks. /// /// We use the ScalarEvolution framework to symbolically evalutate access /// functions pairs. Since we currently don't restructure the loop we can rely /// on the program order of memory accesses to determine their safety. /// At the moment we will only deem accesses as safe for: /// * A negative constant distance assuming program order. /// /// Safe: tmp = a[i + 1]; OR a[i + 1] = x; /// a[i] = tmp; y = a[i]; /// /// The latter case is safe because later checks guarantuee that there can't /// be a cycle through a phi node (that is, we check that "x" and "y" is not /// the same variable: a header phi can only be an induction or a reduction, a /// reduction can't have a memory sink, an induction can't have a memory /// source). This is important and must not be violated (or we have to /// resort to checking for cycles through memory). /// /// * A positive constant distance assuming program order that is bigger /// than the biggest memory access. /// /// tmp = a[i] OR b[i] = x /// a[i+2] = tmp y = b[i+2]; /// /// Safe distance: 2 x sizeof(a[0]), and 2 x sizeof(b[0]), respectively. /// /// * Zero distances and all accesses have the same size. /// class MemoryDepChecker { … }; class RuntimePointerChecking; /// A grouping of pointers. A single memcheck is required between /// two groups. struct RuntimeCheckingPtrGroup { … }; /// A memcheck which made up of a pair of grouped pointers. RuntimePointerCheck; struct PointerDiffInfo { … }; /// Holds information about the memory runtime legality checks to verify /// that a group of pointers do not overlap. class RuntimePointerChecking { … }; /// Drive the analysis of memory accesses in the loop /// /// This class is responsible for analyzing the memory accesses of a loop. It /// collects the accesses and then its main helper the AccessAnalysis class /// finds and categorizes the dependences in buildDependenceSets. /// /// For memory dependences that can be analyzed at compile time, it determines /// whether the dependence is part of cycle inhibiting vectorization. This work /// is delegated to the MemoryDepChecker class. /// /// For memory dependences that cannot be determined at compile time, it /// generates run-time checks to prove independence. This is done by /// AccessAnalysis::canCheckPtrAtRT and the checks are maintained by the /// RuntimePointerCheck class. /// /// If pointers can wrap or can't be expressed as affine AddRec expressions by /// ScalarEvolution, we will generate run-time checks by emitting a /// SCEVUnionPredicate. /// /// Checks for both memory dependences and the SCEV predicates contained in the /// PSE must be emitted in order for the results of this analysis to be valid. class LoopAccessInfo { … }; /// Return the SCEV corresponding to a pointer with the symbolic stride /// replaced with constant one, assuming the SCEV predicate associated with /// \p PSE is true. /// /// If necessary this method will version the stride of the pointer according /// to \p PtrToStride and therefore add further predicates to \p PSE. /// /// \p PtrToStride provides the mapping between the pointer value and its /// stride as collected by LoopVectorizationLegality::collectStridedAccess. const SCEV * replaceSymbolicStrideSCEV(PredicatedScalarEvolution &PSE, const DenseMap<Value *, const SCEV *> &PtrToStride, Value *Ptr); /// If the pointer has a constant stride return it in units of the access type /// size. If the pointer is loop-invariant, return 0. Otherwise return /// std::nullopt. /// /// Ensure that it does not wrap in the address space, assuming the predicate /// associated with \p PSE is true. /// /// If necessary this method will version the stride of the pointer according /// to \p PtrToStride and therefore add further predicates to \p PSE. /// The \p Assume parameter indicates if we are allowed to make additional /// run-time assumptions. /// /// Note that the analysis results are defined if-and-only-if the original /// memory access was defined. If that access was dead, or UB, then the /// result of this function is undefined. std::optional<int64_t> getPtrStride(PredicatedScalarEvolution &PSE, Type *AccessTy, Value *Ptr, const Loop *Lp, const DenseMap<Value *, const SCEV *> &StridesMap = DenseMap<Value *, const SCEV *>(), bool Assume = false, bool ShouldCheckWrap = true); /// Returns the distance between the pointers \p PtrA and \p PtrB iff they are /// compatible and it is possible to calculate the distance between them. This /// is a simple API that does not depend on the analysis pass. /// \param StrictCheck Ensure that the calculated distance matches the /// type-based one after all the bitcasts removal in the provided pointers. std::optional<int> getPointersDiff(Type *ElemTyA, Value *PtrA, Type *ElemTyB, Value *PtrB, const DataLayout &DL, ScalarEvolution &SE, bool StrictCheck = false, bool CheckType = true); /// Attempt to sort the pointers in \p VL and return the sorted indices /// in \p SortedIndices, if reordering is required. /// /// Returns 'true' if sorting is legal, otherwise returns 'false'. /// /// For example, for a given \p VL of memory accesses in program order, a[i+4], /// a[i+0], a[i+1] and a[i+7], this function will sort the \p VL and save the /// sorted indices in \p SortedIndices as a[i+0], a[i+1], a[i+4], a[i+7] and /// saves the mask for actual memory accesses in program order in /// \p SortedIndices as <1,2,0,3> bool sortPtrAccesses(ArrayRef<Value *> VL, Type *ElemTy, const DataLayout &DL, ScalarEvolution &SE, SmallVectorImpl<unsigned> &SortedIndices); /// Returns true if the memory operations \p A and \p B are consecutive. /// This is a simple API that does not depend on the analysis pass. bool isConsecutiveAccess(Value *A, Value *B, const DataLayout &DL, ScalarEvolution &SE, bool CheckType = true); class LoopAccessInfoManager { … }; /// This analysis provides dependence information for the memory /// accesses of a loop. /// /// It runs the analysis for a loop on demand. This can be initiated by /// querying the loop access info via AM.getResult<LoopAccessAnalysis>. /// getResult return a LoopAccessInfo object. See this class for the /// specifics of what information is provided. class LoopAccessAnalysis : public AnalysisInfoMixin<LoopAccessAnalysis> { … }; inline Instruction *MemoryDepChecker::Dependence::getSource( const MemoryDepChecker &DepChecker) const { … } inline Instruction *MemoryDepChecker::Dependence::getDestination( const MemoryDepChecker &DepChecker) const { … } } // End llvm namespace #endif