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