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