Coverage Report

Created: 2026-05-16 06:52

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