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 = 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 = Set.dim(isl::dim::set); 125 Set = Set.project_out(isl::dim::set, 1, SetDims - 1); 126 return bool(Set.is_bounded()); 127 } 128 #endif 129 130 char MaximalStaticExpander::ID = 0; 131 132 isl::union_map MaximalStaticExpander::filterDependences( 133 Scop &S, const isl::union_map &Dependences, MemoryAccess *MA) { 134 auto SAI = MA->getLatestScopArrayInfo(); 135 136 auto AccessDomainSet = MA->getAccessRelation().domain(); 137 auto AccessDomainId = AccessDomainSet.get_tuple_id(); 138 139 isl::union_map MapDependences = isl::union_map::empty(S.getParamSpace()); 140 141 for (isl::map Map : Dependences.get_map_list()) { 142 // Filter out Statement to Statement dependences. 143 if (!Map.can_curry()) 144 continue; 145 146 // Intersect with the relevant SAI. 147 auto TmpMapDomainId = 148 Map.get_space().domain().unwrap().range().get_tuple_id(isl::dim::set); 149 150 ScopArrayInfo *UserSAI = 151 static_cast<ScopArrayInfo *>(TmpMapDomainId.get_user()); 152 153 if (SAI != UserSAI) 154 continue; 155 156 // Get the correct S1[] -> S2[] dependence. 157 auto NewMap = Map.factor_domain(); 158 auto NewMapDomainId = NewMap.domain().get_tuple_id(); 159 160 if (AccessDomainId.get() != NewMapDomainId.get()) 161 continue; 162 163 // Add the corresponding map to MapDependences. 164 MapDependences = MapDependences.add_map(NewMap); 165 } 166 167 return MapDependences; 168 } 169 170 bool MaximalStaticExpander::isExpandable( 171 const ScopArrayInfo *SAI, SmallPtrSetImpl<MemoryAccess *> &Writes, 172 SmallPtrSetImpl<MemoryAccess *> &Reads, Scop &S, 173 const isl::union_map &Dependences) { 174 if (SAI->isValueKind()) { 175 Writes.insert(S.getValueDef(SAI)); 176 for (auto MA : S.getValueUses(SAI)) 177 Reads.insert(MA); 178 return true; 179 } else if (SAI->isPHIKind()) { 180 auto Read = S.getPHIRead(SAI); 181 182 auto StmtDomain = isl::union_set(Read->getStatement()->getDomain()); 183 184 auto Writes = S.getPHIIncomings(SAI); 185 186 // Get the domain where all the writes are writing to. 187 auto WriteDomain = isl::union_set::empty(S.getParamSpace()); 188 189 for (auto Write : Writes) { 190 auto MapDeps = filterDependences(S, Dependences, Write); 191 for (isl::map Map : MapDeps.get_map_list()) 192 WriteDomain = WriteDomain.add_set(Map.range()); 193 } 194 195 // For now, read from original scalar is not possible. 196 if (!StmtDomain.is_equal(WriteDomain)) { 197 emitRemark(SAI->getName() + " read from its original value.", 198 Read->getAccessInstruction()); 199 return false; 200 } 201 202 return true; 203 } else if (SAI->isExitPHIKind()) { 204 // For now, we are not able to expand ExitPhi. 205 emitRemark(SAI->getName() + " is a ExitPhi node.", 206 S.getEnteringBlock()->getFirstNonPHI()); 207 return false; 208 } 209 210 int NumberWrites = 0; 211 for (ScopStmt &Stmt : S) { 212 auto StmtReads = isl::union_map::empty(S.getParamSpace()); 213 auto StmtWrites = isl::union_map::empty(S.getParamSpace()); 214 215 for (MemoryAccess *MA : Stmt) { 216 // Check if the current MemoryAccess involved the current SAI. 217 if (SAI != MA->getLatestScopArrayInfo()) 218 continue; 219 220 // For now, we are not able to expand array where read come after write 221 // (to the same location) in a same statement. 222 auto AccRel = isl::union_map(MA->getAccessRelation()); 223 if (MA->isRead()) { 224 // Reject load after store to same location. 225 if (!StmtWrites.is_disjoint(AccRel)) { 226 emitRemark(SAI->getName() + " has read after write to the same " 227 "element in same statement. The " 228 "dependences found during analysis may " 229 "be wrong because Polly is not able to " 230 "handle such case for now.", 231 MA->getAccessInstruction()); 232 return false; 233 } 234 235 StmtReads = StmtReads.unite(AccRel); 236 } else { 237 StmtWrites = StmtWrites.unite(AccRel); 238 } 239 240 // For now, we are not able to expand MayWrite. 241 if (MA->isMayWrite()) { 242 emitRemark(SAI->getName() + " has a maywrite access.", 243 MA->getAccessInstruction()); 244 return false; 245 } 246 247 // For now, we are not able to expand SAI with more than one write. 248 if (MA->isMustWrite()) { 249 Writes.insert(MA); 250 NumberWrites++; 251 if (NumberWrites > 1) { 252 emitRemark(SAI->getName() + " has more than 1 write access.", 253 MA->getAccessInstruction()); 254 return false; 255 } 256 } 257 258 // Check if it is possible to expand this read. 259 if (MA->isRead()) { 260 // Get the domain of the current ScopStmt. 261 auto StmtDomain = Stmt.getDomain(); 262 263 // Get the domain of the future Read access. 264 auto ReadDomainSet = MA->getAccessRelation().domain(); 265 auto ReadDomain = isl::union_set(ReadDomainSet); 266 267 // Get the dependences relevant for this MA 268 auto MapDependences = filterDependences(S, Dependences.reverse(), MA); 269 unsigned NumberElementMap = isl_union_map_n_map(MapDependences.get()); 270 271 if (NumberElementMap == 0) { 272 emitRemark("The expansion of " + SAI->getName() + 273 " would lead to a read from the original array.", 274 MA->getAccessInstruction()); 275 return false; 276 } 277 278 auto DepsDomain = MapDependences.domain(); 279 280 // If there are multiple maps in the Deps, we cannot handle this case 281 // for now. 282 if (NumberElementMap != 1) { 283 emitRemark(SAI->getName() + 284 " has too many dependences to be handle for now.", 285 MA->getAccessInstruction()); 286 return false; 287 } 288 289 auto DepsDomainSet = isl::set(DepsDomain); 290 291 // For now, read from the original array is not possible. 292 if (!StmtDomain.is_subset(DepsDomainSet)) { 293 emitRemark("The expansion of " + SAI->getName() + 294 " would lead to a read from the original array.", 295 MA->getAccessInstruction()); 296 return false; 297 } 298 299 Reads.insert(MA); 300 } 301 } 302 } 303 304 // No need to expand SAI with no write. 305 if (NumberWrites == 0) { 306 emitRemark(SAI->getName() + " has 0 write access.", 307 S.getEnteringBlock()->getFirstNonPHI()); 308 return false; 309 } 310 311 return true; 312 } 313 314 void MaximalStaticExpander::mapAccess(Scop &S, 315 SmallPtrSetImpl<MemoryAccess *> &Accesses, 316 const isl::union_map &Dependences, 317 ScopArrayInfo *ExpandedSAI, 318 bool Reverse) { 319 for (auto MA : Accesses) { 320 // Get the current AM. 321 auto CurrentAccessMap = MA->getAccessRelation(); 322 323 // Get RAW dependences for the current WA. 324 auto DomainSet = MA->getAccessRelation().domain(); 325 auto Domain = isl::union_set(DomainSet); 326 327 // Get the dependences relevant for this MA. 328 isl::union_map MapDependences = 329 filterDependences(S, Reverse ? Dependences.reverse() : Dependences, MA); 330 331 // If no dependences, no need to modify anything. 332 if (MapDependences.is_empty()) 333 return; 334 335 assert(isl_union_map_n_map(MapDependences.get()) == 1 && 336 "There are more than one RAW dependencies in the union map."); 337 auto NewAccessMap = isl::map::from_union_map(MapDependences); 338 339 auto Id = ExpandedSAI->getBasePtrId(); 340 341 // Replace the out tuple id with the one of the access array. 342 NewAccessMap = NewAccessMap.set_tuple_id(isl::dim::out, Id); 343 344 // Set the new access relation. 345 MA->setNewAccessRelation(NewAccessMap); 346 } 347 } 348 349 ScopArrayInfo *MaximalStaticExpander::expandAccess(Scop &S, MemoryAccess *MA) { 350 // Get the current AM. 351 auto CurrentAccessMap = MA->getAccessRelation(); 352 353 unsigned in_dimensions = CurrentAccessMap.dim(isl::dim::in); 354 355 // Get domain from the current AM. 356 auto Domain = CurrentAccessMap.domain(); 357 358 // Create a new AM from the domain. 359 auto NewAccessMap = isl::map::from_domain(Domain); 360 361 // Add dimensions to the new AM according to the current in_dim. 362 NewAccessMap = NewAccessMap.add_dims(isl::dim::out, in_dimensions); 363 364 // Create the string representing the name of the new SAI. 365 // One new SAI for each statement so that each write go to a different memory 366 // cell. 367 auto CurrentStmtDomain = MA->getStatement()->getDomain(); 368 auto CurrentStmtName = CurrentStmtDomain.get_tuple_name(); 369 auto CurrentOutId = CurrentAccessMap.get_tuple_id(isl::dim::out); 370 std::string CurrentOutIdString = 371 MA->getScopArrayInfo()->getName() + "_" + CurrentStmtName + "_expanded"; 372 373 // Set the tuple id for the out dimension. 374 NewAccessMap = NewAccessMap.set_tuple_id(isl::dim::out, CurrentOutId); 375 376 // Create the size vector. 377 std::vector<unsigned> Sizes; 378 for (unsigned i = 0; i < in_dimensions; i++) { 379 assert(isDimBoundedByConstant(CurrentStmtDomain, i) && 380 "Domain boundary are not constant."); 381 auto UpperBound = getConstant(CurrentStmtDomain.dim_max(i), true, false); 382 assert(!UpperBound.is_null() && UpperBound.is_pos() && 383 !UpperBound.is_nan() && 384 "The upper bound is not a positive integer."); 385 assert(UpperBound.le(isl::val(CurrentAccessMap.get_ctx(), 386 std::numeric_limits<int>::max() - 1)) && 387 "The upper bound overflow a int."); 388 Sizes.push_back(UpperBound.get_num_si() + 1); 389 } 390 391 // Get the ElementType of the current SAI. 392 auto ElementType = MA->getLatestScopArrayInfo()->getElementType(); 393 394 // Create (or get if already existing) the new expanded SAI. 395 auto ExpandedSAI = 396 S.createScopArrayInfo(ElementType, CurrentOutIdString, Sizes); 397 ExpandedSAI->setIsOnHeap(true); 398 399 // Get the out Id of the expanded Array. 400 auto NewOutId = ExpandedSAI->getBasePtrId(); 401 402 // Set the out id of the new AM to the new SAI id. 403 NewAccessMap = NewAccessMap.set_tuple_id(isl::dim::out, NewOutId); 404 405 // Add constraints to linked output with input id. 406 auto SpaceMap = NewAccessMap.get_space(); 407 auto ConstraintBasicMap = 408 isl::basic_map::equal(SpaceMap, SpaceMap.dim(isl::dim::in)); 409 NewAccessMap = isl::map(ConstraintBasicMap); 410 411 // Set the new access relation map. 412 MA->setNewAccessRelation(NewAccessMap); 413 414 return ExpandedSAI; 415 } 416 417 void MaximalStaticExpander::expandPhi(Scop &S, const ScopArrayInfo *SAI, 418 const isl::union_map &Dependences) { 419 SmallPtrSet<MemoryAccess *, 4> Writes; 420 for (auto MA : S.getPHIIncomings(SAI)) 421 Writes.insert(MA); 422 auto Read = S.getPHIRead(SAI); 423 auto ExpandedSAI = expandAccess(S, Read); 424 425 mapAccess(S, Writes, Dependences, ExpandedSAI, false); 426 } 427 428 void MaximalStaticExpander::emitRemark(StringRef Msg, Instruction *Inst) { 429 ORE->emit(OptimizationRemarkAnalysis(DEBUG_TYPE, "ExpansionRejection", Inst) 430 << Msg); 431 } 432 433 bool MaximalStaticExpander::runOnScop(Scop &S) { 434 // Get the ORE from OptimizationRemarkEmitterWrapperPass. 435 ORE = &(getAnalysis<OptimizationRemarkEmitterWrapperPass>().getORE()); 436 437 // Get the RAW Dependences. 438 auto &DI = getAnalysis<DependenceInfo>(); 439 auto &D = DI.getDependences(Dependences::AL_Reference); 440 isl::union_map Dependences = D.getDependences(Dependences::TYPE_RAW); 441 442 SmallVector<ScopArrayInfo *, 4> CurrentSAI(S.arrays().begin(), 443 S.arrays().end()); 444 445 for (auto SAI : CurrentSAI) { 446 SmallPtrSet<MemoryAccess *, 4> AllWrites; 447 SmallPtrSet<MemoryAccess *, 4> AllReads; 448 if (!isExpandable(SAI, AllWrites, AllReads, S, Dependences)) 449 continue; 450 451 if (SAI->isValueKind() || SAI->isArrayKind()) { 452 assert(AllWrites.size() == 1 || SAI->isValueKind()); 453 454 auto TheWrite = *(AllWrites.begin()); 455 ScopArrayInfo *ExpandedArray = expandAccess(S, TheWrite); 456 457 mapAccess(S, AllReads, Dependences, ExpandedArray, true); 458 } else if (SAI->isPHIKind()) { 459 expandPhi(S, SAI, Dependences); 460 } 461 } 462 463 return false; 464 } 465 466 void MaximalStaticExpander::printScop(raw_ostream &OS, Scop &S) const { 467 S.print(OS, false); 468 } 469 470 void MaximalStaticExpander::getAnalysisUsage(AnalysisUsage &AU) const { 471 ScopPass::getAnalysisUsage(AU); 472 AU.addRequired<DependenceInfo>(); 473 AU.addRequired<OptimizationRemarkEmitterWrapperPass>(); 474 } 475 476 Pass *polly::createMaximalStaticExpansionPass() { 477 return new MaximalStaticExpander(); 478 } 479 480 INITIALIZE_PASS_BEGIN(MaximalStaticExpander, "polly-mse", 481 "Polly - Maximal static expansion of SCoP", false, false); 482 INITIALIZE_PASS_DEPENDENCY(DependenceInfo); 483 INITIALIZE_PASS_DEPENDENCY(OptimizationRemarkEmitterWrapperPass); 484 INITIALIZE_PASS_END(MaximalStaticExpander, "polly-mse", 485 "Polly - Maximal static expansion of SCoP", false, false) 486