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