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 #define DeclareCastMethod(target)                  \
185   virtual target* As##target() { return nullptr; } \
186   virtual const target* As##target() const { return nullptr; }
187   DeclareCastMethod(DependenceLine);
188   DeclareCastMethod(DependenceDistance);
189   DeclareCastMethod(DependencePoint);
190   DeclareCastMethod(DependenceNone);
191   DeclareCastMethod(DependenceEmpty);
192 #undef DeclareCastMethod
193 
194  protected:
195   const Loop* loop_;
196 };
197 
198 class DependenceLine : public Constraint {
199  public:
DependenceLine(SENode * a,SENode * b,SENode * c,const Loop * loop)200   DependenceLine(SENode* a, SENode* b, SENode* c, const Loop* loop)
201       : Constraint(loop), a_(a), b_(b), c_(c) {}
202 
GetType()203   ConstraintType GetType() const final { return Line; }
204 
AsDependenceLine()205   DependenceLine* AsDependenceLine() final { return this; }
AsDependenceLine()206   const DependenceLine* AsDependenceLine() const final { return this; }
207 
GetA()208   SENode* GetA() const { return a_; }
GetB()209   SENode* GetB() const { return b_; }
GetC()210   SENode* GetC() const { return c_; }
211 
212  private:
213   SENode* a_;
214   SENode* b_;
215   SENode* c_;
216 };
217 
218 class DependenceDistance : public Constraint {
219  public:
DependenceDistance(SENode * distance,const Loop * loop)220   DependenceDistance(SENode* distance, const Loop* loop)
221       : Constraint(loop), distance_(distance) {}
222 
GetType()223   ConstraintType GetType() const final { return Distance; }
224 
AsDependenceDistance()225   DependenceDistance* AsDependenceDistance() final { return this; }
AsDependenceDistance()226   const DependenceDistance* AsDependenceDistance() const final { return this; }
227 
GetDistance()228   SENode* GetDistance() const { return distance_; }
229 
230  private:
231   SENode* distance_;
232 };
233 
234 class DependencePoint : public Constraint {
235  public:
DependencePoint(SENode * source,SENode * destination,const Loop * loop)236   DependencePoint(SENode* source, SENode* destination, const Loop* loop)
237       : Constraint(loop), source_(source), destination_(destination) {}
238 
GetType()239   ConstraintType GetType() const final { return Point; }
240 
AsDependencePoint()241   DependencePoint* AsDependencePoint() final { return this; }
AsDependencePoint()242   const DependencePoint* AsDependencePoint() const final { return this; }
243 
GetSource()244   SENode* GetSource() const { return source_; }
GetDestination()245   SENode* GetDestination() const { return destination_; }
246 
247  private:
248   SENode* source_;
249   SENode* destination_;
250 };
251 
252 class DependenceNone : public Constraint {
253  public:
DependenceNone()254   DependenceNone() : Constraint(nullptr) {}
GetType()255   ConstraintType GetType() const final { return None; }
256 
AsDependenceNone()257   DependenceNone* AsDependenceNone() final { return this; }
AsDependenceNone()258   const DependenceNone* AsDependenceNone() const final { return this; }
259 };
260 
261 class DependenceEmpty : public Constraint {
262  public:
DependenceEmpty()263   DependenceEmpty() : Constraint(nullptr) {}
GetType()264   ConstraintType GetType() const final { return Empty; }
265 
AsDependenceEmpty()266   DependenceEmpty* AsDependenceEmpty() final { return this; }
AsDependenceEmpty()267   const DependenceEmpty* AsDependenceEmpty() const final { return this; }
268 };
269 
270 // Provides dependence information between a store instruction and a load
271 // instruction inside the same loop in a loop nest.
272 //
273 // The analysis can only check dependence between stores and loads with regard
274 // to the loop nest it is created with.
275 //
276 // The analysis can output debugging information to a stream. The output
277 // describes the control flow of the analysis and what information it can deduce
278 // at each step.
279 // SetDebugStream and ClearDebugStream are provided for this functionality.
280 //
281 // The dependency algorithm is based on the 1990 Paper
282 //   Practical Dependence Testing
283 //   Gina Goff, Ken Kennedy, Chau-Wen Tseng
284 //
285 // The algorithm first identifies subscript pairs between the load and store.
286 // Each pair is tested until all have been tested or independence is found.
287 // The number of induction variables in a pair determines which test to perform
288 // on it;
289 // Zero Index Variable (ZIV) is used when no induction variables are present
290 // in the pair.
291 // Single Index Variable (SIV) is used when only one induction variable is
292 // present, but may occur multiple times in the pair.
293 // Multiple Index Variable (MIV) is used when more than one induction variable
294 // is present in the pair.
295 class LoopDependenceAnalysis {
296  public:
LoopDependenceAnalysis(IRContext * context,std::vector<const Loop * > loops)297   LoopDependenceAnalysis(IRContext* context, std::vector<const Loop*> loops)
298       : context_(context),
299         loops_(loops),
300         scalar_evolution_(context),
301         debug_stream_(nullptr),
302         constraints_{} {}
303 
304   // Finds the dependence between |source| and |destination|.
305   // |source| should be an OpLoad.
306   // |destination| should be an OpStore.
307   // Any direction and distance information found will be stored in
308   // |distance_vector|.
309   // Returns true if independence is found, false otherwise.
310   bool GetDependence(const Instruction* source, const Instruction* destination,
311                      DistanceVector* distance_vector);
312 
313   // Returns true if |subscript_pair| represents a Zero Index Variable pair
314   // (ZIV)
315   bool IsZIV(const std::pair<SENode*, SENode*>& subscript_pair);
316 
317   // Returns true if |subscript_pair| represents a Single Index Variable
318   // (SIV) pair
319   bool IsSIV(const std::pair<SENode*, SENode*>& subscript_pair);
320 
321   // Returns true if |subscript_pair| represents a Multiple Index Variable
322   // (MIV) pair
323   bool IsMIV(const std::pair<SENode*, SENode*>& subscript_pair);
324 
325   // Finds the lower bound of |loop| as an SENode* and returns the result.
326   // The lower bound is the starting value of the loops induction variable
327   SENode* GetLowerBound(const Loop* loop);
328 
329   // Finds the upper bound of |loop| as an SENode* and returns the result.
330   // The upper bound is the last value before the loop exit condition is met.
331   SENode* GetUpperBound(const Loop* loop);
332 
333   // Returns true if |value| is between |bound_one| and |bound_two| (inclusive).
334   bool IsWithinBounds(int64_t value, int64_t bound_one, int64_t bound_two);
335 
336   // Finds the bounds of |loop| as upper_bound - lower_bound and returns the
337   // resulting SENode.
338   // If the operations can not be completed a nullptr is returned.
339   SENode* GetTripCount(const Loop* loop);
340 
341   // Returns the SENode* produced by building an SENode from the result of
342   // calling GetInductionInitValue on |loop|.
343   // If the operation can not be completed a nullptr is returned.
344   SENode* GetFirstTripInductionNode(const Loop* loop);
345 
346   // Returns the SENode* produced by building an SENode from the result of
347   // GetFirstTripInductionNode + (GetTripCount - 1) * induction_coefficient.
348   // If the operation can not be completed a nullptr is returned.
349   SENode* GetFinalTripInductionNode(const Loop* loop,
350                                     SENode* induction_coefficient);
351 
352   // Returns all the distinct loops that appear in |nodes|.
353   std::set<const Loop*> CollectLoops(
354       const std::vector<SERecurrentNode*>& nodes);
355 
356   // Returns all the distinct loops that appear in |source| and |destination|.
357   std::set<const Loop*> CollectLoops(SENode* source, SENode* destination);
358 
359   // Returns true if |distance| is provably outside the loop bounds.
360   // |coefficient| must be an SENode representing the coefficient of the
361   // induction variable of |loop|.
362   // This method is able to handle some symbolic cases which IsWithinBounds
363   // can't handle.
364   bool IsProvablyOutsideOfLoopBounds(const Loop* loop, SENode* distance,
365                                      SENode* coefficient);
366 
367   // Sets the ostream for debug information for the analysis.
SetDebugStream(std::ostream & debug_stream)368   void SetDebugStream(std::ostream& debug_stream) {
369     debug_stream_ = &debug_stream;
370   }
371 
372   // Clears the stored ostream to stop debug information printing.
ClearDebugStream()373   void ClearDebugStream() { debug_stream_ = nullptr; }
374 
375   // Returns the ScalarEvolutionAnalysis used by this analysis.
GetScalarEvolution()376   ScalarEvolutionAnalysis* GetScalarEvolution() { return &scalar_evolution_; }
377 
378   // Creates a new constraint of type |T| and returns the pointer to it.
379   template <typename T, typename... Args>
make_constraint(Args &&...args)380   Constraint* make_constraint(Args&&... args) {
381     constraints_.push_back(
382         std::unique_ptr<Constraint>(new T(std::forward<Args>(args)...)));
383 
384     return constraints_.back().get();
385   }
386 
387   // Subscript partitioning as described in Figure 1 of 'Practical Dependence
388   // Testing' by Gina Goff, Ken Kennedy, and Chau-Wen Tseng from PLDI '91.
389   // Partitions the subscripts into independent subscripts and minimally coupled
390   // sets of subscripts.
391   // Returns the partitioning of subscript pairs. Sets of size 1 indicates an
392   // independent subscript-pair and others indicate coupled sets.
393   using PartitionedSubscripts =
394       std::vector<std::set<std::pair<Instruction*, Instruction*>>>;
395   PartitionedSubscripts PartitionSubscripts(
396       const std::vector<Instruction*>& source_subscripts,
397       const std::vector<Instruction*>& destination_subscripts);
398 
399   // Returns the Loop* matching the loop for |subscript_pair|.
400   // |subscript_pair| must be an SIV pair.
401   const Loop* GetLoopForSubscriptPair(
402       const std::pair<SENode*, SENode*>& subscript_pair);
403 
404   // Returns the DistanceEntry matching the loop for |subscript_pair|.
405   // |subscript_pair| must be an SIV pair.
406   DistanceEntry* GetDistanceEntryForSubscriptPair(
407       const std::pair<SENode*, SENode*>& subscript_pair,
408       DistanceVector* distance_vector);
409 
410   // Returns the DistanceEntry matching |loop|.
411   DistanceEntry* GetDistanceEntryForLoop(const Loop* loop,
412                                          DistanceVector* distance_vector);
413 
414   // Returns a vector of Instruction* which form the subscripts of the array
415   // access defined by the access chain |instruction|.
416   std::vector<Instruction*> GetSubscripts(const Instruction* instruction);
417 
418   // Delta test as described in Figure 3 of 'Practical Dependence
419   // Testing' by Gina Goff, Ken Kennedy, and Chau-Wen Tseng from PLDI '91.
420   bool DeltaTest(
421       const std::vector<std::pair<SENode*, SENode*>>& coupled_subscripts,
422       DistanceVector* dv_entry);
423 
424   // Constraint propagation as described in Figure 5 of 'Practical Dependence
425   // Testing' by Gina Goff, Ken Kennedy, and Chau-Wen Tseng from PLDI '91.
426   std::pair<SENode*, SENode*> PropagateConstraints(
427       const std::pair<SENode*, SENode*>& subscript_pair,
428       const std::vector<Constraint*>& constraints);
429 
430   // Constraint intersection as described in Figure 4 of 'Practical Dependence
431   // Testing' by Gina Goff, Ken Kennedy, and Chau-Wen Tseng from PLDI '91.
432   Constraint* IntersectConstraints(Constraint* constraint_0,
433                                    Constraint* constraint_1,
434                                    const SENode* lower_bound,
435                                    const SENode* upper_bound);
436 
437   // Returns true if each loop in |loops| is in a form supported by this
438   // analysis.
439   // A loop is supported if it has a single induction variable and that
440   // induction variable has a step of +1 or -1 per loop iteration.
441   bool CheckSupportedLoops(std::vector<const Loop*> loops);
442 
443   // Returns true if |loop| is in a form supported by this analysis.
444   // A loop is supported if it has a single induction variable and that
445   // induction variable has a step of +1 or -1 per loop iteration.
446   bool IsSupportedLoop(const Loop* loop);
447 
448  private:
449   IRContext* context_;
450 
451   // The loop nest we are analysing the dependence of.
452   std::vector<const Loop*> loops_;
453 
454   // The ScalarEvolutionAnalysis used by this analysis to store and perform much
455   // of its logic.
456   ScalarEvolutionAnalysis scalar_evolution_;
457 
458   // The ostream debug information for the analysis to print to.
459   std::ostream* debug_stream_;
460 
461   // Stores all the constraints created by the analysis.
462   std::list<std::unique_ptr<Constraint>> constraints_;
463 
464   // Returns true if independence can be proven and false if it can't be proven.
465   bool ZIVTest(const std::pair<SENode*, SENode*>& subscript_pair);
466 
467   // Analyzes the subscript pair to find an applicable SIV test.
468   // Returns true if independence can be proven and false if it can't be proven.
469   bool SIVTest(const std::pair<SENode*, SENode*>& subscript_pair,
470                DistanceVector* distance_vector);
471 
472   // Takes the form a*i + c1, a*i + c2
473   // When c1 and c2 are loop invariant and a is constant
474   // distance = (c1 - c2)/a
475   //              < if distance > 0
476   // direction =  = if distance = 0
477   //              > if distance < 0
478   // Returns true if independence is proven and false if it can't be proven.
479   bool StrongSIVTest(SENode* source, SENode* destination, SENode* coeff,
480                      DistanceEntry* distance_entry);
481 
482   // Takes for form a*i + c1, a*i + c2
483   // where c1 and c2 are loop invariant and a is constant.
484   // c1 and/or c2 contain one or more SEValueUnknown nodes.
485   bool SymbolicStrongSIVTest(SENode* source, SENode* destination,
486                              SENode* coefficient,
487                              DistanceEntry* distance_entry);
488 
489   // Takes the form a1*i + c1, a2*i + c2
490   // where a1 = 0
491   // distance = (c1 - c2) / a2
492   // Returns true if independence is proven and false if it can't be proven.
493   bool WeakZeroSourceSIVTest(SENode* source, SERecurrentNode* destination,
494                              SENode* coefficient,
495                              DistanceEntry* distance_entry);
496 
497   // Takes the form a1*i + c1, a2*i + c2
498   // where a2 = 0
499   // distance = (c2 - c1) / a1
500   // Returns true if independence is proven and false if it can't be proven.
501   bool WeakZeroDestinationSIVTest(SERecurrentNode* source, SENode* destination,
502                                   SENode* coefficient,
503                                   DistanceEntry* distance_entry);
504 
505   // Takes the form a1*i + c1, a2*i + c2
506   // where a1 = -a2
507   // distance = (c2 - c1) / 2*a1
508   // Returns true if independence is proven and false if it can't be proven.
509   bool WeakCrossingSIVTest(SENode* source, SENode* destination,
510                            SENode* coefficient, DistanceEntry* distance_entry);
511 
512   // Uses the def_use_mgr to get the instruction referenced by
513   // SingleWordInOperand(|id|) when called on |instruction|.
514   Instruction* GetOperandDefinition(const Instruction* instruction, int id);
515 
516   // Perform the GCD test if both, the source and the destination nodes, are in
517   // the form a0*i0 + a1*i1 + ... an*in + c.
518   bool GCDMIVTest(const std::pair<SENode*, SENode*>& subscript_pair);
519 
520   // Finds the number of induction variables in |node|.
521   // Returns -1 on failure.
522   int64_t CountInductionVariables(SENode* node);
523 
524   // Finds the number of induction variables shared between |source| and
525   // |destination|.
526   // Returns -1 on failure.
527   int64_t CountInductionVariables(SENode* source, SENode* destination);
528 
529   // Takes the offset from the induction variable and subtracts the lower bound
530   // from it to get the constant term added to the induction.
531   // Returns the resuting constant term, or nullptr if it could not be produced.
532   SENode* GetConstantTerm(const Loop* loop, SERecurrentNode* induction);
533 
534   // Marks all the distance entries in |distance_vector| that were relate to
535   // loops in |loops_| but were not used in any subscripts as irrelevant to the
536   // to the dependence test.
537   void MarkUnsusedDistanceEntriesAsIrrelevant(const Instruction* source,
538                                               const Instruction* destination,
539                                               DistanceVector* distance_vector);
540 
541   // Converts |value| to a std::string and returns the result.
542   // This is required because Android does not compile std::to_string.
543   template <typename valueT>
ToString(valueT value)544   std::string ToString(valueT value) {
545     std::ostringstream string_stream;
546     string_stream << value;
547     return string_stream.str();
548   }
549 
550   // Prints |debug_msg| and "\n" to the ostream pointed to by |debug_stream_|.
551   // Won't print anything if |debug_stream_| is nullptr.
552   void PrintDebug(std::string debug_msg);
553 };
554 
555 }  // namespace opt
556 }  // namespace spvtools
557 
558 #endif  // SOURCE_OPT_LOOP_DEPENDENCE_H_
559