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