• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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