/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/polly/lib/Transform/DeLICM.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===------ DeLICM.cpp -----------------------------------------*- C++ -*-===// |
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 | | // Undo the effect of Loop Invariant Code Motion (LICM) and |
10 | | // GVN Partial Redundancy Elimination (PRE) on SCoP-level. |
11 | | // |
12 | | // Namely, remove register/scalar dependencies by mapping them back to array |
13 | | // elements. |
14 | | // |
15 | | //===----------------------------------------------------------------------===// |
16 | | |
17 | | #include "polly/DeLICM.h" |
18 | | #include "polly/LinkAllPasses.h" |
19 | | #include "polly/Options.h" |
20 | | #include "polly/ScopInfo.h" |
21 | | #include "polly/ScopPass.h" |
22 | | #include "polly/Support/GICHelper.h" |
23 | | #include "polly/Support/ISLOStream.h" |
24 | | #include "polly/Support/ISLTools.h" |
25 | | #include "polly/ZoneAlgo.h" |
26 | | #include "llvm/ADT/Statistic.h" |
27 | | |
28 | 31 | #define DEBUG_TYPE "polly-delicm" |
29 | | |
30 | | using namespace polly; |
31 | | using namespace llvm; |
32 | | |
33 | | namespace { |
34 | | |
35 | | cl::opt<int> |
36 | | DelicmMaxOps("polly-delicm-max-ops", |
37 | | cl::desc("Maximum number of isl operations to invest for " |
38 | | "lifetime analysis; 0=no limit"), |
39 | | cl::init(1000000), cl::cat(PollyCategory)); |
40 | | |
41 | | cl::opt<bool> DelicmOverapproximateWrites( |
42 | | "polly-delicm-overapproximate-writes", |
43 | | cl::desc( |
44 | | "Do more PHI writes than necessary in order to avoid partial accesses"), |
45 | | cl::init(false), cl::Hidden, cl::cat(PollyCategory)); |
46 | | |
47 | | cl::opt<bool> DelicmPartialWrites("polly-delicm-partial-writes", |
48 | | cl::desc("Allow partial writes"), |
49 | | cl::init(true), cl::Hidden, |
50 | | cl::cat(PollyCategory)); |
51 | | |
52 | | cl::opt<bool> |
53 | | DelicmComputeKnown("polly-delicm-compute-known", |
54 | | cl::desc("Compute known content of array elements"), |
55 | | cl::init(true), cl::Hidden, cl::cat(PollyCategory)); |
56 | | |
57 | | STATISTIC(DeLICMAnalyzed, "Number of successfully analyzed SCoPs"); |
58 | | STATISTIC(DeLICMOutOfQuota, |
59 | | "Analyses aborted because max_operations was reached"); |
60 | | STATISTIC(MappedValueScalars, "Number of mapped Value scalars"); |
61 | | STATISTIC(MappedPHIScalars, "Number of mapped PHI scalars"); |
62 | | STATISTIC(TargetsMapped, "Number of stores used for at least one mapping"); |
63 | | STATISTIC(DeLICMScopsModified, "Number of SCoPs optimized"); |
64 | | |
65 | | STATISTIC(NumValueWrites, "Number of scalar value writes after DeLICM"); |
66 | | STATISTIC(NumValueWritesInLoops, |
67 | | "Number of scalar value writes nested in affine loops after DeLICM"); |
68 | | STATISTIC(NumPHIWrites, "Number of scalar phi writes after DeLICM"); |
69 | | STATISTIC(NumPHIWritesInLoops, |
70 | | "Number of scalar phi writes nested in affine loops after DeLICM"); |
71 | | STATISTIC(NumSingletonWrites, "Number of singleton writes after DeLICM"); |
72 | | STATISTIC(NumSingletonWritesInLoops, |
73 | | "Number of singleton writes nested in affine loops after DeLICM"); |
74 | | |
75 | | isl::union_map computeReachingOverwrite(isl::union_map Schedule, |
76 | | isl::union_map Writes, |
77 | | bool InclPrevWrite, |
78 | 42 | bool InclOverwrite) { |
79 | 42 | return computeReachingWrite(Schedule, Writes, true, InclPrevWrite, |
80 | 42 | InclOverwrite); |
81 | 42 | } |
82 | | |
83 | | /// Compute the next overwrite for a scalar. |
84 | | /// |
85 | | /// @param Schedule { DomainWrite[] -> Scatter[] } |
86 | | /// Schedule of (at least) all writes. Instances not in @p |
87 | | /// Writes are ignored. |
88 | | /// @param Writes { DomainWrite[] } |
89 | | /// The element instances that write to the scalar. |
90 | | /// @param InclPrevWrite Whether to extend the timepoints to include |
91 | | /// the timepoint where the previous write happens. |
92 | | /// @param InclOverwrite Whether the reaching overwrite includes the timepoint |
93 | | /// of the overwrite itself. |
94 | | /// |
95 | | /// @return { Scatter[] -> DomainDef[] } |
96 | | isl::union_map computeScalarReachingOverwrite(isl::union_map Schedule, |
97 | | isl::union_set Writes, |
98 | | bool InclPrevWrite, |
99 | 42 | bool InclOverwrite) { |
100 | 42 | |
101 | 42 | // { DomainWrite[] } |
102 | 42 | auto WritesMap = isl::union_map::from_domain(Writes); |
103 | 42 | |
104 | 42 | // { [Element[] -> Scatter[]] -> DomainWrite[] } |
105 | 42 | auto Result = computeReachingOverwrite( |
106 | 42 | std::move(Schedule), std::move(WritesMap), InclPrevWrite, InclOverwrite); |
107 | 42 | |
108 | 42 | return Result.domain_factor_range(); |
109 | 42 | } |
110 | | |
111 | | /// Overload of computeScalarReachingOverwrite, with only one writing statement. |
112 | | /// Consequently, the result consists of only one map space. |
113 | | /// |
114 | | /// @param Schedule { DomainWrite[] -> Scatter[] } |
115 | | /// @param Writes { DomainWrite[] } |
116 | | /// @param InclPrevWrite Include the previous write to result. |
117 | | /// @param InclOverwrite Include the overwrite to the result. |
118 | | /// |
119 | | /// @return { Scatter[] -> DomainWrite[] } |
120 | | isl::map computeScalarReachingOverwrite(isl::union_map Schedule, |
121 | | isl::set Writes, bool InclPrevWrite, |
122 | 42 | bool InclOverwrite) { |
123 | 42 | isl::space ScatterSpace = getScatterSpace(Schedule); |
124 | 42 | isl::space DomSpace = Writes.get_space(); |
125 | 42 | |
126 | 42 | isl::union_map ReachOverwrite = computeScalarReachingOverwrite( |
127 | 42 | Schedule, isl::union_set(Writes), InclPrevWrite, InclOverwrite); |
128 | 42 | |
129 | 42 | isl::space ResultSpace = ScatterSpace.map_from_domain_and_range(DomSpace); |
130 | 42 | return singleton(std::move(ReachOverwrite), ResultSpace); |
131 | 42 | } |
132 | | |
133 | | /// Try to find a 'natural' extension of a mapped to elements outside its |
134 | | /// domain. |
135 | | /// |
136 | | /// @param Relevant The map with mapping that may not be modified. |
137 | | /// @param Universe The domain to which @p Relevant needs to be extended. |
138 | | /// |
139 | | /// @return A map with that associates the domain elements of @p Relevant to the |
140 | | /// same elements and in addition the elements of @p Universe to some |
141 | | /// undefined elements. The function prefers to return simple maps. |
142 | 11 | isl::union_map expandMapping(isl::union_map Relevant, isl::union_set Universe) { |
143 | 11 | Relevant = Relevant.coalesce(); |
144 | 11 | isl::union_set RelevantDomain = Relevant.domain(); |
145 | 11 | isl::union_map Simplified = Relevant.gist_domain(RelevantDomain); |
146 | 11 | Simplified = Simplified.coalesce(); |
147 | 11 | return Simplified.intersect_domain(Universe); |
148 | 11 | } |
149 | | |
150 | | /// Represent the knowledge of the contents of any array elements in any zone or |
151 | | /// the knowledge we would add when mapping a scalar to an array element. |
152 | | /// |
153 | | /// Every array element at every zone unit has one of two states: |
154 | | /// |
155 | | /// - Unused: Not occupied by any value so a transformation can change it to |
156 | | /// other values. |
157 | | /// |
158 | | /// - Occupied: The element contains a value that is still needed. |
159 | | /// |
160 | | /// The union of Unused and Unknown zones forms the universe, the set of all |
161 | | /// elements at every timepoint. The universe can easily be derived from the |
162 | | /// array elements that are accessed someway. Arrays that are never accessed |
163 | | /// also never play a role in any computation and can hence be ignored. With a |
164 | | /// given universe, only one of the sets needs to stored implicitly. Computing |
165 | | /// the complement is also an expensive operation, hence this class has been |
166 | | /// designed that only one of sets is needed while the other is assumed to be |
167 | | /// implicit. It can still be given, but is mostly ignored. |
168 | | /// |
169 | | /// There are two use cases for the Knowledge class: |
170 | | /// |
171 | | /// 1) To represent the knowledge of the current state of ScopInfo. The unused |
172 | | /// state means that an element is currently unused: there is no read of it |
173 | | /// before the next overwrite. Also called 'Existing'. |
174 | | /// |
175 | | /// 2) To represent the requirements for mapping a scalar to array elements. The |
176 | | /// unused state means that there is no change/requirement. Also called |
177 | | /// 'Proposed'. |
178 | | /// |
179 | | /// In addition to these states at unit zones, Knowledge needs to know when |
180 | | /// values are written. This is because written values may have no lifetime (one |
181 | | /// reason is that the value is never read). Such writes would therefore never |
182 | | /// conflict, but overwrite values that might still be required. Another source |
183 | | /// of problems are multiple writes to the same element at the same timepoint, |
184 | | /// because their order is undefined. |
185 | | class Knowledge { |
186 | | private: |
187 | | /// { [Element[] -> Zone[]] } |
188 | | /// Set of array elements and when they are alive. |
189 | | /// Can contain a nullptr; in this case the set is implicitly defined as the |
190 | | /// complement of #Unused. |
191 | | /// |
192 | | /// The set of alive array elements is represented as zone, as the set of live |
193 | | /// values can differ depending on how the elements are interpreted. |
194 | | /// Assuming a value X is written at timestep [0] and read at timestep [1] |
195 | | /// without being used at any later point, then the value is alive in the |
196 | | /// interval ]0,1[. This interval cannot be represented by an integer set, as |
197 | | /// it does not contain any integer point. Zones allow us to represent this |
198 | | /// interval and can be converted to sets of timepoints when needed (e.g., in |
199 | | /// isConflicting when comparing to the write sets). |
200 | | /// @see convertZoneToTimepoints and this file's comment for more details. |
201 | | isl::union_set Occupied; |
202 | | |
203 | | /// { [Element[] -> Zone[]] } |
204 | | /// Set of array elements when they are not alive, i.e. their memory can be |
205 | | /// used for other purposed. Can contain a nullptr; in this case the set is |
206 | | /// implicitly defined as the complement of #Occupied. |
207 | | isl::union_set Unused; |
208 | | |
209 | | /// { [Element[] -> Zone[]] -> ValInst[] } |
210 | | /// Maps to the known content for each array element at any interval. |
211 | | /// |
212 | | /// Any element/interval can map to multiple known elements. This is due to |
213 | | /// multiple llvm::Value referring to the same content. Examples are |
214 | | /// |
215 | | /// - A value stored and loaded again. The LoadInst represents the same value |
216 | | /// as the StoreInst's value operand. |
217 | | /// |
218 | | /// - A PHINode is equal to any one of the incoming values. In case of |
219 | | /// LCSSA-form, it is always equal to its single incoming value. |
220 | | /// |
221 | | /// Two Knowledges are considered not conflicting if at least one of the known |
222 | | /// values match. Not known values are not stored as an unnamed tuple (as |
223 | | /// #Written does), but maps to nothing. |
224 | | /// |
225 | | /// Known values are usually just defined for #Occupied elements. Knowing |
226 | | /// #Unused contents has no advantage as it can be overwritten. |
227 | | isl::union_map Known; |
228 | | |
229 | | /// { [Element[] -> Scatter[]] -> ValInst[] } |
230 | | /// The write actions currently in the scop or that would be added when |
231 | | /// mapping a scalar. Maps to the value that is written. |
232 | | /// |
233 | | /// Written values that cannot be identified are represented by an unknown |
234 | | /// ValInst[] (an unnamed tuple of 0 dimension). It conflicts with itself. |
235 | | isl::union_map Written; |
236 | | |
237 | | /// Check whether this Knowledge object is well-formed. |
238 | 691 | void checkConsistency() const { |
239 | | #ifndef NDEBUG |
240 | | // Default-initialized object |
241 | | if (!Occupied && !Unused && !Known && !Written) |
242 | | return; |
243 | | |
244 | | assert(Occupied || Unused); |
245 | | assert(Known); |
246 | | assert(Written); |
247 | | |
248 | | // If not all fields are defined, we cannot derived the universe. |
249 | | if (!Occupied || !Unused) |
250 | | return; |
251 | | |
252 | | assert(Occupied.is_disjoint(Unused)); |
253 | | auto Universe = Occupied.unite(Unused); |
254 | | |
255 | | assert(!Known.domain().is_subset(Universe).is_false()); |
256 | | assert(!Written.domain().is_subset(Universe).is_false()); |
257 | | #endif |
258 | | } |
259 | | |
260 | | public: |
261 | | /// Initialize a nullptr-Knowledge. This is only provided for convenience; do |
262 | | /// not use such an object. |
263 | 106 | Knowledge() {} |
264 | | |
265 | | /// Create a new object with the given members. |
266 | | Knowledge(isl::union_set Occupied, isl::union_set Unused, |
267 | | isl::union_map Known, isl::union_map Written) |
268 | | : Occupied(std::move(Occupied)), Unused(std::move(Unused)), |
269 | 605 | Known(std::move(Known)), Written(std::move(Written)) { |
270 | 605 | checkConsistency(); |
271 | 605 | } |
272 | | |
273 | | /// Return whether this object was not default-constructed. |
274 | 49 | bool isUsable() const { return (Occupied || Unused) && Known46 && Written46 ; } |
275 | | |
276 | | /// Print the content of this object to @p OS. |
277 | 0 | void print(llvm::raw_ostream &OS, unsigned Indent = 0) const { |
278 | 0 | if (isUsable()) { |
279 | 0 | if (Occupied) |
280 | 0 | OS.indent(Indent) << "Occupied: " << Occupied << "\n"; |
281 | 0 | else |
282 | 0 | OS.indent(Indent) << "Occupied: <Everything else not in Unused>\n"; |
283 | 0 | if (Unused) |
284 | 0 | OS.indent(Indent) << "Unused: " << Unused << "\n"; |
285 | 0 | else |
286 | 0 | OS.indent(Indent) << "Unused: <Everything else not in Occupied>\n"; |
287 | 0 | OS.indent(Indent) << "Known: " << Known << "\n"; |
288 | 0 | OS.indent(Indent) << "Written : " << Written << '\n'; |
289 | 0 | } else { |
290 | 0 | OS.indent(Indent) << "Invalid knowledge\n"; |
291 | 0 | } |
292 | 0 | } |
293 | | |
294 | | /// Combine two knowledges, this and @p That. |
295 | 86 | void learnFrom(Knowledge That) { |
296 | 86 | assert(!isConflicting(*this, That)); |
297 | 86 | assert(Unused && That.Occupied); |
298 | 86 | assert( |
299 | 86 | !That.Unused && |
300 | 86 | "This function is only prepared to learn occupied elements from That"); |
301 | 86 | assert(!Occupied && "This function does not implement " |
302 | 86 | "`this->Occupied = " |
303 | 86 | "this->Occupied.unite(That.Occupied);`"); |
304 | 86 | |
305 | 86 | Unused = Unused.subtract(That.Occupied); |
306 | 86 | Known = Known.unite(That.Known); |
307 | 86 | Written = Written.unite(That.Written); |
308 | 86 | |
309 | 86 | checkConsistency(); |
310 | 86 | } |
311 | | |
312 | | /// Determine whether two Knowledges conflict with each other. |
313 | | /// |
314 | | /// In theory @p Existing and @p Proposed are symmetric, but the |
315 | | /// implementation is constrained by the implicit interpretation. That is, @p |
316 | | /// Existing must have #Unused defined (use case 1) and @p Proposed must have |
317 | | /// #Occupied defined (use case 1). |
318 | | /// |
319 | | /// A conflict is defined as non-preserved semantics when they are merged. For |
320 | | /// instance, when for the same array and zone they assume different |
321 | | /// llvm::Values. |
322 | | /// |
323 | | /// @param Existing One of the knowledges with #Unused defined. |
324 | | /// @param Proposed One of the knowledges with #Occupied defined. |
325 | | /// @param OS Dump the conflict reason to this output stream; use |
326 | | /// nullptr to not output anything. |
327 | | /// @param Indent Indention for the conflict reason. |
328 | | /// |
329 | | /// @return True, iff the two knowledges are conflicting. |
330 | | static bool isConflicting(const Knowledge &Existing, |
331 | | const Knowledge &Proposed, |
332 | | llvm::raw_ostream *OS = nullptr, |
333 | 323 | unsigned Indent = 0) { |
334 | 323 | assert(Existing.Unused); |
335 | 323 | assert(Proposed.Occupied); |
336 | 323 | |
337 | | #ifndef NDEBUG |
338 | | if (Existing.Occupied && Proposed.Unused) { |
339 | | auto ExistingUniverse = Existing.Occupied.unite(Existing.Unused); |
340 | | auto ProposedUniverse = Proposed.Occupied.unite(Proposed.Unused); |
341 | | assert(ExistingUniverse.is_equal(ProposedUniverse) && |
342 | | "Both inputs' Knowledges must be over the same universe"); |
343 | | } |
344 | | #endif |
345 | | |
346 | 323 | // Do the Existing and Proposed lifetimes conflict? |
347 | 323 | // |
348 | 323 | // Lifetimes are described as the cross-product of array elements and zone |
349 | 323 | // intervals in which they are alive (the space { [Element[] -> Zone[]] }). |
350 | 323 | // In the following we call this "element/lifetime interval". |
351 | 323 | // |
352 | 323 | // In order to not conflict, one of the following conditions must apply for |
353 | 323 | // each element/lifetime interval: |
354 | 323 | // |
355 | 323 | // 1. If occupied in one of the knowledges, it is unused in the other. |
356 | 323 | // |
357 | 323 | // - or - |
358 | 323 | // |
359 | 323 | // 2. Both contain the same value. |
360 | 323 | // |
361 | 323 | // Instead of partitioning the element/lifetime intervals into a part that |
362 | 323 | // both Knowledges occupy (which requires an expensive subtraction) and for |
363 | 323 | // these to check whether they are known to be the same value, we check only |
364 | 323 | // the second condition and ensure that it also applies when then first |
365 | 323 | // condition is true. This is done by adding a wildcard value to |
366 | 323 | // Proposed.Known and Existing.Unused such that they match as a common known |
367 | 323 | // value. We use the "unknown ValInst" for this purpose. Every |
368 | 323 | // Existing.Unused may match with an unknown Proposed.Occupied because these |
369 | 323 | // never are in conflict with each other. |
370 | 323 | auto ProposedOccupiedAnyVal = makeUnknownForDomain(Proposed.Occupied); |
371 | 323 | auto ProposedValues = Proposed.Known.unite(ProposedOccupiedAnyVal); |
372 | 323 | |
373 | 323 | auto ExistingUnusedAnyVal = makeUnknownForDomain(Existing.Unused); |
374 | 323 | auto ExistingValues = Existing.Known.unite(ExistingUnusedAnyVal); |
375 | 323 | |
376 | 323 | auto MatchingVals = ExistingValues.intersect(ProposedValues); |
377 | 323 | auto Matches = MatchingVals.domain(); |
378 | 323 | |
379 | 323 | // Any Proposed.Occupied must either have a match between the known values |
380 | 323 | // of Existing and Occupied, or be in Existing.Unused. In the latter case, |
381 | 323 | // the previously added "AnyVal" will match each other. |
382 | 323 | if (!Proposed.Occupied.is_subset(Matches)) { |
383 | 43 | if (OS) { |
384 | 0 | auto Conflicting = Proposed.Occupied.subtract(Matches); |
385 | 0 | auto ExistingConflictingKnown = |
386 | 0 | Existing.Known.intersect_domain(Conflicting); |
387 | 0 | auto ProposedConflictingKnown = |
388 | 0 | Proposed.Known.intersect_domain(Conflicting); |
389 | 0 |
|
390 | 0 | OS->indent(Indent) << "Proposed lifetime conflicting with Existing's\n"; |
391 | 0 | OS->indent(Indent) << "Conflicting occupied: " << Conflicting << "\n"; |
392 | 0 | if (!ExistingConflictingKnown.is_empty()) |
393 | 0 | OS->indent(Indent) |
394 | 0 | << "Existing Known: " << ExistingConflictingKnown << "\n"; |
395 | 0 | if (!ProposedConflictingKnown.is_empty()) |
396 | 0 | OS->indent(Indent) |
397 | 0 | << "Proposed Known: " << ProposedConflictingKnown << "\n"; |
398 | 0 | } |
399 | 43 | return true; |
400 | 43 | } |
401 | 280 | |
402 | 280 | // Do the writes in Existing conflict with occupied values in Proposed? |
403 | 280 | // |
404 | 280 | // In order to not conflict, it must either write to unused lifetime or |
405 | 280 | // write the same value. To check, we remove the writes that write into |
406 | 280 | // Proposed.Unused (they never conflict) and then see whether the written |
407 | 280 | // value is already in Proposed.Known. If there are multiple known values |
408 | 280 | // and a written value is known under different names, it is enough when one |
409 | 280 | // of the written values (assuming that they are the same value under |
410 | 280 | // different names, e.g. a PHINode and one of the incoming values) matches |
411 | 280 | // one of the known names. |
412 | 280 | // |
413 | 280 | // We convert here the set of lifetimes to actual timepoints. A lifetime is |
414 | 280 | // in conflict with a set of write timepoints, if either a live timepoint is |
415 | 280 | // clearly within the lifetime or if a write happens at the beginning of the |
416 | 280 | // lifetime (where it would conflict with the value that actually writes the |
417 | 280 | // value alive). There is no conflict at the end of a lifetime, as the alive |
418 | 280 | // value will always be read, before it is overwritten again. The last |
419 | 280 | // property holds in Polly for all scalar values and we expect all users of |
420 | 280 | // Knowledge to check this property also for accesses to MemoryKind::Array. |
421 | 280 | auto ProposedFixedDefs = |
422 | 280 | convertZoneToTimepoints(Proposed.Occupied, true, false); |
423 | 280 | auto ProposedFixedKnown = |
424 | 280 | convertZoneToTimepoints(Proposed.Known, isl::dim::in, true, false); |
425 | 280 | |
426 | 280 | auto ExistingConflictingWrites = |
427 | 280 | Existing.Written.intersect_domain(ProposedFixedDefs); |
428 | 280 | auto ExistingConflictingWritesDomain = ExistingConflictingWrites.domain(); |
429 | 280 | |
430 | 280 | auto CommonWrittenVal = |
431 | 280 | ProposedFixedKnown.intersect(ExistingConflictingWrites); |
432 | 280 | auto CommonWrittenValDomain = CommonWrittenVal.domain(); |
433 | 280 | |
434 | 280 | if (!ExistingConflictingWritesDomain.is_subset(CommonWrittenValDomain)) { |
435 | 26 | if (OS) { |
436 | 0 | auto ExistingConflictingWritten = |
437 | 0 | ExistingConflictingWrites.subtract_domain(CommonWrittenValDomain); |
438 | 0 | auto ProposedConflictingKnown = ProposedFixedKnown.subtract_domain( |
439 | 0 | ExistingConflictingWritten.domain()); |
440 | 0 |
|
441 | 0 | OS->indent(Indent) |
442 | 0 | << "Proposed a lifetime where there is an Existing write into it\n"; |
443 | 0 | OS->indent(Indent) << "Existing conflicting writes: " |
444 | 0 | << ExistingConflictingWritten << "\n"; |
445 | 0 | if (!ProposedConflictingKnown.is_empty()) |
446 | 0 | OS->indent(Indent) |
447 | 0 | << "Proposed conflicting known: " << ProposedConflictingKnown |
448 | 0 | << "\n"; |
449 | 0 | } |
450 | 26 | return true; |
451 | 26 | } |
452 | 254 | |
453 | 254 | // Do the writes in Proposed conflict with occupied values in Existing? |
454 | 254 | auto ExistingAvailableDefs = |
455 | 254 | convertZoneToTimepoints(Existing.Unused, true, false); |
456 | 254 | auto ExistingKnownDefs = |
457 | 254 | convertZoneToTimepoints(Existing.Known, isl::dim::in, true, false); |
458 | 254 | |
459 | 254 | auto ProposedWrittenDomain = Proposed.Written.domain(); |
460 | 254 | auto KnownIdentical = ExistingKnownDefs.intersect(Proposed.Written); |
461 | 254 | auto IdenticalOrUnused = |
462 | 254 | ExistingAvailableDefs.unite(KnownIdentical.domain()); |
463 | 254 | if (!ProposedWrittenDomain.is_subset(IdenticalOrUnused)) { |
464 | 24 | if (OS) { |
465 | 0 | auto Conflicting = ProposedWrittenDomain.subtract(IdenticalOrUnused); |
466 | 0 | auto ExistingConflictingKnown = |
467 | 0 | ExistingKnownDefs.intersect_domain(Conflicting); |
468 | 0 | auto ProposedConflictingWritten = |
469 | 0 | Proposed.Written.intersect_domain(Conflicting); |
470 | 0 |
|
471 | 0 | OS->indent(Indent) << "Proposed writes into range used by Existing\n"; |
472 | 0 | OS->indent(Indent) << "Proposed conflicting writes: " |
473 | 0 | << ProposedConflictingWritten << "\n"; |
474 | 0 | if (!ExistingConflictingKnown.is_empty()) |
475 | 0 | OS->indent(Indent) |
476 | 0 | << "Existing conflicting known: " << ExistingConflictingKnown |
477 | 0 | << "\n"; |
478 | 0 | } |
479 | 24 | return true; |
480 | 24 | } |
481 | 230 | |
482 | 230 | // Does Proposed write at the same time as Existing already does (order of |
483 | 230 | // writes is undefined)? Writing the same value is permitted. |
484 | 230 | auto ExistingWrittenDomain = Existing.Written.domain(); |
485 | 230 | auto BothWritten = |
486 | 230 | Existing.Written.domain().intersect(Proposed.Written.domain()); |
487 | 230 | auto ExistingKnownWritten = filterKnownValInst(Existing.Written); |
488 | 230 | auto ProposedKnownWritten = filterKnownValInst(Proposed.Written); |
489 | 230 | auto CommonWritten = |
490 | 230 | ExistingKnownWritten.intersect(ProposedKnownWritten).domain(); |
491 | 230 | |
492 | 230 | if (!BothWritten.is_subset(CommonWritten)) { |
493 | 24 | if (OS) { |
494 | 0 | auto Conflicting = BothWritten.subtract(CommonWritten); |
495 | 0 | auto ExistingConflictingWritten = |
496 | 0 | Existing.Written.intersect_domain(Conflicting); |
497 | 0 | auto ProposedConflictingWritten = |
498 | 0 | Proposed.Written.intersect_domain(Conflicting); |
499 | 0 |
|
500 | 0 | OS->indent(Indent) << "Proposed writes at the same time as an already " |
501 | 0 | "Existing write\n"; |
502 | 0 | OS->indent(Indent) << "Conflicting writes: " << Conflicting << "\n"; |
503 | 0 | if (!ExistingConflictingWritten.is_empty()) |
504 | 0 | OS->indent(Indent) |
505 | 0 | << "Exiting write: " << ExistingConflictingWritten << "\n"; |
506 | 0 | if (!ProposedConflictingWritten.is_empty()) |
507 | 0 | OS->indent(Indent) |
508 | 0 | << "Proposed write: " << ProposedConflictingWritten << "\n"; |
509 | 0 | } |
510 | 24 | return true; |
511 | 24 | } |
512 | 206 | |
513 | 206 | return false; |
514 | 206 | } |
515 | | }; |
516 | | |
517 | | /// Implementation of the DeLICM/DePRE transformation. |
518 | | class DeLICMImpl : public ZoneAlgorithm { |
519 | | private: |
520 | | /// Knowledge before any transformation took place. |
521 | | Knowledge OriginalZone; |
522 | | |
523 | | /// Current knowledge of the SCoP including all already applied |
524 | | /// transformations. |
525 | | Knowledge Zone; |
526 | | |
527 | | /// Number of StoreInsts something can be mapped to. |
528 | | int NumberOfCompatibleTargets = 0; |
529 | | |
530 | | /// The number of StoreInsts to which at least one value or PHI has been |
531 | | /// mapped to. |
532 | | int NumberOfTargetsMapped = 0; |
533 | | |
534 | | /// The number of llvm::Value mapped to some array element. |
535 | | int NumberOfMappedValueScalars = 0; |
536 | | |
537 | | /// The number of PHIs mapped to some array element. |
538 | | int NumberOfMappedPHIScalars = 0; |
539 | | |
540 | | /// Determine whether two knowledges are conflicting with each other. |
541 | | /// |
542 | | /// @see Knowledge::isConflicting |
543 | 91 | bool isConflicting(const Knowledge &Proposed) { |
544 | 91 | raw_ostream *OS = nullptr; |
545 | 91 | LLVM_DEBUG(OS = &llvm::dbgs()); |
546 | 91 | return Knowledge::isConflicting(Zone, Proposed, OS, 4); |
547 | 91 | } |
548 | | |
549 | | /// Determine whether @p SAI is a scalar that can be mapped to an array |
550 | | /// element. |
551 | 99 | bool isMappable(const ScopArrayInfo *SAI) { |
552 | 99 | assert(SAI); |
553 | 99 | |
554 | 99 | if (SAI->isValueKind()) { |
555 | 63 | auto *MA = S->getValueDef(SAI); |
556 | 63 | if (!MA) { |
557 | 2 | LLVM_DEBUG( |
558 | 2 | dbgs() |
559 | 2 | << " Reject because value is read-only within the scop\n"); |
560 | 2 | return false; |
561 | 2 | } |
562 | 61 | |
563 | 61 | // Mapping if value is used after scop is not supported. The code |
564 | 61 | // generator would need to reload the scalar after the scop, but it |
565 | 61 | // does not have the information to where it is mapped to. Only the |
566 | 61 | // MemoryAccesses have that information, not the ScopArrayInfo. |
567 | 61 | auto Inst = MA->getAccessInstruction(); |
568 | 100 | for (auto User : Inst->users()) { |
569 | 100 | if (!isa<Instruction>(User)) |
570 | 0 | return false; |
571 | 100 | auto UserInst = cast<Instruction>(User); |
572 | 100 | |
573 | 100 | if (!S->contains(UserInst)) { |
574 | 1 | LLVM_DEBUG(dbgs() << " Reject because value is escaping\n"); |
575 | 1 | return false; |
576 | 1 | } |
577 | 100 | } |
578 | 61 | |
579 | 61 | return true60 ; |
580 | 36 | } |
581 | 36 | |
582 | 36 | if (SAI->isPHIKind()) { |
583 | 36 | auto *MA = S->getPHIRead(SAI); |
584 | 36 | assert(MA); |
585 | 36 | |
586 | 36 | // Mapping of an incoming block from before the SCoP is not supported by |
587 | 36 | // the code generator. |
588 | 36 | auto PHI = cast<PHINode>(MA->getAccessInstruction()); |
589 | 72 | for (auto Incoming : PHI->blocks()) { |
590 | 72 | if (!S->contains(Incoming)) { |
591 | 0 | LLVM_DEBUG(dbgs() |
592 | 0 | << " Reject because at least one incoming block is " |
593 | 0 | "not in the scop region\n"); |
594 | 0 | return false; |
595 | 0 | } |
596 | 72 | } |
597 | 36 | |
598 | 36 | return true; |
599 | 0 | } |
600 | 0 | |
601 | 0 | LLVM_DEBUG(dbgs() << " Reject ExitPHI or other non-value\n"); |
602 | 0 | return false; |
603 | 0 | } |
604 | | |
605 | | /// Compute the uses of a MemoryKind::Value and its lifetime (from its |
606 | | /// definition to the last use). |
607 | | /// |
608 | | /// @param SAI The ScopArrayInfo representing the value's storage. |
609 | | /// |
610 | | /// @return { DomainDef[] -> DomainUse[] }, { DomainDef[] -> Zone[] } |
611 | | /// First element is the set of uses for each definition. |
612 | | /// The second is the lifetime of each definition. |
613 | | std::tuple<isl::union_map, isl::map> |
614 | 56 | computeValueUses(const ScopArrayInfo *SAI) { |
615 | 56 | assert(SAI->isValueKind()); |
616 | 56 | |
617 | 56 | // { DomainRead[] } |
618 | 56 | auto Reads = makeEmptyUnionSet(); |
619 | 56 | |
620 | 56 | // Find all uses. |
621 | 56 | for (auto *MA : S->getValueUses(SAI)) |
622 | 79 | Reads = Reads.add_set(getDomainFor(MA)); |
623 | 56 | |
624 | 56 | // { DomainRead[] -> Scatter[] } |
625 | 56 | auto ReadSchedule = getScatterFor(Reads); |
626 | 56 | |
627 | 56 | auto *DefMA = S->getValueDef(SAI); |
628 | 56 | assert(DefMA); |
629 | 56 | |
630 | 56 | // { DomainDef[] } |
631 | 56 | auto Writes = getDomainFor(DefMA); |
632 | 56 | |
633 | 56 | // { DomainDef[] -> Scatter[] } |
634 | 56 | auto WriteScatter = getScatterFor(Writes); |
635 | 56 | |
636 | 56 | // { Scatter[] -> DomainDef[] } |
637 | 56 | auto ReachDef = getScalarReachingDefinition(DefMA->getStatement()); |
638 | 56 | |
639 | 56 | // { [DomainDef[] -> Scatter[]] -> DomainUse[] } |
640 | 56 | auto Uses = isl::union_map(ReachDef.reverse().range_map()) |
641 | 56 | .apply_range(ReadSchedule.reverse()); |
642 | 56 | |
643 | 56 | // { DomainDef[] -> Scatter[] } |
644 | 56 | auto UseScatter = |
645 | 56 | singleton(Uses.domain().unwrap(), |
646 | 56 | Writes.get_space().map_from_domain_and_range(ScatterSpace)); |
647 | 56 | |
648 | 56 | // { DomainDef[] -> Zone[] } |
649 | 56 | auto Lifetime = betweenScatter(WriteScatter, UseScatter, false, true); |
650 | 56 | |
651 | 56 | // { DomainDef[] -> DomainRead[] } |
652 | 56 | auto DefUses = Uses.domain_factor_domain(); |
653 | 56 | |
654 | 56 | return std::make_pair(DefUses, Lifetime); |
655 | 56 | } |
656 | | |
657 | | /// Try to map a MemoryKind::Value to a given array element. |
658 | | /// |
659 | | /// @param SAI Representation of the scalar's memory to map. |
660 | | /// @param TargetElt { Scatter[] -> Element[] } |
661 | | /// Suggestion where to map a scalar to when at a timepoint. |
662 | | /// |
663 | | /// @return true if the scalar was successfully mapped. |
664 | 59 | bool tryMapValue(const ScopArrayInfo *SAI, isl::map TargetElt) { |
665 | 59 | assert(SAI->isValueKind()); |
666 | 59 | |
667 | 59 | auto *DefMA = S->getValueDef(SAI); |
668 | 59 | assert(DefMA->isValueKind()); |
669 | 59 | assert(DefMA->isMustWrite()); |
670 | 59 | auto *V = DefMA->getAccessValue(); |
671 | 59 | auto *DefInst = DefMA->getAccessInstruction(); |
672 | 59 | |
673 | 59 | // Stop if the scalar has already been mapped. |
674 | 59 | if (!DefMA->getLatestScopArrayInfo()->isValueKind()) |
675 | 1 | return false; |
676 | 58 | |
677 | 58 | // { DomainDef[] -> Scatter[] } |
678 | 58 | auto DefSched = getScatterFor(DefMA); |
679 | 58 | |
680 | 58 | // Where each write is mapped to, according to the suggestion. |
681 | 58 | // { DomainDef[] -> Element[] } |
682 | 58 | auto DefTarget = TargetElt.apply_domain(DefSched.reverse()); |
683 | 58 | simplify(DefTarget); |
684 | 58 | LLVM_DEBUG(dbgs() << " Def Mapping: " << DefTarget << '\n'); |
685 | 58 | |
686 | 58 | auto OrigDomain = getDomainFor(DefMA); |
687 | 58 | auto MappedDomain = DefTarget.domain(); |
688 | 58 | if (!OrigDomain.is_subset(MappedDomain)) { |
689 | 2 | LLVM_DEBUG( |
690 | 2 | dbgs() |
691 | 2 | << " Reject because mapping does not encompass all instances\n"); |
692 | 2 | return false; |
693 | 2 | } |
694 | 56 | |
695 | 56 | // { DomainDef[] -> Zone[] } |
696 | 56 | isl::map Lifetime; |
697 | 56 | |
698 | 56 | // { DomainDef[] -> DomainUse[] } |
699 | 56 | isl::union_map DefUses; |
700 | 56 | |
701 | 56 | std::tie(DefUses, Lifetime) = computeValueUses(SAI); |
702 | 56 | LLVM_DEBUG(dbgs() << " Lifetime: " << Lifetime << '\n'); |
703 | 56 | |
704 | 56 | /// { [Element[] -> Zone[]] } |
705 | 56 | auto EltZone = Lifetime.apply_domain(DefTarget).wrap(); |
706 | 56 | simplify(EltZone); |
707 | 56 | |
708 | 56 | // When known knowledge is disabled, just return the unknown value. It will |
709 | 56 | // either get filtered out or conflict with itself. |
710 | 56 | // { DomainDef[] -> ValInst[] } |
711 | 56 | isl::map ValInst; |
712 | 56 | if (DelicmComputeKnown) |
713 | 56 | ValInst = makeValInst(V, DefMA->getStatement(), |
714 | 56 | LI->getLoopFor(DefInst->getParent())); |
715 | 0 | else |
716 | 0 | ValInst = makeUnknownForDomain(DefMA->getStatement()); |
717 | 56 | |
718 | 56 | // { DomainDef[] -> [Element[] -> Zone[]] } |
719 | 56 | auto EltKnownTranslator = DefTarget.range_product(Lifetime); |
720 | 56 | |
721 | 56 | // { [Element[] -> Zone[]] -> ValInst[] } |
722 | 56 | auto EltKnown = ValInst.apply_domain(EltKnownTranslator); |
723 | 56 | simplify(EltKnown); |
724 | 56 | |
725 | 56 | // { DomainDef[] -> [Element[] -> Scatter[]] } |
726 | 56 | auto WrittenTranslator = DefTarget.range_product(DefSched); |
727 | 56 | |
728 | 56 | // { [Element[] -> Scatter[]] -> ValInst[] } |
729 | 56 | auto DefEltSched = ValInst.apply_domain(WrittenTranslator); |
730 | 56 | simplify(DefEltSched); |
731 | 56 | |
732 | 56 | Knowledge Proposed(EltZone, nullptr, filterKnownValInst(EltKnown), |
733 | 56 | DefEltSched); |
734 | 56 | if (isConflicting(Proposed)) |
735 | 5 | return false; |
736 | 51 | |
737 | 51 | // { DomainUse[] -> Element[] } |
738 | 51 | auto UseTarget = DefUses.reverse().apply_range(DefTarget); |
739 | 51 | |
740 | 51 | mapValue(SAI, std::move(DefTarget), std::move(UseTarget), |
741 | 51 | std::move(Lifetime), std::move(Proposed)); |
742 | 51 | return true; |
743 | 51 | } |
744 | | |
745 | | /// After a scalar has been mapped, update the global knowledge. |
746 | 86 | void applyLifetime(Knowledge Proposed) { |
747 | 86 | Zone.learnFrom(std::move(Proposed)); |
748 | 86 | } |
749 | | |
750 | | /// Map a MemoryKind::Value scalar to an array element. |
751 | | /// |
752 | | /// Callers must have ensured that the mapping is valid and not conflicting. |
753 | | /// |
754 | | /// @param SAI The ScopArrayInfo representing the scalar's memory to |
755 | | /// map. |
756 | | /// @param DefTarget { DomainDef[] -> Element[] } |
757 | | /// The array element to map the scalar to. |
758 | | /// @param UseTarget { DomainUse[] -> Element[] } |
759 | | /// The array elements the uses are mapped to. |
760 | | /// @param Lifetime { DomainDef[] -> Zone[] } |
761 | | /// The lifetime of each llvm::Value definition for |
762 | | /// reporting. |
763 | | /// @param Proposed Mapping constraints for reporting. |
764 | | void mapValue(const ScopArrayInfo *SAI, isl::map DefTarget, |
765 | | isl::union_map UseTarget, isl::map Lifetime, |
766 | 51 | Knowledge Proposed) { |
767 | 51 | // Redirect the read accesses. |
768 | 69 | for (auto *MA : S->getValueUses(SAI)) { |
769 | 69 | // { DomainUse[] } |
770 | 69 | auto Domain = getDomainFor(MA); |
771 | 69 | |
772 | 69 | // { DomainUse[] -> Element[] } |
773 | 69 | auto NewAccRel = UseTarget.intersect_domain(Domain); |
774 | 69 | simplify(NewAccRel); |
775 | 69 | |
776 | 69 | assert(isl_union_map_n_map(NewAccRel.get()) == 1); |
777 | 69 | MA->setNewAccessRelation(isl::map::from_union_map(NewAccRel)); |
778 | 69 | } |
779 | 51 | |
780 | 51 | auto *WA = S->getValueDef(SAI); |
781 | 51 | WA->setNewAccessRelation(DefTarget); |
782 | 51 | applyLifetime(Proposed); |
783 | 51 | |
784 | 51 | MappedValueScalars++; |
785 | 51 | NumberOfMappedValueScalars += 1; |
786 | 51 | } |
787 | | |
788 | | isl::map makeValInst(Value *Val, ScopStmt *UserStmt, Loop *Scope, |
789 | 126 | bool IsCertain = true) { |
790 | 126 | // When known knowledge is disabled, just return the unknown value. It will |
791 | 126 | // either get filtered out or conflict with itself. |
792 | 126 | if (!DelicmComputeKnown) |
793 | 0 | return makeUnknownForDomain(UserStmt); |
794 | 126 | return ZoneAlgorithm::makeValInst(Val, UserStmt, Scope, IsCertain); |
795 | 126 | } |
796 | | |
797 | | /// Express the incoming values of a PHI for each incoming statement in an |
798 | | /// isl::union_map. |
799 | | /// |
800 | | /// @param SAI The PHI scalar represented by a ScopArrayInfo. |
801 | | /// |
802 | | /// @return { PHIWriteDomain[] -> ValInst[] } |
803 | 35 | isl::union_map determinePHIWrittenValues(const ScopArrayInfo *SAI) { |
804 | 35 | auto Result = makeEmptyUnionMap(); |
805 | 35 | |
806 | 35 | // Collect the incoming values. |
807 | 70 | for (auto *MA : S->getPHIIncomings(SAI)) { |
808 | 70 | // { DomainWrite[] -> ValInst[] } |
809 | 70 | isl::union_map ValInst; |
810 | 70 | auto *WriteStmt = MA->getStatement(); |
811 | 70 | |
812 | 70 | auto Incoming = MA->getIncoming(); |
813 | 70 | assert(!Incoming.empty()); |
814 | 70 | if (Incoming.size() == 1) { |
815 | 70 | ValInst = makeValInst(Incoming[0].second, WriteStmt, |
816 | 70 | LI->getLoopFor(Incoming[0].first)); |
817 | 70 | } else { |
818 | 0 | // If the PHI is in a subregion's exit node it can have multiple |
819 | 0 | // incoming values (+ maybe another incoming edge from an unrelated |
820 | 0 | // block). We cannot directly represent it as a single llvm::Value. |
821 | 0 | // We currently model it as unknown value, but modeling as the PHIInst |
822 | 0 | // itself could be OK, too. |
823 | 0 | ValInst = makeUnknownForDomain(WriteStmt); |
824 | 0 | } |
825 | 70 | |
826 | 70 | Result = Result.unite(ValInst); |
827 | 70 | } |
828 | 35 | |
829 | 35 | assert(Result.is_single_valued() && |
830 | 35 | "Cannot have multiple incoming values for same incoming statement"); |
831 | 35 | return Result; |
832 | 35 | } |
833 | | |
834 | | /// Try to map a MemoryKind::PHI scalar to a given array element. |
835 | | /// |
836 | | /// @param SAI Representation of the scalar's memory to map. |
837 | | /// @param TargetElt { Scatter[] -> Element[] } |
838 | | /// Suggestion where to map the scalar to when at a |
839 | | /// timepoint. |
840 | | /// |
841 | | /// @return true if the PHI scalar has been mapped. |
842 | 36 | bool tryMapPHI(const ScopArrayInfo *SAI, isl::map TargetElt) { |
843 | 36 | auto *PHIRead = S->getPHIRead(SAI); |
844 | 36 | assert(PHIRead->isPHIKind()); |
845 | 36 | assert(PHIRead->isRead()); |
846 | 36 | |
847 | 36 | // Skip if already been mapped. |
848 | 36 | if (!PHIRead->getLatestScopArrayInfo()->isPHIKind()) |
849 | 0 | return false; |
850 | 36 | |
851 | 36 | // { DomainRead[] -> Scatter[] } |
852 | 36 | auto PHISched = getScatterFor(PHIRead); |
853 | 36 | |
854 | 36 | // { DomainRead[] -> Element[] } |
855 | 36 | auto PHITarget = PHISched.apply_range(TargetElt); |
856 | 36 | simplify(PHITarget); |
857 | 36 | LLVM_DEBUG(dbgs() << " Mapping: " << PHITarget << '\n'); |
858 | 36 | |
859 | 36 | auto OrigDomain = getDomainFor(PHIRead); |
860 | 36 | auto MappedDomain = PHITarget.domain(); |
861 | 36 | if (!OrigDomain.is_subset(MappedDomain)) { |
862 | 0 | LLVM_DEBUG( |
863 | 0 | dbgs() |
864 | 0 | << " Reject because mapping does not encompass all instances\n"); |
865 | 0 | return false; |
866 | 0 | } |
867 | 36 | |
868 | 36 | // { DomainRead[] -> DomainWrite[] } |
869 | 36 | auto PerPHIWrites = computePerPHI(SAI); |
870 | 36 | |
871 | 36 | // { DomainWrite[] -> Element[] } |
872 | 36 | auto WritesTarget = PerPHIWrites.apply_domain(PHITarget).reverse(); |
873 | 36 | simplify(WritesTarget); |
874 | 36 | |
875 | 36 | // { DomainWrite[] } |
876 | 36 | auto UniverseWritesDom = isl::union_set::empty(ParamSpace); |
877 | 36 | |
878 | 36 | for (auto *MA : S->getPHIIncomings(SAI)) |
879 | 72 | UniverseWritesDom = UniverseWritesDom.add_set(getDomainFor(MA)); |
880 | 36 | |
881 | 36 | auto RelevantWritesTarget = WritesTarget; |
882 | 36 | if (DelicmOverapproximateWrites) |
883 | 11 | WritesTarget = expandMapping(WritesTarget, UniverseWritesDom); |
884 | 36 | |
885 | 36 | auto ExpandedWritesDom = WritesTarget.domain(); |
886 | 36 | if (!DelicmPartialWrites && |
887 | 36 | !UniverseWritesDom.is_subset(ExpandedWritesDom)3 ) { |
888 | 1 | LLVM_DEBUG( |
889 | 1 | dbgs() << " Reject because did not find PHI write mapping for " |
890 | 1 | "all instances\n"); |
891 | 1 | if (DelicmOverapproximateWrites) |
892 | 1 | LLVM_DEBUG(dbgs() << " Relevant Mapping: " |
893 | 1 | << RelevantWritesTarget << '\n'); |
894 | 1 | LLVM_DEBUG(dbgs() << " Deduced Mapping: " << WritesTarget |
895 | 1 | << '\n'); |
896 | 1 | LLVM_DEBUG(dbgs() << " Missing instances: " |
897 | 1 | << UniverseWritesDom.subtract(ExpandedWritesDom) |
898 | 1 | << '\n'); |
899 | 1 | return false; |
900 | 1 | } |
901 | 35 | |
902 | 35 | // { DomainRead[] -> Scatter[] } |
903 | 35 | isl::union_map PerPHIWriteScatterUmap = PerPHIWrites.apply_range(Schedule); |
904 | 35 | isl::map PerPHIWriteScatter = |
905 | 35 | singleton(PerPHIWriteScatterUmap, PHISched.get_space()); |
906 | 35 | |
907 | 35 | // { DomainRead[] -> Zone[] } |
908 | 35 | auto Lifetime = betweenScatter(PerPHIWriteScatter, PHISched, false, true); |
909 | 35 | simplify(Lifetime); |
910 | 35 | LLVM_DEBUG(dbgs() << " Lifetime: " << Lifetime << "\n"); |
911 | 35 | |
912 | 35 | // { DomainWrite[] -> Zone[] } |
913 | 35 | auto WriteLifetime = isl::union_map(Lifetime).apply_domain(PerPHIWrites); |
914 | 35 | |
915 | 35 | // { DomainWrite[] -> ValInst[] } |
916 | 35 | auto WrittenValue = determinePHIWrittenValues(SAI); |
917 | 35 | |
918 | 35 | // { DomainWrite[] -> [Element[] -> Scatter[]] } |
919 | 35 | auto WrittenTranslator = WritesTarget.range_product(Schedule); |
920 | 35 | |
921 | 35 | // { [Element[] -> Scatter[]] -> ValInst[] } |
922 | 35 | auto Written = WrittenValue.apply_domain(WrittenTranslator); |
923 | 35 | simplify(Written); |
924 | 35 | |
925 | 35 | // { DomainWrite[] -> [Element[] -> Zone[]] } |
926 | 35 | auto LifetimeTranslator = WritesTarget.range_product(WriteLifetime); |
927 | 35 | |
928 | 35 | // { DomainWrite[] -> ValInst[] } |
929 | 35 | auto WrittenKnownValue = filterKnownValInst(WrittenValue); |
930 | 35 | |
931 | 35 | // { [Element[] -> Zone[]] -> ValInst[] } |
932 | 35 | auto EltLifetimeInst = WrittenKnownValue.apply_domain(LifetimeTranslator); |
933 | 35 | simplify(EltLifetimeInst); |
934 | 35 | |
935 | 35 | // { [Element[] -> Zone[] } |
936 | 35 | auto Occupied = LifetimeTranslator.range(); |
937 | 35 | simplify(Occupied); |
938 | 35 | |
939 | 35 | Knowledge Proposed(Occupied, nullptr, EltLifetimeInst, Written); |
940 | 35 | if (isConflicting(Proposed)) |
941 | 0 | return false; |
942 | 35 | |
943 | 35 | mapPHI(SAI, std::move(PHITarget), std::move(WritesTarget), |
944 | 35 | std::move(Lifetime), std::move(Proposed)); |
945 | 35 | return true; |
946 | 35 | } |
947 | | |
948 | | /// Map a MemoryKind::PHI scalar to an array element. |
949 | | /// |
950 | | /// Callers must have ensured that the mapping is valid and not conflicting |
951 | | /// with the common knowledge. |
952 | | /// |
953 | | /// @param SAI The ScopArrayInfo representing the scalar's memory to |
954 | | /// map. |
955 | | /// @param ReadTarget { DomainRead[] -> Element[] } |
956 | | /// The array element to map the scalar to. |
957 | | /// @param WriteTarget { DomainWrite[] -> Element[] } |
958 | | /// New access target for each PHI incoming write. |
959 | | /// @param Lifetime { DomainRead[] -> Zone[] } |
960 | | /// The lifetime of each PHI for reporting. |
961 | | /// @param Proposed Mapping constraints for reporting. |
962 | | void mapPHI(const ScopArrayInfo *SAI, isl::map ReadTarget, |
963 | | isl::union_map WriteTarget, isl::map Lifetime, |
964 | 35 | Knowledge Proposed) { |
965 | 35 | // { Element[] } |
966 | 35 | isl::space ElementSpace = ReadTarget.get_space().range(); |
967 | 35 | |
968 | 35 | // Redirect the PHI incoming writes. |
969 | 70 | for (auto *MA : S->getPHIIncomings(SAI)) { |
970 | 70 | // { DomainWrite[] } |
971 | 70 | auto Domain = getDomainFor(MA); |
972 | 70 | |
973 | 70 | // { DomainWrite[] -> Element[] } |
974 | 70 | auto NewAccRel = WriteTarget.intersect_domain(Domain); |
975 | 70 | simplify(NewAccRel); |
976 | 70 | |
977 | 70 | isl::space NewAccRelSpace = |
978 | 70 | Domain.get_space().map_from_domain_and_range(ElementSpace); |
979 | 70 | isl::map NewAccRelMap = singleton(NewAccRel, NewAccRelSpace); |
980 | 70 | MA->setNewAccessRelation(NewAccRelMap); |
981 | 70 | } |
982 | 35 | |
983 | 35 | // Redirect the PHI read. |
984 | 35 | auto *PHIRead = S->getPHIRead(SAI); |
985 | 35 | PHIRead->setNewAccessRelation(ReadTarget); |
986 | 35 | applyLifetime(Proposed); |
987 | 35 | |
988 | 35 | MappedPHIScalars++; |
989 | 35 | NumberOfMappedPHIScalars++; |
990 | 35 | } |
991 | | |
992 | | /// Search and map scalars to memory overwritten by @p TargetStoreMA. |
993 | | /// |
994 | | /// Start trying to map scalars that are used in the same statement as the |
995 | | /// store. For every successful mapping, try to also map scalars of the |
996 | | /// statements where those are written. Repeat, until no more mapping |
997 | | /// opportunity is found. |
998 | | /// |
999 | | /// There is currently no preference in which order scalars are tried. |
1000 | | /// Ideally, we would direct it towards a load instruction of the same array |
1001 | | /// element. |
1002 | 42 | bool collapseScalarsToStore(MemoryAccess *TargetStoreMA) { |
1003 | 42 | assert(TargetStoreMA->isLatestArrayKind()); |
1004 | 42 | assert(TargetStoreMA->isMustWrite()); |
1005 | 42 | |
1006 | 42 | auto TargetStmt = TargetStoreMA->getStatement(); |
1007 | 42 | |
1008 | 42 | // { DomTarget[] } |
1009 | 42 | auto TargetDom = getDomainFor(TargetStmt); |
1010 | 42 | |
1011 | 42 | // { DomTarget[] -> Element[] } |
1012 | 42 | auto TargetAccRel = getAccessRelationFor(TargetStoreMA); |
1013 | 42 | |
1014 | 42 | // { Zone[] -> DomTarget[] } |
1015 | 42 | // For each point in time, find the next target store instance. |
1016 | 42 | auto Target = |
1017 | 42 | computeScalarReachingOverwrite(Schedule, TargetDom, false, true); |
1018 | 42 | |
1019 | 42 | // { Zone[] -> Element[] } |
1020 | 42 | // Use the target store's write location as a suggestion to map scalars to. |
1021 | 42 | auto EltTarget = Target.apply_range(TargetAccRel); |
1022 | 42 | simplify(EltTarget); |
1023 | 42 | LLVM_DEBUG(dbgs() << " Target mapping is " << EltTarget << '\n'); |
1024 | 42 | |
1025 | 42 | // Stack of elements not yet processed. |
1026 | 42 | SmallVector<MemoryAccess *, 16> Worklist; |
1027 | 42 | |
1028 | 42 | // Set of scalars already tested. |
1029 | 42 | SmallPtrSet<const ScopArrayInfo *, 16> Closed; |
1030 | 42 | |
1031 | 42 | // Lambda to add all scalar reads to the work list. |
1032 | 104 | auto ProcessAllIncoming = [&](ScopStmt *Stmt) { |
1033 | 211 | for (auto *MA : *Stmt) { |
1034 | 211 | if (!MA->isLatestScalarKind()) |
1035 | 147 | continue; |
1036 | 64 | if (!MA->isRead()) |
1037 | 10 | continue; |
1038 | 54 | |
1039 | 54 | Worklist.push_back(MA); |
1040 | 54 | } |
1041 | 104 | }; |
1042 | 42 | |
1043 | 42 | auto *WrittenVal = TargetStoreMA->getAccessInstruction()->getOperand(0); |
1044 | 42 | if (auto *WrittenValInputMA = TargetStmt->lookupInputAccessOf(WrittenVal)) |
1045 | 29 | Worklist.push_back(WrittenValInputMA); |
1046 | 13 | else |
1047 | 13 | ProcessAllIncoming(TargetStmt); |
1048 | 42 | |
1049 | 42 | auto AnyMapped = false; |
1050 | 42 | auto &DL = S->getRegion().getEntry()->getModule()->getDataLayout(); |
1051 | 42 | auto StoreSize = |
1052 | 42 | DL.getTypeAllocSize(TargetStoreMA->getAccessValue()->getType()); |
1053 | 42 | |
1054 | 155 | while (!Worklist.empty()) { |
1055 | 113 | auto *MA = Worklist.pop_back_val(); |
1056 | 113 | |
1057 | 113 | auto *SAI = MA->getScopArrayInfo(); |
1058 | 113 | if (Closed.count(SAI)) |
1059 | 14 | continue; |
1060 | 99 | Closed.insert(SAI); |
1061 | 99 | LLVM_DEBUG(dbgs() << "\n Trying to map " << MA << " (SAI: " << SAI |
1062 | 99 | << ")\n"); |
1063 | 99 | |
1064 | 99 | // Skip non-mappable scalars. |
1065 | 99 | if (!isMappable(SAI)) |
1066 | 3 | continue; |
1067 | 96 | |
1068 | 96 | auto MASize = DL.getTypeAllocSize(MA->getAccessValue()->getType()); |
1069 | 96 | if (MASize > StoreSize) { |
1070 | 1 | LLVM_DEBUG( |
1071 | 1 | dbgs() << " Reject because storage size is insufficient\n"); |
1072 | 1 | continue; |
1073 | 1 | } |
1074 | 95 | |
1075 | 95 | // Try to map MemoryKind::Value scalars. |
1076 | 95 | if (SAI->isValueKind()) { |
1077 | 59 | if (!tryMapValue(SAI, EltTarget)) |
1078 | 8 | continue; |
1079 | 51 | |
1080 | 51 | auto *DefAcc = S->getValueDef(SAI); |
1081 | 51 | ProcessAllIncoming(DefAcc->getStatement()); |
1082 | 51 | |
1083 | 51 | AnyMapped = true; |
1084 | 51 | continue; |
1085 | 51 | } |
1086 | 36 | |
1087 | 36 | // Try to map MemoryKind::PHI scalars. |
1088 | 36 | if (SAI->isPHIKind()) { |
1089 | 36 | if (!tryMapPHI(SAI, EltTarget)) |
1090 | 1 | continue; |
1091 | 35 | // Add inputs of all incoming statements to the worklist. Prefer the |
1092 | 35 | // input accesses of the incoming blocks. |
1093 | 70 | for (auto *PHIWrite : S->getPHIIncomings(SAI))35 { |
1094 | 70 | auto *PHIWriteStmt = PHIWrite->getStatement(); |
1095 | 70 | bool FoundAny = false; |
1096 | 70 | for (auto Incoming : PHIWrite->getIncoming()) { |
1097 | 70 | auto *IncomingInputMA = |
1098 | 70 | PHIWriteStmt->lookupInputAccessOf(Incoming.second); |
1099 | 70 | if (!IncomingInputMA) |
1100 | 40 | continue; |
1101 | 30 | |
1102 | 30 | Worklist.push_back(IncomingInputMA); |
1103 | 30 | FoundAny = true; |
1104 | 30 | } |
1105 | 70 | |
1106 | 70 | if (!FoundAny) |
1107 | 40 | ProcessAllIncoming(PHIWrite->getStatement()); |
1108 | 70 | } |
1109 | 35 | |
1110 | 35 | AnyMapped = true; |
1111 | 35 | continue; |
1112 | 35 | } |
1113 | 36 | } |
1114 | 42 | |
1115 | 42 | if (AnyMapped) { |
1116 | 32 | TargetsMapped++; |
1117 | 32 | NumberOfTargetsMapped++; |
1118 | 32 | } |
1119 | 42 | return AnyMapped; |
1120 | 42 | } |
1121 | | |
1122 | | /// Compute when an array element is unused. |
1123 | | /// |
1124 | | /// @return { [Element[] -> Zone[]] } |
1125 | 53 | isl::union_set computeLifetime() const { |
1126 | 53 | // { Element[] -> Zone[] } |
1127 | 53 | auto ArrayUnused = computeArrayUnused(Schedule, AllMustWrites, AllReads, |
1128 | 53 | false, false, true); |
1129 | 53 | |
1130 | 53 | auto Result = ArrayUnused.wrap(); |
1131 | 53 | |
1132 | 53 | simplify(Result); |
1133 | 53 | return Result; |
1134 | 53 | } |
1135 | | |
1136 | | /// Determine when an array element is written to, and which value instance is |
1137 | | /// written. |
1138 | | /// |
1139 | | /// @return { [Element[] -> Scatter[]] -> ValInst[] } |
1140 | 53 | isl::union_map computeWritten() const { |
1141 | 53 | // { [Element[] -> Scatter[]] -> ValInst[] } |
1142 | 53 | auto EltWritten = applyDomainRange(AllWriteValInst, Schedule); |
1143 | 53 | |
1144 | 53 | simplify(EltWritten); |
1145 | 53 | return EltWritten; |
1146 | 53 | } |
1147 | | |
1148 | | /// Determine whether an access touches at most one element. |
1149 | | /// |
1150 | | /// The accessed element could be a scalar or accessing an array with constant |
1151 | | /// subscript, such that all instances access only that element. |
1152 | | /// |
1153 | | /// @param MA The access to test. |
1154 | | /// |
1155 | | /// @return True, if zero or one elements are accessed; False if at least two |
1156 | | /// different elements are accessed. |
1157 | 61 | bool isScalarAccess(MemoryAccess *MA) { |
1158 | 61 | auto Map = getAccessRelationFor(MA); |
1159 | 61 | auto Set = Map.range(); |
1160 | 61 | return Set.is_singleton(); |
1161 | 61 | } |
1162 | | |
1163 | | /// Print mapping statistics to @p OS. |
1164 | 46 | void printStatistics(llvm::raw_ostream &OS, int Indent = 0) const { |
1165 | 46 | OS.indent(Indent) << "Statistics {\n"; |
1166 | 46 | OS.indent(Indent + 4) << "Compatible overwrites: " |
1167 | 46 | << NumberOfCompatibleTargets << "\n"; |
1168 | 46 | OS.indent(Indent + 4) << "Overwrites mapped to: " << NumberOfTargetsMapped |
1169 | 46 | << '\n'; |
1170 | 46 | OS.indent(Indent + 4) << "Value scalars mapped: " |
1171 | 46 | << NumberOfMappedValueScalars << '\n'; |
1172 | 46 | OS.indent(Indent + 4) << "PHI scalars mapped: " |
1173 | 46 | << NumberOfMappedPHIScalars << '\n'; |
1174 | 46 | OS.indent(Indent) << "}\n"; |
1175 | 46 | } |
1176 | | |
1177 | | /// Return whether at least one transformation been applied. |
1178 | 46 | bool isModified() const { return NumberOfTargetsMapped > 0; } |
1179 | | |
1180 | | public: |
1181 | 53 | DeLICMImpl(Scop *S, LoopInfo *LI) : ZoneAlgorithm("polly-delicm", S, LI) {} |
1182 | | |
1183 | | /// Calculate the lifetime (definition to last use) of every array element. |
1184 | | /// |
1185 | | /// @return True if the computed lifetimes (#Zone) is usable. |
1186 | 53 | bool computeZone() { |
1187 | 53 | // Check that nothing strange occurs. |
1188 | 53 | collectCompatibleElts(); |
1189 | 53 | |
1190 | 53 | isl::union_set EltUnused; |
1191 | 53 | isl::union_map EltKnown, EltWritten; |
1192 | 53 | |
1193 | 53 | { |
1194 | 53 | IslMaxOperationsGuard MaxOpGuard(IslCtx.get(), DelicmMaxOps); |
1195 | 53 | |
1196 | 53 | computeCommon(); |
1197 | 53 | |
1198 | 53 | EltUnused = computeLifetime(); |
1199 | 53 | EltKnown = computeKnown(true, false); |
1200 | 53 | EltWritten = computeWritten(); |
1201 | 53 | } |
1202 | 53 | DeLICMAnalyzed++; |
1203 | 53 | |
1204 | 53 | if (!EltUnused || !EltKnown50 || !EltWritten50 ) { |
1205 | 3 | assert(isl_ctx_last_error(IslCtx.get()) == isl_error_quota && |
1206 | 3 | "The only reason that these things have not been computed should " |
1207 | 3 | "be if the max-operations limit hit"); |
1208 | 3 | DeLICMOutOfQuota++; |
1209 | 3 | LLVM_DEBUG(dbgs() << "DeLICM analysis exceeded max_operations\n"); |
1210 | 3 | DebugLoc Begin, End; |
1211 | 3 | getDebugLocations(getBBPairForRegion(&S->getRegion()), Begin, End); |
1212 | 3 | OptimizationRemarkAnalysis R(DEBUG_TYPE, "OutOfQuota", Begin, |
1213 | 3 | S->getEntry()); |
1214 | 3 | R << "maximal number of operations exceeded during zone analysis"; |
1215 | 3 | S->getFunction().getContext().diagnose(R); |
1216 | 3 | return false; |
1217 | 3 | } |
1218 | 50 | |
1219 | 50 | Zone = OriginalZone = Knowledge(nullptr, EltUnused, EltKnown, EltWritten); |
1220 | 50 | LLVM_DEBUG(dbgs() << "Computed Zone:\n"; OriginalZone.print(dbgs(), 4)); |
1221 | 50 | |
1222 | 50 | assert(Zone.isUsable() && OriginalZone.isUsable()); |
1223 | 50 | return true; |
1224 | 50 | } |
1225 | | |
1226 | | /// Try to map as many scalars to unused array elements as possible. |
1227 | | /// |
1228 | | /// Multiple scalars might be mappable to intersecting unused array element |
1229 | | /// zones, but we can only chose one. This is a greedy algorithm, therefore |
1230 | | /// the first processed element claims it. |
1231 | 50 | void greedyCollapse() { |
1232 | 50 | bool Modified = false; |
1233 | 50 | |
1234 | 228 | for (auto &Stmt : *S) { |
1235 | 446 | for (auto *MA : Stmt) { |
1236 | 446 | if (!MA->isLatestArrayKind()) |
1237 | 332 | continue; |
1238 | 114 | if (!MA->isWrite()) |
1239 | 44 | continue; |
1240 | 70 | |
1241 | 70 | if (MA->isMayWrite()) { |
1242 | 4 | LLVM_DEBUG(dbgs() << "Access " << MA |
1243 | 4 | << " pruned because it is a MAY_WRITE\n"); |
1244 | 4 | OptimizationRemarkMissed R(DEBUG_TYPE, "TargetMayWrite", |
1245 | 4 | MA->getAccessInstruction()); |
1246 | 4 | R << "Skipped possible mapping target because it is not an " |
1247 | 4 | "unconditional overwrite"; |
1248 | 4 | S->getFunction().getContext().diagnose(R); |
1249 | 4 | continue; |
1250 | 4 | } |
1251 | 66 | |
1252 | 66 | if (Stmt.getNumIterators() == 0) { |
1253 | 5 | LLVM_DEBUG(dbgs() << "Access " << MA |
1254 | 5 | << " pruned because it is not in a loop\n"); |
1255 | 5 | OptimizationRemarkMissed R(DEBUG_TYPE, "WriteNotInLoop", |
1256 | 5 | MA->getAccessInstruction()); |
1257 | 5 | R << "skipped possible mapping target because it is not in a loop"; |
1258 | 5 | S->getFunction().getContext().diagnose(R); |
1259 | 5 | continue; |
1260 | 5 | } |
1261 | 61 | |
1262 | 61 | if (isScalarAccess(MA)) { |
1263 | 5 | LLVM_DEBUG(dbgs() |
1264 | 5 | << "Access " << MA |
1265 | 5 | << " pruned because it writes only a single element\n"); |
1266 | 5 | OptimizationRemarkMissed R(DEBUG_TYPE, "ScalarWrite", |
1267 | 5 | MA->getAccessInstruction()); |
1268 | 5 | R << "skipped possible mapping target because the memory location " |
1269 | 5 | "written to does not depend on its outer loop"; |
1270 | 5 | S->getFunction().getContext().diagnose(R); |
1271 | 5 | continue; |
1272 | 5 | } |
1273 | 56 | |
1274 | 56 | if (!isa<StoreInst>(MA->getAccessInstruction())) { |
1275 | 9 | LLVM_DEBUG(dbgs() << "Access " << MA |
1276 | 9 | << " pruned because it is not a StoreInst\n"); |
1277 | 9 | OptimizationRemarkMissed R(DEBUG_TYPE, "NotAStore", |
1278 | 9 | MA->getAccessInstruction()); |
1279 | 9 | R << "skipped possible mapping target because non-store instructions " |
1280 | 9 | "are not supported"; |
1281 | 9 | S->getFunction().getContext().diagnose(R); |
1282 | 9 | continue; |
1283 | 9 | } |
1284 | 47 | |
1285 | 47 | // Check for more than one element acces per statement instance. |
1286 | 47 | // Currently we expect write accesses to be functional, eg. disallow |
1287 | 47 | // |
1288 | 47 | // { Stmt[0] -> [i] : 0 <= i < 2 } |
1289 | 47 | // |
1290 | 47 | // This may occur when some accesses to the element write/read only |
1291 | 47 | // parts of the element, eg. a single byte. Polly then divides each |
1292 | 47 | // element into subelements of the smallest access length, normal access |
1293 | 47 | // then touch multiple of such subelements. It is very common when the |
1294 | 47 | // array is accesses with memset, memcpy or memmove which take i8* |
1295 | 47 | // arguments. |
1296 | 47 | isl::union_map AccRel = MA->getLatestAccessRelation(); |
1297 | 47 | if (!AccRel.is_single_valued().is_true()) { |
1298 | 1 | LLVM_DEBUG(dbgs() << "Access " << MA |
1299 | 1 | << " is incompatible because it writes multiple " |
1300 | 1 | "elements per instance\n"); |
1301 | 1 | OptimizationRemarkMissed R(DEBUG_TYPE, "NonFunctionalAccRel", |
1302 | 1 | MA->getAccessInstruction()); |
1303 | 1 | R << "skipped possible mapping target because it writes more than " |
1304 | 1 | "one element"; |
1305 | 1 | S->getFunction().getContext().diagnose(R); |
1306 | 1 | continue; |
1307 | 1 | } |
1308 | 46 | |
1309 | 46 | isl::union_set TouchedElts = AccRel.range(); |
1310 | 46 | if (!TouchedElts.is_subset(CompatibleElts)) { |
1311 | 4 | LLVM_DEBUG( |
1312 | 4 | dbgs() |
1313 | 4 | << "Access " << MA |
1314 | 4 | << " is incompatible because it touches incompatible elements\n"); |
1315 | 4 | OptimizationRemarkMissed R(DEBUG_TYPE, "IncompatibleElts", |
1316 | 4 | MA->getAccessInstruction()); |
1317 | 4 | R << "skipped possible mapping target because a target location " |
1318 | 4 | "cannot be reliably analyzed"; |
1319 | 4 | S->getFunction().getContext().diagnose(R); |
1320 | 4 | continue; |
1321 | 4 | } |
1322 | 42 | |
1323 | 42 | assert(isCompatibleAccess(MA)); |
1324 | 42 | NumberOfCompatibleTargets++; |
1325 | 42 | LLVM_DEBUG(dbgs() << "Analyzing target access " << MA << "\n"); |
1326 | 42 | if (collapseScalarsToStore(MA)) |
1327 | 32 | Modified = true; |
1328 | 42 | } |
1329 | 228 | } |
1330 | 50 | |
1331 | 50 | if (Modified) |
1332 | 32 | DeLICMScopsModified++; |
1333 | 50 | } |
1334 | | |
1335 | | /// Dump the internal information about a performed DeLICM to @p OS. |
1336 | 49 | void print(llvm::raw_ostream &OS, int Indent = 0) { |
1337 | 49 | if (!Zone.isUsable()) { |
1338 | 3 | OS.indent(Indent) << "Zone not computed\n"; |
1339 | 3 | return; |
1340 | 3 | } |
1341 | 46 | |
1342 | 46 | printStatistics(OS, Indent); |
1343 | 46 | if (!isModified()) { |
1344 | 16 | OS.indent(Indent) << "No modification has been made\n"; |
1345 | 16 | return; |
1346 | 16 | } |
1347 | 30 | printAccesses(OS, Indent); |
1348 | 30 | } |
1349 | | }; |
1350 | | |
1351 | | class DeLICM : public ScopPass { |
1352 | | private: |
1353 | | DeLICM(const DeLICM &) = delete; |
1354 | | const DeLICM &operator=(const DeLICM &) = delete; |
1355 | | |
1356 | | /// The pass implementation, also holding per-scop data. |
1357 | | std::unique_ptr<DeLICMImpl> Impl; |
1358 | | |
1359 | 53 | void collapseToUnused(Scop &S) { |
1360 | 53 | auto &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo(); |
1361 | 53 | Impl = make_unique<DeLICMImpl>(&S, &LI); |
1362 | 53 | |
1363 | 53 | if (!Impl->computeZone()) { |
1364 | 3 | LLVM_DEBUG(dbgs() << "Abort because cannot reliably compute lifetimes\n"); |
1365 | 3 | return; |
1366 | 3 | } |
1367 | 50 | |
1368 | 50 | LLVM_DEBUG(dbgs() << "Collapsing scalars to unused array elements...\n"); |
1369 | 50 | Impl->greedyCollapse(); |
1370 | 50 | |
1371 | 50 | LLVM_DEBUG(dbgs() << "\nFinal Scop:\n"); |
1372 | 50 | LLVM_DEBUG(dbgs() << S); |
1373 | 50 | } |
1374 | | |
1375 | | public: |
1376 | | static char ID; |
1377 | 53 | explicit DeLICM() : ScopPass(ID) {} |
1378 | | |
1379 | 53 | virtual void getAnalysisUsage(AnalysisUsage &AU) const override { |
1380 | 53 | AU.addRequiredTransitive<ScopInfoRegionPass>(); |
1381 | 53 | AU.addRequired<LoopInfoWrapperPass>(); |
1382 | 53 | AU.setPreservesAll(); |
1383 | 53 | } |
1384 | | |
1385 | 53 | virtual bool runOnScop(Scop &S) override { |
1386 | 53 | // Free resources for previous scop's computation, if not yet done. |
1387 | 53 | releaseMemory(); |
1388 | 53 | |
1389 | 53 | collapseToUnused(S); |
1390 | 53 | |
1391 | 53 | auto ScopStats = S.getStatistics(); |
1392 | 53 | NumValueWrites += ScopStats.NumValueWrites; |
1393 | 53 | NumValueWritesInLoops += ScopStats.NumValueWritesInLoops; |
1394 | 53 | NumPHIWrites += ScopStats.NumPHIWrites; |
1395 | 53 | NumPHIWritesInLoops += ScopStats.NumPHIWritesInLoops; |
1396 | 53 | NumSingletonWrites += ScopStats.NumSingletonWrites; |
1397 | 53 | NumSingletonWritesInLoops += ScopStats.NumSingletonWritesInLoops; |
1398 | 53 | |
1399 | 53 | return false; |
1400 | 53 | } |
1401 | | |
1402 | 49 | virtual void printScop(raw_ostream &OS, Scop &S) const override { |
1403 | 49 | if (!Impl) |
1404 | 0 | return; |
1405 | 49 | assert(Impl->getScop() == &S); |
1406 | 49 | |
1407 | 49 | OS << "DeLICM result:\n"; |
1408 | 49 | Impl->print(OS); |
1409 | 49 | } |
1410 | | |
1411 | 291 | virtual void releaseMemory() override { Impl.reset(); } |
1412 | | }; |
1413 | | |
1414 | | char DeLICM::ID; |
1415 | | } // anonymous namespace |
1416 | | |
1417 | 0 | Pass *polly::createDeLICMPass() { return new DeLICM(); } |
1418 | | |
1419 | 48.2k | INITIALIZE_PASS_BEGIN(DeLICM, "polly-delicm", "Polly - DeLICM/DePRE", false, |
1420 | 48.2k | false) |
1421 | 48.2k | INITIALIZE_PASS_DEPENDENCY(ScopInfoWrapperPass) |
1422 | 48.2k | INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass) |
1423 | 48.2k | INITIALIZE_PASS_END(DeLICM, "polly-delicm", "Polly - DeLICM/DePRE", false, |
1424 | | false) |
1425 | | |
1426 | | bool polly::isConflicting( |
1427 | | isl::union_set ExistingOccupied, isl::union_set ExistingUnused, |
1428 | | isl::union_map ExistingKnown, isl::union_map ExistingWrites, |
1429 | | isl::union_set ProposedOccupied, isl::union_set ProposedUnused, |
1430 | | isl::union_map ProposedKnown, isl::union_map ProposedWrites, |
1431 | 232 | llvm::raw_ostream *OS, unsigned Indent) { |
1432 | 232 | Knowledge Existing(std::move(ExistingOccupied), std::move(ExistingUnused), |
1433 | 232 | std::move(ExistingKnown), std::move(ExistingWrites)); |
1434 | 232 | Knowledge Proposed(std::move(ProposedOccupied), std::move(ProposedUnused), |
1435 | 232 | std::move(ProposedKnown), std::move(ProposedWrites)); |
1436 | 232 | |
1437 | 232 | return Knowledge::isConflicting(Existing, Proposed, OS, Indent); |
1438 | 232 | } |