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