Home | History | Annotate | Download | only in opt
      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 
     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 
     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 
    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 
    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:
    134   explicit DistanceVector(size_t size) : entries(size, DistanceEntry{}) {}
    135 
    136   explicit DistanceVector(std::vector<DistanceEntry> entries_)
    137       : entries(entries_) {}
    138 
    139   DistanceEntry& GetEntry(size_t index) { return entries[index]; }
    140   const DistanceEntry& GetEntry(size_t index) const { return entries[index]; }
    141 
    142   std::vector<DistanceEntry>& GetEntries() { return entries; }
    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:
    170   explicit Constraint(const Loop* loop) : loop_(loop) {}
    171   enum ConstraintType { Line, Distance, Point, None, Empty };
    172 
    173   virtual ConstraintType GetType() const = 0;
    174 
    175   virtual ~Constraint() {}
    176 
    177   // Get the loop this constraint belongs to.
    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:
    200   DependenceLine(SENode* a, SENode* b, SENode* c, const Loop* loop)
    201       : Constraint(loop), a_(a), b_(b), c_(c) {}
    202 
    203   ConstraintType GetType() const final { return Line; }
    204 
    205   DependenceLine* AsDependenceLine() final { return this; }
    206   const DependenceLine* AsDependenceLine() const final { return this; }
    207 
    208   SENode* GetA() const { return a_; }
    209   SENode* GetB() const { return b_; }
    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:
    220   DependenceDistance(SENode* distance, const Loop* loop)
    221       : Constraint(loop), distance_(distance) {}
    222 
    223   ConstraintType GetType() const final { return Distance; }
    224 
    225   DependenceDistance* AsDependenceDistance() final { return this; }
    226   const DependenceDistance* AsDependenceDistance() const final { return this; }
    227 
    228   SENode* GetDistance() const { return distance_; }
    229 
    230  private:
    231   SENode* distance_;
    232 };
    233 
    234 class DependencePoint : public Constraint {
    235  public:
    236   DependencePoint(SENode* source, SENode* destination, const Loop* loop)
    237       : Constraint(loop), source_(source), destination_(destination) {}
    238 
    239   ConstraintType GetType() const final { return Point; }
    240 
    241   DependencePoint* AsDependencePoint() final { return this; }
    242   const DependencePoint* AsDependencePoint() const final { return this; }
    243 
    244   SENode* GetSource() const { return source_; }
    245   SENode* GetDestination() const { return destination_; }
    246 
    247  private:
    248   SENode* source_;
    249   SENode* destination_;
    250 };
    251 
    252 class DependenceNone : public Constraint {
    253  public:
    254   DependenceNone() : Constraint(nullptr) {}
    255   ConstraintType GetType() const final { return None; }
    256 
    257   DependenceNone* AsDependenceNone() final { return this; }
    258   const DependenceNone* AsDependenceNone() const final { return this; }
    259 };
    260 
    261 class DependenceEmpty : public Constraint {
    262  public:
    263   DependenceEmpty() : Constraint(nullptr) {}
    264   ConstraintType GetType() const final { return Empty; }
    265 
    266   DependenceEmpty* AsDependenceEmpty() final { return this; }
    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:
    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.
    368   void SetDebugStream(std::ostream& debug_stream) {
    369     debug_stream_ = &debug_stream;
    370   }
    371 
    372   // Clears the stored ostream to stop debug information printing.
    373   void ClearDebugStream() { debug_stream_ = nullptr; }
    374 
    375   // Returns the ScalarEvolutionAnalysis used by this analysis.
    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>
    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>
    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