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