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