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