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/Analysis/TargetTransformInfo.h" 57 #include "llvm/Support/Debug.h" 58 #include "isl/aff.h" 59 #include "isl/band.h" 60 #include "isl/constraint.h" 61 #include "isl/map.h" 62 #include "isl/options.h" 63 #include "isl/printer.h" 64 #include "isl/schedule.h" 65 #include "isl/schedule_node.h" 66 #include "isl/space.h" 67 #include "isl/union_map.h" 68 #include "isl/union_set.h" 69 70 using namespace llvm; 71 using namespace polly; 72 73 #define DEBUG_TYPE "polly-opt-isl" 74 75 static cl::opt<std::string> 76 OptimizeDeps("polly-opt-optimize-only", 77 cl::desc("Only a certain kind of dependences (all/raw)"), 78 cl::Hidden, cl::init("all"), cl::ZeroOrMore, 79 cl::cat(PollyCategory)); 80 81 static cl::opt<std::string> 82 SimplifyDeps("polly-opt-simplify-deps", 83 cl::desc("Dependences should be simplified (yes/no)"), 84 cl::Hidden, cl::init("yes"), cl::ZeroOrMore, 85 cl::cat(PollyCategory)); 86 87 static cl::opt<int> MaxConstantTerm( 88 "polly-opt-max-constant-term", 89 cl::desc("The maximal constant term allowed (-1 is unlimited)"), cl::Hidden, 90 cl::init(20), cl::ZeroOrMore, cl::cat(PollyCategory)); 91 92 static cl::opt<int> MaxCoefficient( 93 "polly-opt-max-coefficient", 94 cl::desc("The maximal coefficient allowed (-1 is unlimited)"), cl::Hidden, 95 cl::init(20), cl::ZeroOrMore, cl::cat(PollyCategory)); 96 97 static cl::opt<std::string> FusionStrategy( 98 "polly-opt-fusion", cl::desc("The fusion strategy to choose (min/max)"), 99 cl::Hidden, cl::init("min"), cl::ZeroOrMore, cl::cat(PollyCategory)); 100 101 static cl::opt<std::string> 102 MaximizeBandDepth("polly-opt-maximize-bands", 103 cl::desc("Maximize the band depth (yes/no)"), cl::Hidden, 104 cl::init("yes"), cl::ZeroOrMore, cl::cat(PollyCategory)); 105 106 static cl::opt<std::string> OuterCoincidence( 107 "polly-opt-outer-coincidence", 108 cl::desc("Try to construct schedules where the outer member of each band " 109 "satisfies the coincidence constraints (yes/no)"), 110 cl::Hidden, cl::init("no"), cl::ZeroOrMore, cl::cat(PollyCategory)); 111 112 static cl::opt<int> PrevectorWidth( 113 "polly-prevect-width", 114 cl::desc( 115 "The number of loop iterations to strip-mine for pre-vectorization"), 116 cl::Hidden, cl::init(4), cl::ZeroOrMore, cl::cat(PollyCategory)); 117 118 static cl::opt<bool> FirstLevelTiling("polly-tiling", 119 cl::desc("Enable loop tiling"), 120 cl::init(true), cl::ZeroOrMore, 121 cl::cat(PollyCategory)); 122 123 static cl::opt<int> LatencyVectorFma( 124 "polly-target-latency-vector-fma", 125 cl::desc("The minimal number of cycles between issuing two " 126 "dependent consecutive vector fused multiply-add " 127 "instructions."), 128 cl::Hidden, cl::init(8), cl::ZeroOrMore, cl::cat(PollyCategory)); 129 130 static cl::opt<int> ThrougputVectorFma( 131 "polly-target-througput-vector-fma", 132 cl::desc("A throughput of the processor floating-point arithmetic units " 133 "expressed in the number of vector fused multiply-add " 134 "instructions per clock cycle."), 135 cl::Hidden, cl::init(1), cl::ZeroOrMore, cl::cat(PollyCategory)); 136 137 static cl::opt<int> FirstLevelDefaultTileSize( 138 "polly-default-tile-size", 139 cl::desc("The default tile size (if not enough were provided by" 140 " --polly-tile-sizes)"), 141 cl::Hidden, cl::init(32), cl::ZeroOrMore, cl::cat(PollyCategory)); 142 143 static cl::list<int> FirstLevelTileSizes( 144 "polly-tile-sizes", cl::desc("A tile size for each loop dimension, filled " 145 "with --polly-default-tile-size"), 146 cl::Hidden, cl::ZeroOrMore, cl::CommaSeparated, cl::cat(PollyCategory)); 147 148 static cl::opt<bool> 149 SecondLevelTiling("polly-2nd-level-tiling", 150 cl::desc("Enable a 2nd level loop of loop tiling"), 151 cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory)); 152 153 static cl::opt<int> SecondLevelDefaultTileSize( 154 "polly-2nd-level-default-tile-size", 155 cl::desc("The default 2nd-level tile size (if not enough were provided by" 156 " --polly-2nd-level-tile-sizes)"), 157 cl::Hidden, cl::init(16), cl::ZeroOrMore, cl::cat(PollyCategory)); 158 159 static cl::list<int> 160 SecondLevelTileSizes("polly-2nd-level-tile-sizes", 161 cl::desc("A tile size for each loop dimension, filled " 162 "with --polly-default-tile-size"), 163 cl::Hidden, cl::ZeroOrMore, cl::CommaSeparated, 164 cl::cat(PollyCategory)); 165 166 static cl::opt<bool> RegisterTiling("polly-register-tiling", 167 cl::desc("Enable register tiling"), 168 cl::init(false), cl::ZeroOrMore, 169 cl::cat(PollyCategory)); 170 171 static cl::opt<int> RegisterDefaultTileSize( 172 "polly-register-tiling-default-tile-size", 173 cl::desc("The default register tile size (if not enough were provided by" 174 " --polly-register-tile-sizes)"), 175 cl::Hidden, cl::init(2), cl::ZeroOrMore, cl::cat(PollyCategory)); 176 177 static cl::list<int> 178 RegisterTileSizes("polly-register-tile-sizes", 179 cl::desc("A tile size for each loop dimension, filled " 180 "with --polly-register-tile-size"), 181 cl::Hidden, cl::ZeroOrMore, cl::CommaSeparated, 182 cl::cat(PollyCategory)); 183 184 static cl::opt<bool> 185 PMBasedOpts("polly-pattern-matching-based-opts", 186 cl::desc("Perform optimizations based on pattern matching"), 187 cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory)); 188 189 /// @brief Create an isl_union_set, which describes the isolate option based 190 /// on IsoalteDomain. 191 /// 192 /// @param IsolateDomain An isl_set whose last dimension is the only one that 193 /// should belong to the current band node. 194 static __isl_give isl_union_set * 195 getIsolateOptions(__isl_take isl_set *IsolateDomain) { 196 auto Dims = isl_set_dim(IsolateDomain, isl_dim_set); 197 auto *IsolateRelation = isl_map_from_domain(IsolateDomain); 198 IsolateRelation = isl_map_move_dims(IsolateRelation, isl_dim_out, 0, 199 isl_dim_in, Dims - 1, 1); 200 auto *IsolateOption = isl_map_wrap(IsolateRelation); 201 auto *Id = isl_id_alloc(isl_set_get_ctx(IsolateOption), "isolate", nullptr); 202 return isl_union_set_from_set(isl_set_set_tuple_id(IsolateOption, Id)); 203 } 204 205 /// @brief Create an isl_union_set, which describes the atomic option for the 206 /// dimension of the current node. 207 /// 208 /// It may help to reduce the size of generated code. 209 /// 210 /// @param Ctx An isl_ctx, which is used to create the isl_union_set. 211 static __isl_give isl_union_set *getAtomicOptions(__isl_take isl_ctx *Ctx) { 212 auto *Space = isl_space_set_alloc(Ctx, 0, 1); 213 auto *AtomicOption = isl_set_universe(Space); 214 auto *Id = isl_id_alloc(Ctx, "atomic", nullptr); 215 return isl_union_set_from_set(isl_set_set_tuple_id(AtomicOption, Id)); 216 } 217 218 /// @brief Make the last dimension of Set to take values 219 /// from 0 to VectorWidth - 1. 220 /// 221 /// @param Set A set, which should be modified. 222 /// @param VectorWidth A parameter, which determines the constraint. 223 static __isl_give isl_set *addExtentConstraints(__isl_take isl_set *Set, 224 int VectorWidth) { 225 auto Dims = isl_set_dim(Set, isl_dim_set); 226 auto Space = isl_set_get_space(Set); 227 auto *LocalSpace = isl_local_space_from_space(Space); 228 auto *ExtConstr = 229 isl_constraint_alloc_inequality(isl_local_space_copy(LocalSpace)); 230 ExtConstr = isl_constraint_set_constant_si(ExtConstr, 0); 231 ExtConstr = 232 isl_constraint_set_coefficient_si(ExtConstr, isl_dim_set, Dims - 1, 1); 233 Set = isl_set_add_constraint(Set, ExtConstr); 234 ExtConstr = isl_constraint_alloc_inequality(LocalSpace); 235 ExtConstr = isl_constraint_set_constant_si(ExtConstr, VectorWidth - 1); 236 ExtConstr = 237 isl_constraint_set_coefficient_si(ExtConstr, isl_dim_set, Dims - 1, -1); 238 return isl_set_add_constraint(Set, ExtConstr); 239 } 240 241 /// @brief Build the desired set of partial tile prefixes. 242 /// 243 /// We build a set of partial tile prefixes, which are prefixes of the vector 244 /// loop that have exactly VectorWidth iterations. 245 /// 246 /// 1. Get all prefixes of the vector loop. 247 /// 2. Extend it to a set, which has exactly VectorWidth iterations for 248 /// any prefix from the set that was built on the previous step. 249 /// 3. Subtract loop domain from it, project out the vector loop dimension and 250 /// get a set of prefixes, which don't have exactly VectorWidth iterations. 251 /// 4. Subtract it from all prefixes of the vector loop and get the desired 252 /// set. 253 /// 254 /// @param ScheduleRange A range of a map, which describes a prefix schedule 255 /// relation. 256 static __isl_give isl_set * 257 getPartialTilePrefixes(__isl_take isl_set *ScheduleRange, int VectorWidth) { 258 auto Dims = isl_set_dim(ScheduleRange, isl_dim_set); 259 auto *LoopPrefixes = isl_set_project_out(isl_set_copy(ScheduleRange), 260 isl_dim_set, Dims - 1, 1); 261 auto *ExtentPrefixes = 262 isl_set_add_dims(isl_set_copy(LoopPrefixes), isl_dim_set, 1); 263 ExtentPrefixes = addExtentConstraints(ExtentPrefixes, VectorWidth); 264 auto *BadPrefixes = isl_set_subtract(ExtentPrefixes, ScheduleRange); 265 BadPrefixes = isl_set_project_out(BadPrefixes, isl_dim_set, Dims - 1, 1); 266 return isl_set_subtract(LoopPrefixes, BadPrefixes); 267 } 268 269 __isl_give isl_schedule_node *ScheduleTreeOptimizer::isolateFullPartialTiles( 270 __isl_take isl_schedule_node *Node, int VectorWidth) { 271 assert(isl_schedule_node_get_type(Node) == isl_schedule_node_band); 272 Node = isl_schedule_node_child(Node, 0); 273 Node = isl_schedule_node_child(Node, 0); 274 auto *SchedRelUMap = isl_schedule_node_get_prefix_schedule_relation(Node); 275 auto *ScheduleRelation = isl_map_from_union_map(SchedRelUMap); 276 auto *ScheduleRange = isl_map_range(ScheduleRelation); 277 auto *IsolateDomain = getPartialTilePrefixes(ScheduleRange, VectorWidth); 278 auto *AtomicOption = getAtomicOptions(isl_set_get_ctx(IsolateDomain)); 279 auto *IsolateOption = getIsolateOptions(IsolateDomain); 280 Node = isl_schedule_node_parent(Node); 281 Node = isl_schedule_node_parent(Node); 282 auto *Options = isl_union_set_union(IsolateOption, AtomicOption); 283 Node = isl_schedule_node_band_set_ast_build_options(Node, Options); 284 return Node; 285 } 286 287 __isl_give isl_schedule_node * 288 ScheduleTreeOptimizer::prevectSchedBand(__isl_take isl_schedule_node *Node, 289 unsigned DimToVectorize, 290 int VectorWidth) { 291 assert(isl_schedule_node_get_type(Node) == isl_schedule_node_band); 292 293 auto Space = isl_schedule_node_band_get_space(Node); 294 auto ScheduleDimensions = isl_space_dim(Space, isl_dim_set); 295 isl_space_free(Space); 296 assert(DimToVectorize < ScheduleDimensions); 297 298 if (DimToVectorize > 0) { 299 Node = isl_schedule_node_band_split(Node, DimToVectorize); 300 Node = isl_schedule_node_child(Node, 0); 301 } 302 if (DimToVectorize < ScheduleDimensions - 1) 303 Node = isl_schedule_node_band_split(Node, 1); 304 Space = isl_schedule_node_band_get_space(Node); 305 auto Sizes = isl_multi_val_zero(Space); 306 auto Ctx = isl_schedule_node_get_ctx(Node); 307 Sizes = 308 isl_multi_val_set_val(Sizes, 0, isl_val_int_from_si(Ctx, VectorWidth)); 309 Node = isl_schedule_node_band_tile(Node, Sizes); 310 Node = isolateFullPartialTiles(Node, VectorWidth); 311 Node = isl_schedule_node_child(Node, 0); 312 // Make sure the "trivially vectorizable loop" is not unrolled. Otherwise, 313 // we will have troubles to match it in the backend. 314 Node = isl_schedule_node_band_set_ast_build_options( 315 Node, isl_union_set_read_from_str(Ctx, "{ unroll[x]: 1 = 0 }")); 316 Node = isl_schedule_node_band_sink(Node); 317 Node = isl_schedule_node_child(Node, 0); 318 if (isl_schedule_node_get_type(Node) == isl_schedule_node_leaf) 319 Node = isl_schedule_node_parent(Node); 320 isl_id *LoopMarker = isl_id_alloc(Ctx, "SIMD", nullptr); 321 Node = isl_schedule_node_insert_mark(Node, LoopMarker); 322 return Node; 323 } 324 325 __isl_give isl_schedule_node * 326 ScheduleTreeOptimizer::tileNode(__isl_take isl_schedule_node *Node, 327 const char *Identifier, ArrayRef<int> TileSizes, 328 int DefaultTileSize) { 329 auto Ctx = isl_schedule_node_get_ctx(Node); 330 auto Space = isl_schedule_node_band_get_space(Node); 331 auto Dims = isl_space_dim(Space, isl_dim_set); 332 auto Sizes = isl_multi_val_zero(Space); 333 std::string IdentifierString(Identifier); 334 for (unsigned i = 0; i < Dims; i++) { 335 auto tileSize = i < TileSizes.size() ? TileSizes[i] : DefaultTileSize; 336 Sizes = isl_multi_val_set_val(Sizes, i, isl_val_int_from_si(Ctx, tileSize)); 337 } 338 auto TileLoopMarkerStr = IdentifierString + " - Tiles"; 339 isl_id *TileLoopMarker = 340 isl_id_alloc(Ctx, TileLoopMarkerStr.c_str(), nullptr); 341 Node = isl_schedule_node_insert_mark(Node, TileLoopMarker); 342 Node = isl_schedule_node_child(Node, 0); 343 Node = isl_schedule_node_band_tile(Node, Sizes); 344 Node = isl_schedule_node_child(Node, 0); 345 auto PointLoopMarkerStr = IdentifierString + " - Points"; 346 isl_id *PointLoopMarker = 347 isl_id_alloc(Ctx, PointLoopMarkerStr.c_str(), nullptr); 348 Node = isl_schedule_node_insert_mark(Node, PointLoopMarker); 349 Node = isl_schedule_node_child(Node, 0); 350 return Node; 351 } 352 353 __isl_give isl_schedule_node * 354 ScheduleTreeOptimizer::applyRegisterTiling(__isl_take isl_schedule_node *Node, 355 llvm::ArrayRef<int> TileSizes, 356 int DefaultTileSize) { 357 auto *Ctx = isl_schedule_node_get_ctx(Node); 358 Node = tileNode(Node, "Register tiling", TileSizes, DefaultTileSize); 359 Node = isl_schedule_node_band_set_ast_build_options( 360 Node, isl_union_set_read_from_str(Ctx, "{unroll[x]}")); 361 return Node; 362 } 363 364 bool ScheduleTreeOptimizer::isTileableBandNode( 365 __isl_keep isl_schedule_node *Node) { 366 if (isl_schedule_node_get_type(Node) != isl_schedule_node_band) 367 return false; 368 369 if (isl_schedule_node_n_children(Node) != 1) 370 return false; 371 372 if (!isl_schedule_node_band_get_permutable(Node)) 373 return false; 374 375 auto Space = isl_schedule_node_band_get_space(Node); 376 auto Dims = isl_space_dim(Space, isl_dim_set); 377 isl_space_free(Space); 378 379 if (Dims <= 1) 380 return false; 381 382 auto Child = isl_schedule_node_get_child(Node, 0); 383 auto Type = isl_schedule_node_get_type(Child); 384 isl_schedule_node_free(Child); 385 386 if (Type != isl_schedule_node_leaf) 387 return false; 388 389 return true; 390 } 391 392 __isl_give isl_schedule_node * 393 ScheduleTreeOptimizer::standardBandOpts(__isl_take isl_schedule_node *Node, 394 void *User) { 395 if (FirstLevelTiling) 396 Node = tileNode(Node, "1st level tiling", FirstLevelTileSizes, 397 FirstLevelDefaultTileSize); 398 399 if (SecondLevelTiling) 400 Node = tileNode(Node, "2nd level tiling", SecondLevelTileSizes, 401 SecondLevelDefaultTileSize); 402 403 if (RegisterTiling) 404 Node = 405 applyRegisterTiling(Node, RegisterTileSizes, RegisterDefaultTileSize); 406 407 if (PollyVectorizerChoice == VECTORIZER_NONE) 408 return Node; 409 410 auto Space = isl_schedule_node_band_get_space(Node); 411 auto Dims = isl_space_dim(Space, isl_dim_set); 412 isl_space_free(Space); 413 414 for (int i = Dims - 1; i >= 0; i--) 415 if (isl_schedule_node_band_member_get_coincident(Node, i)) { 416 Node = prevectSchedBand(Node, i, PrevectorWidth); 417 break; 418 } 419 420 return Node; 421 } 422 423 /// @brief Check whether output dimensions of the map rely on the specified 424 /// input dimension. 425 /// 426 /// @param IslMap The isl map to be considered. 427 /// @param DimNum The number of an input dimension to be checked. 428 static bool isInputDimUsed(__isl_take isl_map *IslMap, unsigned DimNum) { 429 auto *CheckedAccessRelation = 430 isl_map_project_out(isl_map_copy(IslMap), isl_dim_in, DimNum, 1); 431 CheckedAccessRelation = 432 isl_map_insert_dims(CheckedAccessRelation, isl_dim_in, DimNum, 1); 433 auto *InputDimsId = isl_map_get_tuple_id(IslMap, isl_dim_in); 434 CheckedAccessRelation = 435 isl_map_set_tuple_id(CheckedAccessRelation, isl_dim_in, InputDimsId); 436 InputDimsId = isl_map_get_tuple_id(IslMap, isl_dim_out); 437 CheckedAccessRelation = 438 isl_map_set_tuple_id(CheckedAccessRelation, isl_dim_out, InputDimsId); 439 auto res = !isl_map_is_equal(CheckedAccessRelation, IslMap); 440 isl_map_free(CheckedAccessRelation); 441 isl_map_free(IslMap); 442 return res; 443 } 444 445 /// @brief Check if the SCoP statement could probably be optimized with 446 /// analytical modeling. 447 /// 448 /// containsMatrMult tries to determine whether the following conditions 449 /// are true: 450 /// 1. all memory accesses of the statement will have stride 0 or 1, 451 /// if we interchange loops (switch the variable used in the inner 452 /// loop to the outer loop). 453 /// 2. all memory accesses of the statement except from the last one, are 454 /// read memory access and the last one is write memory access. 455 /// 3. all subscripts of the last memory access of the statement don't contain 456 /// the variable used in the inner loop. 457 /// 458 /// @param PartialSchedule The PartialSchedule that contains a SCoP statement 459 /// to check. 460 static bool containsMatrMult(__isl_keep isl_map *PartialSchedule) { 461 auto InputDimsId = isl_map_get_tuple_id(PartialSchedule, isl_dim_in); 462 auto *ScpStmt = static_cast<ScopStmt *>(isl_id_get_user(InputDimsId)); 463 isl_id_free(InputDimsId); 464 if (ScpStmt->size() <= 1) 465 return false; 466 auto MemA = ScpStmt->begin(); 467 for (unsigned i = 0; i < ScpStmt->size() - 2 && MemA != ScpStmt->end(); 468 i++, MemA++) 469 if (!(*MemA)->isRead() || 470 ((*MemA)->isArrayKind() && 471 !((*MemA)->isStrideOne(isl_map_copy(PartialSchedule)) || 472 (*MemA)->isStrideZero(isl_map_copy(PartialSchedule))))) 473 return false; 474 MemA++; 475 if (!(*MemA)->isWrite() || !(*MemA)->isArrayKind() || 476 !((*MemA)->isStrideOne(isl_map_copy(PartialSchedule)) || 477 (*MemA)->isStrideZero(isl_map_copy(PartialSchedule)))) 478 return false; 479 auto DimNum = isl_map_dim(PartialSchedule, isl_dim_in); 480 return !isInputDimUsed((*MemA)->getAccessRelation(), DimNum - 1); 481 } 482 483 /// @brief Circular shift of output dimensions of the integer map. 484 /// 485 /// @param IslMap The isl map to be modified. 486 static __isl_give isl_map *circularShiftOutputDims(__isl_take isl_map *IslMap) { 487 auto DimNum = isl_map_dim(IslMap, isl_dim_out); 488 if (DimNum == 0) 489 return IslMap; 490 auto InputDimsId = isl_map_get_tuple_id(IslMap, isl_dim_in); 491 IslMap = isl_map_move_dims(IslMap, isl_dim_in, 0, isl_dim_out, DimNum - 1, 1); 492 IslMap = isl_map_move_dims(IslMap, isl_dim_out, 0, isl_dim_in, 0, 1); 493 return isl_map_set_tuple_id(IslMap, isl_dim_in, InputDimsId); 494 } 495 496 __isl_give isl_schedule_node *ScheduleTreeOptimizer::optimizeMatMulPattern( 497 __isl_take isl_schedule_node *Node, const llvm::TargetTransformInfo *TTI) { 498 assert(TTI && "The target transform info should be provided."); 499 // Get a micro-kernel. 500 // Nvec - Number of double-precision floating-point numbers that can be hold 501 // by a vector register. Use 2 by default. 502 auto Nvec = TTI->getRegisterBitWidth(true) / 64; 503 if (Nvec == 0) 504 Nvec = 2; 505 int Nr = 506 ceil(sqrt(Nvec * LatencyVectorFma * ThrougputVectorFma) / Nvec) * Nvec; 507 int Mr = ceil(Nvec * LatencyVectorFma * ThrougputVectorFma / Nr); 508 std::vector<int> MicroKernelParams{Mr, Nr}; 509 Node = applyRegisterTiling(Node, MicroKernelParams, 1); 510 return Node; 511 } 512 513 bool ScheduleTreeOptimizer::isMatrMultPattern( 514 __isl_keep isl_schedule_node *Node) { 515 auto *PartialSchedule = 516 isl_schedule_node_band_get_partial_schedule_union_map(Node); 517 if (isl_schedule_node_band_n_member(Node) != 3 || 518 isl_union_map_n_map(PartialSchedule) != 1) { 519 isl_union_map_free(PartialSchedule); 520 return false; 521 } 522 auto *NewPartialSchedule = isl_map_from_union_map(PartialSchedule); 523 NewPartialSchedule = circularShiftOutputDims(NewPartialSchedule); 524 if (containsMatrMult(NewPartialSchedule)) { 525 isl_map_free(NewPartialSchedule); 526 return true; 527 } 528 isl_map_free(NewPartialSchedule); 529 return false; 530 } 531 532 __isl_give isl_schedule_node * 533 ScheduleTreeOptimizer::optimizeBand(__isl_take isl_schedule_node *Node, 534 void *User) { 535 if (!isTileableBandNode(Node)) 536 return Node; 537 538 if (PMBasedOpts && User && isMatrMultPattern(Node)) { 539 DEBUG(dbgs() << "The matrix multiplication pattern was detected\n"); 540 const llvm::TargetTransformInfo *TTI; 541 TTI = static_cast<const llvm::TargetTransformInfo *>(User); 542 Node = optimizeMatMulPattern(Node, TTI); 543 } 544 545 return standardBandOpts(Node, User); 546 } 547 548 __isl_give isl_schedule * 549 ScheduleTreeOptimizer::optimizeSchedule(__isl_take isl_schedule *Schedule, 550 const llvm::TargetTransformInfo *TTI) { 551 isl_schedule_node *Root = isl_schedule_get_root(Schedule); 552 Root = optimizeScheduleNode(Root, TTI); 553 isl_schedule_free(Schedule); 554 auto S = isl_schedule_node_get_schedule(Root); 555 isl_schedule_node_free(Root); 556 return S; 557 } 558 559 __isl_give isl_schedule_node *ScheduleTreeOptimizer::optimizeScheduleNode( 560 __isl_take isl_schedule_node *Node, const llvm::TargetTransformInfo *TTI) { 561 Node = isl_schedule_node_map_descendant_bottom_up( 562 Node, optimizeBand, const_cast<void *>(static_cast<const void *>(TTI))); 563 return Node; 564 } 565 566 bool ScheduleTreeOptimizer::isProfitableSchedule( 567 Scop &S, __isl_keep isl_union_map *NewSchedule) { 568 // To understand if the schedule has been optimized we check if the schedule 569 // has changed at all. 570 // TODO: We can improve this by tracking if any necessarily beneficial 571 // transformations have been performed. This can e.g. be tiling, loop 572 // interchange, or ...) We can track this either at the place where the 573 // transformation has been performed or, in case of automatic ILP based 574 // optimizations, by comparing (yet to be defined) performance metrics 575 // before/after the scheduling optimizer 576 // (e.g., #stride-one accesses) 577 isl_union_map *OldSchedule = S.getSchedule(); 578 bool changed = !isl_union_map_is_equal(OldSchedule, NewSchedule); 579 isl_union_map_free(OldSchedule); 580 return changed; 581 } 582 583 namespace { 584 class IslScheduleOptimizer : public ScopPass { 585 public: 586 static char ID; 587 explicit IslScheduleOptimizer() : ScopPass(ID) { LastSchedule = nullptr; } 588 589 ~IslScheduleOptimizer() { isl_schedule_free(LastSchedule); } 590 591 /// @brief Optimize the schedule of the SCoP @p S. 592 bool runOnScop(Scop &S) override; 593 594 /// @brief Print the new schedule for the SCoP @p S. 595 void printScop(raw_ostream &OS, Scop &S) const override; 596 597 /// @brief Register all analyses and transformation required. 598 void getAnalysisUsage(AnalysisUsage &AU) const override; 599 600 /// @brief Release the internal memory. 601 void releaseMemory() override { 602 isl_schedule_free(LastSchedule); 603 LastSchedule = nullptr; 604 } 605 606 private: 607 isl_schedule *LastSchedule; 608 }; 609 } // namespace 610 611 char IslScheduleOptimizer::ID = 0; 612 613 bool IslScheduleOptimizer::runOnScop(Scop &S) { 614 615 // Skip empty SCoPs but still allow code generation as it will delete the 616 // loops present but not needed. 617 if (S.getSize() == 0) { 618 S.markAsOptimized(); 619 return false; 620 } 621 622 const Dependences &D = 623 getAnalysis<DependenceInfo>().getDependences(Dependences::AL_Statement); 624 625 if (!D.hasValidDependences()) 626 return false; 627 628 isl_schedule_free(LastSchedule); 629 LastSchedule = nullptr; 630 631 // Build input data. 632 int ValidityKinds = 633 Dependences::TYPE_RAW | Dependences::TYPE_WAR | Dependences::TYPE_WAW; 634 int ProximityKinds; 635 636 if (OptimizeDeps == "all") 637 ProximityKinds = 638 Dependences::TYPE_RAW | Dependences::TYPE_WAR | Dependences::TYPE_WAW; 639 else if (OptimizeDeps == "raw") 640 ProximityKinds = Dependences::TYPE_RAW; 641 else { 642 errs() << "Do not know how to optimize for '" << OptimizeDeps << "'" 643 << " Falling back to optimizing all dependences.\n"; 644 ProximityKinds = 645 Dependences::TYPE_RAW | Dependences::TYPE_WAR | Dependences::TYPE_WAW; 646 } 647 648 isl_union_set *Domain = S.getDomains(); 649 650 if (!Domain) 651 return false; 652 653 isl_union_map *Validity = D.getDependences(ValidityKinds); 654 isl_union_map *Proximity = D.getDependences(ProximityKinds); 655 656 // Simplify the dependences by removing the constraints introduced by the 657 // domains. This can speed up the scheduling time significantly, as large 658 // constant coefficients will be removed from the dependences. The 659 // introduction of some additional dependences reduces the possible 660 // transformations, but in most cases, such transformation do not seem to be 661 // interesting anyway. In some cases this option may stop the scheduler to 662 // find any schedule. 663 if (SimplifyDeps == "yes") { 664 Validity = isl_union_map_gist_domain(Validity, isl_union_set_copy(Domain)); 665 Validity = isl_union_map_gist_range(Validity, isl_union_set_copy(Domain)); 666 Proximity = 667 isl_union_map_gist_domain(Proximity, isl_union_set_copy(Domain)); 668 Proximity = isl_union_map_gist_range(Proximity, isl_union_set_copy(Domain)); 669 } else if (SimplifyDeps != "no") { 670 errs() << "warning: Option -polly-opt-simplify-deps should either be 'yes' " 671 "or 'no'. Falling back to default: 'yes'\n"; 672 } 673 674 DEBUG(dbgs() << "\n\nCompute schedule from: "); 675 DEBUG(dbgs() << "Domain := " << stringFromIslObj(Domain) << ";\n"); 676 DEBUG(dbgs() << "Proximity := " << stringFromIslObj(Proximity) << ";\n"); 677 DEBUG(dbgs() << "Validity := " << stringFromIslObj(Validity) << ";\n"); 678 679 unsigned IslSerializeSCCs; 680 681 if (FusionStrategy == "max") { 682 IslSerializeSCCs = 0; 683 } else if (FusionStrategy == "min") { 684 IslSerializeSCCs = 1; 685 } else { 686 errs() << "warning: Unknown fusion strategy. Falling back to maximal " 687 "fusion.\n"; 688 IslSerializeSCCs = 0; 689 } 690 691 int IslMaximizeBands; 692 693 if (MaximizeBandDepth == "yes") { 694 IslMaximizeBands = 1; 695 } else if (MaximizeBandDepth == "no") { 696 IslMaximizeBands = 0; 697 } else { 698 errs() << "warning: Option -polly-opt-maximize-bands should either be 'yes'" 699 " or 'no'. Falling back to default: 'yes'\n"; 700 IslMaximizeBands = 1; 701 } 702 703 int IslOuterCoincidence; 704 705 if (OuterCoincidence == "yes") { 706 IslOuterCoincidence = 1; 707 } else if (OuterCoincidence == "no") { 708 IslOuterCoincidence = 0; 709 } else { 710 errs() << "warning: Option -polly-opt-outer-coincidence should either be " 711 "'yes' or 'no'. Falling back to default: 'no'\n"; 712 IslOuterCoincidence = 0; 713 } 714 715 isl_options_set_schedule_outer_coincidence(S.getIslCtx(), 716 IslOuterCoincidence); 717 isl_options_set_schedule_serialize_sccs(S.getIslCtx(), IslSerializeSCCs); 718 isl_options_set_schedule_maximize_band_depth(S.getIslCtx(), IslMaximizeBands); 719 isl_options_set_schedule_max_constant_term(S.getIslCtx(), MaxConstantTerm); 720 isl_options_set_schedule_max_coefficient(S.getIslCtx(), MaxCoefficient); 721 isl_options_set_tile_scale_tile_loops(S.getIslCtx(), 0); 722 723 isl_options_set_on_error(S.getIslCtx(), ISL_ON_ERROR_CONTINUE); 724 725 isl_schedule_constraints *ScheduleConstraints; 726 ScheduleConstraints = isl_schedule_constraints_on_domain(Domain); 727 ScheduleConstraints = 728 isl_schedule_constraints_set_proximity(ScheduleConstraints, Proximity); 729 ScheduleConstraints = isl_schedule_constraints_set_validity( 730 ScheduleConstraints, isl_union_map_copy(Validity)); 731 ScheduleConstraints = 732 isl_schedule_constraints_set_coincidence(ScheduleConstraints, Validity); 733 isl_schedule *Schedule; 734 Schedule = isl_schedule_constraints_compute_schedule(ScheduleConstraints); 735 isl_options_set_on_error(S.getIslCtx(), ISL_ON_ERROR_ABORT); 736 737 // In cases the scheduler is not able to optimize the code, we just do not 738 // touch the schedule. 739 if (!Schedule) 740 return false; 741 742 DEBUG({ 743 auto *P = isl_printer_to_str(S.getIslCtx()); 744 P = isl_printer_set_yaml_style(P, ISL_YAML_STYLE_BLOCK); 745 P = isl_printer_print_schedule(P, Schedule); 746 dbgs() << "NewScheduleTree: \n" << isl_printer_get_str(P) << "\n"; 747 isl_printer_free(P); 748 }); 749 750 Function &F = S.getFunction(); 751 auto *TTI = &getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F); 752 isl_schedule *NewSchedule = 753 ScheduleTreeOptimizer::optimizeSchedule(Schedule, TTI); 754 isl_union_map *NewScheduleMap = isl_schedule_get_map(NewSchedule); 755 756 if (!ScheduleTreeOptimizer::isProfitableSchedule(S, NewScheduleMap)) { 757 isl_union_map_free(NewScheduleMap); 758 isl_schedule_free(NewSchedule); 759 return false; 760 } 761 762 S.setScheduleTree(NewSchedule); 763 S.markAsOptimized(); 764 765 isl_union_map_free(NewScheduleMap); 766 return false; 767 } 768 769 void IslScheduleOptimizer::printScop(raw_ostream &OS, Scop &) const { 770 isl_printer *p; 771 char *ScheduleStr; 772 773 OS << "Calculated schedule:\n"; 774 775 if (!LastSchedule) { 776 OS << "n/a\n"; 777 return; 778 } 779 780 p = isl_printer_to_str(isl_schedule_get_ctx(LastSchedule)); 781 p = isl_printer_print_schedule(p, LastSchedule); 782 ScheduleStr = isl_printer_get_str(p); 783 isl_printer_free(p); 784 785 OS << ScheduleStr << "\n"; 786 } 787 788 void IslScheduleOptimizer::getAnalysisUsage(AnalysisUsage &AU) const { 789 ScopPass::getAnalysisUsage(AU); 790 AU.addRequired<DependenceInfo>(); 791 AU.addRequired<TargetTransformInfoWrapperPass>(); 792 } 793 794 Pass *polly::createIslScheduleOptimizerPass() { 795 return new IslScheduleOptimizer(); 796 } 797 798 INITIALIZE_PASS_BEGIN(IslScheduleOptimizer, "polly-opt-isl", 799 "Polly - Optimize schedule of SCoP", false, false); 800 INITIALIZE_PASS_DEPENDENCY(DependenceInfo); 801 INITIALIZE_PASS_DEPENDENCY(ScopInfoRegionPass); 802 INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass); 803 INITIALIZE_PASS_END(IslScheduleOptimizer, "polly-opt-isl", 804 "Polly - Optimize schedule of SCoP", false, false) 805