| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560 |
- // Copyright (c) 2018 Google LLC.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- #ifndef SOURCE_OPT_LOOP_DEPENDENCE_H_
- #define SOURCE_OPT_LOOP_DEPENDENCE_H_
- #include <algorithm>
- #include <cstdint>
- #include <list>
- #include <map>
- #include <memory>
- #include <ostream>
- #include <set>
- #include <string>
- #include <utility>
- #include <vector>
- #include "source/opt/instruction.h"
- #include "source/opt/ir_context.h"
- #include "source/opt/loop_descriptor.h"
- #include "source/opt/scalar_analysis.h"
- namespace spvtools {
- namespace opt {
- // Stores information about dependence between a load and a store wrt a single
- // loop in a loop nest.
- // DependenceInformation
- // * UNKNOWN if no dependence information can be gathered or is gathered
- // for it.
- // * DIRECTION if a dependence direction could be found, but not a
- // distance.
- // * DISTANCE if a dependence distance could be found.
- // * PEEL if peeling either the first or last iteration will break
- // dependence between the given load and store.
- // * IRRELEVANT if it has no effect on the dependence between the given
- // load and store.
- //
- // If peel_first == true, the analysis has found that peeling the first
- // iteration of this loop will break dependence.
- //
- // If peel_last == true, the analysis has found that peeling the last iteration
- // of this loop will break dependence.
- class DistanceEntry {
- public:
- enum DependenceInformation {
- UNKNOWN = 0,
- DIRECTION = 1,
- DISTANCE = 2,
- PEEL = 3,
- IRRELEVANT = 4,
- POINT = 5
- };
- enum Directions {
- NONE = 0,
- LT = 1,
- EQ = 2,
- LE = 3,
- GT = 4,
- NE = 5,
- GE = 6,
- ALL = 7
- };
- DependenceInformation dependence_information;
- Directions direction;
- int64_t distance;
- bool peel_first;
- bool peel_last;
- int64_t point_x;
- int64_t point_y;
- DistanceEntry()
- : dependence_information(DependenceInformation::UNKNOWN),
- direction(Directions::ALL),
- distance(0),
- peel_first(false),
- peel_last(false),
- point_x(0),
- point_y(0) {}
- explicit DistanceEntry(Directions direction_)
- : dependence_information(DependenceInformation::DIRECTION),
- direction(direction_),
- distance(0),
- peel_first(false),
- peel_last(false),
- point_x(0),
- point_y(0) {}
- DistanceEntry(Directions direction_, int64_t distance_)
- : dependence_information(DependenceInformation::DISTANCE),
- direction(direction_),
- distance(distance_),
- peel_first(false),
- peel_last(false),
- point_x(0),
- point_y(0) {}
- DistanceEntry(int64_t x, int64_t y)
- : dependence_information(DependenceInformation::POINT),
- direction(Directions::ALL),
- distance(0),
- peel_first(false),
- peel_last(false),
- point_x(x),
- point_y(y) {}
- bool operator==(const DistanceEntry& rhs) const {
- return direction == rhs.direction && peel_first == rhs.peel_first &&
- peel_last == rhs.peel_last && distance == rhs.distance &&
- point_x == rhs.point_x && point_y == rhs.point_y;
- }
- bool operator!=(const DistanceEntry& rhs) const { return !(*this == rhs); }
- };
- // Stores a vector of DistanceEntrys, one per loop in the analysis.
- // A DistanceVector holds all of the information gathered in a dependence
- // analysis wrt the loops stored in the LoopDependenceAnalysis performing the
- // analysis.
- class DistanceVector {
- public:
- explicit DistanceVector(size_t size) : entries(size, DistanceEntry{}) {}
- explicit DistanceVector(std::vector<DistanceEntry> entries_)
- : entries(entries_) {}
- DistanceEntry& GetEntry(size_t index) { return entries[index]; }
- const DistanceEntry& GetEntry(size_t index) const { return entries[index]; }
- std::vector<DistanceEntry>& GetEntries() { return entries; }
- const std::vector<DistanceEntry>& GetEntries() const { return entries; }
- bool operator==(const DistanceVector& rhs) const {
- if (entries.size() != rhs.entries.size()) {
- return false;
- }
- for (size_t i = 0; i < entries.size(); ++i) {
- if (entries[i] != rhs.entries[i]) {
- return false;
- }
- }
- return true;
- }
- bool operator!=(const DistanceVector& rhs) const { return !(*this == rhs); }
- private:
- std::vector<DistanceEntry> entries;
- };
- class DependenceLine;
- class DependenceDistance;
- class DependencePoint;
- class DependenceNone;
- class DependenceEmpty;
- class Constraint {
- public:
- explicit Constraint(const Loop* loop) : loop_(loop) {}
- enum ConstraintType { Line, Distance, Point, None, Empty };
- virtual ConstraintType GetType() const = 0;
- virtual ~Constraint() {}
- // Get the loop this constraint belongs to.
- const Loop* GetLoop() const { return loop_; }
- bool operator==(const Constraint& other) const;
- bool operator!=(const Constraint& other) const;
- // clang-format off
- #define DeclareCastMethod(target) \
- virtual target* As##target() { return nullptr; } \
- virtual const target* As##target() const { return nullptr; }
- DeclareCastMethod(DependenceLine)
- DeclareCastMethod(DependenceDistance)
- DeclareCastMethod(DependencePoint)
- DeclareCastMethod(DependenceNone)
- DeclareCastMethod(DependenceEmpty)
- #undef DeclareCastMethod
- protected:
- const Loop* loop_;
- };
- // clang-format on
- class DependenceLine : public Constraint {
- public:
- DependenceLine(SENode* a, SENode* b, SENode* c, const Loop* loop)
- : Constraint(loop), a_(a), b_(b), c_(c) {}
- ConstraintType GetType() const final { return Line; }
- DependenceLine* AsDependenceLine() final { return this; }
- const DependenceLine* AsDependenceLine() const final { return this; }
- SENode* GetA() const { return a_; }
- SENode* GetB() const { return b_; }
- SENode* GetC() const { return c_; }
- private:
- SENode* a_;
- SENode* b_;
- SENode* c_;
- };
- class DependenceDistance : public Constraint {
- public:
- DependenceDistance(SENode* distance, const Loop* loop)
- : Constraint(loop), distance_(distance) {}
- ConstraintType GetType() const final { return Distance; }
- DependenceDistance* AsDependenceDistance() final { return this; }
- const DependenceDistance* AsDependenceDistance() const final { return this; }
- SENode* GetDistance() const { return distance_; }
- private:
- SENode* distance_;
- };
- class DependencePoint : public Constraint {
- public:
- DependencePoint(SENode* source, SENode* destination, const Loop* loop)
- : Constraint(loop), source_(source), destination_(destination) {}
- ConstraintType GetType() const final { return Point; }
- DependencePoint* AsDependencePoint() final { return this; }
- const DependencePoint* AsDependencePoint() const final { return this; }
- SENode* GetSource() const { return source_; }
- SENode* GetDestination() const { return destination_; }
- private:
- SENode* source_;
- SENode* destination_;
- };
- class DependenceNone : public Constraint {
- public:
- DependenceNone() : Constraint(nullptr) {}
- ConstraintType GetType() const final { return None; }
- DependenceNone* AsDependenceNone() final { return this; }
- const DependenceNone* AsDependenceNone() const final { return this; }
- };
- class DependenceEmpty : public Constraint {
- public:
- DependenceEmpty() : Constraint(nullptr) {}
- ConstraintType GetType() const final { return Empty; }
- DependenceEmpty* AsDependenceEmpty() final { return this; }
- const DependenceEmpty* AsDependenceEmpty() const final { return this; }
- };
- // Provides dependence information between a store instruction and a load
- // instruction inside the same loop in a loop nest.
- //
- // The analysis can only check dependence between stores and loads with regard
- // to the loop nest it is created with.
- //
- // The analysis can output debugging information to a stream. The output
- // describes the control flow of the analysis and what information it can deduce
- // at each step.
- // SetDebugStream and ClearDebugStream are provided for this functionality.
- //
- // The dependency algorithm is based on the 1990 Paper
- // Practical Dependence Testing
- // Gina Goff, Ken Kennedy, Chau-Wen Tseng
- //
- // The algorithm first identifies subscript pairs between the load and store.
- // Each pair is tested until all have been tested or independence is found.
- // The number of induction variables in a pair determines which test to perform
- // on it;
- // Zero Index Variable (ZIV) is used when no induction variables are present
- // in the pair.
- // Single Index Variable (SIV) is used when only one induction variable is
- // present, but may occur multiple times in the pair.
- // Multiple Index Variable (MIV) is used when more than one induction variable
- // is present in the pair.
- class LoopDependenceAnalysis {
- public:
- LoopDependenceAnalysis(IRContext* context, std::vector<const Loop*> loops)
- : context_(context),
- loops_(loops),
- scalar_evolution_(context),
- debug_stream_(nullptr),
- constraints_{} {}
- // Finds the dependence between |source| and |destination|.
- // |source| should be an OpLoad.
- // |destination| should be an OpStore.
- // Any direction and distance information found will be stored in
- // |distance_vector|.
- // Returns true if independence is found, false otherwise.
- bool GetDependence(const Instruction* source, const Instruction* destination,
- DistanceVector* distance_vector);
- // Returns true if |subscript_pair| represents a Zero Index Variable pair
- // (ZIV)
- bool IsZIV(const std::pair<SENode*, SENode*>& subscript_pair);
- // Returns true if |subscript_pair| represents a Single Index Variable
- // (SIV) pair
- bool IsSIV(const std::pair<SENode*, SENode*>& subscript_pair);
- // Returns true if |subscript_pair| represents a Multiple Index Variable
- // (MIV) pair
- bool IsMIV(const std::pair<SENode*, SENode*>& subscript_pair);
- // Finds the lower bound of |loop| as an SENode* and returns the result.
- // The lower bound is the starting value of the loops induction variable
- SENode* GetLowerBound(const Loop* loop);
- // Finds the upper bound of |loop| as an SENode* and returns the result.
- // The upper bound is the last value before the loop exit condition is met.
- SENode* GetUpperBound(const Loop* loop);
- // Returns true if |value| is between |bound_one| and |bound_two| (inclusive).
- bool IsWithinBounds(int64_t value, int64_t bound_one, int64_t bound_two);
- // Finds the bounds of |loop| as upper_bound - lower_bound and returns the
- // resulting SENode.
- // If the operations can not be completed a nullptr is returned.
- SENode* GetTripCount(const Loop* loop);
- // Returns the SENode* produced by building an SENode from the result of
- // calling GetInductionInitValue on |loop|.
- // If the operation can not be completed a nullptr is returned.
- SENode* GetFirstTripInductionNode(const Loop* loop);
- // Returns the SENode* produced by building an SENode from the result of
- // GetFirstTripInductionNode + (GetTripCount - 1) * induction_coefficient.
- // If the operation can not be completed a nullptr is returned.
- SENode* GetFinalTripInductionNode(const Loop* loop,
- SENode* induction_coefficient);
- // Returns all the distinct loops that appear in |nodes|.
- std::set<const Loop*> CollectLoops(
- const std::vector<SERecurrentNode*>& nodes);
- // Returns all the distinct loops that appear in |source| and |destination|.
- std::set<const Loop*> CollectLoops(SENode* source, SENode* destination);
- // Returns true if |distance| is provably outside the loop bounds.
- // |coefficient| must be an SENode representing the coefficient of the
- // induction variable of |loop|.
- // This method is able to handle some symbolic cases which IsWithinBounds
- // can't handle.
- bool IsProvablyOutsideOfLoopBounds(const Loop* loop, SENode* distance,
- SENode* coefficient);
- // Sets the ostream for debug information for the analysis.
- void SetDebugStream(std::ostream& debug_stream) {
- debug_stream_ = &debug_stream;
- }
- // Clears the stored ostream to stop debug information printing.
- void ClearDebugStream() { debug_stream_ = nullptr; }
- // Returns the ScalarEvolutionAnalysis used by this analysis.
- ScalarEvolutionAnalysis* GetScalarEvolution() { return &scalar_evolution_; }
- // Creates a new constraint of type |T| and returns the pointer to it.
- template <typename T, typename... Args>
- Constraint* make_constraint(Args&&... args) {
- constraints_.push_back(
- std::unique_ptr<Constraint>(new T(std::forward<Args>(args)...)));
- return constraints_.back().get();
- }
- // Subscript partitioning as described in Figure 1 of 'Practical Dependence
- // Testing' by Gina Goff, Ken Kennedy, and Chau-Wen Tseng from PLDI '91.
- // Partitions the subscripts into independent subscripts and minimally coupled
- // sets of subscripts.
- // Returns the partitioning of subscript pairs. Sets of size 1 indicates an
- // independent subscript-pair and others indicate coupled sets.
- using PartitionedSubscripts =
- std::vector<std::set<std::pair<Instruction*, Instruction*>>>;
- PartitionedSubscripts PartitionSubscripts(
- const std::vector<Instruction*>& source_subscripts,
- const std::vector<Instruction*>& destination_subscripts);
- // Returns the Loop* matching the loop for |subscript_pair|.
- // |subscript_pair| must be an SIV pair.
- const Loop* GetLoopForSubscriptPair(
- const std::pair<SENode*, SENode*>& subscript_pair);
- // Returns the DistanceEntry matching the loop for |subscript_pair|.
- // |subscript_pair| must be an SIV pair.
- DistanceEntry* GetDistanceEntryForSubscriptPair(
- const std::pair<SENode*, SENode*>& subscript_pair,
- DistanceVector* distance_vector);
- // Returns the DistanceEntry matching |loop|.
- DistanceEntry* GetDistanceEntryForLoop(const Loop* loop,
- DistanceVector* distance_vector);
- // Returns a vector of Instruction* which form the subscripts of the array
- // access defined by the access chain |instruction|.
- std::vector<Instruction*> GetSubscripts(const Instruction* instruction);
- // Delta test as described in Figure 3 of 'Practical Dependence
- // Testing' by Gina Goff, Ken Kennedy, and Chau-Wen Tseng from PLDI '91.
- bool DeltaTest(
- const std::vector<std::pair<SENode*, SENode*>>& coupled_subscripts,
- DistanceVector* dv_entry);
- // Constraint propagation as described in Figure 5 of 'Practical Dependence
- // Testing' by Gina Goff, Ken Kennedy, and Chau-Wen Tseng from PLDI '91.
- std::pair<SENode*, SENode*> PropagateConstraints(
- const std::pair<SENode*, SENode*>& subscript_pair,
- const std::vector<Constraint*>& constraints);
- // Constraint intersection as described in Figure 4 of 'Practical Dependence
- // Testing' by Gina Goff, Ken Kennedy, and Chau-Wen Tseng from PLDI '91.
- Constraint* IntersectConstraints(Constraint* constraint_0,
- Constraint* constraint_1,
- const SENode* lower_bound,
- const SENode* upper_bound);
- // Returns true if each loop in |loops| is in a form supported by this
- // analysis.
- // A loop is supported if it has a single induction variable and that
- // induction variable has a step of +1 or -1 per loop iteration.
- bool CheckSupportedLoops(std::vector<const Loop*> loops);
- // Returns true if |loop| is in a form supported by this analysis.
- // A loop is supported if it has a single induction variable and that
- // induction variable has a step of +1 or -1 per loop iteration.
- bool IsSupportedLoop(const Loop* loop);
- private:
- IRContext* context_;
- // The loop nest we are analysing the dependence of.
- std::vector<const Loop*> loops_;
- // The ScalarEvolutionAnalysis used by this analysis to store and perform much
- // of its logic.
- ScalarEvolutionAnalysis scalar_evolution_;
- // The ostream debug information for the analysis to print to.
- std::ostream* debug_stream_;
- // Stores all the constraints created by the analysis.
- std::list<std::unique_ptr<Constraint>> constraints_;
- // Returns true if independence can be proven and false if it can't be proven.
- bool ZIVTest(const std::pair<SENode*, SENode*>& subscript_pair);
- // Analyzes the subscript pair to find an applicable SIV test.
- // Returns true if independence can be proven and false if it can't be proven.
- bool SIVTest(const std::pair<SENode*, SENode*>& subscript_pair,
- DistanceVector* distance_vector);
- // Takes the form a*i + c1, a*i + c2
- // When c1 and c2 are loop invariant and a is constant
- // distance = (c1 - c2)/a
- // < if distance > 0
- // direction = = if distance = 0
- // > if distance < 0
- // Returns true if independence is proven and false if it can't be proven.
- bool StrongSIVTest(SENode* source, SENode* destination, SENode* coeff,
- DistanceEntry* distance_entry);
- // Takes for form a*i + c1, a*i + c2
- // where c1 and c2 are loop invariant and a is constant.
- // c1 and/or c2 contain one or more SEValueUnknown nodes.
- bool SymbolicStrongSIVTest(SENode* source, SENode* destination,
- SENode* coefficient,
- DistanceEntry* distance_entry);
- // Takes the form a1*i + c1, a2*i + c2
- // where a1 = 0
- // distance = (c1 - c2) / a2
- // Returns true if independence is proven and false if it can't be proven.
- bool WeakZeroSourceSIVTest(SENode* source, SERecurrentNode* destination,
- SENode* coefficient,
- DistanceEntry* distance_entry);
- // Takes the form a1*i + c1, a2*i + c2
- // where a2 = 0
- // distance = (c2 - c1) / a1
- // Returns true if independence is proven and false if it can't be proven.
- bool WeakZeroDestinationSIVTest(SERecurrentNode* source, SENode* destination,
- SENode* coefficient,
- DistanceEntry* distance_entry);
- // Takes the form a1*i + c1, a2*i + c2
- // where a1 = -a2
- // distance = (c2 - c1) / 2*a1
- // Returns true if independence is proven and false if it can't be proven.
- bool WeakCrossingSIVTest(SENode* source, SENode* destination,
- SENode* coefficient, DistanceEntry* distance_entry);
- // Uses the def_use_mgr to get the instruction referenced by
- // SingleWordInOperand(|id|) when called on |instruction|.
- Instruction* GetOperandDefinition(const Instruction* instruction, int id);
- // Perform the GCD test if both, the source and the destination nodes, are in
- // the form a0*i0 + a1*i1 + ... an*in + c.
- bool GCDMIVTest(const std::pair<SENode*, SENode*>& subscript_pair);
- // Finds the number of induction variables in |node|.
- // Returns -1 on failure.
- int64_t CountInductionVariables(SENode* node);
- // Finds the number of induction variables shared between |source| and
- // |destination|.
- // Returns -1 on failure.
- int64_t CountInductionVariables(SENode* source, SENode* destination);
- // Takes the offset from the induction variable and subtracts the lower bound
- // from it to get the constant term added to the induction.
- // Returns the resuting constant term, or nullptr if it could not be produced.
- SENode* GetConstantTerm(const Loop* loop, SERecurrentNode* induction);
- // Marks all the distance entries in |distance_vector| that were relate to
- // loops in |loops_| but were not used in any subscripts as irrelevant to the
- // to the dependence test.
- void MarkUnsusedDistanceEntriesAsIrrelevant(const Instruction* source,
- const Instruction* destination,
- DistanceVector* distance_vector);
- // Converts |value| to a std::string and returns the result.
- // This is required because Android does not compile std::to_string.
- template <typename valueT>
- std::string ToString(valueT value) {
- std::ostringstream string_stream;
- string_stream << value;
- return string_stream.str();
- }
- // Prints |debug_msg| and "\n" to the ostream pointed to by |debug_stream_|.
- // Won't print anything if |debug_stream_| is nullptr.
- void PrintDebug(std::string debug_msg);
- };
- } // namespace opt
- } // namespace spvtools
- #endif // SOURCE_OPT_LOOP_DEPENDENCE_H_
|