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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
377 | Instruction* LoopDependenceAnalysis::GetOperandDefinition( |
378 | const Instruction* instruction, int id) { |
379 | return context_->get_def_use_mgr()->GetDef( |
380 | instruction->GetSingleWordInOperand(id)); |
381 | } |
382 | |
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 | |
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 | |
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 | |
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 | |
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 | |
478 | void LoopDependenceAnalysis::PrintDebug(std::string debug_msg) { |
479 | if (debug_stream_) { |
480 | (*debug_stream_) << debug_msg << "\n" ; |
481 | } |
482 | } |
483 | |
484 | 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 | |
536 | bool Constraint::operator!=(const Constraint& other) const { |
537 | return !(*this == other); |
538 | } |
539 | |
540 | } // namespace opt |
541 | } // namespace spvtools |
542 | |