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