| // Copyright (c) 2018 Google LLC. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "source/opt/loop_dependence.h" |
| |
| #include <ostream> |
| #include <set> |
| #include <string> |
| #include <unordered_set> |
| #include <utility> |
| #include <vector> |
| |
| #include "source/opt/basic_block.h" |
| #include "source/opt/instruction.h" |
| #include "source/opt/scalar_analysis.h" |
| #include "source/opt/scalar_analysis_nodes.h" |
| |
| namespace spvtools { |
| namespace opt { |
| |
| bool LoopDependenceAnalysis::IsZIV( |
| const std::pair<SENode*, SENode*>& subscript_pair) { |
| return CountInductionVariables(subscript_pair.first, subscript_pair.second) == |
| 0; |
| } |
| |
| bool LoopDependenceAnalysis::IsSIV( |
| const std::pair<SENode*, SENode*>& subscript_pair) { |
| return CountInductionVariables(subscript_pair.first, subscript_pair.second) == |
| 1; |
| } |
| |
| bool LoopDependenceAnalysis::IsMIV( |
| const std::pair<SENode*, SENode*>& subscript_pair) { |
| return CountInductionVariables(subscript_pair.first, subscript_pair.second) > |
| 1; |
| } |
| |
| SENode* LoopDependenceAnalysis::GetLowerBound(const Loop* loop) { |
| Instruction* cond_inst = loop->GetConditionInst(); |
| if (!cond_inst) { |
| return nullptr; |
| } |
| Instruction* lower_inst = GetOperandDefinition(cond_inst, 0); |
| switch (cond_inst->opcode()) { |
| case SpvOpULessThan: |
| case SpvOpSLessThan: |
| case SpvOpULessThanEqual: |
| case SpvOpSLessThanEqual: |
| case SpvOpUGreaterThan: |
| case SpvOpSGreaterThan: |
| case SpvOpUGreaterThanEqual: |
| case SpvOpSGreaterThanEqual: { |
| // If we have a phi we are looking at the induction variable. We look |
| // through the phi to the initial value of the phi upon entering the loop. |
| if (lower_inst->opcode() == SpvOpPhi) { |
| lower_inst = GetOperandDefinition(lower_inst, 0); |
| // We don't handle looking through multiple phis. |
| if (lower_inst->opcode() == SpvOpPhi) { |
| return nullptr; |
| } |
| } |
| return scalar_evolution_.SimplifyExpression( |
| scalar_evolution_.AnalyzeInstruction(lower_inst)); |
| } |
| default: |
| return nullptr; |
| } |
| } |
| |
| SENode* LoopDependenceAnalysis::GetUpperBound(const Loop* loop) { |
| Instruction* cond_inst = loop->GetConditionInst(); |
| if (!cond_inst) { |
| return nullptr; |
| } |
| Instruction* upper_inst = GetOperandDefinition(cond_inst, 1); |
| switch (cond_inst->opcode()) { |
| case SpvOpULessThan: |
| case SpvOpSLessThan: { |
| // When we have a < condition we must subtract 1 from the analyzed upper |
| // instruction. |
| SENode* upper_bound = scalar_evolution_.SimplifyExpression( |
| scalar_evolution_.CreateSubtraction( |
| scalar_evolution_.AnalyzeInstruction(upper_inst), |
| scalar_evolution_.CreateConstant(1))); |
| return upper_bound; |
| } |
| case SpvOpUGreaterThan: |
| case SpvOpSGreaterThan: { |
| // When we have a > condition we must add 1 to the analyzed upper |
| // instruction. |
| SENode* upper_bound = |
| scalar_evolution_.SimplifyExpression(scalar_evolution_.CreateAddNode( |
| scalar_evolution_.AnalyzeInstruction(upper_inst), |
| scalar_evolution_.CreateConstant(1))); |
| return upper_bound; |
| } |
| case SpvOpULessThanEqual: |
| case SpvOpSLessThanEqual: |
| case SpvOpUGreaterThanEqual: |
| case SpvOpSGreaterThanEqual: { |
| // We don't need to modify the results of analyzing when we have <= or >=. |
| SENode* upper_bound = scalar_evolution_.SimplifyExpression( |
| scalar_evolution_.AnalyzeInstruction(upper_inst)); |
| return upper_bound; |
| } |
| default: |
| return nullptr; |
| } |
| } |
| |
| bool LoopDependenceAnalysis::IsWithinBounds(int64_t value, int64_t bound_one, |
| int64_t bound_two) { |
| if (bound_one < bound_two) { |
| // If |bound_one| is the lower bound. |
| return (value >= bound_one && value <= bound_two); |
| } else if (bound_one > bound_two) { |
| // If |bound_two| is the lower bound. |
| return (value >= bound_two && value <= bound_one); |
| } else { |
| // Both bounds have the same value. |
| return value == bound_one; |
| } |
| } |
| |
| bool LoopDependenceAnalysis::IsProvablyOutsideOfLoopBounds( |
| const Loop* loop, SENode* distance, SENode* coefficient) { |
| // We test to see if we can reduce the coefficient to an integral constant. |
| SEConstantNode* coefficient_constant = coefficient->AsSEConstantNode(); |
| if (!coefficient_constant) { |
| PrintDebug( |
| "IsProvablyOutsideOfLoopBounds could not reduce coefficient to a " |
| "SEConstantNode so must exit."); |
| return false; |
| } |
| |
| SENode* lower_bound = GetLowerBound(loop); |
| SENode* upper_bound = GetUpperBound(loop); |
| if (!lower_bound || !upper_bound) { |
| PrintDebug( |
| "IsProvablyOutsideOfLoopBounds could not get both the lower and upper " |
| "bounds so must exit."); |
| return false; |
| } |
| // If the coefficient is positive we calculate bounds as upper - lower |
| // If the coefficient is negative we calculate bounds as lower - upper |
| SENode* bounds = nullptr; |
| if (coefficient_constant->FoldToSingleValue() >= 0) { |
| PrintDebug( |
| "IsProvablyOutsideOfLoopBounds found coefficient >= 0.\n" |
| "Using bounds as upper - lower."); |
| bounds = scalar_evolution_.SimplifyExpression( |
| scalar_evolution_.CreateSubtraction(upper_bound, lower_bound)); |
| } else { |
| PrintDebug( |
| "IsProvablyOutsideOfLoopBounds found coefficient < 0.\n" |
| "Using bounds as lower - upper."); |
| bounds = scalar_evolution_.SimplifyExpression( |
| scalar_evolution_.CreateSubtraction(lower_bound, upper_bound)); |
| } |
| |
| // We can attempt to deal with symbolic cases by subtracting |distance| and |
| // the bound nodes. If we can subtract, simplify and produce a SEConstantNode |
| // we can produce some information. |
| SEConstantNode* distance_minus_bounds = |
| scalar_evolution_ |
| .SimplifyExpression( |
| scalar_evolution_.CreateSubtraction(distance, bounds)) |
| ->AsSEConstantNode(); |
| if (distance_minus_bounds) { |
| PrintDebug( |
| "IsProvablyOutsideOfLoopBounds found distance - bounds as a " |
| "SEConstantNode with value " + |
| ToString(distance_minus_bounds->FoldToSingleValue())); |
| // If distance - bounds > 0 we prove the distance is outwith the loop |
| // bounds. |
| if (distance_minus_bounds->FoldToSingleValue() > 0) { |
| PrintDebug( |
| "IsProvablyOutsideOfLoopBounds found distance escaped the loop " |
| "bounds."); |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| const Loop* LoopDependenceAnalysis::GetLoopForSubscriptPair( |
| const std::pair<SENode*, SENode*>& subscript_pair) { |
| // Collect all the SERecurrentNodes. |
| std::vector<SERecurrentNode*> source_nodes = |
| std::get<0>(subscript_pair)->CollectRecurrentNodes(); |
| std::vector<SERecurrentNode*> destination_nodes = |
| std::get<1>(subscript_pair)->CollectRecurrentNodes(); |
| |
| // Collect all the loops stored by the SERecurrentNodes. |
| std::unordered_set<const Loop*> loops{}; |
| for (auto source_nodes_it = source_nodes.begin(); |
| source_nodes_it != source_nodes.end(); ++source_nodes_it) { |
| loops.insert((*source_nodes_it)->GetLoop()); |
| } |
| for (auto destination_nodes_it = destination_nodes.begin(); |
| destination_nodes_it != destination_nodes.end(); |
| ++destination_nodes_it) { |
| loops.insert((*destination_nodes_it)->GetLoop()); |
| } |
| |
| // If we didn't find 1 loop |subscript_pair| is a subscript over multiple or 0 |
| // loops. We don't handle this so return nullptr. |
| if (loops.size() != 1) { |
| PrintDebug("GetLoopForSubscriptPair found loops.size() != 1."); |
| return nullptr; |
| } |
| return *loops.begin(); |
| } |
| |
| DistanceEntry* LoopDependenceAnalysis::GetDistanceEntryForLoop( |
| const Loop* loop, DistanceVector* distance_vector) { |
| if (!loop) { |
| return nullptr; |
| } |
| |
| DistanceEntry* distance_entry = nullptr; |
| for (size_t loop_index = 0; loop_index < loops_.size(); ++loop_index) { |
| if (loop == loops_[loop_index]) { |
| distance_entry = &(distance_vector->GetEntries()[loop_index]); |
| break; |
| } |
| } |
| |
| return distance_entry; |
| } |
| |
| DistanceEntry* LoopDependenceAnalysis::GetDistanceEntryForSubscriptPair( |
| const std::pair<SENode*, SENode*>& subscript_pair, |
| DistanceVector* distance_vector) { |
| const Loop* loop = GetLoopForSubscriptPair(subscript_pair); |
| |
| return GetDistanceEntryForLoop(loop, distance_vector); |
| } |
| |
| SENode* LoopDependenceAnalysis::GetTripCount(const Loop* loop) { |
| BasicBlock* condition_block = loop->FindConditionBlock(); |
| if (!condition_block) { |
| return nullptr; |
| } |
| Instruction* induction_instr = loop->FindConditionVariable(condition_block); |
| if (!induction_instr) { |
| return nullptr; |
| } |
| Instruction* cond_instr = loop->GetConditionInst(); |
| if (!cond_instr) { |
| return nullptr; |
| } |
| |
| size_t iteration_count = 0; |
| |
| // We have to check the instruction type here. If the condition instruction |
| // isn't a supported type we can't calculate the trip count. |
| if (loop->IsSupportedCondition(cond_instr->opcode())) { |
| if (loop->FindNumberOfIterations(induction_instr, &*condition_block->tail(), |
| &iteration_count)) { |
| return scalar_evolution_.CreateConstant( |
| static_cast<int64_t>(iteration_count)); |
| } |
| } |
| |
| return nullptr; |
| } |
| |
| SENode* LoopDependenceAnalysis::GetFirstTripInductionNode(const Loop* loop) { |
| BasicBlock* condition_block = loop->FindConditionBlock(); |
| if (!condition_block) { |
| return nullptr; |
| } |
| Instruction* induction_instr = loop->FindConditionVariable(condition_block); |
| if (!induction_instr) { |
| return nullptr; |
| } |
| int64_t induction_initial_value = 0; |
| if (!loop->GetInductionInitValue(induction_instr, &induction_initial_value)) { |
| return nullptr; |
| } |
| |
| SENode* induction_init_SENode = scalar_evolution_.SimplifyExpression( |
| scalar_evolution_.CreateConstant(induction_initial_value)); |
| return induction_init_SENode; |
| } |
| |
| SENode* LoopDependenceAnalysis::GetFinalTripInductionNode( |
| const Loop* loop, SENode* induction_coefficient) { |
| SENode* first_trip_induction_node = GetFirstTripInductionNode(loop); |
| if (!first_trip_induction_node) { |
| return nullptr; |
| } |
| // Get trip_count as GetTripCount - 1 |
| // This is because the induction variable is not stepped on the first |
| // iteration of the loop |
| SENode* trip_count = |
| scalar_evolution_.SimplifyExpression(scalar_evolution_.CreateSubtraction( |
| GetTripCount(loop), scalar_evolution_.CreateConstant(1))); |
| // Return first_trip_induction_node + trip_count * induction_coefficient |
| return scalar_evolution_.SimplifyExpression(scalar_evolution_.CreateAddNode( |
| first_trip_induction_node, |
| scalar_evolution_.CreateMultiplyNode(trip_count, induction_coefficient))); |
| } |
| |
| std::set<const Loop*> LoopDependenceAnalysis::CollectLoops( |
| const std::vector<SERecurrentNode*>& recurrent_nodes) { |
| // We don't handle loops with more than one induction variable. Therefore we |
| // can identify the number of induction variables by collecting all of the |
| // loops the collected recurrent nodes belong to. |
| std::set<const Loop*> loops{}; |
| for (auto recurrent_nodes_it = recurrent_nodes.begin(); |
| recurrent_nodes_it != recurrent_nodes.end(); ++recurrent_nodes_it) { |
| loops.insert((*recurrent_nodes_it)->GetLoop()); |
| } |
| |
| return loops; |
| } |
| |
| int64_t LoopDependenceAnalysis::CountInductionVariables(SENode* node) { |
| if (!node) { |
| return -1; |
| } |
| |
| std::vector<SERecurrentNode*> recurrent_nodes = node->CollectRecurrentNodes(); |
| |
| // We don't handle loops with more than one induction variable. Therefore we |
| // can identify the number of induction variables by collecting all of the |
| // loops the collected recurrent nodes belong to. |
| std::set<const Loop*> loops = CollectLoops(recurrent_nodes); |
| |
| return static_cast<int64_t>(loops.size()); |
| } |
| |
| std::set<const Loop*> LoopDependenceAnalysis::CollectLoops( |
| SENode* source, SENode* destination) { |
| if (!source || !destination) { |
| return std::set<const Loop*>{}; |
| } |
| |
| std::vector<SERecurrentNode*> source_nodes = source->CollectRecurrentNodes(); |
| std::vector<SERecurrentNode*> destination_nodes = |
| destination->CollectRecurrentNodes(); |
| |
| std::set<const Loop*> loops = CollectLoops(source_nodes); |
| std::set<const Loop*> destination_loops = CollectLoops(destination_nodes); |
| |
| loops.insert(std::begin(destination_loops), std::end(destination_loops)); |
| |
| return loops; |
| } |
| |
| int64_t LoopDependenceAnalysis::CountInductionVariables(SENode* source, |
| SENode* destination) { |
| if (!source || !destination) { |
| return -1; |
| } |
| |
| std::set<const Loop*> loops = CollectLoops(source, destination); |
| |
| return static_cast<int64_t>(loops.size()); |
| } |
| |
| Instruction* LoopDependenceAnalysis::GetOperandDefinition( |
| const Instruction* instruction, int id) { |
| return context_->get_def_use_mgr()->GetDef( |
| instruction->GetSingleWordInOperand(id)); |
| } |
| |
| std::vector<Instruction*> LoopDependenceAnalysis::GetSubscripts( |
| const Instruction* instruction) { |
| Instruction* access_chain = GetOperandDefinition(instruction, 0); |
| |
| std::vector<Instruction*> subscripts; |
| |
| for (auto i = 1u; i < access_chain->NumInOperandWords(); ++i) { |
| subscripts.push_back(GetOperandDefinition(access_chain, i)); |
| } |
| |
| return subscripts; |
| } |
| |
| SENode* LoopDependenceAnalysis::GetConstantTerm(const Loop* loop, |
| SERecurrentNode* induction) { |
| SENode* offset = induction->GetOffset(); |
| SENode* lower_bound = GetLowerBound(loop); |
| if (!offset || !lower_bound) { |
| return nullptr; |
| } |
| SENode* constant_term = scalar_evolution_.SimplifyExpression( |
| scalar_evolution_.CreateSubtraction(offset, lower_bound)); |
| return constant_term; |
| } |
| |
| bool LoopDependenceAnalysis::CheckSupportedLoops( |
| std::vector<const Loop*> loops) { |
| for (auto loop : loops) { |
| if (!IsSupportedLoop(loop)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| void LoopDependenceAnalysis::MarkUnsusedDistanceEntriesAsIrrelevant( |
| const Instruction* source, const Instruction* destination, |
| DistanceVector* distance_vector) { |
| std::vector<Instruction*> source_subscripts = GetSubscripts(source); |
| std::vector<Instruction*> destination_subscripts = GetSubscripts(destination); |
| |
| std::set<const Loop*> used_loops{}; |
| |
| for (Instruction* source_inst : source_subscripts) { |
| SENode* source_node = scalar_evolution_.SimplifyExpression( |
| scalar_evolution_.AnalyzeInstruction(source_inst)); |
| std::vector<SERecurrentNode*> recurrent_nodes = |
| source_node->CollectRecurrentNodes(); |
| for (SERecurrentNode* recurrent_node : recurrent_nodes) { |
| used_loops.insert(recurrent_node->GetLoop()); |
| } |
| } |
| |
| for (Instruction* destination_inst : destination_subscripts) { |
| SENode* destination_node = scalar_evolution_.SimplifyExpression( |
| scalar_evolution_.AnalyzeInstruction(destination_inst)); |
| std::vector<SERecurrentNode*> recurrent_nodes = |
| destination_node->CollectRecurrentNodes(); |
| for (SERecurrentNode* recurrent_node : recurrent_nodes) { |
| used_loops.insert(recurrent_node->GetLoop()); |
| } |
| } |
| |
| for (size_t i = 0; i < loops_.size(); ++i) { |
| if (used_loops.find(loops_[i]) == used_loops.end()) { |
| distance_vector->GetEntries()[i].dependence_information = |
| DistanceEntry::DependenceInformation::IRRELEVANT; |
| } |
| } |
| } |
| |
| bool LoopDependenceAnalysis::IsSupportedLoop(const Loop* loop) { |
| std::vector<Instruction*> inductions{}; |
| loop->GetInductionVariables(inductions); |
| if (inductions.size() != 1) { |
| return false; |
| } |
| Instruction* induction = inductions[0]; |
| SENode* induction_node = scalar_evolution_.SimplifyExpression( |
| scalar_evolution_.AnalyzeInstruction(induction)); |
| if (!induction_node->AsSERecurrentNode()) { |
| return false; |
| } |
| SENode* induction_step = |
| induction_node->AsSERecurrentNode()->GetCoefficient(); |
| if (!induction_step->AsSEConstantNode()) { |
| return false; |
| } |
| if (!(induction_step->AsSEConstantNode()->FoldToSingleValue() == 1 || |
| induction_step->AsSEConstantNode()->FoldToSingleValue() == -1)) { |
| return false; |
| } |
| return true; |
| } |
| |
| void LoopDependenceAnalysis::PrintDebug(std::string debug_msg) { |
| if (debug_stream_) { |
| (*debug_stream_) << debug_msg << "\n"; |
| } |
| } |
| |
| bool Constraint::operator==(const Constraint& other) const { |
| // A distance of |d| is equivalent to a line |x - y = -d| |
| if ((GetType() == ConstraintType::Distance && |
| other.GetType() == ConstraintType::Line) || |
| (GetType() == ConstraintType::Line && |
| other.GetType() == ConstraintType::Distance)) { |
| auto is_distance = AsDependenceLine() != nullptr; |
| |
| auto as_distance = |
| is_distance ? AsDependenceDistance() : other.AsDependenceDistance(); |
| auto distance = as_distance->GetDistance(); |
| |
| auto line = other.AsDependenceLine(); |
| |
| auto scalar_evolution = distance->GetParentAnalysis(); |
| |
| auto neg_distance = scalar_evolution->SimplifyExpression( |
| scalar_evolution->CreateNegation(distance)); |
| |
| return *scalar_evolution->CreateConstant(1) == *line->GetA() && |
| *scalar_evolution->CreateConstant(-1) == *line->GetB() && |
| *neg_distance == *line->GetC(); |
| } |
| |
| if (GetType() != other.GetType()) { |
| return false; |
| } |
| |
| if (AsDependenceDistance()) { |
| return *AsDependenceDistance()->GetDistance() == |
| *other.AsDependenceDistance()->GetDistance(); |
| } |
| |
| if (AsDependenceLine()) { |
| auto this_line = AsDependenceLine(); |
| auto other_line = other.AsDependenceLine(); |
| return *this_line->GetA() == *other_line->GetA() && |
| *this_line->GetB() == *other_line->GetB() && |
| *this_line->GetC() == *other_line->GetC(); |
| } |
| |
| if (AsDependencePoint()) { |
| auto this_point = AsDependencePoint(); |
| auto other_point = other.AsDependencePoint(); |
| |
| return *this_point->GetSource() == *other_point->GetSource() && |
| *this_point->GetDestination() == *other_point->GetDestination(); |
| } |
| |
| return true; |
| } |
| |
| bool Constraint::operator!=(const Constraint& other) const { |
| return !(*this == other); |
| } |
| |
| } // namespace opt |
| } // namespace spvtools |