1 | //===------ ISLTools.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 | // Tools, utilities, helpers and extensions useful in conjunction with the |
10 | // Integer Set Library (isl). |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "polly/Support/ISLTools.h" |
15 | #include "polly/Support/GICHelper.h" |
16 | #include "llvm/Support/raw_ostream.h" |
17 | #include <cassert> |
18 | #include <vector> |
19 | |
20 | using namespace polly; |
21 | |
22 | namespace { |
23 | /// Create a map that shifts one dimension by an offset. |
24 | /// |
25 | /// Example: |
26 | /// makeShiftDimAff({ [i0, i1] -> [o0, o1] }, 1, -2) |
27 | /// = { [i0, i1] -> [i0, i1 - 1] } |
28 | /// |
29 | /// @param Space The map space of the result. Must have equal number of in- and |
30 | /// out-dimensions. |
31 | /// @param Pos Position to shift. |
32 | /// @param Amount Value added to the shifted dimension. |
33 | /// |
34 | /// @return An isl_multi_aff for the map with this shifted dimension. |
35 | isl::multi_aff makeShiftDimAff(isl::space Space, int Pos, int Amount) { |
36 | auto Identity = isl::multi_aff::identity(space: Space); |
37 | if (Amount == 0) |
38 | return Identity; |
39 | auto ShiftAff = Identity.at(pos: Pos); |
40 | ShiftAff = ShiftAff.set_constant_si(Amount); |
41 | return Identity.set_aff(pos: Pos, el: ShiftAff); |
42 | } |
43 | |
44 | /// Construct a map that swaps two nested tuples. |
45 | /// |
46 | /// @param FromSpace1 { Space1[] } |
47 | /// @param FromSpace2 { Space2[] } |
48 | /// |
49 | /// @return { [Space1[] -> Space2[]] -> [Space2[] -> Space1[]] } |
50 | isl::basic_map makeTupleSwapBasicMap(isl::space FromSpace1, |
51 | isl::space FromSpace2) { |
52 | // Fast-path on out-of-quota. |
53 | if (FromSpace1.is_null() || FromSpace2.is_null()) |
54 | return {}; |
55 | |
56 | assert(FromSpace1.is_set()); |
57 | assert(FromSpace2.is_set()); |
58 | |
59 | unsigned Dims1 = unsignedFromIslSize(Size: FromSpace1.dim(type: isl::dim::set)); |
60 | unsigned Dims2 = unsignedFromIslSize(Size: FromSpace2.dim(type: isl::dim::set)); |
61 | |
62 | isl::space FromSpace = |
63 | FromSpace1.map_from_domain_and_range(range: FromSpace2).wrap(); |
64 | isl::space ToSpace = FromSpace2.map_from_domain_and_range(range: FromSpace1).wrap(); |
65 | isl::space MapSpace = FromSpace.map_from_domain_and_range(range: ToSpace); |
66 | |
67 | isl::basic_map Result = isl::basic_map::universe(space: MapSpace); |
68 | for (unsigned i = 0u; i < Dims1; i += 1) |
69 | Result = Result.equate(type1: isl::dim::in, pos1: i, type2: isl::dim::out, pos2: Dims2 + i); |
70 | for (unsigned i = 0u; i < Dims2; i += 1) { |
71 | Result = Result.equate(type1: isl::dim::in, pos1: Dims1 + i, type2: isl::dim::out, pos2: i); |
72 | } |
73 | |
74 | return Result; |
75 | } |
76 | |
77 | /// Like makeTupleSwapBasicMap(isl::space,isl::space), but returns |
78 | /// an isl_map. |
79 | isl::map makeTupleSwapMap(isl::space FromSpace1, isl::space FromSpace2) { |
80 | isl::basic_map BMapResult = makeTupleSwapBasicMap(FromSpace1, FromSpace2); |
81 | return isl::map(BMapResult); |
82 | } |
83 | } // anonymous namespace |
84 | |
85 | isl::map polly::beforeScatter(isl::map Map, bool Strict) { |
86 | isl::space RangeSpace = Map.get_space().range(); |
87 | isl::map ScatterRel = |
88 | Strict ? isl::map::lex_gt(set_space: RangeSpace) : isl::map::lex_ge(set_space: RangeSpace); |
89 | return Map.apply_range(map2: ScatterRel); |
90 | } |
91 | |
92 | isl::union_map polly::beforeScatter(isl::union_map UMap, bool Strict) { |
93 | isl::union_map Result = isl::union_map::empty(ctx: UMap.ctx()); |
94 | |
95 | for (isl::map Map : UMap.get_map_list()) { |
96 | isl::map After = beforeScatter(Map, Strict); |
97 | Result = Result.unite(umap2: After); |
98 | } |
99 | |
100 | return Result; |
101 | } |
102 | |
103 | isl::map polly::afterScatter(isl::map Map, bool Strict) { |
104 | isl::space RangeSpace = Map.get_space().range(); |
105 | isl::map ScatterRel = |
106 | Strict ? isl::map::lex_lt(set_space: RangeSpace) : isl::map::lex_le(set_space: RangeSpace); |
107 | return Map.apply_range(map2: ScatterRel); |
108 | } |
109 | |
110 | isl::union_map polly::afterScatter(const isl::union_map &UMap, bool Strict) { |
111 | isl::union_map Result = isl::union_map::empty(ctx: UMap.ctx()); |
112 | for (isl::map Map : UMap.get_map_list()) { |
113 | isl::map After = afterScatter(Map, Strict); |
114 | Result = Result.unite(umap2: After); |
115 | } |
116 | return Result; |
117 | } |
118 | |
119 | isl::map polly::betweenScatter(isl::map From, isl::map To, bool InclFrom, |
120 | bool InclTo) { |
121 | isl::map AfterFrom = afterScatter(Map: From, Strict: !InclFrom); |
122 | isl::map BeforeTo = beforeScatter(Map: To, Strict: !InclTo); |
123 | |
124 | return AfterFrom.intersect(map2: BeforeTo); |
125 | } |
126 | |
127 | isl::union_map polly::betweenScatter(isl::union_map From, isl::union_map To, |
128 | bool InclFrom, bool InclTo) { |
129 | isl::union_map AfterFrom = afterScatter(UMap: From, Strict: !InclFrom); |
130 | isl::union_map BeforeTo = beforeScatter(UMap: To, Strict: !InclTo); |
131 | |
132 | return AfterFrom.intersect(umap2: BeforeTo); |
133 | } |
134 | |
135 | isl::map polly::singleton(isl::union_map UMap, isl::space ExpectedSpace) { |
136 | if (UMap.is_null()) |
137 | return {}; |
138 | |
139 | if (isl_union_map_n_map(umap: UMap.get()) == 0) |
140 | return isl::map::empty(space: ExpectedSpace); |
141 | |
142 | isl::map Result = isl::map::from_union_map(umap: UMap); |
143 | assert(Result.is_null() || |
144 | Result.get_space().has_equal_tuples(ExpectedSpace)); |
145 | |
146 | return Result; |
147 | } |
148 | |
149 | isl::set polly::singleton(isl::union_set USet, isl::space ExpectedSpace) { |
150 | if (USet.is_null()) |
151 | return {}; |
152 | |
153 | if (isl_union_set_n_set(uset: USet.get()) == 0) |
154 | return isl::set::empty(space: ExpectedSpace); |
155 | |
156 | isl::set Result(USet); |
157 | assert(Result.is_null() || |
158 | Result.get_space().has_equal_tuples(ExpectedSpace)); |
159 | |
160 | return Result; |
161 | } |
162 | |
163 | unsigned polly::getNumScatterDims(const isl::union_map &Schedule) { |
164 | unsigned Dims = 0; |
165 | for (isl::map Map : Schedule.get_map_list()) { |
166 | if (Map.is_null()) |
167 | continue; |
168 | |
169 | Dims = std::max(a: Dims, b: unsignedFromIslSize(Size: Map.range_tuple_dim())); |
170 | } |
171 | return Dims; |
172 | } |
173 | |
174 | isl::space polly::getScatterSpace(const isl::union_map &Schedule) { |
175 | if (Schedule.is_null()) |
176 | return {}; |
177 | unsigned Dims = getNumScatterDims(Schedule); |
178 | isl::space ScatterSpace = Schedule.get_space().set_from_params(); |
179 | return ScatterSpace.add_dims(type: isl::dim::set, n: Dims); |
180 | } |
181 | |
182 | isl::map polly::makeIdentityMap(const isl::set &Set, bool RestrictDomain) { |
183 | isl::map Result = isl::map::identity(space: Set.get_space().map_from_set()); |
184 | if (RestrictDomain) |
185 | Result = Result.intersect_domain(set: Set); |
186 | return Result; |
187 | } |
188 | |
189 | isl::union_map polly::makeIdentityMap(const isl::union_set &USet, |
190 | bool RestrictDomain) { |
191 | isl::union_map Result = isl::union_map::empty(ctx: USet.ctx()); |
192 | for (isl::set Set : USet.get_set_list()) { |
193 | isl::map IdentityMap = makeIdentityMap(Set, RestrictDomain); |
194 | Result = Result.unite(umap2: IdentityMap); |
195 | } |
196 | return Result; |
197 | } |
198 | |
199 | isl::map polly::reverseDomain(isl::map Map) { |
200 | isl::space DomSpace = Map.get_space().domain().unwrap(); |
201 | isl::space Space1 = DomSpace.domain(); |
202 | isl::space Space2 = DomSpace.range(); |
203 | isl::map Swap = makeTupleSwapMap(FromSpace1: Space1, FromSpace2: Space2); |
204 | return Map.apply_domain(map2: Swap); |
205 | } |
206 | |
207 | isl::union_map polly::reverseDomain(const isl::union_map &UMap) { |
208 | isl::union_map Result = isl::union_map::empty(ctx: UMap.ctx()); |
209 | for (isl::map Map : UMap.get_map_list()) { |
210 | auto Reversed = reverseDomain(Map: std::move(Map)); |
211 | Result = Result.unite(umap2: Reversed); |
212 | } |
213 | return Result; |
214 | } |
215 | |
216 | isl::set polly::shiftDim(isl::set Set, int Pos, int Amount) { |
217 | unsigned NumDims = unsignedFromIslSize(Size: Set.tuple_dim()); |
218 | if (Pos < 0) |
219 | Pos = NumDims + Pos; |
220 | assert(unsigned(Pos) < NumDims && "Dimension index must be in range" ); |
221 | isl::space Space = Set.get_space(); |
222 | Space = Space.map_from_domain_and_range(range: Space); |
223 | isl::multi_aff Translator = makeShiftDimAff(Space, Pos, Amount); |
224 | isl::map TranslatorMap = isl::map::from_multi_aff(maff: Translator); |
225 | return Set.apply(map: TranslatorMap); |
226 | } |
227 | |
228 | isl::union_set polly::shiftDim(isl::union_set USet, int Pos, int Amount) { |
229 | isl::union_set Result = isl::union_set::empty(ctx: USet.ctx()); |
230 | for (isl::set Set : USet.get_set_list()) { |
231 | isl::set Shifted = shiftDim(Set, Pos, Amount); |
232 | Result = Result.unite(uset2: Shifted); |
233 | } |
234 | return Result; |
235 | } |
236 | |
237 | isl::map polly::shiftDim(isl::map Map, isl::dim Dim, int Pos, int Amount) { |
238 | unsigned NumDims = unsignedFromIslSize(Size: Map.dim(type: Dim)); |
239 | if (Pos < 0) |
240 | Pos = NumDims + Pos; |
241 | assert(unsigned(Pos) < NumDims && "Dimension index must be in range" ); |
242 | isl::space Space = Map.get_space(); |
243 | switch (Dim) { |
244 | case isl::dim::in: |
245 | Space = Space.domain(); |
246 | break; |
247 | case isl::dim::out: |
248 | Space = Space.range(); |
249 | break; |
250 | default: |
251 | llvm_unreachable("Unsupported value for 'dim'" ); |
252 | } |
253 | Space = Space.map_from_domain_and_range(range: Space); |
254 | isl::multi_aff Translator = makeShiftDimAff(Space, Pos, Amount); |
255 | isl::map TranslatorMap = isl::map::from_multi_aff(maff: Translator); |
256 | switch (Dim) { |
257 | case isl::dim::in: |
258 | return Map.apply_domain(map2: TranslatorMap); |
259 | case isl::dim::out: |
260 | return Map.apply_range(map2: TranslatorMap); |
261 | default: |
262 | llvm_unreachable("Unsupported value for 'dim'" ); |
263 | } |
264 | } |
265 | |
266 | isl::val polly::getConstant(isl::map Map, isl::dim Dim, int Pos) { |
267 | unsigned NumDims = unsignedFromIslSize(Size: Map.dim(type: Dim)); |
268 | if (Pos < 0) |
269 | Pos = NumDims + Pos; |
270 | assert(unsigned(Pos) < NumDims && "Dimension index must be in range" ); |
271 | // TODO: The isl_map_plain_get_val_if_fixed function is not robust, since its |
272 | // result is different depending on the internal representation. |
273 | // Replace it with a different implementation. |
274 | return isl::manage(ptr: isl_map_plain_get_val_if_fixed( |
275 | map: Map.get(), type: static_cast<enum isl_dim_type>(Dim), pos: Pos)); |
276 | } |
277 | |
278 | isl::union_map polly::shiftDim(isl::union_map UMap, isl::dim Dim, int Pos, |
279 | int Amount) { |
280 | isl::union_map Result = isl::union_map::empty(ctx: UMap.ctx()); |
281 | |
282 | for (isl::map Map : UMap.get_map_list()) { |
283 | isl::map Shifted = shiftDim(Map, Dim, Pos, Amount); |
284 | Result = Result.unite(umap2: Shifted); |
285 | } |
286 | return Result; |
287 | } |
288 | |
289 | void polly::simplify(isl::set &Set) { |
290 | Set = isl::manage(ptr: isl_set_compute_divs(set: Set.copy())); |
291 | Set = Set.detect_equalities(); |
292 | Set = Set.coalesce(); |
293 | } |
294 | |
295 | void polly::simplify(isl::union_set &USet) { |
296 | USet = isl::manage(ptr: isl_union_set_compute_divs(uset: USet.copy())); |
297 | USet = USet.detect_equalities(); |
298 | USet = USet.coalesce(); |
299 | } |
300 | |
301 | void polly::simplify(isl::map &Map) { |
302 | Map = isl::manage(ptr: isl_map_compute_divs(map: Map.copy())); |
303 | Map = Map.detect_equalities(); |
304 | Map = Map.coalesce(); |
305 | } |
306 | |
307 | void polly::simplify(isl::union_map &UMap) { |
308 | UMap = isl::manage(ptr: isl_union_map_compute_divs(umap: UMap.copy())); |
309 | UMap = UMap.detect_equalities(); |
310 | UMap = UMap.coalesce(); |
311 | } |
312 | |
313 | isl::union_map polly::computeReachingWrite(isl::union_map Schedule, |
314 | isl::union_map Writes, bool Reverse, |
315 | bool InclPrevDef, bool InclNextDef) { |
316 | |
317 | // { Scatter[] } |
318 | isl::space ScatterSpace = getScatterSpace(Schedule); |
319 | |
320 | // { ScatterRead[] -> ScatterWrite[] } |
321 | isl::map Relation; |
322 | if (Reverse) |
323 | Relation = InclPrevDef ? isl::map::lex_lt(set_space: ScatterSpace) |
324 | : isl::map::lex_le(set_space: ScatterSpace); |
325 | else |
326 | Relation = InclNextDef ? isl::map::lex_gt(set_space: ScatterSpace) |
327 | : isl::map::lex_ge(set_space: ScatterSpace); |
328 | |
329 | // { ScatterWrite[] -> [ScatterRead[] -> ScatterWrite[]] } |
330 | isl::map RelationMap = Relation.range_map().reverse(); |
331 | |
332 | // { Element[] -> ScatterWrite[] } |
333 | isl::union_map WriteAction = Schedule.apply_domain(umap2: Writes); |
334 | |
335 | // { ScatterWrite[] -> Element[] } |
336 | isl::union_map WriteActionRev = WriteAction.reverse(); |
337 | |
338 | // { Element[] -> [ScatterUse[] -> ScatterWrite[]] } |
339 | isl::union_map DefSchedRelation = |
340 | isl::union_map(RelationMap).apply_domain(umap2: WriteActionRev); |
341 | |
342 | // For each element, at every point in time, map to the times of previous |
343 | // definitions. { [Element[] -> ScatterRead[]] -> ScatterWrite[] } |
344 | isl::union_map ReachableWrites = DefSchedRelation.uncurry(); |
345 | if (Reverse) |
346 | ReachableWrites = ReachableWrites.lexmin(); |
347 | else |
348 | ReachableWrites = ReachableWrites.lexmax(); |
349 | |
350 | // { [Element[] -> ScatterWrite[]] -> ScatterWrite[] } |
351 | isl::union_map SelfUse = WriteAction.range_map(); |
352 | |
353 | if (InclPrevDef && InclNextDef) { |
354 | // Add the Def itself to the solution. |
355 | ReachableWrites = ReachableWrites.unite(umap2: SelfUse).coalesce(); |
356 | } else if (!InclPrevDef && !InclNextDef) { |
357 | // Remove Def itself from the solution. |
358 | ReachableWrites = ReachableWrites.subtract(umap2: SelfUse); |
359 | } |
360 | |
361 | // { [Element[] -> ScatterRead[]] -> Domain[] } |
362 | return ReachableWrites.apply_range(umap2: Schedule.reverse()); |
363 | } |
364 | |
365 | isl::union_map |
366 | polly::computeArrayUnused(isl::union_map Schedule, isl::union_map Writes, |
367 | isl::union_map Reads, bool ReadEltInSameInst, |
368 | bool IncludeLastRead, bool IncludeWrite) { |
369 | // { Element[] -> Scatter[] } |
370 | isl::union_map ReadActions = Schedule.apply_domain(umap2: Reads); |
371 | isl::union_map WriteActions = Schedule.apply_domain(umap2: Writes); |
372 | |
373 | // { [Element[] -> DomainWrite[]] -> Scatter[] } |
374 | isl::union_map EltDomWrites = |
375 | Writes.reverse().range_map().apply_range(umap2: Schedule); |
376 | |
377 | // { [Element[] -> Scatter[]] -> DomainWrite[] } |
378 | isl::union_map ReachingOverwrite = computeReachingWrite( |
379 | Schedule, Writes, Reverse: true, InclPrevDef: ReadEltInSameInst, InclNextDef: !ReadEltInSameInst); |
380 | |
381 | // { [Element[] -> Scatter[]] -> DomainWrite[] } |
382 | isl::union_map ReadsOverwritten = |
383 | ReachingOverwrite.intersect_domain(uset: ReadActions.wrap()); |
384 | |
385 | // { [Element[] -> DomainWrite[]] -> Scatter[] } |
386 | isl::union_map ReadsOverwrittenRotated = |
387 | reverseDomain(UMap: ReadsOverwritten).curry().reverse(); |
388 | isl::union_map LastOverwrittenRead = ReadsOverwrittenRotated.lexmax(); |
389 | |
390 | // { [Element[] -> DomainWrite[]] -> Scatter[] } |
391 | isl::union_map BetweenLastReadOverwrite = betweenScatter( |
392 | From: LastOverwrittenRead, To: EltDomWrites, InclFrom: IncludeLastRead, InclTo: IncludeWrite); |
393 | |
394 | // { [Element[] -> Scatter[]] -> DomainWrite[] } |
395 | isl::union_map ReachingOverwriteZone = computeReachingWrite( |
396 | Schedule, Writes, Reverse: true, InclPrevDef: IncludeLastRead, InclNextDef: IncludeWrite); |
397 | |
398 | // { [Element[] -> DomainWrite[]] -> Scatter[] } |
399 | isl::union_map ReachingOverwriteRotated = |
400 | reverseDomain(UMap: ReachingOverwriteZone).curry().reverse(); |
401 | |
402 | // { [Element[] -> DomainWrite[]] -> Scatter[] } |
403 | isl::union_map WritesWithoutReads = ReachingOverwriteRotated.subtract_domain( |
404 | dom: ReadsOverwrittenRotated.domain()); |
405 | |
406 | return BetweenLastReadOverwrite.unite(umap2: WritesWithoutReads) |
407 | .domain_factor_domain(); |
408 | } |
409 | |
410 | isl::union_set polly::convertZoneToTimepoints(isl::union_set Zone, |
411 | bool InclStart, bool InclEnd) { |
412 | if (!InclStart && InclEnd) |
413 | return Zone; |
414 | |
415 | auto ShiftedZone = shiftDim(USet: Zone, Pos: -1, Amount: -1); |
416 | if (InclStart && !InclEnd) |
417 | return ShiftedZone; |
418 | else if (!InclStart && !InclEnd) |
419 | return Zone.intersect(uset2: ShiftedZone); |
420 | |
421 | assert(InclStart && InclEnd); |
422 | return Zone.unite(uset2: ShiftedZone); |
423 | } |
424 | |
425 | isl::union_map polly::convertZoneToTimepoints(isl::union_map Zone, isl::dim Dim, |
426 | bool InclStart, bool InclEnd) { |
427 | if (!InclStart && InclEnd) |
428 | return Zone; |
429 | |
430 | auto ShiftedZone = shiftDim(UMap: Zone, Dim, Pos: -1, Amount: -1); |
431 | if (InclStart && !InclEnd) |
432 | return ShiftedZone; |
433 | else if (!InclStart && !InclEnd) |
434 | return Zone.intersect(umap2: ShiftedZone); |
435 | |
436 | assert(InclStart && InclEnd); |
437 | return Zone.unite(umap2: ShiftedZone); |
438 | } |
439 | |
440 | isl::map polly::convertZoneToTimepoints(isl::map Zone, isl::dim Dim, |
441 | bool InclStart, bool InclEnd) { |
442 | if (!InclStart && InclEnd) |
443 | return Zone; |
444 | |
445 | auto ShiftedZone = shiftDim(Map: Zone, Dim, Pos: -1, Amount: -1); |
446 | if (InclStart && !InclEnd) |
447 | return ShiftedZone; |
448 | else if (!InclStart && !InclEnd) |
449 | return Zone.intersect(map2: ShiftedZone); |
450 | |
451 | assert(InclStart && InclEnd); |
452 | return Zone.unite(map2: ShiftedZone); |
453 | } |
454 | |
455 | isl::map polly::distributeDomain(isl::map Map) { |
456 | // Note that we cannot take Map apart into { Domain[] -> Range1[] } and { |
457 | // Domain[] -> Range2[] } and combine again. We would loose any relation |
458 | // between Range1[] and Range2[] that is not also a constraint to Domain[]. |
459 | |
460 | isl::space Space = Map.get_space(); |
461 | isl::space DomainSpace = Space.domain(); |
462 | if (DomainSpace.is_null()) |
463 | return {}; |
464 | unsigned DomainDims = unsignedFromIslSize(Size: DomainSpace.dim(type: isl::dim::set)); |
465 | isl::space RangeSpace = Space.range().unwrap(); |
466 | isl::space Range1Space = RangeSpace.domain(); |
467 | if (Range1Space.is_null()) |
468 | return {}; |
469 | unsigned Range1Dims = unsignedFromIslSize(Size: Range1Space.dim(type: isl::dim::set)); |
470 | isl::space Range2Space = RangeSpace.range(); |
471 | if (Range2Space.is_null()) |
472 | return {}; |
473 | unsigned Range2Dims = unsignedFromIslSize(Size: Range2Space.dim(type: isl::dim::set)); |
474 | |
475 | isl::space OutputSpace = |
476 | DomainSpace.map_from_domain_and_range(range: Range1Space) |
477 | .wrap() |
478 | .map_from_domain_and_range( |
479 | range: DomainSpace.map_from_domain_and_range(range: Range2Space).wrap()); |
480 | |
481 | isl::basic_map Translator = isl::basic_map::universe( |
482 | space: Space.wrap().map_from_domain_and_range(range: OutputSpace.wrap())); |
483 | |
484 | for (unsigned i = 0; i < DomainDims; i += 1) { |
485 | Translator = Translator.equate(type1: isl::dim::in, pos1: i, type2: isl::dim::out, pos2: i); |
486 | Translator = Translator.equate(type1: isl::dim::in, pos1: i, type2: isl::dim::out, |
487 | pos2: DomainDims + Range1Dims + i); |
488 | } |
489 | for (unsigned i = 0; i < Range1Dims; i += 1) |
490 | Translator = Translator.equate(type1: isl::dim::in, pos1: DomainDims + i, type2: isl::dim::out, |
491 | pos2: DomainDims + i); |
492 | for (unsigned i = 0; i < Range2Dims; i += 1) |
493 | Translator = Translator.equate(type1: isl::dim::in, pos1: DomainDims + Range1Dims + i, |
494 | type2: isl::dim::out, |
495 | pos2: DomainDims + Range1Dims + DomainDims + i); |
496 | |
497 | return Map.wrap().apply(map: Translator).unwrap(); |
498 | } |
499 | |
500 | isl::union_map polly::distributeDomain(isl::union_map UMap) { |
501 | isl::union_map Result = isl::union_map::empty(ctx: UMap.ctx()); |
502 | for (isl::map Map : UMap.get_map_list()) { |
503 | auto Distributed = distributeDomain(Map); |
504 | Result = Result.unite(umap2: Distributed); |
505 | } |
506 | return Result; |
507 | } |
508 | |
509 | isl::union_map polly::liftDomains(isl::union_map UMap, isl::union_set Factor) { |
510 | |
511 | // { Factor[] -> Factor[] } |
512 | isl::union_map Factors = makeIdentityMap(USet: Factor, RestrictDomain: true); |
513 | |
514 | return Factors.product(umap2: UMap); |
515 | } |
516 | |
517 | isl::union_map polly::applyDomainRange(isl::union_map UMap, |
518 | isl::union_map Func) { |
519 | // This implementation creates unnecessary cross products of the |
520 | // DomainDomain[] and Func. An alternative implementation could reverse |
521 | // domain+uncurry,apply Func to what now is the domain, then undo the |
522 | // preparing transformation. Another alternative implementation could create a |
523 | // translator map for each piece. |
524 | |
525 | // { DomainDomain[] } |
526 | isl::union_set DomainDomain = UMap.domain().unwrap().domain(); |
527 | |
528 | // { [DomainDomain[] -> DomainRange[]] -> [DomainDomain[] -> NewDomainRange[]] |
529 | // } |
530 | isl::union_map LifetedFunc = liftDomains(UMap: std::move(Func), Factor: DomainDomain); |
531 | |
532 | return UMap.apply_domain(umap2: LifetedFunc); |
533 | } |
534 | |
535 | isl::map polly::intersectRange(isl::map Map, isl::union_set Range) { |
536 | isl::set RangeSet = Range.extract_set(space: Map.get_space().range()); |
537 | return Map.intersect_range(set: RangeSet); |
538 | } |
539 | |
540 | isl::map polly::subtractParams(isl::map Map, isl::set Params) { |
541 | auto MapSpace = Map.get_space(); |
542 | auto ParamsMap = isl::map::universe(space: MapSpace).intersect_params(params: Params); |
543 | return Map.subtract(map2: ParamsMap); |
544 | } |
545 | |
546 | isl::set polly::subtractParams(isl::set Set, isl::set Params) { |
547 | isl::space SetSpace = Set.get_space(); |
548 | isl::set ParamsSet = isl::set::universe(space: SetSpace).intersect_params(params: Params); |
549 | return Set.subtract(set2: ParamsSet); |
550 | } |
551 | |
552 | isl::val polly::getConstant(isl::pw_aff PwAff, bool Max, bool Min) { |
553 | assert(!Max || !Min); // Cannot return min and max at the same time. |
554 | isl::val Result; |
555 | isl::stat Stat = PwAff.foreach_piece( |
556 | fn: [=, &Result](isl::set Set, isl::aff Aff) -> isl::stat { |
557 | if (!Result.is_null() && Result.is_nan()) |
558 | return isl::stat::ok(); |
559 | |
560 | // TODO: If Min/Max, we can also determine a minimum/maximum value if |
561 | // Set is constant-bounded. |
562 | if (!Aff.is_cst()) { |
563 | Result = isl::val::nan(ctx: Aff.ctx()); |
564 | return isl::stat::error(); |
565 | } |
566 | |
567 | isl::val ThisVal = Aff.get_constant_val(); |
568 | if (Result.is_null()) { |
569 | Result = ThisVal; |
570 | return isl::stat::ok(); |
571 | } |
572 | |
573 | if (Result.eq(v2: ThisVal)) |
574 | return isl::stat::ok(); |
575 | |
576 | if (Max && ThisVal.gt(v2: Result)) { |
577 | Result = ThisVal; |
578 | return isl::stat::ok(); |
579 | } |
580 | |
581 | if (Min && ThisVal.lt(v2: Result)) { |
582 | Result = ThisVal; |
583 | return isl::stat::ok(); |
584 | } |
585 | |
586 | // Not compatible |
587 | Result = isl::val::nan(ctx: Aff.ctx()); |
588 | return isl::stat::error(); |
589 | }); |
590 | |
591 | if (Stat.is_error()) |
592 | return {}; |
593 | |
594 | return Result; |
595 | } |
596 | |
597 | llvm::iota_range<unsigned> polly::rangeIslSize(unsigned Begin, isl::size End) { |
598 | unsigned UEnd = unsignedFromIslSize(Size: End); |
599 | return llvm::seq<unsigned>(Begin: std::min(a: Begin, b: UEnd), End: UEnd); |
600 | } |
601 | |
602 | #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) |
603 | static void foreachPoint(const isl::set &Set, |
604 | const std::function<void(isl::point P)> &F) { |
605 | Set.foreach_point(fn: [&](isl::point P) -> isl::stat { |
606 | F(P); |
607 | return isl::stat::ok(); |
608 | }); |
609 | } |
610 | |
611 | static void foreachPoint(isl::basic_set BSet, |
612 | const std::function<void(isl::point P)> &F) { |
613 | foreachPoint(Set: isl::set(BSet), F); |
614 | } |
615 | |
616 | /// Determine the sorting order of the sets @p A and @p B without considering |
617 | /// the space structure. |
618 | /// |
619 | /// Ordering is based on the lower bounds of the set's dimensions. First |
620 | /// dimensions are considered first. |
621 | static int flatCompare(const isl::basic_set &A, const isl::basic_set &B) { |
622 | // Quick bail-out on out-of-quota. |
623 | if (A.is_null() || B.is_null()) |
624 | return 0; |
625 | |
626 | unsigned ALen = unsignedFromIslSize(Size: A.dim(type: isl::dim::set)); |
627 | unsigned BLen = unsignedFromIslSize(Size: B.dim(type: isl::dim::set)); |
628 | unsigned Len = std::min(a: ALen, b: BLen); |
629 | |
630 | for (unsigned i = 0; i < Len; i += 1) { |
631 | isl::basic_set ADim = |
632 | A.project_out(type: isl::dim::param, first: 0, |
633 | n: unsignedFromIslSize(Size: A.dim(type: isl::dim::param))) |
634 | .project_out(type: isl::dim::set, first: i + 1, n: ALen - i - 1) |
635 | .project_out(type: isl::dim::set, first: 0, n: i); |
636 | isl::basic_set BDim = |
637 | B.project_out(type: isl::dim::param, first: 0, |
638 | n: unsignedFromIslSize(Size: B.dim(type: isl::dim::param))) |
639 | .project_out(type: isl::dim::set, first: i + 1, n: BLen - i - 1) |
640 | .project_out(type: isl::dim::set, first: 0, n: i); |
641 | |
642 | isl::basic_set AHull = isl::set(ADim).convex_hull(); |
643 | isl::basic_set BHull = isl::set(BDim).convex_hull(); |
644 | |
645 | bool ALowerBounded = |
646 | bool(isl::set(AHull).dim_has_any_lower_bound(type: isl::dim::set, pos: 0)); |
647 | bool BLowerBounded = |
648 | bool(isl::set(BHull).dim_has_any_lower_bound(type: isl::dim::set, pos: 0)); |
649 | |
650 | int BoundedCompare = BLowerBounded - ALowerBounded; |
651 | if (BoundedCompare != 0) |
652 | return BoundedCompare; |
653 | |
654 | if (!ALowerBounded || !BLowerBounded) |
655 | continue; |
656 | |
657 | isl::pw_aff AMin = isl::set(ADim).dim_min(pos: 0); |
658 | isl::pw_aff BMin = isl::set(BDim).dim_min(pos: 0); |
659 | |
660 | isl::val AMinVal = polly::getConstant(PwAff: AMin, Max: false, Min: true); |
661 | isl::val BMinVal = polly::getConstant(PwAff: BMin, Max: false, Min: true); |
662 | |
663 | int MinCompare = AMinVal.sub(v2: BMinVal).sgn(); |
664 | if (MinCompare != 0) |
665 | return MinCompare; |
666 | } |
667 | |
668 | // If all the dimensions' lower bounds are equal or incomparable, sort based |
669 | // on the number of dimensions. |
670 | return ALen - BLen; |
671 | } |
672 | |
673 | /// Compare the sets @p A and @p B according to their nested space structure. |
674 | /// Returns 0 if the structure is considered equal. |
675 | /// If @p ConsiderTupleLen is false, the number of dimensions in a tuple are |
676 | /// ignored, i.e. a tuple with the same name but different number of dimensions |
677 | /// are considered equal. |
678 | static int structureCompare(const isl::space &ASpace, const isl::space &BSpace, |
679 | bool ConsiderTupleLen) { |
680 | int WrappingCompare = bool(ASpace.is_wrapping()) - bool(BSpace.is_wrapping()); |
681 | if (WrappingCompare != 0) |
682 | return WrappingCompare; |
683 | |
684 | if (ASpace.is_wrapping() && BSpace.is_wrapping()) { |
685 | isl::space AMap = ASpace.unwrap(); |
686 | isl::space BMap = BSpace.unwrap(); |
687 | |
688 | int FirstResult = |
689 | structureCompare(ASpace: AMap.domain(), BSpace: BMap.domain(), ConsiderTupleLen); |
690 | if (FirstResult != 0) |
691 | return FirstResult; |
692 | |
693 | return structureCompare(ASpace: AMap.range(), BSpace: BMap.range(), ConsiderTupleLen); |
694 | } |
695 | |
696 | std::string AName; |
697 | if (!ASpace.is_params() && ASpace.has_tuple_name(type: isl::dim::set)) |
698 | AName = ASpace.get_tuple_name(type: isl::dim::set); |
699 | |
700 | std::string BName; |
701 | if (!BSpace.is_params() && BSpace.has_tuple_name(type: isl::dim::set)) |
702 | BName = BSpace.get_tuple_name(type: isl::dim::set); |
703 | |
704 | int NameCompare = AName.compare(str: BName); |
705 | if (NameCompare != 0) |
706 | return NameCompare; |
707 | |
708 | if (ConsiderTupleLen) { |
709 | int LenCompare = (int)unsignedFromIslSize(Size: BSpace.dim(type: isl::dim::set)) - |
710 | (int)unsignedFromIslSize(Size: ASpace.dim(type: isl::dim::set)); |
711 | if (LenCompare != 0) |
712 | return LenCompare; |
713 | } |
714 | |
715 | return 0; |
716 | } |
717 | |
718 | /// Compare the sets @p A and @p B according to their nested space structure. If |
719 | /// the structure is the same, sort using the dimension lower bounds. |
720 | /// Returns an std::sort compatible bool. |
721 | static bool orderComparer(const isl::basic_set &A, const isl::basic_set &B) { |
722 | isl::space ASpace = A.get_space(); |
723 | isl::space BSpace = B.get_space(); |
724 | |
725 | // Ignoring number of dimensions first ensures that structures with same tuple |
726 | // names, but different number of dimensions are still sorted close together. |
727 | int TupleNestingCompare = structureCompare(ASpace, BSpace, ConsiderTupleLen: false); |
728 | if (TupleNestingCompare != 0) |
729 | return TupleNestingCompare < 0; |
730 | |
731 | int TupleCompare = structureCompare(ASpace, BSpace, ConsiderTupleLen: true); |
732 | if (TupleCompare != 0) |
733 | return TupleCompare < 0; |
734 | |
735 | return flatCompare(A, B) < 0; |
736 | } |
737 | |
738 | /// Print a string representation of @p USet to @p OS. |
739 | /// |
740 | /// The pieces of @p USet are printed in a sorted order. Spaces with equal or |
741 | /// similar nesting structure are printed together. Compared to isl's own |
742 | /// printing function the uses the structure itself as base of the sorting, not |
743 | /// a hash of it. It ensures that e.g. maps spaces with same domain structure |
744 | /// are printed together. Set pieces with same structure are printed in order of |
745 | /// their lower bounds. |
746 | /// |
747 | /// @param USet Polyhedra to print. |
748 | /// @param OS Target stream. |
749 | /// @param Simplify Whether to simplify the polyhedron before printing. |
750 | /// @param IsMap Whether @p USet is a wrapped map. If true, sets are |
751 | /// unwrapped before printing to again appear as a map. |
752 | static void printSortedPolyhedra(isl::union_set USet, llvm::raw_ostream &OS, |
753 | bool Simplify, bool IsMap) { |
754 | if (USet.is_null()) { |
755 | OS << "<null>\n" ; |
756 | return; |
757 | } |
758 | |
759 | if (Simplify) |
760 | simplify(USet); |
761 | |
762 | // Get all the polyhedra. |
763 | std::vector<isl::basic_set> BSets; |
764 | |
765 | for (isl::set Set : USet.get_set_list()) { |
766 | for (isl::basic_set BSet : Set.get_basic_set_list()) { |
767 | BSets.push_back(x: BSet); |
768 | } |
769 | } |
770 | |
771 | if (BSets.empty()) { |
772 | OS << "{\n}\n" ; |
773 | return; |
774 | } |
775 | |
776 | // Sort the polyhedra. |
777 | llvm::sort(C&: BSets, Comp: orderComparer); |
778 | |
779 | // Print the polyhedra. |
780 | bool First = true; |
781 | for (const isl::basic_set &BSet : BSets) { |
782 | std::string Str; |
783 | if (IsMap) |
784 | Str = stringFromIslObj(Obj: isl::map(BSet.unwrap())); |
785 | else |
786 | Str = stringFromIslObj(Obj: isl::set(BSet)); |
787 | size_t OpenPos = Str.find_first_of(c: '{'); |
788 | assert(OpenPos != std::string::npos); |
789 | size_t ClosePos = Str.find_last_of(c: '}'); |
790 | assert(ClosePos != std::string::npos); |
791 | |
792 | if (First) |
793 | OS << llvm::StringRef(Str).substr(Start: 0, N: OpenPos + 1) << "\n " ; |
794 | else |
795 | OS << ";\n " ; |
796 | |
797 | OS << llvm::StringRef(Str).substr(Start: OpenPos + 1, N: ClosePos - OpenPos - 2); |
798 | First = false; |
799 | } |
800 | assert(!First); |
801 | OS << "\n}\n" ; |
802 | } |
803 | |
804 | static void recursiveExpand(isl::basic_set BSet, unsigned Dim, |
805 | isl::set &Expanded) { |
806 | unsigned Dims = unsignedFromIslSize(Size: BSet.dim(type: isl::dim::set)); |
807 | if (Dim >= Dims) { |
808 | Expanded = Expanded.unite(set2: BSet); |
809 | return; |
810 | } |
811 | |
812 | isl::basic_set DimOnly = |
813 | BSet.project_out(type: isl::dim::param, first: 0, |
814 | n: unsignedFromIslSize(Size: BSet.dim(type: isl::dim::param))) |
815 | .project_out(type: isl::dim::set, first: Dim + 1, n: Dims - Dim - 1) |
816 | .project_out(type: isl::dim::set, first: 0, n: Dim); |
817 | if (!DimOnly.is_bounded()) { |
818 | recursiveExpand(BSet, Dim: Dim + 1, Expanded); |
819 | return; |
820 | } |
821 | |
822 | foreachPoint(BSet: DimOnly, F: [&, Dim](isl::point P) { |
823 | isl::val Val = P.get_coordinate_val(type: isl::dim::set, pos: 0); |
824 | isl::basic_set FixBSet = BSet.fix_val(type: isl::dim::set, pos: Dim, v: Val); |
825 | recursiveExpand(BSet: FixBSet, Dim: Dim + 1, Expanded); |
826 | }); |
827 | } |
828 | |
829 | /// Make each point of a set explicit. |
830 | /// |
831 | /// "Expanding" makes each point a set contains explicit. That is, the result is |
832 | /// a set of singleton polyhedra. Unbounded dimensions are not expanded. |
833 | /// |
834 | /// Example: |
835 | /// { [i] : 0 <= i < 2 } |
836 | /// is expanded to: |
837 | /// { [0]; [1] } |
838 | static isl::set expand(const isl::set &Set) { |
839 | isl::set Expanded = isl::set::empty(space: Set.get_space()); |
840 | for (isl::basic_set BSet : Set.get_basic_set_list()) |
841 | recursiveExpand(BSet, Dim: 0, Expanded); |
842 | return Expanded; |
843 | } |
844 | |
845 | /// Expand all points of a union set explicit. |
846 | /// |
847 | /// @see expand(const isl::set) |
848 | static isl::union_set expand(const isl::union_set &USet) { |
849 | isl::union_set Expanded = isl::union_set::empty(ctx: USet.ctx()); |
850 | for (isl::set Set : USet.get_set_list()) { |
851 | isl::set SetExpanded = expand(Set); |
852 | Expanded = Expanded.unite(uset2: SetExpanded); |
853 | } |
854 | return Expanded; |
855 | } |
856 | |
857 | LLVM_DUMP_METHOD void polly::dumpPw(const isl::set &Set) { |
858 | printSortedPolyhedra(USet: Set, OS&: llvm::errs(), Simplify: true, IsMap: false); |
859 | } |
860 | |
861 | LLVM_DUMP_METHOD void polly::dumpPw(const isl::map &Map) { |
862 | printSortedPolyhedra(USet: Map.wrap(), OS&: llvm::errs(), Simplify: true, IsMap: true); |
863 | } |
864 | |
865 | LLVM_DUMP_METHOD void polly::dumpPw(const isl::union_set &USet) { |
866 | printSortedPolyhedra(USet, OS&: llvm::errs(), Simplify: true, IsMap: false); |
867 | } |
868 | |
869 | LLVM_DUMP_METHOD void polly::dumpPw(const isl::union_map &UMap) { |
870 | printSortedPolyhedra(USet: UMap.wrap(), OS&: llvm::errs(), Simplify: true, IsMap: true); |
871 | } |
872 | |
873 | LLVM_DUMP_METHOD void polly::dumpPw(__isl_keep isl_set *Set) { |
874 | dumpPw(Set: isl::manage_copy(ptr: Set)); |
875 | } |
876 | |
877 | LLVM_DUMP_METHOD void polly::dumpPw(__isl_keep isl_map *Map) { |
878 | dumpPw(Map: isl::manage_copy(ptr: Map)); |
879 | } |
880 | |
881 | LLVM_DUMP_METHOD void polly::dumpPw(__isl_keep isl_union_set *USet) { |
882 | dumpPw(USet: isl::manage_copy(ptr: USet)); |
883 | } |
884 | |
885 | LLVM_DUMP_METHOD void polly::dumpPw(__isl_keep isl_union_map *UMap) { |
886 | dumpPw(UMap: isl::manage_copy(ptr: UMap)); |
887 | } |
888 | |
889 | LLVM_DUMP_METHOD void polly::dumpExpanded(const isl::set &Set) { |
890 | printSortedPolyhedra(USet: expand(Set), OS&: llvm::errs(), Simplify: false, IsMap: false); |
891 | } |
892 | |
893 | LLVM_DUMP_METHOD void polly::dumpExpanded(const isl::map &Map) { |
894 | printSortedPolyhedra(USet: expand(Set: Map.wrap()), OS&: llvm::errs(), Simplify: false, IsMap: true); |
895 | } |
896 | |
897 | LLVM_DUMP_METHOD void polly::dumpExpanded(const isl::union_set &USet) { |
898 | printSortedPolyhedra(USet: expand(USet), OS&: llvm::errs(), Simplify: false, IsMap: false); |
899 | } |
900 | |
901 | LLVM_DUMP_METHOD void polly::dumpExpanded(const isl::union_map &UMap) { |
902 | printSortedPolyhedra(USet: expand(USet: UMap.wrap()), OS&: llvm::errs(), Simplify: false, IsMap: true); |
903 | } |
904 | |
905 | LLVM_DUMP_METHOD void polly::dumpExpanded(__isl_keep isl_set *Set) { |
906 | dumpExpanded(Set: isl::manage_copy(ptr: Set)); |
907 | } |
908 | |
909 | LLVM_DUMP_METHOD void polly::dumpExpanded(__isl_keep isl_map *Map) { |
910 | dumpExpanded(Map: isl::manage_copy(ptr: Map)); |
911 | } |
912 | |
913 | LLVM_DUMP_METHOD void polly::dumpExpanded(__isl_keep isl_union_set *USet) { |
914 | dumpExpanded(USet: isl::manage_copy(ptr: USet)); |
915 | } |
916 | |
917 | LLVM_DUMP_METHOD void polly::dumpExpanded(__isl_keep isl_union_map *UMap) { |
918 | dumpExpanded(UMap: isl::manage_copy(ptr: UMap)); |
919 | } |
920 | #endif |
921 | |