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