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