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