1 //===------ ISLTools.cpp ----------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // Tools, utilities, helpers and extensions useful in conjunction with the
11 // Integer Set Library (isl).
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #include "polly/Support/ISLTools.h"
16 
17 using namespace polly;
18 
19 namespace {
20 /// Create a map that shifts one dimension by an offset.
21 ///
22 /// Example:
23 /// makeShiftDimAff({ [i0, i1] -> [o0, o1] }, 1, -2)
24 ///   = { [i0, i1] -> [i0, i1 - 1] }
25 ///
26 /// @param Space  The map space of the result. Must have equal number of in- and
27 ///               out-dimensions.
28 /// @param Pos    Position to shift.
29 /// @param Amount Value added to the shifted dimension.
30 ///
31 /// @return An isl_multi_aff for the map with this shifted dimension.
32 IslPtr<isl_multi_aff> makeShiftDimAff(IslPtr<isl_space> Space, int Pos,
33                                       int Amount) {
34   auto Identity = give(isl_multi_aff_identity(Space.take()));
35   if (Amount == 0)
36     return Identity;
37   auto ShiftAff = give(isl_multi_aff_get_aff(Identity.keep(), Pos));
38   ShiftAff = give(isl_aff_set_constant_si(ShiftAff.take(), Amount));
39   return give(isl_multi_aff_set_aff(Identity.take(), Pos, ShiftAff.take()));
40 }
41 
42 /// Construct a map that swaps two nested tuples.
43 ///
44 /// @param FromSpace1 { Space1[] }
45 /// @param FromSpace2 { Space2[] }
46 ///
47 /// @return { [Space1[] -> Space2[]] -> [Space2[] -> Space1[]] }
48 IslPtr<isl_basic_map> makeTupleSwapBasicMap(IslPtr<isl_space> FromSpace1,
49                                             IslPtr<isl_space> FromSpace2) {
50   assert(isl_space_is_set(FromSpace1.keep()) != isl_bool_false);
51   assert(isl_space_is_set(FromSpace2.keep()) != isl_bool_false);
52 
53   auto Dims1 = isl_space_dim(FromSpace1.keep(), isl_dim_set);
54   auto Dims2 = isl_space_dim(FromSpace2.keep(), isl_dim_set);
55   auto FromSpace = give(isl_space_wrap(isl_space_map_from_domain_and_range(
56       FromSpace1.copy(), FromSpace2.copy())));
57   auto ToSpace = give(isl_space_wrap(isl_space_map_from_domain_and_range(
58       FromSpace2.take(), FromSpace1.take())));
59   auto MapSpace = give(
60       isl_space_map_from_domain_and_range(FromSpace.take(), ToSpace.take()));
61 
62   auto Result = give(isl_basic_map_universe(MapSpace.take()));
63   for (auto i = Dims1 - Dims1; i < Dims1; i += 1) {
64     Result = give(isl_basic_map_equate(Result.take(), isl_dim_in, i,
65                                        isl_dim_out, Dims2 + i));
66   }
67   for (auto i = Dims2 - Dims2; i < Dims2; i += 1) {
68     Result = give(isl_basic_map_equate(Result.take(), isl_dim_in, Dims1 + i,
69                                        isl_dim_out, i));
70   }
71 
72   return Result;
73 }
74 
75 /// Like makeTupleSwapBasicMap(IslPtr<isl_space>,IslPtr<isl_space>), but returns
76 /// an isl_map.
77 IslPtr<isl_map> makeTupleSwapMap(IslPtr<isl_space> FromSpace1,
78                                  IslPtr<isl_space> FromSpace2) {
79   auto BMapResult =
80       makeTupleSwapBasicMap(std::move(FromSpace1), std::move(FromSpace2));
81   return give(isl_map_from_basic_map(BMapResult.take()));
82 }
83 } // anonymous namespace
84 
85 IslPtr<isl_map> polly::beforeScatter(IslPtr<isl_map> Map, bool Strict) {
86   auto RangeSpace = give(isl_space_range(isl_map_get_space(Map.keep())));
87   auto ScatterRel = give(Strict ? isl_map_lex_gt(RangeSpace.take())
88                                 : isl_map_lex_ge(RangeSpace.take()));
89   return give(isl_map_apply_range(Map.take(), ScatterRel.take()));
90 }
91 
92 IslPtr<isl_union_map> polly::beforeScatter(IslPtr<isl_union_map> UMap,
93                                            bool Strict) {
94   auto Result = give(isl_union_map_empty(isl_union_map_get_space(UMap.keep())));
95   foreachElt(UMap, [=, &Result](IslPtr<isl_map> Map) {
96     auto After = beforeScatter(Map, Strict);
97     Result = give(isl_union_map_add_map(Result.take(), After.take()));
98   });
99   return Result;
100 }
101 
102 IslPtr<isl_map> polly::afterScatter(IslPtr<isl_map> Map, bool Strict) {
103   auto RangeSpace = give(isl_space_range(isl_map_get_space(Map.keep())));
104   auto ScatterRel = give(Strict ? isl_map_lex_lt(RangeSpace.take())
105                                 : isl_map_lex_le(RangeSpace.take()));
106   return give(isl_map_apply_range(Map.take(), ScatterRel.take()));
107 }
108 
109 IslPtr<isl_union_map> polly::afterScatter(const IslPtr<isl_union_map> &UMap,
110                                           bool Strict) {
111   auto Result = give(isl_union_map_empty(isl_union_map_get_space(UMap.keep())));
112   foreachElt(UMap, [=, &Result](IslPtr<isl_map> Map) {
113     auto After = afterScatter(Map, Strict);
114     Result = give(isl_union_map_add_map(Result.take(), After.take()));
115   });
116   return Result;
117 }
118 
119 IslPtr<isl_map> polly::betweenScatter(IslPtr<isl_map> From, IslPtr<isl_map> To,
120                                       bool InclFrom, bool InclTo) {
121   auto AfterFrom = afterScatter(From, !InclFrom);
122   auto BeforeTo = beforeScatter(To, !InclTo);
123 
124   return give(isl_map_intersect(AfterFrom.take(), BeforeTo.take()));
125 }
126 
127 IslPtr<isl_union_map> polly::betweenScatter(IslPtr<isl_union_map> From,
128                                             IslPtr<isl_union_map> To,
129                                             bool InclFrom, bool InclTo) {
130   auto AfterFrom = afterScatter(From, !InclFrom);
131   auto BeforeTo = beforeScatter(To, !InclTo);
132 
133   return give(isl_union_map_intersect(AfterFrom.take(), BeforeTo.take()));
134 }
135 
136 IslPtr<isl_map> polly::singleton(IslPtr<isl_union_map> UMap,
137                                  IslPtr<isl_space> ExpectedSpace) {
138   if (!UMap)
139     return nullptr;
140 
141   if (isl_union_map_n_map(UMap.keep()) == 0)
142     return give(isl_map_empty(ExpectedSpace.take()));
143 
144   auto Result = give(isl_map_from_union_map(UMap.take()));
145   assert(!Result || isl_space_has_equal_tuples(
146                         give(isl_map_get_space(Result.keep())).keep(),
147                         ExpectedSpace.keep()) == isl_bool_true);
148   return Result;
149 }
150 
151 IslPtr<isl_set> polly::singleton(IslPtr<isl_union_set> USet,
152                                  IslPtr<isl_space> ExpectedSpace) {
153   if (!USet)
154     return nullptr;
155 
156   if (isl_union_set_n_set(USet.keep()) == 0)
157     return give(isl_set_empty(ExpectedSpace.copy()));
158 
159   auto Result = give(isl_set_from_union_set(USet.take()));
160   assert(!Result || isl_space_has_equal_tuples(
161                         give(isl_set_get_space(Result.keep())).keep(),
162                         ExpectedSpace.keep()) == isl_bool_true);
163   return Result;
164 }
165 
166 unsigned polly::getNumScatterDims(const IslPtr<isl_union_map> &Schedule) {
167   unsigned Dims = 0;
168   foreachElt(Schedule, [&Dims](IslPtr<isl_map> Map) {
169     Dims = std::max(Dims, isl_map_dim(Map.keep(), isl_dim_out));
170   });
171   return Dims;
172 }
173 
174 IslPtr<isl_space>
175 polly::getScatterSpace(const IslPtr<isl_union_map> &Schedule) {
176   if (!Schedule)
177     return nullptr;
178   auto Dims = getNumScatterDims(Schedule);
179   auto ScatterSpace =
180       give(isl_space_set_from_params(isl_union_map_get_space(Schedule.keep())));
181   return give(isl_space_add_dims(ScatterSpace.take(), isl_dim_set, Dims));
182 }
183 
184 IslPtr<isl_union_map> polly::makeIdentityMap(const IslPtr<isl_union_set> &USet,
185                                              bool RestrictDomain) {
186   auto Result = give(isl_union_map_empty(isl_union_set_get_space(USet.keep())));
187   foreachElt(USet, [=, &Result](IslPtr<isl_set> Set) {
188     auto IdentityMap = give(isl_map_identity(
189         isl_space_map_from_set(isl_set_get_space(Set.keep()))));
190     if (RestrictDomain)
191       IdentityMap =
192           give(isl_map_intersect_domain(IdentityMap.take(), Set.take()));
193     Result = give(isl_union_map_add_map(Result.take(), IdentityMap.take()));
194   });
195   return Result;
196 }
197 
198 IslPtr<isl_map> polly::reverseDomain(IslPtr<isl_map> Map) {
199   auto DomSpace =
200       give(isl_space_unwrap(isl_space_domain(isl_map_get_space(Map.keep()))));
201   auto Space1 = give(isl_space_domain(DomSpace.copy()));
202   auto Space2 = give(isl_space_range(DomSpace.take()));
203   auto Swap = makeTupleSwapMap(std::move(Space1), std::move(Space2));
204   return give(isl_map_apply_domain(Map.take(), Swap.take()));
205 }
206 
207 IslPtr<isl_union_map> polly::reverseDomain(const IslPtr<isl_union_map> &UMap) {
208   auto Result = give(isl_union_map_empty(isl_union_map_get_space(UMap.keep())));
209   foreachElt(UMap, [=, &Result](IslPtr<isl_map> Map) {
210     auto Reversed = reverseDomain(std::move(Map));
211     Result = give(isl_union_map_add_map(Result.take(), Reversed.take()));
212   });
213   return Result;
214 }
215 
216 IslPtr<isl_set> polly::shiftDim(IslPtr<isl_set> Set, int Pos, int Amount) {
217   int NumDims = isl_set_dim(Set.keep(), isl_dim_set);
218   if (Pos < 0)
219     Pos = NumDims + Pos;
220   assert(Pos < NumDims && "Dimension index must be in range");
221   auto Space = give(isl_set_get_space(Set.keep()));
222   Space = give(isl_space_map_from_domain_and_range(Space.copy(), Space.copy()));
223   auto Translator = makeShiftDimAff(std::move(Space), Pos, Amount);
224   auto TranslatorMap = give(isl_map_from_multi_aff(Translator.take()));
225   return give(isl_set_apply(Set.take(), TranslatorMap.take()));
226 }
227 
228 IslPtr<isl_union_set> polly::shiftDim(IslPtr<isl_union_set> USet, int Pos,
229                                       int Amount) {
230   auto Result = give(isl_union_set_empty(isl_union_set_get_space(USet.keep())));
231   foreachElt(USet, [=, &Result](IslPtr<isl_set> Set) {
232     auto Shifted = shiftDim(Set, Pos, Amount);
233     Result = give(isl_union_set_add_set(Result.take(), Shifted.take()));
234   });
235   return Result;
236 }
237 
238 void polly::simplify(IslPtr<isl_set> &Set) {
239   Set = give(isl_set_compute_divs(Set.take()));
240   Set = give(isl_set_detect_equalities(Set.take()));
241   Set = give(isl_set_coalesce(Set.take()));
242 }
243 
244 void polly::simplify(IslPtr<isl_union_set> &USet) {
245   USet = give(isl_union_set_compute_divs(USet.take()));
246   USet = give(isl_union_set_detect_equalities(USet.take()));
247   USet = give(isl_union_set_coalesce(USet.take()));
248 }
249 
250 void polly::simplify(IslPtr<isl_map> &Map) {
251   Map = give(isl_map_compute_divs(Map.take()));
252   Map = give(isl_map_detect_equalities(Map.take()));
253   Map = give(isl_map_coalesce(Map.take()));
254 }
255 
256 void polly::simplify(IslPtr<isl_union_map> &UMap) {
257   UMap = give(isl_union_map_compute_divs(UMap.take()));
258   UMap = give(isl_union_map_detect_equalities(UMap.take()));
259   UMap = give(isl_union_map_coalesce(UMap.take()));
260 }
261 
262 IslPtr<isl_union_map>
263 polly::computeReachingWrite(IslPtr<isl_union_map> Schedule,
264                             IslPtr<isl_union_map> Writes, bool Reverse,
265                             bool InclPrevDef, bool InclNextDef) {
266 
267   // { Scatter[] }
268   auto ScatterSpace = getScatterSpace(Schedule);
269 
270   // { ScatterRead[] -> ScatterWrite[] }
271   IslPtr<isl_map> Relation;
272   if (Reverse)
273     Relation = give(InclPrevDef ? isl_map_lex_lt(ScatterSpace.take())
274                                 : isl_map_lex_le(ScatterSpace.take()));
275   else
276     Relation = give(InclNextDef ? isl_map_lex_gt(ScatterSpace.take())
277                                 : isl_map_lex_ge(ScatterSpace.take()));
278 
279   // { ScatterWrite[] -> [ScatterRead[] -> ScatterWrite[]] }
280   auto RelationMap = give(isl_map_reverse(isl_map_range_map(Relation.take())));
281 
282   // { Element[] -> ScatterWrite[] }
283   auto WriteAction =
284       give(isl_union_map_apply_domain(Schedule.copy(), Writes.take()));
285 
286   // { ScatterWrite[] -> Element[] }
287   auto WriteActionRev = give(isl_union_map_reverse(WriteAction.copy()));
288 
289   // { Element[] -> [ScatterUse[] -> ScatterWrite[]] }
290   auto DefSchedRelation = give(isl_union_map_apply_domain(
291       isl_union_map_from_map(RelationMap.take()), WriteActionRev.take()));
292 
293   // For each element, at every point in time, map to the times of previous
294   // definitions. { [Element[] -> ScatterRead[]] -> ScatterWrite[] }
295   auto ReachableWrites = give(isl_union_map_uncurry(DefSchedRelation.take()));
296   if (Reverse)
297     ReachableWrites = give(isl_union_map_lexmin(ReachableWrites.copy()));
298   else
299     ReachableWrites = give(isl_union_map_lexmax(ReachableWrites.copy()));
300 
301   // { [Element[] -> ScatterWrite[]] -> ScatterWrite[] }
302   auto SelfUse = give(isl_union_map_range_map(WriteAction.take()));
303 
304   if (InclPrevDef && InclNextDef) {
305     // Add the Def itself to the solution.
306     ReachableWrites =
307         give(isl_union_map_union(ReachableWrites.take(), SelfUse.take()));
308     ReachableWrites = give(isl_union_map_coalesce(ReachableWrites.take()));
309   } else if (!InclPrevDef && !InclNextDef) {
310     // Remove Def itself from the solution.
311     ReachableWrites =
312         give(isl_union_map_subtract(ReachableWrites.take(), SelfUse.take()));
313   }
314 
315   // { [Element[] -> ScatterRead[]] -> Domain[] }
316   auto ReachableWriteDomain = give(isl_union_map_apply_range(
317       ReachableWrites.take(), isl_union_map_reverse(Schedule.take())));
318 
319   return ReachableWriteDomain;
320 }
321 
322 IslPtr<isl_union_map> polly::computeArrayUnused(IslPtr<isl_union_map> Schedule,
323                                                 IslPtr<isl_union_map> Writes,
324                                                 IslPtr<isl_union_map> Reads,
325                                                 bool ReadEltInSameInst,
326                                                 bool IncludeLastRead,
327                                                 bool IncludeWrite) {
328   // { Element[] -> Scatter[] }
329   auto ReadActions =
330       give(isl_union_map_apply_domain(Schedule.copy(), Reads.take()));
331   auto WriteActions =
332       give(isl_union_map_apply_domain(Schedule.copy(), Writes.copy()));
333 
334   // { [Element[] -> Scatter[] }
335   auto AfterReads = afterScatter(ReadActions, ReadEltInSameInst);
336   auto WritesBeforeAnyReads =
337       give(isl_union_map_subtract(WriteActions.take(), AfterReads.take()));
338   auto BeforeWritesBeforeAnyReads =
339       beforeScatter(WritesBeforeAnyReads, !IncludeWrite);
340 
341   // { [Element[] -> DomainWrite[]] -> Scatter[] }
342   auto EltDomWrites = give(isl_union_map_apply_range(
343       isl_union_map_range_map(isl_union_map_reverse(Writes.copy())),
344       Schedule.copy()));
345 
346   // { [Element[] -> Scatter[]] -> DomainWrite[] }
347   auto ReachingOverwrite = computeReachingWrite(
348       Schedule, Writes, true, ReadEltInSameInst, !ReadEltInSameInst);
349 
350   // { [Element[] -> Scatter[]] -> DomainWrite[] }
351   auto ReadsOverwritten = give(isl_union_map_intersect_domain(
352       ReachingOverwrite.take(), isl_union_map_wrap(ReadActions.take())));
353 
354   // { [Element[] -> DomainWrite[]] -> Scatter[] }
355   auto ReadsOverwrittenRotated = give(isl_union_map_reverse(
356       isl_union_map_curry(reverseDomain(ReadsOverwritten).take())));
357   auto LastOverwrittenRead =
358       give(isl_union_map_lexmax(ReadsOverwrittenRotated.take()));
359 
360   // { [Element[] -> DomainWrite[]] -> Scatter[] }
361   auto BetweenLastReadOverwrite = betweenScatter(
362       LastOverwrittenRead, EltDomWrites, IncludeLastRead, IncludeWrite);
363 
364   return give(isl_union_map_union(
365       BeforeWritesBeforeAnyReads.take(),
366       isl_union_map_domain_factor_domain(BetweenLastReadOverwrite.take())));
367 }
368 
369 IslPtr<isl_union_set> polly::convertZoneToTimepoints(IslPtr<isl_union_set> Zone,
370                                                      bool InclStart,
371                                                      bool InclEnd) {
372   if (!InclStart && InclEnd)
373     return Zone;
374 
375   auto ShiftedZone = shiftDim(Zone, -1, -1);
376   if (InclStart && !InclEnd)
377     return ShiftedZone;
378   else if (!InclStart && !InclEnd)
379     return give(isl_union_set_intersect(Zone.take(), ShiftedZone.take()));
380 
381   assert(InclStart && InclEnd);
382   return give(isl_union_set_union(Zone.take(), ShiftedZone.take()));
383 }
384