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