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