1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
|
// 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_
|