MaximalStaticExpansion.cpp 17 KB

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