1 //===--- polly/DependenceInfo.h - Polyhedral dependency analysis *- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Calculate the data dependency relations for a Scop using ISL. 10 // 11 // The integer set library (ISL) from Sven has an integrated dependency analysis 12 // to calculate data dependences. This pass takes advantage of this and 13 // calculates those dependences of a Scop. 14 // 15 // The dependences in this pass are exact in terms that for a specific read 16 // statement instance only the last write statement instance is returned. In 17 // case of may-writes, a set of possible write instances is returned. This 18 // analysis will never produce redundant dependences. 19 // 20 //===----------------------------------------------------------------------===// 21 22 #ifndef POLLY_DEPENDENCE_INFO_H 23 #define POLLY_DEPENDENCE_INFO_H 24 25 #include "polly/ScopPass.h" 26 #include "isl/ctx.h" 27 #include "isl/isl-noexceptions.h" 28 29 using namespace llvm; 30 31 namespace polly { 32 33 /// The accumulated dependence information for a SCoP. 34 /// 35 /// The Dependences struct holds all dependence information we collect and 36 /// compute for one SCoP. It also offers an interface that allows users to 37 /// query only specific parts. 38 struct Dependences { 39 // Granularities of the current dependence analysis 40 enum AnalysisLevel { 41 AL_Statement = 0, 42 // Distinguish accessed memory references in the same statement 43 AL_Reference, 44 // Distinguish memory access instances in the same statement 45 AL_Access, 46 47 NumAnalysisLevels 48 }; 49 50 /// Map type for reduction dependences. 51 using ReductionDependencesMapTy = DenseMap<MemoryAccess *, isl_map *>; 52 53 /// Map type to associate statements with schedules. 54 using StatementToIslMapTy = DenseMap<ScopStmt *, isl::map>; 55 56 /// The type of the dependences. 57 /// 58 /// Reduction dependences are separated from RAW/WAW/WAR dependences because 59 /// we can ignore them during the scheduling. That's because the order 60 /// in which the reduction statements are executed does not matter. However, 61 /// if they are executed in parallel we need to take additional measures 62 /// (e.g, privatization) to ensure a correct result. The (reverse) transitive 63 /// closure of the reduction dependences are used to check for parallel 64 /// executed reduction statements during code generation. These dependences 65 /// connect all instances of a reduction with each other, they are therefore 66 /// cyclic and possibly "reversed". 67 enum Type { 68 // Write after read 69 TYPE_WAR = 1 << 0, 70 71 // Read after write 72 TYPE_RAW = 1 << 1, 73 74 // Write after write 75 TYPE_WAW = 1 << 2, 76 77 // Reduction dependences 78 TYPE_RED = 1 << 3, 79 80 // Transitive closure of the reduction dependences (& the reverse) 81 TYPE_TC_RED = 1 << 4, 82 }; 83 getSharedIslCtxDependences84 const std::shared_ptr<isl_ctx> &getSharedIslCtx() const { return IslCtx; } 85 86 /// Get the dependences of type @p Kinds. 87 /// 88 /// @param Kinds This integer defines the different kinds of dependences 89 /// that will be returned. To return more than one kind, the 90 /// different kinds are 'ored' together. 91 isl::union_map getDependences(int Kinds) const; 92 93 /// Report if valid dependences are available. 94 bool hasValidDependences() const; 95 96 /// Return the reduction dependences caused by @p MA. 97 /// 98 /// @return The reduction dependences caused by @p MA or nullptr if none. 99 __isl_give isl_map *getReductionDependences(MemoryAccess *MA) const; 100 101 /// Return all reduction dependences. getReductionDependencesDependences102 const ReductionDependencesMapTy &getReductionDependences() const { 103 return ReductionDependences; 104 } 105 106 /// Check if a partial schedule is parallel wrt to @p Deps. 107 /// 108 /// @param Schedule The subset of the schedule space that we want to 109 /// check. 110 /// @param Deps The dependences @p Schedule needs to respect. 111 /// @param MinDistancePtr If not nullptr, the minimal dependence distance will 112 /// be returned at the address of that pointer 113 /// 114 /// @return Returns true, if executing parallel the outermost dimension of 115 /// @p Schedule is valid according to the dependences @p Deps. 116 bool isParallel(__isl_keep isl_union_map *Schedule, 117 __isl_take isl_union_map *Deps, 118 __isl_give isl_pw_aff **MinDistancePtr = nullptr) const; 119 120 /// Check if a new schedule is valid. 121 /// 122 /// @param S The current SCoP. 123 /// @param NewSchedules The new schedules 124 /// 125 /// @return True if the new schedule is valid, false if it reverses 126 /// dependences. 127 bool isValidSchedule(Scop &S, const StatementToIslMapTy &NewSchedules) const; 128 129 /// Print the stored dependence information. 130 void print(llvm::raw_ostream &OS) const; 131 132 /// Dump the dependence information stored to the dbgs stream. 133 void dump() const; 134 135 /// Return the granularity of this dependence analysis. getDependenceLevelDependences136 AnalysisLevel getDependenceLevel() { return Level; } 137 138 /// Allow the DependenceInfo access to private members and methods. 139 /// 140 /// To restrict access to the internal state, only the DependenceInfo class 141 /// is able to call or modify a Dependences struct. 142 friend struct DependenceAnalysis; 143 friend struct DependenceInfoPrinterPass; 144 friend class DependenceInfo; 145 friend class DependenceInfoWrapperPass; 146 147 /// Destructor that will free internal objects. ~DependencesDependences148 ~Dependences() { releaseMemory(); } 149 150 private: 151 /// Create an empty dependences struct. DependencesDependences152 explicit Dependences(const std::shared_ptr<isl_ctx> &IslCtx, 153 AnalysisLevel Level) 154 : RAW(nullptr), WAR(nullptr), WAW(nullptr), RED(nullptr), TC_RED(nullptr), 155 IslCtx(IslCtx), Level(Level) {} 156 157 /// Calculate and add at the privatization dependences. 158 void addPrivatizationDependences(); 159 160 /// Calculate the dependences for a certain SCoP @p S. 161 void calculateDependences(Scop &S); 162 163 /// Set the reduction dependences for @p MA to @p Deps. 164 void setReductionDependences(MemoryAccess *MA, __isl_take isl_map *Deps); 165 166 /// Free the objects associated with this Dependences struct. 167 /// 168 /// The Dependences struct will again be "empty" afterwards. 169 void releaseMemory(); 170 171 /// The different basic kinds of dependences we calculate. 172 isl_union_map *RAW; 173 isl_union_map *WAR; 174 isl_union_map *WAW; 175 176 /// The special reduction dependences. 177 isl_union_map *RED; 178 179 /// The (reverse) transitive closure of reduction dependences. 180 isl_union_map *TC_RED; 181 182 /// Mapping from memory accesses to their reduction dependences. 183 ReductionDependencesMapTy ReductionDependences; 184 185 /// Isl context from the SCoP. 186 std::shared_ptr<isl_ctx> IslCtx; 187 188 /// Granularity of this dependence analysis. 189 const AnalysisLevel Level; 190 }; 191 192 struct DependenceAnalysis : public AnalysisInfoMixin<DependenceAnalysis> { 193 static AnalysisKey Key; 194 struct Result { 195 Scop &S; 196 std::unique_ptr<Dependences> D[Dependences::NumAnalysisLevels]; 197 198 /// Return the dependence information for the current SCoP. 199 /// 200 /// @param Level The granularity of dependence analysis result. 201 /// 202 /// @return The dependence analysis result 203 /// 204 const Dependences &getDependences(Dependences::AnalysisLevel Level); 205 206 /// Recompute dependences from schedule and memory accesses. 207 const Dependences &recomputeDependences(Dependences::AnalysisLevel Level); 208 }; 209 Result run(Scop &S, ScopAnalysisManager &SAM, 210 ScopStandardAnalysisResults &SAR); 211 }; 212 213 struct DependenceInfoPrinterPass 214 : public PassInfoMixin<DependenceInfoPrinterPass> { DependenceInfoPrinterPassDependenceInfoPrinterPass215 DependenceInfoPrinterPass(raw_ostream &OS) : OS(OS) {} 216 217 PreservedAnalyses run(Scop &S, ScopAnalysisManager &, 218 ScopStandardAnalysisResults &, SPMUpdater &); 219 220 raw_ostream &OS; 221 }; 222 223 class DependenceInfo : public ScopPass { 224 public: 225 static char ID; 226 227 /// Construct a new DependenceInfo pass. DependenceInfo()228 DependenceInfo() : ScopPass(ID) {} 229 230 /// Return the dependence information for the current SCoP. 231 /// 232 /// @param Level The granularity of dependence analysis result. 233 /// 234 /// @return The dependence analysis result 235 /// 236 const Dependences &getDependences(Dependences::AnalysisLevel Level); 237 238 /// Recompute dependences from schedule and memory accesses. 239 const Dependences &recomputeDependences(Dependences::AnalysisLevel Level); 240 241 /// Compute the dependence information for the SCoP @p S. 242 bool runOnScop(Scop &S) override; 243 244 /// Print the dependences for the given SCoP to @p OS. 245 void printScop(raw_ostream &OS, Scop &) const override; 246 247 /// Release the internal memory. releaseMemory()248 void releaseMemory() override { 249 for (auto &d : D) 250 d.reset(); 251 } 252 253 /// Register all analyses and transformation required. 254 void getAnalysisUsage(AnalysisUsage &AU) const override; 255 256 private: 257 Scop *S; 258 259 /// Dependences struct for the current SCoP. 260 std::unique_ptr<Dependences> D[Dependences::NumAnalysisLevels]; 261 }; 262 263 /// Construct a new DependenceInfoWrapper pass. 264 class DependenceInfoWrapperPass : public FunctionPass { 265 public: 266 static char ID; 267 268 /// Construct a new DependenceInfoWrapper pass. DependenceInfoWrapperPass()269 DependenceInfoWrapperPass() : FunctionPass(ID) {} 270 271 /// Return the dependence information for the given SCoP. 272 /// 273 /// @param S SCoP object. 274 /// @param Level The granularity of dependence analysis result. 275 /// 276 /// @return The dependence analysis result 277 /// 278 const Dependences &getDependences(Scop *S, Dependences::AnalysisLevel Level); 279 280 /// Recompute dependences from schedule and memory accesses. 281 const Dependences &recomputeDependences(Scop *S, 282 Dependences::AnalysisLevel Level); 283 284 /// Compute the dependence information on-the-fly for the function. 285 bool runOnFunction(Function &F) override; 286 287 /// Print the dependences for the current function to @p OS. 288 void print(raw_ostream &OS, const Module *M = nullptr) const override; 289 290 /// Release the internal memory. releaseMemory()291 void releaseMemory() override { ScopToDepsMap.clear(); } 292 293 /// Register all analyses and transformation required. 294 void getAnalysisUsage(AnalysisUsage &AU) const override; 295 296 private: 297 using ScopToDepsMapTy = DenseMap<Scop *, std::unique_ptr<Dependences>>; 298 299 /// Scop to Dependence map for the current function. 300 ScopToDepsMapTy ScopToDepsMap; 301 }; 302 } // namespace polly 303 304 namespace llvm { 305 void initializeDependenceInfoPass(llvm::PassRegistry &); 306 void initializeDependenceInfoWrapperPassPass(llvm::PassRegistry &); 307 } // namespace llvm 308 309 #endif 310