• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2018 Google LLC.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef SOURCE_OPT_LOOP_DEPENDENCE_H_
16 #define SOURCE_OPT_LOOP_DEPENDENCE_H_
17 
18 #include <algorithm>
19 #include <cstdint>
20 #include <list>
21 #include <map>
22 #include <memory>
23 #include <ostream>
24 #include <set>
25 #include <string>
26 #include <utility>
27 #include <vector>
28 
29 #include "source/opt/instruction.h"
30 #include "source/opt/ir_context.h"
31 #include "source/opt/loop_descriptor.h"
32 #include "source/opt/scalar_analysis.h"
33 
34 namespace spvtools {
35 namespace opt {
36 
37 // Stores information about dependence between a load and a store wrt a single
38 // loop in a loop nest.
39 // DependenceInformation
40 // * UNKNOWN if no dependence information can be gathered or is gathered
41 //   for it.
42 // * DIRECTION if a dependence direction could be found, but not a
43 //   distance.
44 // * DISTANCE if a dependence distance could be found.
45 // * PEEL if peeling either the first or last iteration will break
46 //   dependence between the given load and store.
47 // * IRRELEVANT if it has no effect on the dependence between the given
48 //   load and store.
49 //
50 // If peel_first == true, the analysis has found that peeling the first
51 // iteration of this loop will break dependence.
52 //
53 // If peel_last == true, the analysis has found that peeling the last iteration
54 // of this loop will break dependence.
55 class DistanceEntry {
56  public:
57   enum DependenceInformation {
58     UNKNOWN = 0,
59     DIRECTION = 1,
60     DISTANCE = 2,
61     PEEL = 3,
62     IRRELEVANT = 4,
63     POINT = 5
64   };
65   enum Directions {
66     NONE = 0,
67     LT = 1,
68     EQ = 2,
69     LE = 3,
70     GT = 4,
71     NE = 5,
72     GE = 6,
73     ALL = 7
74   };
75   DependenceInformation dependence_information;
76   Directions direction;
77   int64_t distance;
78   bool peel_first;
79   bool peel_last;
80   int64_t point_x;
81   int64_t point_y;
82 
DistanceEntry()83   DistanceEntry()
84       : dependence_information(DependenceInformation::UNKNOWN),
85         direction(Directions::ALL),
86         distance(0),
87         peel_first(false),
88         peel_last(false),
89         point_x(0),
90         point_y(0) {}
91 
DistanceEntry(Directions direction_)92   explicit DistanceEntry(Directions direction_)
93       : dependence_information(DependenceInformation::DIRECTION),
94         direction(direction_),
95         distance(0),
96         peel_first(false),
97         peel_last(false),
98         point_x(0),
99         point_y(0) {}
100 
DistanceEntry(Directions direction_,int64_t distance_)101   DistanceEntry(Directions direction_, int64_t distance_)
102       : dependence_information(DependenceInformation::DISTANCE),
103         direction(direction_),
104         distance(distance_),
105         peel_first(false),
106         peel_last(false),
107         point_x(0),
108         point_y(0) {}
109 
DistanceEntry(int64_t x,int64_t y)110   DistanceEntry(int64_t x, int64_t y)
111       : dependence_information(DependenceInformation::POINT),
112         direction(Directions::ALL),
113         distance(0),
114         peel_first(false),
115         peel_last(false),
116         point_x(x),
117         point_y(y) {}
118 
119   bool operator==(const DistanceEntry& rhs) const {
120     return direction == rhs.direction && peel_first == rhs.peel_first &&
121            peel_last == rhs.peel_last && distance == rhs.distance &&
122            point_x == rhs.point_x && point_y == rhs.point_y;
123   }
124 
125   bool operator!=(const DistanceEntry& rhs) const { return !(*this == rhs); }
126 };
127 
128 // Stores a vector of DistanceEntrys, one per loop in the analysis.
129 // A DistanceVector holds all of the information gathered in a dependence
130 // analysis wrt the loops stored in the LoopDependenceAnalysis performing the
131 // analysis.
132 class DistanceVector {
133  public:
DistanceVector(size_t size)134   explicit DistanceVector(size_t size) : entries(size, DistanceEntry{}) {}
135 
DistanceVector(std::vector<DistanceEntry> entries_)136   explicit DistanceVector(std::vector<DistanceEntry> entries_)
137       : entries(entries_) {}
138 
GetEntry(size_t index)139   DistanceEntry& GetEntry(size_t index) { return entries[index]; }
GetEntry(size_t index)140   const DistanceEntry& GetEntry(size_t index) const { return entries[index]; }
141 
GetEntries()142   std::vector<DistanceEntry>& GetEntries() { return entries; }
GetEntries()143   const std::vector<DistanceEntry>& GetEntries() const { return entries; }
144 
145   bool operator==(const DistanceVector& rhs) const {
146     if (entries.size() != rhs.entries.size()) {
147       return false;
148     }
149     for (size_t i = 0; i < entries.size(); ++i) {
150       if (entries[i] != rhs.entries[i]) {
151         return false;
152       }
153     }
154     return true;
155   }
156   bool operator!=(const DistanceVector& rhs) const { return !(*this == rhs); }
157 
158  private:
159   std::vector<DistanceEntry> entries;
160 };
161 
162 class DependenceLine;
163 class DependenceDistance;
164 class DependencePoint;
165 class DependenceNone;
166 class DependenceEmpty;
167 
168 class Constraint {
169  public:
Constraint(const Loop * loop)170   explicit Constraint(const Loop* loop) : loop_(loop) {}
171   enum ConstraintType { Line, Distance, Point, None, Empty };
172 
173   virtual ConstraintType GetType() const = 0;
174 
~Constraint()175   virtual ~Constraint() {}
176 
177   // Get the loop this constraint belongs to.
GetLoop()178   const Loop* GetLoop() const { return loop_; }
179 
180   bool operator==(const Constraint& other) const;
181 
182   bool operator!=(const Constraint& other) const;
183 
184 // clang-format off
185 #define DeclareCastMethod(target)                  \
186   virtual target* As##target() { return nullptr; } \
187   virtual const target* As##target() const { return nullptr; }
188   DeclareCastMethod(DependenceLine)
189   DeclareCastMethod(DependenceDistance)
190   DeclareCastMethod(DependencePoint)
191   DeclareCastMethod(DependenceNone)
192   DeclareCastMethod(DependenceEmpty)
193 #undef DeclareCastMethod
194 
195  protected:
196   const Loop* loop_;
197 };
198 // clang-format on
199 
200 class DependenceLine : public Constraint {
201  public:
DependenceLine(SENode * a,SENode * b,SENode * c,const Loop * loop)202   DependenceLine(SENode* a, SENode* b, SENode* c, const Loop* loop)
203       : Constraint(loop), a_(a), b_(b), c_(c) {}
204 
GetType()205   ConstraintType GetType() const final { return Line; }
206 
AsDependenceLine()207   DependenceLine* AsDependenceLine() final { return this; }
AsDependenceLine()208   const DependenceLine* AsDependenceLine() const final { return this; }
209 
GetA()210   SENode* GetA() const { return a_; }
GetB()211   SENode* GetB() const { return b_; }
GetC()212   SENode* GetC() const { return c_; }
213 
214  private:
215   SENode* a_;
216   SENode* b_;
217   SENode* c_;
218 };
219 
220 class DependenceDistance : public Constraint {
221  public:
DependenceDistance(SENode * distance,const Loop * loop)222   DependenceDistance(SENode* distance, const Loop* loop)
223       : Constraint(loop), distance_(distance) {}
224 
GetType()225   ConstraintType GetType() const final { return Distance; }
226 
AsDependenceDistance()227   DependenceDistance* AsDependenceDistance() final { return this; }
AsDependenceDistance()228   const DependenceDistance* AsDependenceDistance() const final { return this; }
229 
GetDistance()230   SENode* GetDistance() const { return distance_; }
231 
232  private:
233   SENode* distance_;
234 };
235 
236 class DependencePoint : public Constraint {
237  public:
DependencePoint(SENode * source,SENode * destination,const Loop * loop)238   DependencePoint(SENode* source, SENode* destination, const Loop* loop)
239       : Constraint(loop), source_(source), destination_(destination) {}
240 
GetType()241   ConstraintType GetType() const final { return Point; }
242 
AsDependencePoint()243   DependencePoint* AsDependencePoint() final { return this; }
AsDependencePoint()244   const DependencePoint* AsDependencePoint() const final { return this; }
245 
GetSource()246   SENode* GetSource() const { return source_; }
GetDestination()247   SENode* GetDestination() const { return destination_; }
248 
249  private:
250   SENode* source_;
251   SENode* destination_;
252 };
253 
254 class DependenceNone : public Constraint {
255  public:
DependenceNone()256   DependenceNone() : Constraint(nullptr) {}
GetType()257   ConstraintType GetType() const final { return None; }
258 
AsDependenceNone()259   DependenceNone* AsDependenceNone() final { return this; }
AsDependenceNone()260   const DependenceNone* AsDependenceNone() const final { return this; }
261 };
262 
263 class DependenceEmpty : public Constraint {
264  public:
DependenceEmpty()265   DependenceEmpty() : Constraint(nullptr) {}
GetType()266   ConstraintType GetType() const final { return Empty; }
267 
AsDependenceEmpty()268   DependenceEmpty* AsDependenceEmpty() final { return this; }
AsDependenceEmpty()269   const DependenceEmpty* AsDependenceEmpty() const final { return this; }
270 };
271 
272 // Provides dependence information between a store instruction and a load
273 // instruction inside the same loop in a loop nest.
274 //
275 // The analysis can only check dependence between stores and loads with regard
276 // to the loop nest it is created with.
277 //
278 // The analysis can output debugging information to a stream. The output
279 // describes the control flow of the analysis and what information it can deduce
280 // at each step.
281 // SetDebugStream and ClearDebugStream are provided for this functionality.
282 //
283 // The dependency algorithm is based on the 1990 Paper
284 //   Practical Dependence Testing
285 //   Gina Goff, Ken Kennedy, Chau-Wen Tseng
286 //
287 // The algorithm first identifies subscript pairs between the load and store.
288 // Each pair is tested until all have been tested or independence is found.
289 // The number of induction variables in a pair determines which test to perform
290 // on it;
291 // Zero Index Variable (ZIV) is used when no induction variables are present
292 // in the pair.
293 // Single Index Variable (SIV) is used when only one induction variable is
294 // present, but may occur multiple times in the pair.
295 // Multiple Index Variable (MIV) is used when more than one induction variable
296 // is present in the pair.
297 class LoopDependenceAnalysis {
298  public:
LoopDependenceAnalysis(IRContext * context,std::vector<const Loop * > loops)299   LoopDependenceAnalysis(IRContext* context, std::vector<const Loop*> loops)
300       : context_(context),
301         loops_(loops),
302         scalar_evolution_(context),
303         debug_stream_(nullptr),
304         constraints_{} {}
305 
306   // Finds the dependence between |source| and |destination|.
307   // |source| should be an OpLoad.
308   // |destination| should be an OpStore.
309   // Any direction and distance information found will be stored in
310   // |distance_vector|.
311   // Returns true if independence is found, false otherwise.
312   bool GetDependence(const Instruction* source, const Instruction* destination,
313                      DistanceVector* distance_vector);
314 
315   // Returns true if |subscript_pair| represents a Zero Index Variable pair
316   // (ZIV)
317   bool IsZIV(const std::pair<SENode*, SENode*>& subscript_pair);
318 
319   // Returns true if |subscript_pair| represents a Single Index Variable
320   // (SIV) pair
321   bool IsSIV(const std::pair<SENode*, SENode*>& subscript_pair);
322 
323   // Returns true if |subscript_pair| represents a Multiple Index Variable
324   // (MIV) pair
325   bool IsMIV(const std::pair<SENode*, SENode*>& subscript_pair);
326 
327   // Finds the lower bound of |loop| as an SENode* and returns the result.
328   // The lower bound is the starting value of the loops induction variable
329   SENode* GetLowerBound(const Loop* loop);
330 
331   // Finds the upper bound of |loop| as an SENode* and returns the result.
332   // The upper bound is the last value before the loop exit condition is met.
333   SENode* GetUpperBound(const Loop* loop);
334 
335   // Returns true if |value| is between |bound_one| and |bound_two| (inclusive).
336   bool IsWithinBounds(int64_t value, int64_t bound_one, int64_t bound_two);
337 
338   // Finds the bounds of |loop| as upper_bound - lower_bound and returns the
339   // resulting SENode.
340   // If the operations can not be completed a nullptr is returned.
341   SENode* GetTripCount(const Loop* loop);
342 
343   // Returns the SENode* produced by building an SENode from the result of
344   // calling GetInductionInitValue on |loop|.
345   // If the operation can not be completed a nullptr is returned.
346   SENode* GetFirstTripInductionNode(const Loop* loop);
347 
348   // Returns the SENode* produced by building an SENode from the result of
349   // GetFirstTripInductionNode + (GetTripCount - 1) * induction_coefficient.
350   // If the operation can not be completed a nullptr is returned.
351   SENode* GetFinalTripInductionNode(const Loop* loop,
352                                     SENode* induction_coefficient);
353 
354   // Returns all the distinct loops that appear in |nodes|.
355   std::set<const Loop*> CollectLoops(
356       const std::vector<SERecurrentNode*>& nodes);
357 
358   // Returns all the distinct loops that appear in |source| and |destination|.
359   std::set<const Loop*> CollectLoops(SENode* source, SENode* destination);
360 
361   // Returns true if |distance| is provably outside the loop bounds.
362   // |coefficient| must be an SENode representing the coefficient of the
363   // induction variable of |loop|.
364   // This method is able to handle some symbolic cases which IsWithinBounds
365   // can't handle.
366   bool IsProvablyOutsideOfLoopBounds(const Loop* loop, SENode* distance,
367                                      SENode* coefficient);
368 
369   // Sets the ostream for debug information for the analysis.
SetDebugStream(std::ostream & debug_stream)370   void SetDebugStream(std::ostream& debug_stream) {
371     debug_stream_ = &debug_stream;
372   }
373 
374   // Clears the stored ostream to stop debug information printing.
ClearDebugStream()375   void ClearDebugStream() { debug_stream_ = nullptr; }
376 
377   // Returns the ScalarEvolutionAnalysis used by this analysis.
GetScalarEvolution()378   ScalarEvolutionAnalysis* GetScalarEvolution() { return &scalar_evolution_; }
379 
380   // Creates a new constraint of type |T| and returns the pointer to it.
381   template <typename T, typename... Args>
make_constraint(Args &&...args)382   Constraint* make_constraint(Args&&... args) {
383     constraints_.push_back(
384         std::unique_ptr<Constraint>(new T(std::forward<Args>(args)...)));
385 
386     return constraints_.back().get();
387   }
388 
389   // Subscript partitioning as described in Figure 1 of 'Practical Dependence
390   // Testing' by Gina Goff, Ken Kennedy, and Chau-Wen Tseng from PLDI '91.
391   // Partitions the subscripts into independent subscripts and minimally coupled
392   // sets of subscripts.
393   // Returns the partitioning of subscript pairs. Sets of size 1 indicates an
394   // independent subscript-pair and others indicate coupled sets.
395   using PartitionedSubscripts =
396       std::vector<std::set<std::pair<Instruction*, Instruction*>>>;
397   PartitionedSubscripts PartitionSubscripts(
398       const std::vector<Instruction*>& source_subscripts,
399       const std::vector<Instruction*>& destination_subscripts);
400 
401   // Returns the Loop* matching the loop for |subscript_pair|.
402   // |subscript_pair| must be an SIV pair.
403   const Loop* GetLoopForSubscriptPair(
404       const std::pair<SENode*, SENode*>& subscript_pair);
405 
406   // Returns the DistanceEntry matching the loop for |subscript_pair|.
407   // |subscript_pair| must be an SIV pair.
408   DistanceEntry* GetDistanceEntryForSubscriptPair(
409       const std::pair<SENode*, SENode*>& subscript_pair,
410       DistanceVector* distance_vector);
411 
412   // Returns the DistanceEntry matching |loop|.
413   DistanceEntry* GetDistanceEntryForLoop(const Loop* loop,
414                                          DistanceVector* distance_vector);
415 
416   // Returns a vector of Instruction* which form the subscripts of the array
417   // access defined by the access chain |instruction|.
418   std::vector<Instruction*> GetSubscripts(const Instruction* instruction);
419 
420   // Delta test as described in Figure 3 of 'Practical Dependence
421   // Testing' by Gina Goff, Ken Kennedy, and Chau-Wen Tseng from PLDI '91.
422   bool DeltaTest(
423       const std::vector<std::pair<SENode*, SENode*>>& coupled_subscripts,
424       DistanceVector* dv_entry);
425 
426   // Constraint propagation as described in Figure 5 of 'Practical Dependence
427   // Testing' by Gina Goff, Ken Kennedy, and Chau-Wen Tseng from PLDI '91.
428   std::pair<SENode*, SENode*> PropagateConstraints(
429       const std::pair<SENode*, SENode*>& subscript_pair,
430       const std::vector<Constraint*>& constraints);
431 
432   // Constraint intersection as described in Figure 4 of 'Practical Dependence
433   // Testing' by Gina Goff, Ken Kennedy, and Chau-Wen Tseng from PLDI '91.
434   Constraint* IntersectConstraints(Constraint* constraint_0,
435                                    Constraint* constraint_1,
436                                    const SENode* lower_bound,
437                                    const SENode* upper_bound);
438 
439   // Returns true if each loop in |loops| is in a form supported by this
440   // analysis.
441   // A loop is supported if it has a single induction variable and that
442   // induction variable has a step of +1 or -1 per loop iteration.
443   bool CheckSupportedLoops(std::vector<const Loop*> loops);
444 
445   // Returns true if |loop| is in a form supported by this analysis.
446   // A loop is supported if it has a single induction variable and that
447   // induction variable has a step of +1 or -1 per loop iteration.
448   bool IsSupportedLoop(const Loop* loop);
449 
450  private:
451   IRContext* context_;
452 
453   // The loop nest we are analysing the dependence of.
454   std::vector<const Loop*> loops_;
455 
456   // The ScalarEvolutionAnalysis used by this analysis to store and perform much
457   // of its logic.
458   ScalarEvolutionAnalysis scalar_evolution_;
459 
460   // The ostream debug information for the analysis to print to.
461   std::ostream* debug_stream_;
462 
463   // Stores all the constraints created by the analysis.
464   std::list<std::unique_ptr<Constraint>> constraints_;
465 
466   // Returns true if independence can be proven and false if it can't be proven.
467   bool ZIVTest(const std::pair<SENode*, SENode*>& subscript_pair);
468 
469   // Analyzes the subscript pair to find an applicable SIV test.
470   // Returns true if independence can be proven and false if it can't be proven.
471   bool SIVTest(const std::pair<SENode*, SENode*>& subscript_pair,
472                DistanceVector* distance_vector);
473 
474   // Takes the form a*i + c1, a*i + c2
475   // When c1 and c2 are loop invariant and a is constant
476   // distance = (c1 - c2)/a
477   //              < if distance > 0
478   // direction =  = if distance = 0
479   //              > if distance < 0
480   // Returns true if independence is proven and false if it can't be proven.
481   bool StrongSIVTest(SENode* source, SENode* destination, SENode* coeff,
482                      DistanceEntry* distance_entry);
483 
484   // Takes for form a*i + c1, a*i + c2
485   // where c1 and c2 are loop invariant and a is constant.
486   // c1 and/or c2 contain one or more SEValueUnknown nodes.
487   bool SymbolicStrongSIVTest(SENode* source, SENode* destination,
488                              SENode* coefficient,
489                              DistanceEntry* distance_entry);
490 
491   // Takes the form a1*i + c1, a2*i + c2
492   // where a1 = 0
493   // distance = (c1 - c2) / a2
494   // Returns true if independence is proven and false if it can't be proven.
495   bool WeakZeroSourceSIVTest(SENode* source, SERecurrentNode* destination,
496                              SENode* coefficient,
497                              DistanceEntry* distance_entry);
498 
499   // Takes the form a1*i + c1, a2*i + c2
500   // where a2 = 0
501   // distance = (c2 - c1) / a1
502   // Returns true if independence is proven and false if it can't be proven.
503   bool WeakZeroDestinationSIVTest(SERecurrentNode* source, SENode* destination,
504                                   SENode* coefficient,
505                                   DistanceEntry* distance_entry);
506 
507   // Takes the form a1*i + c1, a2*i + c2
508   // where a1 = -a2
509   // distance = (c2 - c1) / 2*a1
510   // Returns true if independence is proven and false if it can't be proven.
511   bool WeakCrossingSIVTest(SENode* source, SENode* destination,
512                            SENode* coefficient, DistanceEntry* distance_entry);
513 
514   // Uses the def_use_mgr to get the instruction referenced by
515   // SingleWordInOperand(|id|) when called on |instruction|.
516   Instruction* GetOperandDefinition(const Instruction* instruction, int id);
517 
518   // Perform the GCD test if both, the source and the destination nodes, are in
519   // the form a0*i0 + a1*i1 + ... an*in + c.
520   bool GCDMIVTest(const std::pair<SENode*, SENode*>& subscript_pair);
521 
522   // Finds the number of induction variables in |node|.
523   // Returns -1 on failure.
524   int64_t CountInductionVariables(SENode* node);
525 
526   // Finds the number of induction variables shared between |source| and
527   // |destination|.
528   // Returns -1 on failure.
529   int64_t CountInductionVariables(SENode* source, SENode* destination);
530 
531   // Takes the offset from the induction variable and subtracts the lower bound
532   // from it to get the constant term added to the induction.
533   // Returns the resuting constant term, or nullptr if it could not be produced.
534   SENode* GetConstantTerm(const Loop* loop, SERecurrentNode* induction);
535 
536   // Marks all the distance entries in |distance_vector| that were relate to
537   // loops in |loops_| but were not used in any subscripts as irrelevant to the
538   // to the dependence test.
539   void MarkUnsusedDistanceEntriesAsIrrelevant(const Instruction* source,
540                                               const Instruction* destination,
541                                               DistanceVector* distance_vector);
542 
543   // Converts |value| to a std::string and returns the result.
544   // This is required because Android does not compile std::to_string.
545   template <typename valueT>
ToString(valueT value)546   std::string ToString(valueT value) {
547     std::ostringstream string_stream;
548     string_stream << value;
549     return string_stream.str();
550   }
551 
552   // Prints |debug_msg| and "\n" to the ostream pointed to by |debug_stream_|.
553   // Won't print anything if |debug_stream_| is nullptr.
554   void PrintDebug(std::string debug_msg);
555 };
556 
557 }  // namespace opt
558 }  // namespace spvtools
559 
560 #endif  // SOURCE_OPT_LOOP_DEPENDENCE_H_
561