1 //===- ScheduleOptimizer.cpp - Calculate an optimized schedule ------------===// 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 // This pass generates an entirely new schedule tree from the data dependences 10 // and iteration domains. The new schedule tree is computed in two steps: 11 // 12 // 1) The isl scheduling optimizer is run 13 // 14 // The isl scheduling optimizer creates a new schedule tree that maximizes 15 // parallelism and tileability and minimizes data-dependence distances. The 16 // algorithm used is a modified version of the ``Pluto'' algorithm: 17 // 18 // U. Bondhugula, A. Hartono, J. Ramanujam, and P. Sadayappan. 19 // A Practical Automatic Polyhedral Parallelizer and Locality Optimizer. 20 // In Proceedings of the 2008 ACM SIGPLAN Conference On Programming Language 21 // Design and Implementation, PLDI ’08, pages 101–113. ACM, 2008. 22 // 23 // 2) A set of post-scheduling transformations is applied on the schedule tree. 24 // 25 // These optimizations include: 26 // 27 // - Tiling of the innermost tilable bands 28 // - Prevectorization - The choice of a possible outer loop that is strip-mined 29 // to the innermost level to enable inner-loop 30 // vectorization. 31 // - Some optimizations for spatial locality are also planned. 32 // 33 // For a detailed description of the schedule tree itself please see section 6 34 // of: 35 // 36 // Polyhedral AST generation is more than scanning polyhedra 37 // Tobias Grosser, Sven Verdoolaege, Albert Cohen 38 // ACM Transactions on Programming Languages and Systems (TOPLAS), 39 // 37(4), July 2015 40 // http://www.grosser.es/#pub-polyhedral-AST-generation 41 // 42 // This publication also contains a detailed discussion of the different options 43 // for polyhedral loop unrolling, full/partial tile separation and other uses 44 // of the schedule tree. 45 // 46 //===----------------------------------------------------------------------===// 47 48 #include "polly/ScheduleOptimizer.h" 49 #include "polly/CodeGen/CodeGeneration.h" 50 #include "polly/DependenceInfo.h" 51 #include "polly/ManualOptimizer.h" 52 #include "polly/MatmulOptimizer.h" 53 #include "polly/Options.h" 54 #include "polly/ScheduleTreeTransform.h" 55 #include "polly/Support/ISLOStream.h" 56 #include "llvm/ADT/Sequence.h" 57 #include "llvm/ADT/Statistic.h" 58 #include "llvm/Analysis/OptimizationRemarkEmitter.h" 59 #include "llvm/InitializePasses.h" 60 #include "llvm/Support/CommandLine.h" 61 #include "isl/options.h" 62 63 using namespace llvm; 64 using namespace polly; 65 66 namespace llvm { 67 class Loop; 68 class Module; 69 } // namespace llvm 70 71 #define DEBUG_TYPE "polly-opt-isl" 72 73 static cl::opt<std::string> 74 OptimizeDeps("polly-opt-optimize-only", 75 cl::desc("Only a certain kind of dependences (all/raw)"), 76 cl::Hidden, cl::init("all"), cl::ZeroOrMore, 77 cl::cat(PollyCategory)); 78 79 static cl::opt<std::string> 80 SimplifyDeps("polly-opt-simplify-deps", 81 cl::desc("Dependences should be simplified (yes/no)"), 82 cl::Hidden, cl::init("yes"), cl::ZeroOrMore, 83 cl::cat(PollyCategory)); 84 85 static cl::opt<int> MaxConstantTerm( 86 "polly-opt-max-constant-term", 87 cl::desc("The maximal constant term allowed (-1 is unlimited)"), cl::Hidden, 88 cl::init(20), cl::ZeroOrMore, cl::cat(PollyCategory)); 89 90 static cl::opt<int> MaxCoefficient( 91 "polly-opt-max-coefficient", 92 cl::desc("The maximal coefficient allowed (-1 is unlimited)"), cl::Hidden, 93 cl::init(20), cl::ZeroOrMore, cl::cat(PollyCategory)); 94 95 static cl::opt<std::string> 96 MaximizeBandDepth("polly-opt-maximize-bands", 97 cl::desc("Maximize the band depth (yes/no)"), cl::Hidden, 98 cl::init("yes"), cl::ZeroOrMore, cl::cat(PollyCategory)); 99 100 static cl::opt<std::string> OuterCoincidence( 101 "polly-opt-outer-coincidence", 102 cl::desc("Try to construct schedules where the outer member of each band " 103 "satisfies the coincidence constraints (yes/no)"), 104 cl::Hidden, cl::init("no"), cl::ZeroOrMore, cl::cat(PollyCategory)); 105 106 static cl::opt<int> PrevectorWidth( 107 "polly-prevect-width", 108 cl::desc( 109 "The number of loop iterations to strip-mine for pre-vectorization"), 110 cl::Hidden, cl::init(4), cl::ZeroOrMore, cl::cat(PollyCategory)); 111 112 static cl::opt<bool> FirstLevelTiling("polly-tiling", 113 cl::desc("Enable loop tiling"), 114 cl::init(true), cl::ZeroOrMore, 115 cl::cat(PollyCategory)); 116 117 static cl::opt<int> FirstLevelDefaultTileSize( 118 "polly-default-tile-size", 119 cl::desc("The default tile size (if not enough were provided by" 120 " --polly-tile-sizes)"), 121 cl::Hidden, cl::init(32), cl::ZeroOrMore, cl::cat(PollyCategory)); 122 123 static cl::list<int> 124 FirstLevelTileSizes("polly-tile-sizes", 125 cl::desc("A tile size for each loop dimension, filled " 126 "with --polly-default-tile-size"), 127 cl::Hidden, cl::ZeroOrMore, cl::CommaSeparated, 128 cl::cat(PollyCategory)); 129 130 static cl::opt<bool> 131 SecondLevelTiling("polly-2nd-level-tiling", 132 cl::desc("Enable a 2nd level loop of loop tiling"), 133 cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory)); 134 135 static cl::opt<int> SecondLevelDefaultTileSize( 136 "polly-2nd-level-default-tile-size", 137 cl::desc("The default 2nd-level tile size (if not enough were provided by" 138 " --polly-2nd-level-tile-sizes)"), 139 cl::Hidden, cl::init(16), cl::ZeroOrMore, cl::cat(PollyCategory)); 140 141 static cl::list<int> 142 SecondLevelTileSizes("polly-2nd-level-tile-sizes", 143 cl::desc("A tile size for each loop dimension, filled " 144 "with --polly-default-tile-size"), 145 cl::Hidden, cl::ZeroOrMore, cl::CommaSeparated, 146 cl::cat(PollyCategory)); 147 148 static cl::opt<bool> RegisterTiling("polly-register-tiling", 149 cl::desc("Enable register tiling"), 150 cl::init(false), cl::ZeroOrMore, 151 cl::cat(PollyCategory)); 152 153 static cl::opt<int> RegisterDefaultTileSize( 154 "polly-register-tiling-default-tile-size", 155 cl::desc("The default register tile size (if not enough were provided by" 156 " --polly-register-tile-sizes)"), 157 cl::Hidden, cl::init(2), cl::ZeroOrMore, cl::cat(PollyCategory)); 158 159 static cl::list<int> 160 RegisterTileSizes("polly-register-tile-sizes", 161 cl::desc("A tile size for each loop dimension, filled " 162 "with --polly-register-tile-size"), 163 cl::Hidden, cl::ZeroOrMore, cl::CommaSeparated, 164 cl::cat(PollyCategory)); 165 166 static cl::opt<bool> PragmaBasedOpts( 167 "polly-pragma-based-opts", 168 cl::desc("Apply user-directed transformation from metadata"), 169 cl::init(true), cl::ZeroOrMore, cl::cat(PollyCategory)); 170 171 static cl::opt<bool> EnableReschedule("polly-reschedule", 172 cl::desc("Optimize SCoPs using ISL"), 173 cl::init(true), cl::ZeroOrMore, 174 cl::cat(PollyCategory)); 175 176 static cl::opt<bool> 177 PMBasedOpts("polly-pattern-matching-based-opts", 178 cl::desc("Perform optimizations based on pattern matching"), 179 cl::init(true), cl::ZeroOrMore, cl::cat(PollyCategory)); 180 181 static cl::opt<bool> 182 EnablePostopts("polly-postopts", 183 cl::desc("Apply post-rescheduling optimizations such as " 184 "tiling (requires -polly-reschedule)"), 185 cl::init(true), cl::ZeroOrMore, cl::cat(PollyCategory)); 186 187 static cl::opt<bool> OptimizedScops( 188 "polly-optimized-scops", 189 cl::desc("Polly - Dump polyhedral description of Scops optimized with " 190 "the isl scheduling optimizer and the set of post-scheduling " 191 "transformations is applied on the schedule tree"), 192 cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory)); 193 194 STATISTIC(ScopsProcessed, "Number of scops processed"); 195 STATISTIC(ScopsRescheduled, "Number of scops rescheduled"); 196 STATISTIC(ScopsOptimized, "Number of scops optimized"); 197 198 STATISTIC(NumAffineLoopsOptimized, "Number of affine loops optimized"); 199 STATISTIC(NumBoxedLoopsOptimized, "Number of boxed loops optimized"); 200 201 #define THREE_STATISTICS(VARNAME, DESC) \ 202 static Statistic VARNAME[3] = { \ 203 {DEBUG_TYPE, #VARNAME "0", DESC " (original)"}, \ 204 {DEBUG_TYPE, #VARNAME "1", DESC " (after scheduler)"}, \ 205 {DEBUG_TYPE, #VARNAME "2", DESC " (after optimizer)"}} 206 207 THREE_STATISTICS(NumBands, "Number of bands"); 208 THREE_STATISTICS(NumBandMembers, "Number of band members"); 209 THREE_STATISTICS(NumCoincident, "Number of coincident band members"); 210 THREE_STATISTICS(NumPermutable, "Number of permutable bands"); 211 THREE_STATISTICS(NumFilters, "Number of filter nodes"); 212 THREE_STATISTICS(NumExtension, "Number of extension nodes"); 213 214 STATISTIC(FirstLevelTileOpts, "Number of first level tiling applied"); 215 STATISTIC(SecondLevelTileOpts, "Number of second level tiling applied"); 216 STATISTIC(RegisterTileOpts, "Number of register tiling applied"); 217 STATISTIC(PrevectOpts, "Number of strip-mining for prevectorization applied"); 218 STATISTIC(MatMulOpts, 219 "Number of matrix multiplication patterns detected and optimized"); 220 221 namespace { 222 /// Additional parameters of the schedule optimizer. 223 /// 224 /// Target Transform Info and the SCoP dependencies used by the schedule 225 /// optimizer. 226 struct OptimizerAdditionalInfoTy { 227 const llvm::TargetTransformInfo *TTI; 228 const Dependences *D; 229 bool PatternOpts; 230 bool Postopts; 231 bool Prevect; 232 }; 233 234 class ScheduleTreeOptimizer { 235 public: 236 /// Apply schedule tree transformations. 237 /// 238 /// This function takes an (possibly already optimized) schedule tree and 239 /// applies a set of additional optimizations on the schedule tree. The 240 /// transformations applied include: 241 /// 242 /// - Pattern-based optimizations 243 /// - Tiling 244 /// - Prevectorization 245 /// 246 /// @param Schedule The schedule object the transformations will be applied 247 /// to. 248 /// @param OAI Target Transform Info and the SCoP dependencies. 249 /// @returns The transformed schedule. 250 static isl::schedule 251 optimizeSchedule(isl::schedule Schedule, 252 const OptimizerAdditionalInfoTy *OAI = nullptr); 253 254 /// Apply schedule tree transformations. 255 /// 256 /// This function takes a node in an (possibly already optimized) schedule 257 /// tree and applies a set of additional optimizations on this schedule tree 258 /// node and its descendants. The transformations applied include: 259 /// 260 /// - Pattern-based optimizations 261 /// - Tiling 262 /// - Prevectorization 263 /// 264 /// @param Node The schedule object post-transformations will be applied to. 265 /// @param OAI Target Transform Info and the SCoP dependencies. 266 /// @returns The transformed schedule. 267 static isl::schedule_node 268 optimizeScheduleNode(isl::schedule_node Node, 269 const OptimizerAdditionalInfoTy *OAI = nullptr); 270 271 /// Decide if the @p NewSchedule is profitable for @p S. 272 /// 273 /// @param S The SCoP we optimize. 274 /// @param NewSchedule The new schedule we computed. 275 /// 276 /// @return True, if we believe @p NewSchedule is an improvement for @p S. 277 static bool isProfitableSchedule(polly::Scop &S, isl::schedule NewSchedule); 278 279 /// Isolate a set of partial tile prefixes. 280 /// 281 /// This set should ensure that it contains only partial tile prefixes that 282 /// have exactly VectorWidth iterations. 283 /// 284 /// @param Node A schedule node band, which is a parent of a band node, 285 /// that contains a vector loop. 286 /// @return Modified isl_schedule_node. 287 static isl::schedule_node isolateFullPartialTiles(isl::schedule_node Node, 288 int VectorWidth); 289 290 private: 291 /// Check if this node is a band node we want to tile. 292 /// 293 /// We look for innermost band nodes where individual dimensions are marked as 294 /// permutable. 295 /// 296 /// @param Node The node to check. 297 static bool isTileableBandNode(isl::schedule_node Node); 298 299 /// Pre-vectorizes one scheduling dimension of a schedule band. 300 /// 301 /// prevectSchedBand splits out the dimension DimToVectorize, tiles it and 302 /// sinks the resulting point loop. 303 /// 304 /// Example (DimToVectorize=0, VectorWidth=4): 305 /// 306 /// | Before transformation: 307 /// | 308 /// | A[i,j] -> [i,j] 309 /// | 310 /// | for (i = 0; i < 128; i++) 311 /// | for (j = 0; j < 128; j++) 312 /// | A(i,j); 313 /// 314 /// | After transformation: 315 /// | 316 /// | for (it = 0; it < 32; it+=1) 317 /// | for (j = 0; j < 128; j++) 318 /// | for (ip = 0; ip <= 3; ip++) 319 /// | A(4 * it + ip,j); 320 /// 321 /// The goal of this transformation is to create a trivially vectorizable 322 /// loop. This means a parallel loop at the innermost level that has a 323 /// constant number of iterations corresponding to the target vector width. 324 /// 325 /// This transformation creates a loop at the innermost level. The loop has 326 /// a constant number of iterations, if the number of loop iterations at 327 /// DimToVectorize can be divided by VectorWidth. The default VectorWidth is 328 /// currently constant and not yet target specific. This function does not 329 /// reason about parallelism. 330 static isl::schedule_node prevectSchedBand(isl::schedule_node Node, 331 unsigned DimToVectorize, 332 int VectorWidth); 333 334 /// Apply additional optimizations on the bands in the schedule tree. 335 /// 336 /// We are looking for an innermost band node and apply the following 337 /// transformations: 338 /// 339 /// - Tile the band 340 /// - if the band is tileable 341 /// - if the band has more than one loop dimension 342 /// 343 /// - Prevectorize the schedule of the band (or the point loop in case of 344 /// tiling). 345 /// - if vectorization is enabled 346 /// 347 /// @param Node The schedule node to (possibly) optimize. 348 /// @param User A pointer to forward some use information 349 /// (currently unused). 350 static isl_schedule_node *optimizeBand(isl_schedule_node *Node, void *User); 351 352 /// Apply tiling optimizations on the bands in the schedule tree. 353 /// 354 /// @param Node The schedule node to (possibly) optimize. 355 static isl::schedule_node applyTileBandOpt(isl::schedule_node Node); 356 357 /// Apply prevectorization on the bands in the schedule tree. 358 /// 359 /// @param Node The schedule node to (possibly) prevectorize. 360 static isl::schedule_node applyPrevectBandOpt(isl::schedule_node Node); 361 }; 362 363 isl::schedule_node 364 ScheduleTreeOptimizer::isolateFullPartialTiles(isl::schedule_node Node, 365 int VectorWidth) { 366 assert(isl_schedule_node_get_type(Node.get()) == isl_schedule_node_band); 367 Node = Node.child(0).child(0); 368 isl::union_map SchedRelUMap = Node.get_prefix_schedule_relation(); 369 isl::union_set ScheduleRangeUSet = SchedRelUMap.range(); 370 isl::set ScheduleRange{ScheduleRangeUSet}; 371 isl::set IsolateDomain = getPartialTilePrefixes(ScheduleRange, VectorWidth); 372 auto AtomicOption = getDimOptions(IsolateDomain.ctx(), "atomic"); 373 isl::union_set IsolateOption = getIsolateOptions(IsolateDomain, 1); 374 Node = Node.parent().parent(); 375 isl::union_set Options = IsolateOption.unite(AtomicOption); 376 isl::schedule_node_band Result = 377 Node.as<isl::schedule_node_band>().set_ast_build_options(Options); 378 return Result; 379 } 380 381 isl::schedule_node ScheduleTreeOptimizer::prevectSchedBand( 382 isl::schedule_node Node, unsigned DimToVectorize, int VectorWidth) { 383 assert(isl_schedule_node_get_type(Node.get()) == isl_schedule_node_band); 384 385 auto Space = isl::manage(isl_schedule_node_band_get_space(Node.get())); 386 isl_size ScheduleDimensions = Space.dim(isl::dim::set).release(); 387 assert((isl_size)DimToVectorize < ScheduleDimensions); 388 389 if (DimToVectorize > 0) { 390 Node = isl::manage( 391 isl_schedule_node_band_split(Node.release(), DimToVectorize)); 392 Node = Node.child(0); 393 } 394 if ((isl_size)DimToVectorize < ScheduleDimensions - 1) 395 Node = isl::manage(isl_schedule_node_band_split(Node.release(), 1)); 396 Space = isl::manage(isl_schedule_node_band_get_space(Node.get())); 397 auto Sizes = isl::multi_val::zero(Space); 398 Sizes = Sizes.set_val(0, isl::val(Node.ctx(), VectorWidth)); 399 Node = 400 isl::manage(isl_schedule_node_band_tile(Node.release(), Sizes.release())); 401 Node = isolateFullPartialTiles(Node, VectorWidth); 402 Node = Node.child(0); 403 // Make sure the "trivially vectorizable loop" is not unrolled. Otherwise, 404 // we will have troubles to match it in the backend. 405 isl::schedule_node_band NodeBand = 406 Node.as<isl::schedule_node_band>().set_ast_build_options( 407 isl::union_set(Node.ctx(), "{ unroll[x]: 1 = 0 }")); 408 Node = isl::manage(isl_schedule_node_band_sink(NodeBand.release())); 409 Node = Node.child(0); 410 if (isl_schedule_node_get_type(Node.get()) == isl_schedule_node_leaf) 411 Node = Node.parent(); 412 auto LoopMarker = isl::id::alloc(Node.ctx(), "SIMD", nullptr); 413 PrevectOpts++; 414 return Node.insert_mark(LoopMarker); 415 } 416 417 static bool isSimpleInnermostBand(const isl::schedule_node &Node) { 418 assert(isl_schedule_node_get_type(Node.get()) == isl_schedule_node_band); 419 assert(isl_schedule_node_n_children(Node.get()) == 1); 420 421 auto ChildType = isl_schedule_node_get_type(Node.child(0).get()); 422 423 if (ChildType == isl_schedule_node_leaf) 424 return true; 425 426 if (ChildType != isl_schedule_node_sequence) 427 return false; 428 429 auto Sequence = Node.child(0); 430 431 for (int c = 0, nc = isl_schedule_node_n_children(Sequence.get()); c < nc; 432 ++c) { 433 auto Child = Sequence.child(c); 434 if (isl_schedule_node_get_type(Child.get()) != isl_schedule_node_filter) 435 return false; 436 if (isl_schedule_node_get_type(Child.child(0).get()) != 437 isl_schedule_node_leaf) 438 return false; 439 } 440 return true; 441 } 442 443 bool ScheduleTreeOptimizer::isTileableBandNode(isl::schedule_node Node) { 444 if (isl_schedule_node_get_type(Node.get()) != isl_schedule_node_band) 445 return false; 446 447 if (isl_schedule_node_n_children(Node.get()) != 1) 448 return false; 449 450 if (!isl_schedule_node_band_get_permutable(Node.get())) 451 return false; 452 453 auto Space = isl::manage(isl_schedule_node_band_get_space(Node.get())); 454 auto Dims = Space.dim(isl::dim::set).release(); 455 456 if (Dims <= 1) 457 return false; 458 459 return isSimpleInnermostBand(Node); 460 } 461 462 __isl_give isl::schedule_node 463 ScheduleTreeOptimizer::applyTileBandOpt(isl::schedule_node Node) { 464 if (FirstLevelTiling) { 465 Node = tileNode(Node, "1st level tiling", FirstLevelTileSizes, 466 FirstLevelDefaultTileSize); 467 FirstLevelTileOpts++; 468 } 469 470 if (SecondLevelTiling) { 471 Node = tileNode(Node, "2nd level tiling", SecondLevelTileSizes, 472 SecondLevelDefaultTileSize); 473 SecondLevelTileOpts++; 474 } 475 476 if (RegisterTiling) { 477 Node = 478 applyRegisterTiling(Node, RegisterTileSizes, RegisterDefaultTileSize); 479 RegisterTileOpts++; 480 } 481 482 return Node; 483 } 484 485 isl::schedule_node 486 ScheduleTreeOptimizer::applyPrevectBandOpt(isl::schedule_node Node) { 487 auto Space = isl::manage(isl_schedule_node_band_get_space(Node.get())); 488 auto Dims = Space.dim(isl::dim::set).release(); 489 490 for (int i = Dims - 1; i >= 0; i--) 491 if (Node.as<isl::schedule_node_band>().member_get_coincident(i)) { 492 Node = prevectSchedBand(Node, i, PrevectorWidth); 493 break; 494 } 495 496 return Node; 497 } 498 499 __isl_give isl_schedule_node * 500 ScheduleTreeOptimizer::optimizeBand(__isl_take isl_schedule_node *NodeArg, 501 void *User) { 502 const OptimizerAdditionalInfoTy *OAI = 503 static_cast<const OptimizerAdditionalInfoTy *>(User); 504 assert(OAI && "Expecting optimization options"); 505 506 isl::schedule_node Node = isl::manage(NodeArg); 507 if (!isTileableBandNode(Node)) 508 return Node.release(); 509 510 if (OAI->PatternOpts) { 511 isl::schedule_node PatternOptimizedSchedule = 512 tryOptimizeMatMulPattern(Node, OAI->TTI, OAI->D); 513 if (!PatternOptimizedSchedule.is_null()) { 514 MatMulOpts++; 515 return PatternOptimizedSchedule.release(); 516 } 517 } 518 519 if (OAI->Postopts) 520 Node = applyTileBandOpt(Node); 521 522 if (OAI->Prevect) { 523 // FIXME: Prevectorization requirements are different from those checked by 524 // isTileableBandNode. 525 Node = applyPrevectBandOpt(Node); 526 } 527 528 return Node.release(); 529 } 530 531 isl::schedule 532 ScheduleTreeOptimizer::optimizeSchedule(isl::schedule Schedule, 533 const OptimizerAdditionalInfoTy *OAI) { 534 auto Root = Schedule.get_root(); 535 Root = optimizeScheduleNode(Root, OAI); 536 return Root.get_schedule(); 537 } 538 539 isl::schedule_node ScheduleTreeOptimizer::optimizeScheduleNode( 540 isl::schedule_node Node, const OptimizerAdditionalInfoTy *OAI) { 541 Node = isl::manage(isl_schedule_node_map_descendant_bottom_up( 542 Node.release(), optimizeBand, 543 const_cast<void *>(static_cast<const void *>(OAI)))); 544 return Node; 545 } 546 547 bool ScheduleTreeOptimizer::isProfitableSchedule(Scop &S, 548 isl::schedule NewSchedule) { 549 // To understand if the schedule has been optimized we check if the schedule 550 // has changed at all. 551 // TODO: We can improve this by tracking if any necessarily beneficial 552 // transformations have been performed. This can e.g. be tiling, loop 553 // interchange, or ...) We can track this either at the place where the 554 // transformation has been performed or, in case of automatic ILP based 555 // optimizations, by comparing (yet to be defined) performance metrics 556 // before/after the scheduling optimizer 557 // (e.g., #stride-one accesses) 558 // FIXME: A schedule tree whose union_map-conversion is identical to the 559 // original schedule map may still allow for parallelization, i.e. can still 560 // be profitable. 561 auto NewScheduleMap = NewSchedule.get_map(); 562 auto OldSchedule = S.getSchedule(); 563 assert(!OldSchedule.is_null() && 564 "Only IslScheduleOptimizer can insert extension nodes " 565 "that make Scop::getSchedule() return nullptr."); 566 bool changed = !OldSchedule.is_equal(NewScheduleMap); 567 return changed; 568 } 569 570 class IslScheduleOptimizerWrapperPass : public ScopPass { 571 public: 572 static char ID; 573 574 explicit IslScheduleOptimizerWrapperPass() : ScopPass(ID) {} 575 576 /// Optimize the schedule of the SCoP @p S. 577 bool runOnScop(Scop &S) override; 578 579 /// Print the new schedule for the SCoP @p S. 580 void printScop(raw_ostream &OS, Scop &S) const override; 581 582 /// Register all analyses and transformation required. 583 void getAnalysisUsage(AnalysisUsage &AU) const override; 584 585 /// Release the internal memory. 586 void releaseMemory() override { 587 LastSchedule = {}; 588 IslCtx.reset(); 589 } 590 591 private: 592 std::shared_ptr<isl_ctx> IslCtx; 593 isl::schedule LastSchedule; 594 }; 595 596 char IslScheduleOptimizerWrapperPass::ID = 0; 597 598 #ifndef NDEBUG 599 static void printSchedule(llvm::raw_ostream &OS, const isl::schedule &Schedule, 600 StringRef Desc) { 601 isl::ctx Ctx = Schedule.ctx(); 602 isl_printer *P = isl_printer_to_str(Ctx.get()); 603 P = isl_printer_set_yaml_style(P, ISL_YAML_STYLE_BLOCK); 604 P = isl_printer_print_schedule(P, Schedule.get()); 605 char *Str = isl_printer_get_str(P); 606 OS << Desc << ": \n" << Str << "\n"; 607 free(Str); 608 isl_printer_free(P); 609 } 610 #endif 611 612 /// Collect statistics for the schedule tree. 613 /// 614 /// @param Schedule The schedule tree to analyze. If not a schedule tree it is 615 /// ignored. 616 /// @param Version The version of the schedule tree that is analyzed. 617 /// 0 for the original schedule tree before any transformation. 618 /// 1 for the schedule tree after isl's rescheduling. 619 /// 2 for the schedule tree after optimizations are applied 620 /// (tiling, pattern matching) 621 static void walkScheduleTreeForStatistics(isl::schedule Schedule, int Version) { 622 auto Root = Schedule.get_root(); 623 if (Root.is_null()) 624 return; 625 626 isl_schedule_node_foreach_descendant_top_down( 627 Root.get(), 628 [](__isl_keep isl_schedule_node *nodeptr, void *user) -> isl_bool { 629 isl::schedule_node Node = isl::manage_copy(nodeptr); 630 int Version = *static_cast<int *>(user); 631 632 switch (isl_schedule_node_get_type(Node.get())) { 633 case isl_schedule_node_band: { 634 NumBands[Version]++; 635 if (isl_schedule_node_band_get_permutable(Node.get()) == 636 isl_bool_true) 637 NumPermutable[Version]++; 638 639 int CountMembers = isl_schedule_node_band_n_member(Node.get()); 640 NumBandMembers[Version] += CountMembers; 641 for (int i = 0; i < CountMembers; i += 1) { 642 if (Node.as<isl::schedule_node_band>().member_get_coincident(i)) 643 NumCoincident[Version]++; 644 } 645 break; 646 } 647 648 case isl_schedule_node_filter: 649 NumFilters[Version]++; 650 break; 651 652 case isl_schedule_node_extension: 653 NumExtension[Version]++; 654 break; 655 656 default: 657 break; 658 } 659 660 return isl_bool_true; 661 }, 662 &Version); 663 } 664 665 static bool runIslScheduleOptimizer( 666 Scop &S, 667 function_ref<const Dependences &(Dependences::AnalysisLevel)> GetDeps, 668 TargetTransformInfo *TTI, OptimizationRemarkEmitter *ORE, 669 isl::schedule &LastSchedule) { 670 671 // Skip SCoPs in case they're already optimised by PPCGCodeGeneration 672 if (S.isToBeSkipped()) 673 return false; 674 675 // Skip empty SCoPs but still allow code generation as it will delete the 676 // loops present but not needed. 677 if (S.getSize() == 0) { 678 S.markAsOptimized(); 679 return false; 680 } 681 682 ScopsProcessed++; 683 684 // Schedule without optimizations. 685 isl::schedule Schedule = S.getScheduleTree(); 686 walkScheduleTreeForStatistics(S.getScheduleTree(), 0); 687 LLVM_DEBUG(printSchedule(dbgs(), Schedule, "Original schedule tree")); 688 689 bool HasUserTransformation = false; 690 if (PragmaBasedOpts) { 691 isl::schedule ManuallyTransformed = applyManualTransformations( 692 &S, Schedule, GetDeps(Dependences::AL_Statement), ORE); 693 if (ManuallyTransformed.is_null()) { 694 LLVM_DEBUG(dbgs() << "Error during manual optimization\n"); 695 return false; 696 } 697 698 if (ManuallyTransformed.get() != Schedule.get()) { 699 // User transformations have precedence over other transformations. 700 HasUserTransformation = true; 701 Schedule = std::move(ManuallyTransformed); 702 LLVM_DEBUG( 703 printSchedule(dbgs(), Schedule, "After manual transformations")); 704 } 705 } 706 707 // Only continue if either manual transformations have been applied or we are 708 // allowed to apply heuristics. 709 // TODO: Detect disabled heuristics and no user-directed transformation 710 // metadata earlier in ScopDetection. 711 if (!HasUserTransformation && S.hasDisableHeuristicsHint()) { 712 LLVM_DEBUG(dbgs() << "Heuristic optimizations disabled by metadata\n"); 713 return false; 714 } 715 716 // Get dependency analysis. 717 const Dependences &D = GetDeps(Dependences::AL_Statement); 718 if (D.getSharedIslCtx() != S.getSharedIslCtx()) { 719 LLVM_DEBUG(dbgs() << "DependenceInfo for another SCoP/isl_ctx\n"); 720 return false; 721 } 722 if (!D.hasValidDependences()) { 723 LLVM_DEBUG(dbgs() << "Dependency information not available\n"); 724 return false; 725 } 726 727 // Apply ISL's algorithm only if not overriden by the user. Note that 728 // post-rescheduling optimizations (tiling, pattern-based, prevectorization) 729 // rely on the coincidence/permutable annotations on schedule tree bands that 730 // are added by the rescheduling analyzer. Therefore, disabling the 731 // rescheduler implicitly also disables these optimizations. 732 if (!EnableReschedule) { 733 LLVM_DEBUG(dbgs() << "Skipping rescheduling due to command line option\n"); 734 } else if (HasUserTransformation) { 735 LLVM_DEBUG( 736 dbgs() << "Skipping rescheduling due to manual transformation\n"); 737 } else { 738 // Build input data. 739 int ValidityKinds = 740 Dependences::TYPE_RAW | Dependences::TYPE_WAR | Dependences::TYPE_WAW; 741 int ProximityKinds; 742 743 if (OptimizeDeps == "all") 744 ProximityKinds = 745 Dependences::TYPE_RAW | Dependences::TYPE_WAR | Dependences::TYPE_WAW; 746 else if (OptimizeDeps == "raw") 747 ProximityKinds = Dependences::TYPE_RAW; 748 else { 749 errs() << "Do not know how to optimize for '" << OptimizeDeps << "'" 750 << " Falling back to optimizing all dependences.\n"; 751 ProximityKinds = 752 Dependences::TYPE_RAW | Dependences::TYPE_WAR | Dependences::TYPE_WAW; 753 } 754 755 isl::union_set Domain = S.getDomains(); 756 757 if (Domain.is_null()) 758 return false; 759 760 isl::union_map Validity = D.getDependences(ValidityKinds); 761 isl::union_map Proximity = D.getDependences(ProximityKinds); 762 763 // Simplify the dependences by removing the constraints introduced by the 764 // domains. This can speed up the scheduling time significantly, as large 765 // constant coefficients will be removed from the dependences. The 766 // introduction of some additional dependences reduces the possible 767 // transformations, but in most cases, such transformation do not seem to be 768 // interesting anyway. In some cases this option may stop the scheduler to 769 // find any schedule. 770 if (SimplifyDeps == "yes") { 771 Validity = Validity.gist_domain(Domain); 772 Validity = Validity.gist_range(Domain); 773 Proximity = Proximity.gist_domain(Domain); 774 Proximity = Proximity.gist_range(Domain); 775 } else if (SimplifyDeps != "no") { 776 errs() 777 << "warning: Option -polly-opt-simplify-deps should either be 'yes' " 778 "or 'no'. Falling back to default: 'yes'\n"; 779 } 780 781 LLVM_DEBUG(dbgs() << "\n\nCompute schedule from: "); 782 LLVM_DEBUG(dbgs() << "Domain := " << Domain << ";\n"); 783 LLVM_DEBUG(dbgs() << "Proximity := " << Proximity << ";\n"); 784 LLVM_DEBUG(dbgs() << "Validity := " << Validity << ";\n"); 785 786 int IslMaximizeBands; 787 if (MaximizeBandDepth == "yes") { 788 IslMaximizeBands = 1; 789 } else if (MaximizeBandDepth == "no") { 790 IslMaximizeBands = 0; 791 } else { 792 errs() 793 << "warning: Option -polly-opt-maximize-bands should either be 'yes'" 794 " or 'no'. Falling back to default: 'yes'\n"; 795 IslMaximizeBands = 1; 796 } 797 798 int IslOuterCoincidence; 799 if (OuterCoincidence == "yes") { 800 IslOuterCoincidence = 1; 801 } else if (OuterCoincidence == "no") { 802 IslOuterCoincidence = 0; 803 } else { 804 errs() << "warning: Option -polly-opt-outer-coincidence should either be " 805 "'yes' or 'no'. Falling back to default: 'no'\n"; 806 IslOuterCoincidence = 0; 807 } 808 809 isl_ctx *Ctx = S.getIslCtx().get(); 810 811 isl_options_set_schedule_outer_coincidence(Ctx, IslOuterCoincidence); 812 isl_options_set_schedule_maximize_band_depth(Ctx, IslMaximizeBands); 813 isl_options_set_schedule_max_constant_term(Ctx, MaxConstantTerm); 814 isl_options_set_schedule_max_coefficient(Ctx, MaxCoefficient); 815 isl_options_set_tile_scale_tile_loops(Ctx, 0); 816 817 auto OnErrorStatus = isl_options_get_on_error(Ctx); 818 isl_options_set_on_error(Ctx, ISL_ON_ERROR_CONTINUE); 819 820 auto SC = isl::schedule_constraints::on_domain(Domain); 821 SC = SC.set_proximity(Proximity); 822 SC = SC.set_validity(Validity); 823 SC = SC.set_coincidence(Validity); 824 Schedule = SC.compute_schedule(); 825 isl_options_set_on_error(Ctx, OnErrorStatus); 826 827 ScopsRescheduled++; 828 LLVM_DEBUG(printSchedule(dbgs(), Schedule, "After rescheduling")); 829 } 830 831 walkScheduleTreeForStatistics(Schedule, 1); 832 833 // In cases the scheduler is not able to optimize the code, we just do not 834 // touch the schedule. 835 if (Schedule.is_null()) 836 return false; 837 838 // Apply post-rescheduling optimizations (if enabled) and/or prevectorization. 839 const OptimizerAdditionalInfoTy OAI = { 840 TTI, const_cast<Dependences *>(&D), 841 /*PatternOpts=*/!HasUserTransformation && PMBasedOpts, 842 /*Postopts=*/!HasUserTransformation && EnablePostopts, 843 /*Prevect=*/PollyVectorizerChoice != VECTORIZER_NONE}; 844 if (OAI.PatternOpts || OAI.Postopts || OAI.Prevect) { 845 Schedule = ScheduleTreeOptimizer::optimizeSchedule(Schedule, &OAI); 846 Schedule = hoistExtensionNodes(Schedule); 847 LLVM_DEBUG(printSchedule(dbgs(), Schedule, "After post-optimizations")); 848 walkScheduleTreeForStatistics(Schedule, 2); 849 } 850 851 // Skip profitability check if user transformation(s) have been applied. 852 if (!HasUserTransformation && 853 !ScheduleTreeOptimizer::isProfitableSchedule(S, Schedule)) 854 return false; 855 856 auto ScopStats = S.getStatistics(); 857 ScopsOptimized++; 858 NumAffineLoopsOptimized += ScopStats.NumAffineLoops; 859 NumBoxedLoopsOptimized += ScopStats.NumBoxedLoops; 860 LastSchedule = Schedule; 861 862 S.setScheduleTree(Schedule); 863 S.markAsOptimized(); 864 865 if (OptimizedScops) 866 errs() << S; 867 868 return false; 869 } 870 871 bool IslScheduleOptimizerWrapperPass::runOnScop(Scop &S) { 872 releaseMemory(); 873 874 Function &F = S.getFunction(); 875 IslCtx = S.getSharedIslCtx(); 876 877 auto getDependences = 878 [this](Dependences::AnalysisLevel) -> const Dependences & { 879 return getAnalysis<DependenceInfo>().getDependences( 880 Dependences::AL_Statement); 881 }; 882 OptimizationRemarkEmitter &ORE = 883 getAnalysis<OptimizationRemarkEmitterWrapperPass>().getORE(); 884 TargetTransformInfo *TTI = 885 &getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F); 886 return runIslScheduleOptimizer(S, getDependences, TTI, &ORE, LastSchedule); 887 } 888 889 static void runScheduleOptimizerPrinter(raw_ostream &OS, 890 isl::schedule LastSchedule) { 891 isl_printer *p; 892 char *ScheduleStr; 893 894 OS << "Calculated schedule:\n"; 895 896 if (LastSchedule.is_null()) { 897 OS << "n/a\n"; 898 return; 899 } 900 901 p = isl_printer_to_str(LastSchedule.ctx().get()); 902 p = isl_printer_set_yaml_style(p, ISL_YAML_STYLE_BLOCK); 903 p = isl_printer_print_schedule(p, LastSchedule.get()); 904 ScheduleStr = isl_printer_get_str(p); 905 isl_printer_free(p); 906 907 OS << ScheduleStr << "\n"; 908 909 free(ScheduleStr); 910 } 911 912 void IslScheduleOptimizerWrapperPass::printScop(raw_ostream &OS, Scop &) const { 913 runScheduleOptimizerPrinter(OS, LastSchedule); 914 } 915 916 void IslScheduleOptimizerWrapperPass::getAnalysisUsage( 917 AnalysisUsage &AU) const { 918 ScopPass::getAnalysisUsage(AU); 919 AU.addRequired<DependenceInfo>(); 920 AU.addRequired<TargetTransformInfoWrapperPass>(); 921 AU.addRequired<OptimizationRemarkEmitterWrapperPass>(); 922 923 AU.addPreserved<DependenceInfo>(); 924 AU.addPreserved<OptimizationRemarkEmitterWrapperPass>(); 925 } 926 927 } // namespace 928 929 Pass *polly::createIslScheduleOptimizerWrapperPass() { 930 return new IslScheduleOptimizerWrapperPass(); 931 } 932 933 INITIALIZE_PASS_BEGIN(IslScheduleOptimizerWrapperPass, "polly-opt-isl", 934 "Polly - Optimize schedule of SCoP", false, false); 935 INITIALIZE_PASS_DEPENDENCY(DependenceInfo); 936 INITIALIZE_PASS_DEPENDENCY(ScopInfoRegionPass); 937 INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass); 938 INITIALIZE_PASS_DEPENDENCY(OptimizationRemarkEmitterWrapperPass); 939 INITIALIZE_PASS_END(IslScheduleOptimizerWrapperPass, "polly-opt-isl", 940 "Polly - Optimize schedule of SCoP", false, false) 941 942 static llvm::PreservedAnalyses 943 runIslScheduleOptimizerUsingNPM(Scop &S, ScopAnalysisManager &SAM, 944 ScopStandardAnalysisResults &SAR, SPMUpdater &U, 945 raw_ostream *OS) { 946 DependenceAnalysis::Result &Deps = SAM.getResult<DependenceAnalysis>(S, SAR); 947 auto GetDeps = [&Deps](Dependences::AnalysisLevel) -> const Dependences & { 948 return Deps.getDependences(Dependences::AL_Statement); 949 }; 950 OptimizationRemarkEmitter ORE(&S.getFunction()); 951 TargetTransformInfo *TTI = &SAR.TTI; 952 isl::schedule LastSchedule; 953 bool Modified = runIslScheduleOptimizer(S, GetDeps, TTI, &ORE, LastSchedule); 954 if (OS) { 955 *OS << "Printing analysis 'Polly - Optimize schedule of SCoP' for region: '" 956 << S.getName() << "' in function '" << S.getFunction().getName() 957 << "':\n"; 958 runScheduleOptimizerPrinter(*OS, LastSchedule); 959 } 960 961 if (!Modified) 962 return PreservedAnalyses::all(); 963 964 PreservedAnalyses PA; 965 PA.preserveSet<AllAnalysesOn<Module>>(); 966 PA.preserveSet<AllAnalysesOn<Function>>(); 967 PA.preserveSet<AllAnalysesOn<Loop>>(); 968 return PA; 969 } 970 971 llvm::PreservedAnalyses 972 IslScheduleOptimizerPass::run(Scop &S, ScopAnalysisManager &SAM, 973 ScopStandardAnalysisResults &SAR, SPMUpdater &U) { 974 return runIslScheduleOptimizerUsingNPM(S, SAM, SAR, U, nullptr); 975 } 976 977 llvm::PreservedAnalyses 978 IslScheduleOptimizerPrinterPass::run(Scop &S, ScopAnalysisManager &SAM, 979 ScopStandardAnalysisResults &SAR, 980 SPMUpdater &U) { 981 return runIslScheduleOptimizerUsingNPM(S, SAM, SAR, U, &OS); 982 } 983