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