Coverage Report

Created: 2026-01-16 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/spirv-tools/source/opt/loop_dependence_helpers.cpp
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
#include <ostream>
16
#include <set>
17
#include <string>
18
#include <unordered_set>
19
#include <utility>
20
#include <vector>
21
22
#include "source/opt/basic_block.h"
23
#include "source/opt/instruction.h"
24
#include "source/opt/loop_dependence.h"
25
#include "source/opt/scalar_analysis_nodes.h"
26
27
namespace spvtools {
28
namespace opt {
29
30
bool LoopDependenceAnalysis::IsZIV(
31
0
    const std::pair<SENode*, SENode*>& subscript_pair) {
32
0
  return CountInductionVariables(subscript_pair.first, subscript_pair.second) ==
33
0
         0;
34
0
}
35
36
bool LoopDependenceAnalysis::IsSIV(
37
0
    const std::pair<SENode*, SENode*>& subscript_pair) {
38
0
  return CountInductionVariables(subscript_pair.first, subscript_pair.second) ==
39
0
         1;
40
0
}
41
42
bool LoopDependenceAnalysis::IsMIV(
43
0
    const std::pair<SENode*, SENode*>& subscript_pair) {
44
0
  return CountInductionVariables(subscript_pair.first, subscript_pair.second) >
45
0
         1;
46
0
}
47
48
0
SENode* LoopDependenceAnalysis::GetLowerBound(const Loop* loop) {
49
0
  Instruction* cond_inst = loop->GetConditionInst();
50
0
  if (!cond_inst) {
51
0
    return nullptr;
52
0
  }
53
0
  Instruction* lower_inst = GetOperandDefinition(cond_inst, 0);
54
0
  switch (cond_inst->opcode()) {
55
0
    case spv::Op::OpULessThan:
56
0
    case spv::Op::OpSLessThan:
57
0
    case spv::Op::OpULessThanEqual:
58
0
    case spv::Op::OpSLessThanEqual:
59
0
    case spv::Op::OpUGreaterThan:
60
0
    case spv::Op::OpSGreaterThan:
61
0
    case spv::Op::OpUGreaterThanEqual:
62
0
    case spv::Op::OpSGreaterThanEqual: {
63
      // If we have a phi we are looking at the induction variable. We look
64
      // through the phi to the initial value of the phi upon entering the loop.
65
0
      if (lower_inst->opcode() == spv::Op::OpPhi) {
66
0
        lower_inst = GetOperandDefinition(lower_inst, 0);
67
        // We don't handle looking through multiple phis.
68
0
        if (lower_inst->opcode() == spv::Op::OpPhi) {
69
0
          return nullptr;
70
0
        }
71
0
      }
72
0
      return scalar_evolution_.SimplifyExpression(
73
0
          scalar_evolution_.AnalyzeInstruction(lower_inst));
74
0
    }
75
0
    default:
76
0
      return nullptr;
77
0
  }
78
0
}
79
80
0
SENode* LoopDependenceAnalysis::GetUpperBound(const Loop* loop) {
81
0
  Instruction* cond_inst = loop->GetConditionInst();
82
0
  if (!cond_inst) {
83
0
    return nullptr;
84
0
  }
85
0
  Instruction* upper_inst = GetOperandDefinition(cond_inst, 1);
86
0
  switch (cond_inst->opcode()) {
87
0
    case spv::Op::OpULessThan:
88
0
    case spv::Op::OpSLessThan: {
89
      // When we have a < condition we must subtract 1 from the analyzed upper
90
      // instruction.
91
0
      SENode* upper_bound = scalar_evolution_.SimplifyExpression(
92
0
          scalar_evolution_.CreateSubtraction(
93
0
              scalar_evolution_.AnalyzeInstruction(upper_inst),
94
0
              scalar_evolution_.CreateConstant(1)));
95
0
      return upper_bound;
96
0
    }
97
0
    case spv::Op::OpUGreaterThan:
98
0
    case spv::Op::OpSGreaterThan: {
99
      // When we have a > condition we must add 1 to the analyzed upper
100
      // instruction.
101
0
      SENode* upper_bound =
102
0
          scalar_evolution_.SimplifyExpression(scalar_evolution_.CreateAddNode(
103
0
              scalar_evolution_.AnalyzeInstruction(upper_inst),
104
0
              scalar_evolution_.CreateConstant(1)));
105
0
      return upper_bound;
106
0
    }
107
0
    case spv::Op::OpULessThanEqual:
108
0
    case spv::Op::OpSLessThanEqual:
109
0
    case spv::Op::OpUGreaterThanEqual:
110
0
    case spv::Op::OpSGreaterThanEqual: {
111
      // We don't need to modify the results of analyzing when we have <= or >=.
112
0
      SENode* upper_bound = scalar_evolution_.SimplifyExpression(
113
0
          scalar_evolution_.AnalyzeInstruction(upper_inst));
114
0
      return upper_bound;
115
0
    }
116
0
    default:
117
0
      return nullptr;
118
0
  }
119
0
}
120
121
bool LoopDependenceAnalysis::IsWithinBounds(int64_t value, int64_t bound_one,
122
0
                                            int64_t bound_two) {
123
0
  if (bound_one < bound_two) {
124
    // If |bound_one| is the lower bound.
125
0
    return (value >= bound_one && value <= bound_two);
126
0
  } else if (bound_one > bound_two) {
127
    // If |bound_two| is the lower bound.
128
0
    return (value >= bound_two && value <= bound_one);
129
0
  } else {
130
    // Both bounds have the same value.
131
0
    return value == bound_one;
132
0
  }
133
0
}
134
135
bool LoopDependenceAnalysis::IsProvablyOutsideOfLoopBounds(
136
0
    const Loop* loop, SENode* distance, SENode* coefficient) {
137
  // We test to see if we can reduce the coefficient to an integral constant.
138
0
  SEConstantNode* coefficient_constant = coefficient->AsSEConstantNode();
139
0
  if (!coefficient_constant) {
140
0
    PrintDebug(
141
0
        "IsProvablyOutsideOfLoopBounds could not reduce coefficient to a "
142
0
        "SEConstantNode so must exit.");
143
0
    return false;
144
0
  }
145
146
0
  SENode* lower_bound = GetLowerBound(loop);
147
0
  SENode* upper_bound = GetUpperBound(loop);
148
0
  if (!lower_bound || !upper_bound) {
149
0
    PrintDebug(
150
0
        "IsProvablyOutsideOfLoopBounds could not get both the lower and upper "
151
0
        "bounds so must exit.");
152
0
    return false;
153
0
  }
154
  // If the coefficient is positive we calculate bounds as upper - lower
155
  // If the coefficient is negative we calculate bounds as lower - upper
156
0
  SENode* bounds = nullptr;
157
0
  if (coefficient_constant->FoldToSingleValue() >= 0) {
158
0
    PrintDebug(
159
0
        "IsProvablyOutsideOfLoopBounds found coefficient >= 0.\n"
160
0
        "Using bounds as upper - lower.");
161
0
    bounds = scalar_evolution_.SimplifyExpression(
162
0
        scalar_evolution_.CreateSubtraction(upper_bound, lower_bound));
163
0
  } else {
164
0
    PrintDebug(
165
0
        "IsProvablyOutsideOfLoopBounds found coefficient < 0.\n"
166
0
        "Using bounds as lower - upper.");
167
0
    bounds = scalar_evolution_.SimplifyExpression(
168
0
        scalar_evolution_.CreateSubtraction(lower_bound, upper_bound));
169
0
  }
170
171
  // We can attempt to deal with symbolic cases by subtracting |distance| and
172
  // the bound nodes. If we can subtract, simplify and produce a SEConstantNode
173
  // we can produce some information.
174
0
  SEConstantNode* distance_minus_bounds =
175
0
      scalar_evolution_
176
0
          .SimplifyExpression(
177
0
              scalar_evolution_.CreateSubtraction(distance, bounds))
178
0
          ->AsSEConstantNode();
179
0
  if (distance_minus_bounds) {
180
0
    PrintDebug(
181
0
        "IsProvablyOutsideOfLoopBounds found distance - bounds as a "
182
0
        "SEConstantNode with value " +
183
0
        ToString(distance_minus_bounds->FoldToSingleValue()));
184
    // If distance - bounds > 0 we prove the distance is outwith the loop
185
    // bounds.
186
0
    if (distance_minus_bounds->FoldToSingleValue() > 0) {
187
0
      PrintDebug(
188
0
          "IsProvablyOutsideOfLoopBounds found distance escaped the loop "
189
0
          "bounds.");
190
0
      return true;
191
0
    }
192
0
  }
193
194
0
  return false;
195
0
}
196
197
const Loop* LoopDependenceAnalysis::GetLoopForSubscriptPair(
198
0
    const std::pair<SENode*, SENode*>& subscript_pair) {
199
  // Collect all the SERecurrentNodes.
200
0
  std::vector<SERecurrentNode*> source_nodes =
201
0
      std::get<0>(subscript_pair)->CollectRecurrentNodes();
202
0
  std::vector<SERecurrentNode*> destination_nodes =
203
0
      std::get<1>(subscript_pair)->CollectRecurrentNodes();
204
205
  // Collect all the loops stored by the SERecurrentNodes.
206
0
  std::unordered_set<const Loop*> loops{};
207
0
  for (auto source_nodes_it = source_nodes.begin();
208
0
       source_nodes_it != source_nodes.end(); ++source_nodes_it) {
209
0
    loops.insert((*source_nodes_it)->GetLoop());
210
0
  }
211
0
  for (auto destination_nodes_it = destination_nodes.begin();
212
0
       destination_nodes_it != destination_nodes.end();
213
0
       ++destination_nodes_it) {
214
0
    loops.insert((*destination_nodes_it)->GetLoop());
215
0
  }
216
217
  // If we didn't find 1 loop |subscript_pair| is a subscript over multiple or 0
218
  // loops. We don't handle this so return nullptr.
219
0
  if (loops.size() != 1) {
220
0
    PrintDebug("GetLoopForSubscriptPair found loops.size() != 1.");
221
0
    return nullptr;
222
0
  }
223
0
  return *loops.begin();
224
0
}
225
226
DistanceEntry* LoopDependenceAnalysis::GetDistanceEntryForLoop(
227
0
    const Loop* loop, DistanceVector* distance_vector) {
228
0
  if (!loop) {
229
0
    return nullptr;
230
0
  }
231
232
0
  DistanceEntry* distance_entry = nullptr;
233
0
  for (size_t loop_index = 0; loop_index < loops_.size(); ++loop_index) {
234
0
    if (loop == loops_[loop_index]) {
235
0
      distance_entry = &(distance_vector->GetEntries()[loop_index]);
236
0
      break;
237
0
    }
238
0
  }
239
240
0
  return distance_entry;
241
0
}
242
243
DistanceEntry* LoopDependenceAnalysis::GetDistanceEntryForSubscriptPair(
244
    const std::pair<SENode*, SENode*>& subscript_pair,
245
0
    DistanceVector* distance_vector) {
246
0
  const Loop* loop = GetLoopForSubscriptPair(subscript_pair);
247
248
0
  return GetDistanceEntryForLoop(loop, distance_vector);
249
0
}
250
251
0
SENode* LoopDependenceAnalysis::GetTripCount(const Loop* loop) {
252
0
  BasicBlock* condition_block = loop->FindConditionBlock();
253
0
  if (!condition_block) {
254
0
    return nullptr;
255
0
  }
256
0
  Instruction* induction_instr = loop->FindConditionVariable(condition_block);
257
0
  if (!induction_instr) {
258
0
    return nullptr;
259
0
  }
260
0
  Instruction* cond_instr = loop->GetConditionInst();
261
0
  if (!cond_instr) {
262
0
    return nullptr;
263
0
  }
264
265
0
  size_t iteration_count = 0;
266
267
  // We have to check the instruction type here. If the condition instruction
268
  // isn't a supported type we can't calculate the trip count.
269
0
  if (loop->IsSupportedCondition(cond_instr->opcode())) {
270
0
    if (loop->FindNumberOfIterations(induction_instr, &*condition_block->tail(),
271
0
                                     &iteration_count)) {
272
0
      return scalar_evolution_.CreateConstant(
273
0
          static_cast<int64_t>(iteration_count));
274
0
    }
275
0
  }
276
277
0
  return nullptr;
278
0
}
279
280
0
SENode* LoopDependenceAnalysis::GetFirstTripInductionNode(const Loop* loop) {
281
0
  BasicBlock* condition_block = loop->FindConditionBlock();
282
0
  if (!condition_block) {
283
0
    return nullptr;
284
0
  }
285
0
  Instruction* induction_instr = loop->FindConditionVariable(condition_block);
286
0
  if (!induction_instr) {
287
0
    return nullptr;
288
0
  }
289
0
  int64_t induction_initial_value = 0;
290
0
  if (!loop->GetInductionInitValue(induction_instr, &induction_initial_value)) {
291
0
    return nullptr;
292
0
  }
293
294
0
  SENode* induction_init_SENode = scalar_evolution_.SimplifyExpression(
295
0
      scalar_evolution_.CreateConstant(induction_initial_value));
296
0
  return induction_init_SENode;
297
0
}
298
299
SENode* LoopDependenceAnalysis::GetFinalTripInductionNode(
300
0
    const Loop* loop, SENode* induction_coefficient) {
301
0
  SENode* first_trip_induction_node = GetFirstTripInductionNode(loop);
302
0
  if (!first_trip_induction_node) {
303
0
    return nullptr;
304
0
  }
305
  // Get trip_count as GetTripCount - 1
306
  // This is because the induction variable is not stepped on the first
307
  // iteration of the loop
308
0
  SENode* trip_count =
309
0
      scalar_evolution_.SimplifyExpression(scalar_evolution_.CreateSubtraction(
310
0
          GetTripCount(loop), scalar_evolution_.CreateConstant(1)));
311
  // Return first_trip_induction_node + trip_count * induction_coefficient
312
0
  return scalar_evolution_.SimplifyExpression(scalar_evolution_.CreateAddNode(
313
0
      first_trip_induction_node,
314
0
      scalar_evolution_.CreateMultiplyNode(trip_count, induction_coefficient)));
315
0
}
316
317
std::set<const Loop*> LoopDependenceAnalysis::CollectLoops(
318
0
    const std::vector<SERecurrentNode*>& recurrent_nodes) {
319
  // We don't handle loops with more than one induction variable. Therefore we
320
  // can identify the number of induction variables by collecting all of the
321
  // loops the collected recurrent nodes belong to.
322
0
  std::set<const Loop*> loops{};
323
0
  for (auto recurrent_nodes_it = recurrent_nodes.begin();
324
0
       recurrent_nodes_it != recurrent_nodes.end(); ++recurrent_nodes_it) {
325
0
    loops.insert((*recurrent_nodes_it)->GetLoop());
326
0
  }
327
328
0
  return loops;
329
0
}
330
331
0
int64_t LoopDependenceAnalysis::CountInductionVariables(SENode* node) {
332
0
  if (!node) {
333
0
    return -1;
334
0
  }
335
336
0
  std::vector<SERecurrentNode*> recurrent_nodes = node->CollectRecurrentNodes();
337
338
  // We don't handle loops with more than one induction variable. Therefore we
339
  // can identify the number of induction variables by collecting all of the
340
  // loops the collected recurrent nodes belong to.
341
0
  std::set<const Loop*> loops = CollectLoops(recurrent_nodes);
342
343
0
  return static_cast<int64_t>(loops.size());
344
0
}
345
346
std::set<const Loop*> LoopDependenceAnalysis::CollectLoops(
347
0
    SENode* source, SENode* destination) {
348
0
  if (!source || !destination) {
349
0
    return std::set<const Loop*>{};
350
0
  }
351
352
0
  std::vector<SERecurrentNode*> source_nodes = source->CollectRecurrentNodes();
353
0
  std::vector<SERecurrentNode*> destination_nodes =
354
0
      destination->CollectRecurrentNodes();
355
356
0
  std::set<const Loop*> loops = CollectLoops(source_nodes);
357
0
  std::set<const Loop*> destination_loops = CollectLoops(destination_nodes);
358
359
0
  loops.insert(std::begin(destination_loops), std::end(destination_loops));
360
361
0
  return loops;
362
0
}
363
364
int64_t LoopDependenceAnalysis::CountInductionVariables(SENode* source,
365
0
                                                        SENode* destination) {
366
0
  if (!source || !destination) {
367
0
    return -1;
368
0
  }
369
370
0
  std::set<const Loop*> loops = CollectLoops(source, destination);
371
372
0
  return static_cast<int64_t>(loops.size());
373
0
}
374
375
Instruction* LoopDependenceAnalysis::GetOperandDefinition(
376
0
    const Instruction* instruction, int id) {
377
0
  return context_->get_def_use_mgr()->GetDef(
378
0
      instruction->GetSingleWordInOperand(id));
379
0
}
380
381
std::vector<Instruction*> LoopDependenceAnalysis::GetSubscripts(
382
0
    const Instruction* instruction) {
383
0
  Instruction* access_chain = GetOperandDefinition(instruction, 0);
384
385
0
  std::vector<Instruction*> subscripts;
386
387
0
  for (auto i = 1u; i < access_chain->NumInOperandWords(); ++i) {
388
0
    subscripts.push_back(GetOperandDefinition(access_chain, i));
389
0
  }
390
391
0
  return subscripts;
392
0
}
393
394
SENode* LoopDependenceAnalysis::GetConstantTerm(const Loop* loop,
395
0
                                                SERecurrentNode* induction) {
396
0
  SENode* offset = induction->GetOffset();
397
0
  SENode* lower_bound = GetLowerBound(loop);
398
0
  if (!offset || !lower_bound) {
399
0
    return nullptr;
400
0
  }
401
0
  SENode* constant_term = scalar_evolution_.SimplifyExpression(
402
0
      scalar_evolution_.CreateSubtraction(offset, lower_bound));
403
0
  return constant_term;
404
0
}
405
406
bool LoopDependenceAnalysis::CheckSupportedLoops(
407
0
    std::vector<const Loop*> loops) {
408
0
  for (auto loop : loops) {
409
0
    if (!IsSupportedLoop(loop)) {
410
0
      return false;
411
0
    }
412
0
  }
413
0
  return true;
414
0
}
415
416
void LoopDependenceAnalysis::MarkUnsusedDistanceEntriesAsIrrelevant(
417
    const Instruction* source, const Instruction* destination,
418
0
    DistanceVector* distance_vector) {
419
0
  std::vector<Instruction*> source_subscripts = GetSubscripts(source);
420
0
  std::vector<Instruction*> destination_subscripts = GetSubscripts(destination);
421
422
0
  std::set<const Loop*> used_loops{};
423
424
0
  for (Instruction* source_inst : source_subscripts) {
425
0
    SENode* source_node = scalar_evolution_.SimplifyExpression(
426
0
        scalar_evolution_.AnalyzeInstruction(source_inst));
427
0
    std::vector<SERecurrentNode*> recurrent_nodes =
428
0
        source_node->CollectRecurrentNodes();
429
0
    for (SERecurrentNode* recurrent_node : recurrent_nodes) {
430
0
      used_loops.insert(recurrent_node->GetLoop());
431
0
    }
432
0
  }
433
434
0
  for (Instruction* destination_inst : destination_subscripts) {
435
0
    SENode* destination_node = scalar_evolution_.SimplifyExpression(
436
0
        scalar_evolution_.AnalyzeInstruction(destination_inst));
437
0
    std::vector<SERecurrentNode*> recurrent_nodes =
438
0
        destination_node->CollectRecurrentNodes();
439
0
    for (SERecurrentNode* recurrent_node : recurrent_nodes) {
440
0
      used_loops.insert(recurrent_node->GetLoop());
441
0
    }
442
0
  }
443
444
0
  for (size_t i = 0; i < loops_.size(); ++i) {
445
0
    if (used_loops.find(loops_[i]) == used_loops.end()) {
446
0
      distance_vector->GetEntries()[i].dependence_information =
447
0
          DistanceEntry::DependenceInformation::IRRELEVANT;
448
0
    }
449
0
  }
450
0
}
451
452
0
bool LoopDependenceAnalysis::IsSupportedLoop(const Loop* loop) {
453
0
  std::vector<Instruction*> inductions{};
454
0
  loop->GetInductionVariables(inductions);
455
0
  if (inductions.size() != 1) {
456
0
    return false;
457
0
  }
458
0
  Instruction* induction = inductions[0];
459
0
  SENode* induction_node = scalar_evolution_.SimplifyExpression(
460
0
      scalar_evolution_.AnalyzeInstruction(induction));
461
0
  if (!induction_node->AsSERecurrentNode()) {
462
0
    return false;
463
0
  }
464
0
  SENode* induction_step =
465
0
      induction_node->AsSERecurrentNode()->GetCoefficient();
466
0
  if (!induction_step->AsSEConstantNode()) {
467
0
    return false;
468
0
  }
469
0
  if (!(induction_step->AsSEConstantNode()->FoldToSingleValue() == 1 ||
470
0
        induction_step->AsSEConstantNode()->FoldToSingleValue() == -1)) {
471
0
    return false;
472
0
  }
473
0
  return true;
474
0
}
475
476
0
void LoopDependenceAnalysis::PrintDebug(std::string debug_msg) {
477
0
  if (debug_stream_) {
478
0
    (*debug_stream_) << debug_msg << "\n";
479
0
  }
480
0
}
481
482
0
bool Constraint::operator==(const Constraint& other) const {
483
  // A distance of |d| is equivalent to a line |x - y = -d|
484
0
  if ((GetType() == ConstraintType::Distance &&
485
0
       other.GetType() == ConstraintType::Line) ||
486
0
      (GetType() == ConstraintType::Line &&
487
0
       other.GetType() == ConstraintType::Distance)) {
488
0
    auto is_distance = AsDependenceLine() != nullptr;
489
490
0
    auto as_distance =
491
0
        is_distance ? AsDependenceDistance() : other.AsDependenceDistance();
492
0
    auto distance = as_distance->GetDistance();
493
494
0
    auto line = other.AsDependenceLine();
495
496
0
    auto scalar_evolution = distance->GetParentAnalysis();
497
498
0
    auto neg_distance = scalar_evolution->SimplifyExpression(
499
0
        scalar_evolution->CreateNegation(distance));
500
501
0
    return *scalar_evolution->CreateConstant(1) == *line->GetA() &&
502
0
           *scalar_evolution->CreateConstant(-1) == *line->GetB() &&
503
0
           *neg_distance == *line->GetC();
504
0
  }
505
506
0
  if (GetType() != other.GetType()) {
507
0
    return false;
508
0
  }
509
510
0
  if (AsDependenceDistance()) {
511
0
    return *AsDependenceDistance()->GetDistance() ==
512
0
           *other.AsDependenceDistance()->GetDistance();
513
0
  }
514
515
0
  if (AsDependenceLine()) {
516
0
    auto this_line = AsDependenceLine();
517
0
    auto other_line = other.AsDependenceLine();
518
0
    return *this_line->GetA() == *other_line->GetA() &&
519
0
           *this_line->GetB() == *other_line->GetB() &&
520
0
           *this_line->GetC() == *other_line->GetC();
521
0
  }
522
523
0
  if (AsDependencePoint()) {
524
0
    auto this_point = AsDependencePoint();
525
0
    auto other_point = other.AsDependencePoint();
526
527
0
    return *this_point->GetSource() == *other_point->GetSource() &&
528
0
           *this_point->GetDestination() == *other_point->GetDestination();
529
0
  }
530
531
0
  return true;
532
0
}
533
534
0
bool Constraint::operator!=(const Constraint& other) const {
535
0
  return !(*this == other);
536
0
}
537
538
}  // namespace opt
539
}  // namespace spvtools