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