1 //===- Schedule.cpp - Calculate an optimized schedule ---------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This pass generates an entirey new schedule tree from the data dependences 11 // and iteration domains. The new schedule tree is computed in two steps: 12 // 13 // 1) The isl scheduling optimizer is run 14 // 15 // The isl scheduling optimizer creates a new schedule tree that maximizes 16 // parallelism and tileability and minimizes data-dependence distances. The 17 // algorithm used is a modified version of the ``Pluto'' algorithm: 18 // 19 // U. Bondhugula, A. Hartono, J. Ramanujam, and P. Sadayappan. 20 // A Practical Automatic Polyhedral Parallelizer and Locality Optimizer. 21 // In Proceedings of the 2008 ACM SIGPLAN Conference On Programming Language 22 // Design and Implementation, PLDI ’08, pages 101–113. ACM, 2008. 23 // 24 // 2) A set of post-scheduling transformations is applied on the schedule tree. 25 // 26 // These optimizations include: 27 // 28 // - Tiling of the innermost tilable bands 29 // - Prevectorization - The coice of a possible outer loop that is strip-mined 30 // to the innermost level to enable inner-loop 31 // vectorization. 32 // - Some optimizations for spatial locality are also planned. 33 // 34 // For a detailed description of the schedule tree itself please see section 6 35 // of: 36 // 37 // Polyhedral AST generation is more than scanning polyhedra 38 // Tobias Grosser, Sven Verdoolaege, Albert Cohen 39 // ACM Transations on Programming Languages and Systems (TOPLAS), 40 // 37(4), July 2015 41 // http://www.grosser.es/#pub-polyhedral-AST-generation 42 // 43 // This publication also contains a detailed discussion of the different options 44 // for polyhedral loop unrolling, full/partial tile separation and other uses 45 // of the schedule tree. 46 // 47 //===----------------------------------------------------------------------===// 48 49 #include "polly/ScheduleOptimizer.h" 50 #include "polly/CodeGen/CodeGeneration.h" 51 #include "polly/DependenceInfo.h" 52 #include "polly/LinkAllPasses.h" 53 #include "polly/Options.h" 54 #include "polly/ScopInfo.h" 55 #include "polly/Support/GICHelper.h" 56 #include "llvm/Support/Debug.h" 57 #include "isl/aff.h" 58 #include "isl/band.h" 59 #include "isl/constraint.h" 60 #include "isl/map.h" 61 #include "isl/options.h" 62 #include "isl/printer.h" 63 #include "isl/schedule.h" 64 #include "isl/schedule_node.h" 65 #include "isl/space.h" 66 #include "isl/union_map.h" 67 #include "isl/union_set.h" 68 69 using namespace llvm; 70 using namespace polly; 71 72 #define DEBUG_TYPE "polly-opt-isl" 73 74 static cl::opt<bool> EnableTiling("polly-tiling", 75 cl::desc("Enable loop tiling"), 76 cl::init(true), cl::ZeroOrMore, 77 cl::cat(PollyCategory)); 78 79 static cl::opt<std::string> 80 OptimizeDeps("polly-opt-optimize-only", 81 cl::desc("Only a certain kind of dependences (all/raw)"), 82 cl::Hidden, cl::init("all"), cl::ZeroOrMore, 83 cl::cat(PollyCategory)); 84 85 static cl::opt<std::string> 86 SimplifyDeps("polly-opt-simplify-deps", 87 cl::desc("Dependences should be simplified (yes/no)"), 88 cl::Hidden, cl::init("yes"), cl::ZeroOrMore, 89 cl::cat(PollyCategory)); 90 91 static cl::opt<int> MaxConstantTerm( 92 "polly-opt-max-constant-term", 93 cl::desc("The maximal constant term allowed (-1 is unlimited)"), cl::Hidden, 94 cl::init(20), cl::ZeroOrMore, cl::cat(PollyCategory)); 95 96 static cl::opt<int> MaxCoefficient( 97 "polly-opt-max-coefficient", 98 cl::desc("The maximal coefficient allowed (-1 is unlimited)"), cl::Hidden, 99 cl::init(20), cl::ZeroOrMore, cl::cat(PollyCategory)); 100 101 static cl::opt<std::string> FusionStrategy( 102 "polly-opt-fusion", cl::desc("The fusion strategy to choose (min/max)"), 103 cl::Hidden, cl::init("min"), cl::ZeroOrMore, cl::cat(PollyCategory)); 104 105 static cl::opt<std::string> 106 MaximizeBandDepth("polly-opt-maximize-bands", 107 cl::desc("Maximize the band depth (yes/no)"), cl::Hidden, 108 cl::init("yes"), cl::ZeroOrMore, cl::cat(PollyCategory)); 109 110 static cl::opt<int> PrevectorWidth( 111 "polly-prevect-width", 112 cl::desc( 113 "The number of loop iterations to strip-mine for pre-vectorization"), 114 cl::Hidden, cl::init(4), cl::ZeroOrMore, cl::cat(PollyCategory)); 115 116 static cl::opt<int> DefaultTileSize( 117 "polly-default-tile-size", 118 cl::desc("The default tile size (if not enough were provided by" 119 " --polly-tile-sizes)"), 120 cl::Hidden, cl::init(32), cl::ZeroOrMore, cl::cat(PollyCategory)); 121 122 static cl::list<int> TileSizes("polly-tile-sizes", 123 cl::desc("A tile size" 124 " for each loop dimension, filled with" 125 " --polly-default-tile-size"), 126 cl::Hidden, cl::ZeroOrMore, cl::CommaSeparated, 127 cl::cat(PollyCategory)); 128 namespace { 129 130 class IslScheduleOptimizer : public ScopPass { 131 public: 132 static char ID; 133 explicit IslScheduleOptimizer() : ScopPass(ID) { LastSchedule = nullptr; } 134 135 ~IslScheduleOptimizer() { isl_schedule_free(LastSchedule); } 136 137 bool runOnScop(Scop &S) override; 138 void printScop(raw_ostream &OS, Scop &S) const override; 139 void getAnalysisUsage(AnalysisUsage &AU) const override; 140 141 private: 142 isl_schedule *LastSchedule; 143 144 /// @brief Decide if the @p NewSchedule is profitable for @p S. 145 /// 146 /// @param S The SCoP we optimize. 147 /// @param NewSchedule The new schedule we computed. 148 /// 149 /// @return True, if we believe @p NewSchedule is an improvement for @p S. 150 bool isProfitableSchedule(Scop &S, __isl_keep isl_union_map *NewSchedule); 151 152 /// @brief Pre-vectorizes one scheduling dimension of a schedule band. 153 /// 154 /// prevectSchedBand splits out the dimension DimToVectorize, tiles it and 155 /// sinks the resulting point loop. 156 /// 157 /// Example (DimToVectorize=0, VectorWidth=4): 158 /// 159 /// | Before transformation: 160 /// | 161 /// | A[i,j] -> [i,j] 162 /// | 163 /// | for (i = 0; i < 128; i++) 164 /// | for (j = 0; j < 128; j++) 165 /// | A(i,j); 166 /// 167 /// | After transformation: 168 /// | 169 /// | for (it = 0; it < 32; it+=1) 170 /// | for (j = 0; j < 128; j++) 171 /// | for (ip = 0; ip <= 3; ip++) 172 /// | A(4 * it + ip,j); 173 /// 174 /// The goal of this transformation is to create a trivially vectorizable 175 /// loop. This means a parallel loop at the innermost level that has a 176 /// constant number of iterations corresponding to the target vector width. 177 /// 178 /// This transformation creates a loop at the innermost level. The loop has 179 /// a constant number of iterations, if the number of loop iterations at 180 /// DimToVectorize can be divided by VectorWidth. The default VectorWidth is 181 /// currently constant and not yet target specific. This function does not 182 /// reason about parallelism. 183 static __isl_give isl_schedule_node * 184 prevectSchedBand(__isl_take isl_schedule_node *Node, unsigned DimToVectorize, 185 int VectorWidth); 186 187 /// @brief Apply additional optimizations on the bands in the schedule tree. 188 /// 189 /// We are looking for an innermost band node and apply the following 190 /// transformations: 191 /// 192 /// - Tile the band 193 /// - if the band is tileable 194 /// - if the band has more than one loop dimension 195 /// 196 /// - Prevectorize the schedule of the band (or the point loop in case of 197 /// tiling). 198 /// - if vectorization is enabled 199 /// 200 /// @param Node The schedule node to (possibly) optimize. 201 /// @param User A pointer to forward some use information (currently unused). 202 static isl_schedule_node *optimizeBand(isl_schedule_node *Node, void *User); 203 204 /// @brief Apply post-scheduling transformations. 205 /// 206 /// This function applies a set of additional local transformations on the 207 /// schedule tree as it computed by the isl scheduler. Local transformations 208 /// applied include: 209 /// 210 /// - Tiling 211 /// - Prevectorization 212 /// 213 /// @param Schedule The schedule object post-transformations will be applied 214 /// on. 215 /// @returns The transformed schedule. 216 static __isl_give isl_schedule * 217 addPostTransforms(__isl_take isl_schedule *Schedule); 218 219 using llvm::Pass::doFinalization; 220 221 virtual bool doFinalization() override { 222 isl_schedule_free(LastSchedule); 223 LastSchedule = nullptr; 224 return true; 225 } 226 }; 227 } 228 229 char IslScheduleOptimizer::ID = 0; 230 231 __isl_give isl_schedule_node * 232 IslScheduleOptimizer::prevectSchedBand(__isl_take isl_schedule_node *Node, 233 unsigned DimToVectorize, 234 int VectorWidth) { 235 assert(isl_schedule_node_get_type(Node) == isl_schedule_node_band); 236 237 auto Space = isl_schedule_node_band_get_space(Node); 238 auto ScheduleDimensions = isl_space_dim(Space, isl_dim_set); 239 isl_space_free(Space); 240 assert(DimToVectorize < ScheduleDimensions); 241 242 if (DimToVectorize > 0) { 243 Node = isl_schedule_node_band_split(Node, DimToVectorize); 244 Node = isl_schedule_node_child(Node, 0); 245 } 246 if (DimToVectorize < ScheduleDimensions - 1) 247 Node = isl_schedule_node_band_split(Node, 1); 248 Space = isl_schedule_node_band_get_space(Node); 249 auto Sizes = isl_multi_val_zero(Space); 250 auto Ctx = isl_schedule_node_get_ctx(Node); 251 Sizes = 252 isl_multi_val_set_val(Sizes, 0, isl_val_int_from_si(Ctx, VectorWidth)); 253 Node = isl_schedule_node_band_tile(Node, Sizes); 254 Node = isl_schedule_node_child(Node, 0); 255 Node = isl_schedule_node_band_sink(Node); 256 Node = isl_schedule_node_child(Node, 0); 257 return Node; 258 } 259 260 isl_schedule_node *IslScheduleOptimizer::optimizeBand(isl_schedule_node *Node, 261 void *User) { 262 if (isl_schedule_node_get_type(Node) != isl_schedule_node_band) 263 return Node; 264 265 if (isl_schedule_node_n_children(Node) != 1) 266 return Node; 267 268 if (!isl_schedule_node_band_get_permutable(Node)) 269 return Node; 270 271 auto Space = isl_schedule_node_band_get_space(Node); 272 auto Dims = isl_space_dim(Space, isl_dim_set); 273 274 if (Dims <= 1) { 275 isl_space_free(Space); 276 return Node; 277 } 278 279 auto Child = isl_schedule_node_get_child(Node, 0); 280 auto Type = isl_schedule_node_get_type(Child); 281 isl_schedule_node_free(Child); 282 283 if (Type != isl_schedule_node_leaf) { 284 isl_space_free(Space); 285 return Node; 286 } 287 288 if (EnableTiling) { 289 auto Ctx = isl_schedule_node_get_ctx(Node); 290 auto Sizes = isl_multi_val_zero(isl_space_copy(Space)); 291 for (unsigned i = 0; i < Dims; i++) { 292 auto tileSize = TileSizes.size() > i ? TileSizes[i] : DefaultTileSize; 293 Sizes = 294 isl_multi_val_set_val(Sizes, i, isl_val_int_from_si(Ctx, tileSize)); 295 } 296 Node = isl_schedule_node_band_tile(Node, Sizes); 297 Node = isl_schedule_node_child(Node, 0); 298 } 299 300 isl_space_free(Space); 301 302 if (PollyVectorizerChoice == VECTORIZER_NONE) 303 return Node; 304 305 for (int i = Dims - 1; i >= 0; i--) 306 if (isl_schedule_node_band_member_get_coincident(Node, i)) { 307 Node = IslScheduleOptimizer::prevectSchedBand(Node, i, PrevectorWidth); 308 break; 309 } 310 311 return Node; 312 } 313 314 __isl_give isl_schedule * 315 IslScheduleOptimizer::addPostTransforms(__isl_take isl_schedule *Schedule) { 316 isl_schedule_node *Root = isl_schedule_get_root(Schedule); 317 isl_schedule_free(Schedule); 318 Root = isl_schedule_node_map_descendant_bottom_up( 319 Root, IslScheduleOptimizer::optimizeBand, NULL); 320 auto S = isl_schedule_node_get_schedule(Root); 321 isl_schedule_node_free(Root); 322 return S; 323 } 324 325 bool IslScheduleOptimizer::isProfitableSchedule( 326 Scop &S, __isl_keep isl_union_map *NewSchedule) { 327 // To understand if the schedule has been optimized we check if the schedule 328 // has changed at all. 329 // TODO: We can improve this by tracking if any necessarily beneficial 330 // transformations have been performed. This can e.g. be tiling, loop 331 // interchange, or ...) We can track this either at the place where the 332 // transformation has been performed or, in case of automatic ILP based 333 // optimizations, by comparing (yet to be defined) performance metrics 334 // before/after the scheduling optimizer 335 // (e.g., #stride-one accesses) 336 isl_union_map *OldSchedule = S.getSchedule(); 337 bool changed = !isl_union_map_is_equal(OldSchedule, NewSchedule); 338 isl_union_map_free(OldSchedule); 339 return changed; 340 } 341 342 bool IslScheduleOptimizer::runOnScop(Scop &S) { 343 344 // Skip empty SCoPs but still allow code generation as it will delete the 345 // loops present but not needed. 346 if (S.getSize() == 0) { 347 S.markAsOptimized(); 348 return false; 349 } 350 351 const Dependences &D = getAnalysis<DependenceInfo>().getDependences(); 352 353 if (!D.hasValidDependences()) 354 return false; 355 356 isl_schedule_free(LastSchedule); 357 LastSchedule = nullptr; 358 359 // Build input data. 360 int ValidityKinds = 361 Dependences::TYPE_RAW | Dependences::TYPE_WAR | Dependences::TYPE_WAW; 362 int ProximityKinds; 363 364 if (OptimizeDeps == "all") 365 ProximityKinds = 366 Dependences::TYPE_RAW | Dependences::TYPE_WAR | Dependences::TYPE_WAW; 367 else if (OptimizeDeps == "raw") 368 ProximityKinds = Dependences::TYPE_RAW; 369 else { 370 errs() << "Do not know how to optimize for '" << OptimizeDeps << "'" 371 << " Falling back to optimizing all dependences.\n"; 372 ProximityKinds = 373 Dependences::TYPE_RAW | Dependences::TYPE_WAR | Dependences::TYPE_WAW; 374 } 375 376 isl_union_set *Domain = S.getDomains(); 377 378 if (!Domain) 379 return false; 380 381 isl_union_map *Validity = D.getDependences(ValidityKinds); 382 isl_union_map *Proximity = D.getDependences(ProximityKinds); 383 384 // Simplify the dependences by removing the constraints introduced by the 385 // domains. This can speed up the scheduling time significantly, as large 386 // constant coefficients will be removed from the dependences. The 387 // introduction of some additional dependences reduces the possible 388 // transformations, but in most cases, such transformation do not seem to be 389 // interesting anyway. In some cases this option may stop the scheduler to 390 // find any schedule. 391 if (SimplifyDeps == "yes") { 392 Validity = isl_union_map_gist_domain(Validity, isl_union_set_copy(Domain)); 393 Validity = isl_union_map_gist_range(Validity, isl_union_set_copy(Domain)); 394 Proximity = 395 isl_union_map_gist_domain(Proximity, isl_union_set_copy(Domain)); 396 Proximity = isl_union_map_gist_range(Proximity, isl_union_set_copy(Domain)); 397 } else if (SimplifyDeps != "no") { 398 errs() << "warning: Option -polly-opt-simplify-deps should either be 'yes' " 399 "or 'no'. Falling back to default: 'yes'\n"; 400 } 401 402 DEBUG(dbgs() << "\n\nCompute schedule from: "); 403 DEBUG(dbgs() << "Domain := " << stringFromIslObj(Domain) << ";\n"); 404 DEBUG(dbgs() << "Proximity := " << stringFromIslObj(Proximity) << ";\n"); 405 DEBUG(dbgs() << "Validity := " << stringFromIslObj(Validity) << ";\n"); 406 407 unsigned IslSerializeSCCs; 408 409 if (FusionStrategy == "max") { 410 IslSerializeSCCs = 0; 411 } else if (FusionStrategy == "min") { 412 IslSerializeSCCs = 1; 413 } else { 414 errs() << "warning: Unknown fusion strategy. Falling back to maximal " 415 "fusion.\n"; 416 IslSerializeSCCs = 0; 417 } 418 419 int IslMaximizeBands; 420 421 if (MaximizeBandDepth == "yes") { 422 IslMaximizeBands = 1; 423 } else if (MaximizeBandDepth == "no") { 424 IslMaximizeBands = 0; 425 } else { 426 errs() << "warning: Option -polly-opt-maximize-bands should either be 'yes'" 427 " or 'no'. Falling back to default: 'yes'\n"; 428 IslMaximizeBands = 1; 429 } 430 431 isl_options_set_schedule_serialize_sccs(S.getIslCtx(), IslSerializeSCCs); 432 isl_options_set_schedule_maximize_band_depth(S.getIslCtx(), IslMaximizeBands); 433 isl_options_set_schedule_max_constant_term(S.getIslCtx(), MaxConstantTerm); 434 isl_options_set_schedule_max_coefficient(S.getIslCtx(), MaxCoefficient); 435 isl_options_set_tile_scale_tile_loops(S.getIslCtx(), 0); 436 437 isl_options_set_on_error(S.getIslCtx(), ISL_ON_ERROR_CONTINUE); 438 439 isl_schedule_constraints *ScheduleConstraints; 440 ScheduleConstraints = isl_schedule_constraints_on_domain(Domain); 441 ScheduleConstraints = 442 isl_schedule_constraints_set_proximity(ScheduleConstraints, Proximity); 443 ScheduleConstraints = isl_schedule_constraints_set_validity( 444 ScheduleConstraints, isl_union_map_copy(Validity)); 445 ScheduleConstraints = 446 isl_schedule_constraints_set_coincidence(ScheduleConstraints, Validity); 447 isl_schedule *Schedule; 448 Schedule = isl_schedule_constraints_compute_schedule(ScheduleConstraints); 449 isl_options_set_on_error(S.getIslCtx(), ISL_ON_ERROR_ABORT); 450 451 // In cases the scheduler is not able to optimize the code, we just do not 452 // touch the schedule. 453 if (!Schedule) 454 return false; 455 456 DEBUG({ 457 auto *P = isl_printer_to_str(S.getIslCtx()); 458 P = isl_printer_set_yaml_style(P, ISL_YAML_STYLE_BLOCK); 459 P = isl_printer_print_schedule(P, Schedule); 460 dbgs() << "NewScheduleTree: \n" << isl_printer_get_str(P) << "\n"; 461 isl_printer_free(P); 462 }); 463 464 isl_schedule *NewSchedule = addPostTransforms(Schedule); 465 isl_union_map *NewScheduleMap = isl_schedule_get_map(NewSchedule); 466 467 if (!isProfitableSchedule(S, NewScheduleMap)) { 468 isl_union_map_free(NewScheduleMap); 469 isl_schedule_free(NewSchedule); 470 return false; 471 } 472 473 S.setScheduleTree(NewSchedule); 474 S.markAsOptimized(); 475 476 isl_union_map_free(NewScheduleMap); 477 return false; 478 } 479 480 void IslScheduleOptimizer::printScop(raw_ostream &OS, Scop &) const { 481 isl_printer *p; 482 char *ScheduleStr; 483 484 OS << "Calculated schedule:\n"; 485 486 if (!LastSchedule) { 487 OS << "n/a\n"; 488 return; 489 } 490 491 p = isl_printer_to_str(isl_schedule_get_ctx(LastSchedule)); 492 p = isl_printer_print_schedule(p, LastSchedule); 493 ScheduleStr = isl_printer_get_str(p); 494 isl_printer_free(p); 495 496 OS << ScheduleStr << "\n"; 497 } 498 499 void IslScheduleOptimizer::getAnalysisUsage(AnalysisUsage &AU) const { 500 ScopPass::getAnalysisUsage(AU); 501 AU.addRequired<DependenceInfo>(); 502 } 503 504 Pass *polly::createIslScheduleOptimizerPass() { 505 return new IslScheduleOptimizer(); 506 } 507 508 INITIALIZE_PASS_BEGIN(IslScheduleOptimizer, "polly-opt-isl", 509 "Polly - Optimize schedule of SCoP", false, false); 510 INITIALIZE_PASS_DEPENDENCY(DependenceInfo); 511 INITIALIZE_PASS_DEPENDENCY(ScopInfo); 512 INITIALIZE_PASS_END(IslScheduleOptimizer, "polly-opt-isl", 513 "Polly - Optimize schedule of SCoP", false, false) 514