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