MaximalStaticExpansion.cpp 19 KB

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