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