1 //===- MaximalStaticExpansion.cpp -----------------------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This pass fully expand the memory accesses of a Scop to get rid of 11 // dependencies. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #include "polly/DependenceInfo.h" 16 #include "polly/LinkAllPasses.h" 17 #include "polly/ScopInfo.h" 18 #include "polly/ScopPass.h" 19 #include "polly/Support/GICHelper.h" 20 #include "polly/Support/ISLTools.h" 21 #include "llvm/ADT/SmallPtrSet.h" 22 #include "llvm/ADT/StringRef.h" 23 #include "llvm/Analysis/OptimizationRemarkEmitter.h" 24 #include "llvm/Pass.h" 25 #include "isl/isl-noexceptions.h" 26 #include "isl/union_map.h" 27 #include <cassert> 28 #include <limits> 29 #include <string> 30 #include <vector> 31 32 using namespace llvm; 33 using namespace polly; 34 35 #define DEBUG_TYPE "polly-mse" 36 37 namespace { 38 39 class MaximalStaticExpander : public ScopPass { 40 public: 41 static char ID; 42 43 explicit MaximalStaticExpander() : ScopPass(ID) {} 44 45 ~MaximalStaticExpander() override = default; 46 47 /// Expand the accesses of the SCoP. 48 /// 49 /// @param S The SCoP that must be expanded. 50 bool runOnScop(Scop &S) override; 51 52 /// Print the SCoP. 53 /// 54 /// @param OS The stream where to print. 55 /// @param S The SCop that must be printed. 56 void printScop(raw_ostream &OS, Scop &S) const override; 57 58 /// Register all analyses and transformations required. 59 void getAnalysisUsage(AnalysisUsage &AU) const override; 60 61 private: 62 /// OptimizationRemarkEmitter object for displaying diagnostic remarks. 63 OptimizationRemarkEmitter *ORE; 64 65 /// Emit remark 66 void emitRemark(StringRef Msg, Instruction *Inst); 67 68 /// Return true if the SAI in parameter is expandable. 69 /// 70 /// @param SAI the SAI that need to be checked. 71 /// @param Writes A set that will contains all the write accesses. 72 /// @param Reads A set that will contains all the read accesses. 73 /// @param S The SCop in which the SAI is in. 74 /// @param Dependences The RAW dependences of the SCop. 75 bool isExpandable(const ScopArrayInfo *SAI, 76 SmallPtrSetImpl<MemoryAccess *> &Writes, 77 SmallPtrSetImpl<MemoryAccess *> &Reads, Scop &S, 78 const isl::union_map &Dependences); 79 80 /// Expand the MemoryAccess according to its domain. 81 /// 82 /// @param S The SCop in which the memory access appears in. 83 /// @param MA The memory access that need to be expanded. 84 ScopArrayInfo *expandAccess(Scop &S, MemoryAccess *MA); 85 86 /// Filter the dependences to have only one related to current memory access. 87 /// 88 /// @param S The SCop in which the memory access appears in. 89 /// @param MapDependences The dependences to filter. 90 /// @param MA The memory access that need to be expanded. 91 isl::union_map filterDependences(Scop &S, 92 const isl::union_map &MapDependences, 93 MemoryAccess *MA); 94 95 /// Expand the MemoryAccess according to Dependences and already expanded 96 /// MemoryAccesses. 97 /// 98 /// @param The SCop in which the memory access appears in. 99 /// @param The memory access that need to be expanded. 100 /// @param Dependences The RAW dependences of the SCop. 101 /// @param ExpandedSAI The expanded SAI created during write expansion. 102 /// @param Reverse if true, the Dependences union_map is reversed before 103 /// intersection. 104 void mapAccess(Scop &S, SmallPtrSetImpl<MemoryAccess *> &Accesses, 105 const isl::union_map &Dependences, ScopArrayInfo *ExpandedSAI, 106 bool Reverse); 107 108 /// Expand PHI memory accesses. 109 /// 110 /// @param The SCop in which the memory access appears in. 111 /// @param The ScopArrayInfo representing the PHI accesses to expand. 112 /// @param Dependences The RAW dependences of the SCop. 113 void expandPhi(Scop &S, const ScopArrayInfo *SAI, 114 const isl::union_map &Dependences); 115 }; 116 } // namespace 117 118 #ifndef NDEBUG 119 /// Whether a dimension of a set is bounded (lower and upper) by a constant, 120 /// i.e. there are two constants Min and Max, such that every value x of the 121 /// chosen dimensions is Min <= x <= Max. 122 static bool isDimBoundedByConstant(isl::set Set, unsigned dim) { 123 auto ParamDims = Set.dim(isl::dim::param); 124 Set = Set.project_out(isl::dim::param, 0, ParamDims); 125 Set = Set.project_out(isl::dim::set, 0, dim); 126 auto SetDims = Set.dim(isl::dim::set); 127 Set = Set.project_out(isl::dim::set, 1, SetDims - 1); 128 return bool(Set.is_bounded()); 129 } 130 #endif 131 132 char MaximalStaticExpander::ID = 0; 133 134 isl::union_map MaximalStaticExpander::filterDependences( 135 Scop &S, const isl::union_map &Dependences, MemoryAccess *MA) { 136 auto SAI = MA->getLatestScopArrayInfo(); 137 138 auto AccessDomainSet = MA->getAccessRelation().domain(); 139 auto AccessDomainId = AccessDomainSet.get_tuple_id(); 140 141 isl::union_map MapDependences = isl::union_map::empty(S.getParamSpace()); 142 143 Dependences.foreach_map([&MapDependences, &AccessDomainId, 144 &SAI](isl::map Map) -> isl::stat { 145 // Filter out Statement to Statement dependences. 146 if (!Map.can_curry()) 147 return isl::stat::ok; 148 149 // Intersect with the relevant SAI. 150 auto TmpMapDomainId = 151 Map.get_space().domain().unwrap().range().get_tuple_id(isl::dim::set); 152 153 ScopArrayInfo *UserSAI = 154 static_cast<ScopArrayInfo *>(TmpMapDomainId.get_user()); 155 156 if (SAI != UserSAI) 157 return isl::stat::ok; 158 159 // Get the correct S1[] -> S2[] dependence. 160 auto NewMap = Map.factor_domain(); 161 auto NewMapDomainId = NewMap.domain().get_tuple_id(); 162 163 if (AccessDomainId.get() != NewMapDomainId.get()) 164 return isl::stat::ok; 165 166 // Add the corresponding map to MapDependences. 167 MapDependences = MapDependences.add_map(NewMap); 168 169 return isl::stat::ok; 170 }); 171 172 return MapDependences; 173 } 174 175 bool MaximalStaticExpander::isExpandable( 176 const ScopArrayInfo *SAI, SmallPtrSetImpl<MemoryAccess *> &Writes, 177 SmallPtrSetImpl<MemoryAccess *> &Reads, Scop &S, 178 const isl::union_map &Dependences) { 179 if (SAI->isValueKind()) { 180 Writes.insert(S.getValueDef(SAI)); 181 for (auto MA : S.getValueUses(SAI)) 182 Reads.insert(MA); 183 return true; 184 } else if (SAI->isPHIKind()) { 185 auto Read = S.getPHIRead(SAI); 186 187 auto StmtDomain = isl::union_set(Read->getStatement()->getDomain()); 188 189 auto Writes = S.getPHIIncomings(SAI); 190 191 // Get the domain where all the writes are writing to. 192 auto WriteDomain = isl::union_set::empty(S.getParamSpace()); 193 194 for (auto Write : Writes) { 195 auto MapDeps = filterDependences(S, Dependences, Write); 196 MapDeps.foreach_map([&WriteDomain](isl::map Map) -> isl::stat { 197 WriteDomain = WriteDomain.add_set(Map.range()); 198 return isl::stat::ok; 199 }); 200 } 201 202 // For now, read from original scalar is not possible. 203 if (!StmtDomain.is_equal(WriteDomain)) { 204 emitRemark(SAI->getName() + " read from its original value.", 205 Read->getAccessInstruction()); 206 return false; 207 } 208 209 return true; 210 } else if (SAI->isExitPHIKind()) { 211 // For now, we are not able to expand ExitPhi. 212 emitRemark(SAI->getName() + " is a ExitPhi node.", 213 S.getEnteringBlock()->getFirstNonPHI()); 214 return false; 215 } 216 217 int NumberWrites = 0; 218 for (ScopStmt &Stmt : S) { 219 auto StmtReads = isl::union_map::empty(S.getParamSpace()); 220 auto StmtWrites = isl::union_map::empty(S.getParamSpace()); 221 222 for (MemoryAccess *MA : Stmt) { 223 // Check if the current MemoryAccess involved the current SAI. 224 if (SAI != MA->getLatestScopArrayInfo()) 225 continue; 226 227 // For now, we are not able to expand array where read come after write 228 // (to the same location) in a same statement. 229 auto AccRel = isl::union_map(MA->getAccessRelation()); 230 if (MA->isRead()) { 231 // Reject load after store to same location. 232 if (!StmtWrites.is_disjoint(AccRel)) { 233 emitRemark(SAI->getName() + " has read after write to the same " 234 "element in same statement. The " 235 "dependences found during analysis may " 236 "be wrong because Polly is not able to " 237 "handle such case for now.", 238 MA->getAccessInstruction()); 239 return false; 240 } 241 242 StmtReads = StmtReads.unite(AccRel); 243 } else { 244 StmtWrites = StmtWrites.unite(AccRel); 245 } 246 247 // For now, we are not able to expand MayWrite. 248 if (MA->isMayWrite()) { 249 emitRemark(SAI->getName() + " has a maywrite access.", 250 MA->getAccessInstruction()); 251 return false; 252 } 253 254 // For now, we are not able to expand SAI with more than one write. 255 if (MA->isMustWrite()) { 256 Writes.insert(MA); 257 NumberWrites++; 258 if (NumberWrites > 1) { 259 emitRemark(SAI->getName() + " has more than 1 write access.", 260 MA->getAccessInstruction()); 261 return false; 262 } 263 } 264 265 // Check if it is possible to expand this read. 266 if (MA->isRead()) { 267 // Get the domain of the current ScopStmt. 268 auto StmtDomain = Stmt.getDomain(); 269 270 // Get the domain of the future Read access. 271 auto ReadDomainSet = MA->getAccessRelation().domain(); 272 auto ReadDomain = isl::union_set(ReadDomainSet); 273 274 // Get the dependences relevant for this MA 275 auto MapDependences = filterDependences(S, Dependences.reverse(), MA); 276 unsigned NumberElementMap = isl_union_map_n_map(MapDependences.get()); 277 278 if (NumberElementMap == 0) { 279 emitRemark("The expansion of " + SAI->getName() + 280 " would lead to a read from the original array.", 281 MA->getAccessInstruction()); 282 return false; 283 } 284 285 auto DepsDomain = MapDependences.domain(); 286 287 // If there are multiple maps in the Deps, we cannot handle this case 288 // for now. 289 if (NumberElementMap != 1) { 290 emitRemark(SAI->getName() + 291 " has too many dependences to be handle for now.", 292 MA->getAccessInstruction()); 293 return false; 294 } 295 296 auto DepsDomainSet = isl::set(DepsDomain); 297 298 // For now, read from the original array is not possible. 299 if (!StmtDomain.is_subset(DepsDomainSet)) { 300 emitRemark("The expansion of " + SAI->getName() + 301 " would lead to a read from the original array.", 302 MA->getAccessInstruction()); 303 return false; 304 } 305 306 Reads.insert(MA); 307 } 308 } 309 } 310 311 // No need to expand SAI with no write. 312 if (NumberWrites == 0) { 313 emitRemark(SAI->getName() + " has 0 write access.", 314 S.getEnteringBlock()->getFirstNonPHI()); 315 return false; 316 } 317 318 return true; 319 } 320 321 void MaximalStaticExpander::mapAccess(Scop &S, 322 SmallPtrSetImpl<MemoryAccess *> &Accesses, 323 const isl::union_map &Dependences, 324 ScopArrayInfo *ExpandedSAI, 325 bool Reverse) { 326 for (auto MA : Accesses) { 327 // Get the current AM. 328 auto CurrentAccessMap = MA->getAccessRelation(); 329 330 // Get RAW dependences for the current WA. 331 auto DomainSet = MA->getAccessRelation().domain(); 332 auto Domain = isl::union_set(DomainSet); 333 334 // Get the dependences relevant for this MA. 335 isl::union_map MapDependences = 336 filterDependences(S, Reverse ? Dependences.reverse() : Dependences, MA); 337 338 // If no dependences, no need to modify anything. 339 if (MapDependences.is_empty()) 340 return; 341 342 assert(isl_union_map_n_map(MapDependences.get()) == 1 && 343 "There are more than one RAW dependencies in the union map."); 344 auto NewAccessMap = isl::map::from_union_map(MapDependences); 345 346 auto Id = ExpandedSAI->getBasePtrId(); 347 348 // Replace the out tuple id with the one of the access array. 349 NewAccessMap = NewAccessMap.set_tuple_id(isl::dim::out, Id); 350 351 // Set the new access relation. 352 MA->setNewAccessRelation(NewAccessMap); 353 } 354 } 355 356 ScopArrayInfo *MaximalStaticExpander::expandAccess(Scop &S, MemoryAccess *MA) { 357 // Get the current AM. 358 auto CurrentAccessMap = MA->getAccessRelation(); 359 360 unsigned in_dimensions = CurrentAccessMap.dim(isl::dim::in); 361 362 // Get domain from the current AM. 363 auto Domain = CurrentAccessMap.domain(); 364 365 // Create a new AM from the domain. 366 auto NewAccessMap = isl::map::from_domain(Domain); 367 368 // Add dimensions to the new AM according to the current in_dim. 369 NewAccessMap = NewAccessMap.add_dims(isl::dim::out, in_dimensions); 370 371 // Create the string representing the name of the new SAI. 372 // One new SAI for each statement so that each write go to a different memory 373 // cell. 374 auto CurrentStmtDomain = MA->getStatement()->getDomain(); 375 auto CurrentStmtName = CurrentStmtDomain.get_tuple_name(); 376 auto CurrentOutId = CurrentAccessMap.get_tuple_id(isl::dim::out); 377 std::string CurrentOutIdString = 378 MA->getScopArrayInfo()->getName() + "_" + CurrentStmtName + "_expanded"; 379 380 // Set the tuple id for the out dimension. 381 NewAccessMap = NewAccessMap.set_tuple_id(isl::dim::out, CurrentOutId); 382 383 // Create the size vector. 384 std::vector<unsigned> Sizes; 385 for (unsigned i = 0; i < in_dimensions; i++) { 386 assert(isDimBoundedByConstant(CurrentStmtDomain, i) && 387 "Domain boundary are not constant."); 388 auto UpperBound = getConstant(CurrentStmtDomain.dim_max(i), true, false); 389 assert(!UpperBound.is_null() && UpperBound.is_pos() && 390 !UpperBound.is_nan() && 391 "The upper bound is not a positive integer."); 392 assert(UpperBound.le(isl::val(CurrentAccessMap.get_ctx(), 393 std::numeric_limits<int>::max() - 1)) && 394 "The upper bound overflow a int."); 395 Sizes.push_back(UpperBound.get_num_si() + 1); 396 } 397 398 // Get the ElementType of the current SAI. 399 auto ElementType = MA->getLatestScopArrayInfo()->getElementType(); 400 401 // Create (or get if already existing) the new expanded SAI. 402 auto ExpandedSAI = 403 S.createScopArrayInfo(ElementType, CurrentOutIdString, Sizes); 404 ExpandedSAI->setIsOnHeap(true); 405 406 // Get the out Id of the expanded Array. 407 auto NewOutId = ExpandedSAI->getBasePtrId(); 408 409 // Set the out id of the new AM to the new SAI id. 410 NewAccessMap = NewAccessMap.set_tuple_id(isl::dim::out, NewOutId); 411 412 // Add constraints to linked output with input id. 413 auto SpaceMap = NewAccessMap.get_space(); 414 auto ConstraintBasicMap = 415 isl::basic_map::equal(SpaceMap, SpaceMap.dim(isl::dim::in)); 416 NewAccessMap = isl::map(ConstraintBasicMap); 417 418 // Set the new access relation map. 419 MA->setNewAccessRelation(NewAccessMap); 420 421 return ExpandedSAI; 422 } 423 424 void MaximalStaticExpander::expandPhi(Scop &S, const ScopArrayInfo *SAI, 425 const isl::union_map &Dependences) { 426 SmallPtrSet<MemoryAccess *, 4> Writes; 427 for (auto MA : S.getPHIIncomings(SAI)) 428 Writes.insert(MA); 429 auto Read = S.getPHIRead(SAI); 430 auto ExpandedSAI = expandAccess(S, Read); 431 432 mapAccess(S, Writes, Dependences, ExpandedSAI, false); 433 } 434 435 void MaximalStaticExpander::emitRemark(StringRef Msg, Instruction *Inst) { 436 ORE->emit(OptimizationRemarkAnalysis(DEBUG_TYPE, "ExpansionRejection", Inst) 437 << Msg); 438 } 439 440 bool MaximalStaticExpander::runOnScop(Scop &S) { 441 // Get the ORE from OptimizationRemarkEmitterWrapperPass. 442 ORE = &(getAnalysis<OptimizationRemarkEmitterWrapperPass>().getORE()); 443 444 // Get the RAW Dependences. 445 auto &DI = getAnalysis<DependenceInfo>(); 446 auto &D = DI.getDependences(Dependences::AL_Reference); 447 auto Dependences = isl::manage(D.getDependences(Dependences::TYPE_RAW)); 448 449 SmallVector<ScopArrayInfo *, 4> CurrentSAI(S.arrays().begin(), 450 S.arrays().end()); 451 452 for (auto SAI : CurrentSAI) { 453 SmallPtrSet<MemoryAccess *, 4> AllWrites; 454 SmallPtrSet<MemoryAccess *, 4> AllReads; 455 if (!isExpandable(SAI, AllWrites, AllReads, S, Dependences)) 456 continue; 457 458 if (SAI->isValueKind() || SAI->isArrayKind()) { 459 assert(AllWrites.size() == 1 || SAI->isValueKind()); 460 461 auto TheWrite = *(AllWrites.begin()); 462 ScopArrayInfo *ExpandedArray = expandAccess(S, TheWrite); 463 464 mapAccess(S, AllReads, Dependences, ExpandedArray, true); 465 } else if (SAI->isPHIKind()) { 466 expandPhi(S, SAI, Dependences); 467 } 468 } 469 470 return false; 471 } 472 473 void MaximalStaticExpander::printScop(raw_ostream &OS, Scop &S) const { 474 S.print(OS, false); 475 } 476 477 void MaximalStaticExpander::getAnalysisUsage(AnalysisUsage &AU) const { 478 ScopPass::getAnalysisUsage(AU); 479 AU.addRequired<DependenceInfo>(); 480 AU.addRequired<OptimizationRemarkEmitterWrapperPass>(); 481 } 482 483 Pass *polly::createMaximalStaticExpansionPass() { 484 return new MaximalStaticExpander(); 485 } 486 487 INITIALIZE_PASS_BEGIN(MaximalStaticExpander, "polly-mse", 488 "Polly - Maximal static expansion of SCoP", false, false); 489 INITIALIZE_PASS_DEPENDENCY(DependenceInfo); 490 INITIALIZE_PASS_DEPENDENCY(OptimizationRemarkEmitterWrapperPass); 491 INITIALIZE_PASS_END(MaximalStaticExpander, "polly-mse", 492 "Polly - Maximal static expansion of SCoP", false, false) 493