| 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 | |