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