// 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 <functional> #include <memory> #include <numeric> #include <string> #include <utility> #include <vector> #include "source/opt/instruction.h" #include "source/opt/scalar_analysis.h" #include "source/opt/scalar_analysis_nodes.h" namespace spvtools { namespace opt { using SubscriptPair = std::pair<SENode*, SENode*>; namespace { // Calculate the greatest common divisor of a & b using Stein's algorithm. // https://en.wikipedia.org/wiki/Binary_GCD_algorithm int64_t GreatestCommonDivisor(int64_t a, int64_t b) { // Simple cases if (a == b) { return a; } else if (a == 0) { return b; } else if (b == 0) { return a; } // Both even if (a % 2 == 0 && b % 2 == 0) { return 2 * GreatestCommonDivisor(a / 2, b / 2); } // Even a, odd b if (a % 2 == 0 && b % 2 == 1) { return GreatestCommonDivisor(a / 2, b); } // Odd a, even b if (a % 2 == 1 && b % 2 == 0) { return GreatestCommonDivisor(a, b / 2); } // Both odd, reduce the larger argument if (a > b) { return GreatestCommonDivisor((a - b) / 2, b); } else { return GreatestCommonDivisor((b - a) / 2, a); } } // Check if node is affine, ie in the form: a0*i0 + a1*i1 + ... an*in + c // and contains only the following types of nodes: SERecurrentNode, SEAddNode // and SEConstantNode bool IsInCorrectFormForGCDTest(SENode* node) { bool children_ok = true; if (auto add_node = node->AsSEAddNode()) { for (auto child : add_node->GetChildren()) { children_ok &= IsInCorrectFormForGCDTest(child); } } bool this_ok = node->AsSERecurrentNode() || node->AsSEAddNode() || node->AsSEConstantNode(); return children_ok && this_ok; } // If |node| is an SERecurrentNode then returns |node| or if |node| is an // SEAddNode returns a vector of SERecurrentNode that are its children. std::vector<SERecurrentNode*> GetAllTopLevelRecurrences(SENode* node) { auto nodes = std::vector<SERecurrentNode*>{}; if (auto recurrent_node = node->AsSERecurrentNode()) { nodes.push_back(recurrent_node); } if (auto add_node = node->AsSEAddNode()) { for (auto child : add_node->GetChildren()) { auto child_nodes = GetAllTopLevelRecurrences(child); nodes.insert(nodes.end(), child_nodes.begin(), child_nodes.end()); } } return nodes; } // If |node| is an SEConstantNode then returns |node| or if |node| is an // SEAddNode returns a vector of SEConstantNode that are its children. std::vector<SEConstantNode*> GetAllTopLevelConstants(SENode* node) { auto nodes = std::vector<SEConstantNode*>{}; if (auto recurrent_node = node->AsSEConstantNode()) { nodes.push_back(recurrent_node); } if (auto add_node = node->AsSEAddNode()) { for (auto child : add_node->GetChildren()) { auto child_nodes = GetAllTopLevelConstants(child); nodes.insert(nodes.end(), child_nodes.begin(), child_nodes.end()); } } return nodes; } bool AreOffsetsAndCoefficientsConstant( const std::vector<SERecurrentNode*>& nodes) { for (auto node : nodes) { if (!node->GetOffset()->AsSEConstantNode() || !node->GetOffset()->AsSEConstantNode()) { return false; } } return true; } // Fold all SEConstantNode that appear in |recurrences| and |constants| into a // single integer value. int64_t CalculateConstantTerm(const std::vector<SERecurrentNode*>& recurrences, const std::vector<SEConstantNode*>& constants) { int64_t constant_term = 0; for (auto recurrence : recurrences) { constant_term += recurrence->GetOffset()->AsSEConstantNode()->FoldToSingleValue(); } for (auto constant : constants) { constant_term += constant->FoldToSingleValue(); } return constant_term; } int64_t CalculateGCDFromCoefficients( const std::vector<SERecurrentNode*>& recurrences, int64_t running_gcd) { for (SERecurrentNode* recurrence : recurrences) { auto coefficient = recurrence->GetCoefficient()->AsSEConstantNode(); running_gcd = GreatestCommonDivisor( running_gcd, std::abs(coefficient->FoldToSingleValue())); } return running_gcd; } // Compare 2 fractions while first normalizing them, e.g. 2/4 and 4/8 will both // be simplified to 1/2 and then determined to be equal. bool NormalizeAndCompareFractions(int64_t numerator_0, int64_t denominator_0, int64_t numerator_1, int64_t denominator_1) { auto gcd_0 = GreatestCommonDivisor(std::abs(numerator_0), std::abs(denominator_0)); auto gcd_1 = GreatestCommonDivisor(std::abs(numerator_1), std::abs(denominator_1)); auto normalized_numerator_0 = numerator_0 / gcd_0; auto normalized_denominator_0 = denominator_0 / gcd_0; auto normalized_numerator_1 = numerator_1 / gcd_1; auto normalized_denominator_1 = denominator_1 / gcd_1; return normalized_numerator_0 == normalized_numerator_1 && normalized_denominator_0 == normalized_denominator_1; } } // namespace bool LoopDependenceAnalysis::GetDependence(const Instruction* source, const Instruction* destination, DistanceVector* distance_vector) { // Start off by finding and marking all the loops in |loops_| that are // irrelevant to the dependence analysis. MarkUnsusedDistanceEntriesAsIrrelevant(source, destination, distance_vector); Instruction* source_access_chain = GetOperandDefinition(source, 0); Instruction* destination_access_chain = GetOperandDefinition(destination, 0); auto num_access_chains = (source_access_chain->opcode() == SpvOpAccessChain) + (destination_access_chain->opcode() == SpvOpAccessChain); // If neither is an access chain, then they are load/store to a variable. if (num_access_chains == 0) { if (source_access_chain != destination_access_chain) { // Not the same location, report independence return true; } else { // Accessing the same variable for (auto& entry : distance_vector->GetEntries()) { entry = DistanceEntry(); } return false; } } // If only one is an access chain, it could be accessing a part of a struct if (num_access_chains == 1) { auto source_is_chain = source_access_chain->opcode() == SpvOpAccessChain; auto access_chain = source_is_chain ? source_access_chain : destination_access_chain; auto variable = source_is_chain ? destination_access_chain : source_access_chain; auto location_in_chain = GetOperandDefinition(access_chain, 0); if (variable != location_in_chain) { // Not the same location, report independence return true; } else { // Accessing the same variable for (auto& entry : distance_vector->GetEntries()) { entry = DistanceEntry(); } return false; } } // If the access chains aren't collecting from the same structure there is no // dependence. Instruction* source_array = GetOperandDefinition(source_access_chain, 0); Instruction* destination_array = GetOperandDefinition(destination_access_chain, 0); // Nested access chains are not supported yet, bail out. if (source_array->opcode() == SpvOpAccessChain || destination_array->opcode() == SpvOpAccessChain) { for (auto& entry : distance_vector->GetEntries()) { entry = DistanceEntry(); } return false; } if (source_array != destination_array) { PrintDebug("Proved independence through different arrays."); return true; } // To handle multiple subscripts we must get every operand in the access // chains past the first. std::vector<Instruction*> source_subscripts = GetSubscripts(source); std::vector<Instruction*> destination_subscripts = GetSubscripts(destination); auto sets_of_subscripts = PartitionSubscripts(source_subscripts, destination_subscripts); auto first_coupled = std::partition( std::begin(sets_of_subscripts), std::end(sets_of_subscripts), [](const std::set<std::pair<Instruction*, Instruction*>>& set) { return set.size() == 1; }); // Go through each subscript testing for independence. // If any subscript results in independence, we prove independence between the // load and store. // If we can't prove independence we store what information we can gather in // a DistanceVector. for (auto it = std::begin(sets_of_subscripts); it < first_coupled; ++it) { auto source_subscript = std::get<0>(*(*it).begin()); auto destination_subscript = std::get<1>(*(*it).begin()); SENode* source_node = scalar_evolution_.SimplifyExpression( scalar_evolution_.AnalyzeInstruction(source_subscript)); SENode* destination_node = scalar_evolution_.SimplifyExpression( scalar_evolution_.AnalyzeInstruction(destination_subscript)); // Check the loops are in a form we support. auto subscript_pair = std::make_pair(source_node, destination_node); const Loop* loop = GetLoopForSubscriptPair(subscript_pair); if (loop) { if (!IsSupportedLoop(loop)) { PrintDebug( "GetDependence found an unsupported loop form. Assuming <=> for " "loop."); DistanceEntry* distance_entry = GetDistanceEntryForSubscriptPair(subscript_pair, distance_vector); if (distance_entry) { distance_entry->direction = DistanceEntry::Directions::ALL; } continue; } } // If either node is simplified to a CanNotCompute we can't perform any // analysis so must assume <=> dependence and return. if (source_node->GetType() == SENode::CanNotCompute || destination_node->GetType() == SENode::CanNotCompute) { // Record the <=> dependence if we can get a DistanceEntry PrintDebug( "GetDependence found source_node || destination_node as " "CanNotCompute. Abandoning evaluation for this subscript."); DistanceEntry* distance_entry = GetDistanceEntryForSubscriptPair(subscript_pair, distance_vector); if (distance_entry) { distance_entry->direction = DistanceEntry::Directions::ALL; } continue; } // We have no induction variables so can apply a ZIV test. if (IsZIV(subscript_pair)) { PrintDebug("Found a ZIV subscript pair"); if (ZIVTest(subscript_pair)) { PrintDebug("Proved independence with ZIVTest."); return true; } } // We have only one induction variable so should attempt an SIV test. if (IsSIV(subscript_pair)) { PrintDebug("Found a SIV subscript pair."); if (SIVTest(subscript_pair, distance_vector)) { PrintDebug("Proved independence with SIVTest."); return true; } } // We have multiple induction variables so should attempt an MIV test. if (IsMIV(subscript_pair)) { PrintDebug("Found a MIV subscript pair."); if (GCDMIVTest(subscript_pair)) { PrintDebug("Proved independence with the GCD test."); auto current_loops = CollectLoops(source_node, destination_node); for (auto current_loop : current_loops) { auto distance_entry = GetDistanceEntryForLoop(current_loop, distance_vector); distance_entry->direction = DistanceEntry::Directions::NONE; } return true; } } } for (auto it = first_coupled; it < std::end(sets_of_subscripts); ++it) { auto coupled_instructions = *it; std::vector<SubscriptPair> coupled_subscripts{}; for (const auto& elem : coupled_instructions) { auto source_subscript = std::get<0>(elem); auto destination_subscript = std::get<1>(elem); SENode* source_node = scalar_evolution_.SimplifyExpression( scalar_evolution_.AnalyzeInstruction(source_subscript)); SENode* destination_node = scalar_evolution_.SimplifyExpression( scalar_evolution_.AnalyzeInstruction(destination_subscript)); coupled_subscripts.push_back({source_node, destination_node}); } auto supported = true; for (const auto& subscript : coupled_subscripts) { auto loops = CollectLoops(std::get<0>(subscript), std::get<1>(subscript)); auto is_subscript_supported = std::all_of(std::begin(loops), std::end(loops), [this](const Loop* l) { return IsSupportedLoop(l); }); supported = supported && is_subscript_supported; } if (DeltaTest(coupled_subscripts, distance_vector)) { return true; } } // We were unable to prove independence so must gather all of the direction // information we found. PrintDebug( "Couldn't prove independence.\n" "All possible direction information has been collected in the input " "DistanceVector."); return false; } bool LoopDependenceAnalysis::ZIVTest( const std::pair<SENode*, SENode*>& subscript_pair) { auto source = std::get<0>(subscript_pair); auto destination = std::get<1>(subscript_pair); PrintDebug("Performing ZIVTest"); // If source == destination, dependence with direction = and distance 0. if (source == destination) { PrintDebug("ZIVTest found EQ dependence."); return false; } else { PrintDebug("ZIVTest found independence."); // Otherwise we prove independence. return true; } } bool LoopDependenceAnalysis::SIVTest( const std::pair<SENode*, SENode*>& subscript_pair, DistanceVector* distance_vector) { DistanceEntry* distance_entry = GetDistanceEntryForSubscriptPair(subscript_pair, distance_vector); if (!distance_entry) { PrintDebug( "SIVTest could not find a DistanceEntry for subscript_pair. Exiting"); } SENode* source_node = std::get<0>(subscript_pair); SENode* destination_node = std::get<1>(subscript_pair); int64_t source_induction_count = CountInductionVariables(source_node); int64_t destination_induction_count = CountInductionVariables(destination_node); // If the source node has no induction variables we can apply a // WeakZeroSrcTest. if (source_induction_count == 0) { PrintDebug("Found source has no induction variable."); if (WeakZeroSourceSIVTest( source_node, destination_node->AsSERecurrentNode(), destination_node->AsSERecurrentNode()->GetCoefficient(), distance_entry)) { PrintDebug("Proved independence with WeakZeroSourceSIVTest."); distance_entry->dependence_information = DistanceEntry::DependenceInformation::DIRECTION; distance_entry->direction = DistanceEntry::Directions::NONE; return true; } } // If the destination has no induction variables we can apply a // WeakZeroDestTest. if (destination_induction_count == 0) { PrintDebug("Found destination has no induction variable."); if (WeakZeroDestinationSIVTest( source_node->AsSERecurrentNode(), destination_node, source_node->AsSERecurrentNode()->GetCoefficient(), distance_entry)) { PrintDebug("Proved independence with WeakZeroDestinationSIVTest."); distance_entry->dependence_information = DistanceEntry::DependenceInformation::DIRECTION; distance_entry->direction = DistanceEntry::Directions::NONE; return true; } } // We now need to collect the SERecurrentExpr nodes from source and // destination. We do not handle cases where source or destination have // multiple SERecurrentExpr nodes. std::vector<SERecurrentNode*> source_recurrent_nodes = source_node->CollectRecurrentNodes(); std::vector<SERecurrentNode*> destination_recurrent_nodes = destination_node->CollectRecurrentNodes(); if (source_recurrent_nodes.size() == 1 && destination_recurrent_nodes.size() == 1) { PrintDebug("Found source and destination have 1 induction variable."); SERecurrentNode* source_recurrent_expr = *source_recurrent_nodes.begin(); SERecurrentNode* destination_recurrent_expr = *destination_recurrent_nodes.begin(); // If the coefficients are identical we can apply a StrongSIVTest. if (source_recurrent_expr->GetCoefficient() == destination_recurrent_expr->GetCoefficient()) { PrintDebug("Found source and destination share coefficient."); if (StrongSIVTest(source_node, destination_node, source_recurrent_expr->GetCoefficient(), distance_entry)) { PrintDebug("Proved independence with StrongSIVTest"); distance_entry->dependence_information = DistanceEntry::DependenceInformation::DIRECTION; distance_entry->direction = DistanceEntry::Directions::NONE; return true; } } // If the coefficients are of equal magnitude and opposite sign we can // apply a WeakCrossingSIVTest. if (source_recurrent_expr->GetCoefficient() == scalar_evolution_.CreateNegation( destination_recurrent_expr->GetCoefficient())) { PrintDebug("Found source coefficient = -destination coefficient."); if (WeakCrossingSIVTest(source_node, destination_node, source_recurrent_expr->GetCoefficient(), distance_entry)) { PrintDebug("Proved independence with WeakCrossingSIVTest"); distance_entry->dependence_information = DistanceEntry::DependenceInformation::DIRECTION; distance_entry->direction = DistanceEntry::Directions::NONE; return true; } } } return false; } bool LoopDependenceAnalysis::StrongSIVTest(SENode* source, SENode* destination, SENode* coefficient, DistanceEntry* distance_entry) { PrintDebug("Performing StrongSIVTest."); // If both source and destination are SERecurrentNodes we can perform tests // based on distance. // If either source or destination contain value unknown nodes or if one or // both are not SERecurrentNodes we must attempt a symbolic test. std::vector<SEValueUnknown*> source_value_unknown_nodes = source->CollectValueUnknownNodes(); std::vector<SEValueUnknown*> destination_value_unknown_nodes = destination->CollectValueUnknownNodes(); if (source_value_unknown_nodes.size() > 0 || destination_value_unknown_nodes.size() > 0) { PrintDebug( "StrongSIVTest found symbolics. Will attempt SymbolicStrongSIVTest."); return SymbolicStrongSIVTest(source, destination, coefficient, distance_entry); } if (!source->AsSERecurrentNode() || !destination->AsSERecurrentNode()) { PrintDebug( "StrongSIVTest could not simplify source and destination to " "SERecurrentNodes so will exit."); distance_entry->direction = DistanceEntry::Directions::ALL; return false; } // Build an SENode for distance. std::pair<SENode*, SENode*> subscript_pair = std::make_pair(source, destination); const Loop* subscript_loop = GetLoopForSubscriptPair(subscript_pair); SENode* source_constant_term = GetConstantTerm(subscript_loop, source->AsSERecurrentNode()); SENode* destination_constant_term = GetConstantTerm(subscript_loop, destination->AsSERecurrentNode()); if (!source_constant_term || !destination_constant_term) { PrintDebug( "StrongSIVTest could not collect the constant terms of either source " "or destination so will exit."); return false; } SENode* constant_term_delta = scalar_evolution_.SimplifyExpression(scalar_evolution_.CreateSubtraction( destination_constant_term, source_constant_term)); // Scalar evolution doesn't perform division, so we must fold to constants and // do it manually. // We must check the offset delta and coefficient are constants. int64_t distance = 0; SEConstantNode* delta_constant = constant_term_delta->AsSEConstantNode(); SEConstantNode* coefficient_constant = coefficient->AsSEConstantNode(); if (delta_constant && coefficient_constant) { int64_t delta_value = delta_constant->FoldToSingleValue(); int64_t coefficient_value = coefficient_constant->FoldToSingleValue(); PrintDebug( "StrongSIVTest found delta value and coefficient value as constants " "with values:\n" "\tdelta value: " + ToString(delta_value) + "\n\tcoefficient value: " + ToString(coefficient_value) + "\n"); // Check if the distance is not integral to try to prove independence. if (delta_value % coefficient_value != 0) { PrintDebug( "StrongSIVTest proved independence through distance not being an " "integer."); distance_entry->dependence_information = DistanceEntry::DependenceInformation::DIRECTION; distance_entry->direction = DistanceEntry::Directions::NONE; return true; } else { distance = delta_value / coefficient_value; PrintDebug("StrongSIV test found distance as " + ToString(distance)); } } else { // If we can't fold delta and coefficient to single values we can't produce // distance. // As a result we can't perform the rest of the pass and must assume // dependence in all directions. PrintDebug("StrongSIVTest could not produce a distance. Must exit."); distance_entry->distance = DistanceEntry::Directions::ALL; return false; } // Next we gather the upper and lower bounds as constants if possible. If // distance > upper_bound - lower_bound we prove independence. SENode* lower_bound = GetLowerBound(subscript_loop); SENode* upper_bound = GetUpperBound(subscript_loop); if (lower_bound && upper_bound) { PrintDebug("StrongSIVTest found bounds."); SENode* bounds = scalar_evolution_.SimplifyExpression( scalar_evolution_.CreateSubtraction(upper_bound, lower_bound)); if (bounds->GetType() == SENode::SENodeType::Constant) { int64_t bounds_value = bounds->AsSEConstantNode()->FoldToSingleValue(); PrintDebug( "StrongSIVTest found upper_bound - lower_bound as a constant with " "value " + ToString(bounds_value)); // If the absolute value of the distance is > upper bound - lower bound // then we prove independence. if (llabs(distance) > llabs(bounds_value)) { PrintDebug( "StrongSIVTest proved independence through distance escaping the " "loop bounds."); distance_entry->dependence_information = DistanceEntry::DependenceInformation::DISTANCE; distance_entry->direction = DistanceEntry::Directions::NONE; distance_entry->distance = distance; return true; } } } else { PrintDebug("StrongSIVTest was unable to gather lower and upper bounds."); } // Otherwise we can get a direction as follows // { < if distance > 0 // direction = { = if distance == 0 // { > if distance < 0 PrintDebug( "StrongSIVTest could not prove independence. Gathering direction " "information."); if (distance > 0) { distance_entry->dependence_information = DistanceEntry::DependenceInformation::DISTANCE; distance_entry->direction = DistanceEntry::Directions::LT; distance_entry->distance = distance; return false; } if (distance == 0) { distance_entry->dependence_information = DistanceEntry::DependenceInformation::DISTANCE; distance_entry->direction = DistanceEntry::Directions::EQ; distance_entry->distance = 0; return false; } if (distance < 0) { distance_entry->dependence_information = DistanceEntry::DependenceInformation::DISTANCE; distance_entry->direction = DistanceEntry::Directions::GT; distance_entry->distance = distance; return false; } // We were unable to prove independence or discern any additional information // Must assume <=> direction. PrintDebug( "StrongSIVTest was unable to determine any dependence information."); distance_entry->direction = DistanceEntry::Directions::ALL; return false; } bool LoopDependenceAnalysis::SymbolicStrongSIVTest( SENode* source, SENode* destination, SENode* coefficient, DistanceEntry* distance_entry) { PrintDebug("Performing SymbolicStrongSIVTest."); SENode* source_destination_delta = scalar_evolution_.SimplifyExpression( scalar_evolution_.CreateSubtraction(source, destination)); // By cancelling out the induction variables by subtracting the source and // destination we can produce an expression of symbolics and constants. This // expression can be compared to the loop bounds to find if the offset is // outwith the bounds. std::pair<SENode*, SENode*> subscript_pair = std::make_pair(source, destination); const Loop* subscript_loop = GetLoopForSubscriptPair(subscript_pair); if (IsProvablyOutsideOfLoopBounds(subscript_loop, source_destination_delta, coefficient)) { PrintDebug( "SymbolicStrongSIVTest proved independence through loop bounds."); distance_entry->dependence_information = DistanceEntry::DependenceInformation::DIRECTION; distance_entry->direction = DistanceEntry::Directions::NONE; return true; } // We were unable to prove independence or discern any additional information. // Must assume <=> direction. PrintDebug( "SymbolicStrongSIVTest was unable to determine any dependence " "information."); distance_entry->direction = DistanceEntry::Directions::ALL; return false; } bool LoopDependenceAnalysis::WeakZeroSourceSIVTest( SENode* source, SERecurrentNode* destination, SENode* coefficient, DistanceEntry* distance_entry) { PrintDebug("Performing WeakZeroSourceSIVTest."); std::pair<SENode*, SENode*> subscript_pair = std::make_pair(source, destination); const Loop* subscript_loop = GetLoopForSubscriptPair(subscript_pair); // Build an SENode for distance. SENode* destination_constant_term = GetConstantTerm(subscript_loop, destination); SENode* delta = scalar_evolution_.SimplifyExpression( scalar_evolution_.CreateSubtraction(source, destination_constant_term)); // Scalar evolution doesn't perform division, so we must fold to constants and // do it manually. int64_t distance = 0; SEConstantNode* delta_constant = delta->AsSEConstantNode(); SEConstantNode* coefficient_constant = coefficient->AsSEConstantNode(); if (delta_constant && coefficient_constant) { PrintDebug( "WeakZeroSourceSIVTest folding delta and coefficient to constants."); int64_t delta_value = delta_constant->FoldToSingleValue(); int64_t coefficient_value = coefficient_constant->FoldToSingleValue(); // Check if the distance is not integral. if (delta_value % coefficient_value != 0) { PrintDebug( "WeakZeroSourceSIVTest proved independence through distance not " "being an integer."); distance_entry->dependence_information = DistanceEntry::DependenceInformation::DIRECTION; distance_entry->direction = DistanceEntry::Directions::NONE; return true; } else { distance = delta_value / coefficient_value; PrintDebug( "WeakZeroSourceSIVTest calculated distance with the following " "values\n" "\tdelta value: " + ToString(delta_value) + "\n\tcoefficient value: " + ToString(coefficient_value) + "\n\tdistance: " + ToString(distance) + "\n"); } } else { PrintDebug( "WeakZeroSourceSIVTest was unable to fold delta and coefficient to " "constants."); } // If we can prove the distance is outside the bounds we prove independence. SEConstantNode* lower_bound = GetLowerBound(subscript_loop)->AsSEConstantNode(); SEConstantNode* upper_bound = GetUpperBound(subscript_loop)->AsSEConstantNode(); if (lower_bound && upper_bound) { PrintDebug("WeakZeroSourceSIVTest found bounds as SEConstantNodes."); int64_t lower_bound_value = lower_bound->FoldToSingleValue(); int64_t upper_bound_value = upper_bound->FoldToSingleValue(); if (!IsWithinBounds(llabs(distance), lower_bound_value, upper_bound_value)) { PrintDebug( "WeakZeroSourceSIVTest proved independence through distance escaping " "the loop bounds."); PrintDebug( "Bound values were as follow\n" "\tlower bound value: " + ToString(lower_bound_value) + "\n\tupper bound value: " + ToString(upper_bound_value) + "\n\tdistance value: " + ToString(distance) + "\n"); distance_entry->dependence_information = DistanceEntry::DependenceInformation::DISTANCE; distance_entry->direction = DistanceEntry::Directions::NONE; distance_entry->distance = distance; return true; } } else { PrintDebug( "WeakZeroSourceSIVTest was unable to find lower and upper bound as " "SEConstantNodes."); } // Now we want to see if we can detect to peel the first or last iterations. // We get the FirstTripValue as GetFirstTripInductionNode() + // GetConstantTerm(destination) SENode* first_trip_SENode = scalar_evolution_.SimplifyExpression(scalar_evolution_.CreateAddNode( GetFirstTripInductionNode(subscript_loop), GetConstantTerm(subscript_loop, destination))); // If source == FirstTripValue, peel_first. if (first_trip_SENode) { PrintDebug("WeakZeroSourceSIVTest built first_trip_SENode."); if (first_trip_SENode->AsSEConstantNode()) { PrintDebug( "WeakZeroSourceSIVTest has found first_trip_SENode as an " "SEConstantNode with value: " + ToString(first_trip_SENode->AsSEConstantNode()->FoldToSingleValue()) + "\n"); } if (source == first_trip_SENode) { // We have found that peeling the first iteration will break dependency. PrintDebug( "WeakZeroSourceSIVTest has found peeling first iteration will break " "dependency"); distance_entry->dependence_information = DistanceEntry::DependenceInformation::PEEL; distance_entry->peel_first = true; return false; } } else { PrintDebug("WeakZeroSourceSIVTest was unable to build first_trip_SENode"); } // We get the LastTripValue as GetFinalTripInductionNode(coefficient) + // GetConstantTerm(destination) SENode* final_trip_SENode = scalar_evolution_.SimplifyExpression(scalar_evolution_.CreateAddNode( GetFinalTripInductionNode(subscript_loop, coefficient), GetConstantTerm(subscript_loop, destination))); // If source == LastTripValue, peel_last. if (final_trip_SENode) { PrintDebug("WeakZeroSourceSIVTest built final_trip_SENode."); if (first_trip_SENode->AsSEConstantNode()) { PrintDebug( "WeakZeroSourceSIVTest has found final_trip_SENode as an " "SEConstantNode with value: " + ToString(final_trip_SENode->AsSEConstantNode()->FoldToSingleValue()) + "\n"); } if (source == final_trip_SENode) { // We have found that peeling the last iteration will break dependency. PrintDebug( "WeakZeroSourceSIVTest has found peeling final iteration will break " "dependency"); distance_entry->dependence_information = DistanceEntry::DependenceInformation::PEEL; distance_entry->peel_last = true; return false; } } else { PrintDebug("WeakZeroSourceSIVTest was unable to build final_trip_SENode"); } // We were unable to prove independence or discern any additional information. // Must assume <=> direction. PrintDebug( "WeakZeroSourceSIVTest was unable to determine any dependence " "information."); distance_entry->direction = DistanceEntry::Directions::ALL; return false; } bool LoopDependenceAnalysis::WeakZeroDestinationSIVTest( SERecurrentNode* source, SENode* destination, SENode* coefficient, DistanceEntry* distance_entry) { PrintDebug("Performing WeakZeroDestinationSIVTest."); // Build an SENode for distance. std::pair<SENode*, SENode*> subscript_pair = std::make_pair(source, destination); const Loop* subscript_loop = GetLoopForSubscriptPair(subscript_pair); SENode* source_constant_term = GetConstantTerm(subscript_loop, source); SENode* delta = scalar_evolution_.SimplifyExpression( scalar_evolution_.CreateSubtraction(destination, source_constant_term)); // Scalar evolution doesn't perform division, so we must fold to constants and // do it manually. int64_t distance = 0; SEConstantNode* delta_constant = delta->AsSEConstantNode(); SEConstantNode* coefficient_constant = coefficient->AsSEConstantNode(); if (delta_constant && coefficient_constant) { PrintDebug( "WeakZeroDestinationSIVTest folding delta and coefficient to " "constants."); int64_t delta_value = delta_constant->FoldToSingleValue(); int64_t coefficient_value = coefficient_constant->FoldToSingleValue(); // Check if the distance is not integral. if (delta_value % coefficient_value != 0) { PrintDebug( "WeakZeroDestinationSIVTest proved independence through distance not " "being an integer."); distance_entry->dependence_information = DistanceEntry::DependenceInformation::DIRECTION; distance_entry->direction = DistanceEntry::Directions::NONE; return true; } else { distance = delta_value / coefficient_value; PrintDebug( "WeakZeroDestinationSIVTest calculated distance with the following " "values\n" "\tdelta value: " + ToString(delta_value) + "\n\tcoefficient value: " + ToString(coefficient_value) + "\n\tdistance: " + ToString(distance) + "\n"); } } else { PrintDebug( "WeakZeroDestinationSIVTest was unable to fold delta and coefficient " "to constants."); } // If we can prove the distance is outside the bounds we prove independence. SEConstantNode* lower_bound = GetLowerBound(subscript_loop)->AsSEConstantNode(); SEConstantNode* upper_bound = GetUpperBound(subscript_loop)->AsSEConstantNode(); if (lower_bound && upper_bound) { PrintDebug("WeakZeroDestinationSIVTest found bounds as SEConstantNodes."); int64_t lower_bound_value = lower_bound->FoldToSingleValue(); int64_t upper_bound_value = upper_bound->FoldToSingleValue(); if (!IsWithinBounds(llabs(distance), lower_bound_value, upper_bound_value)) { PrintDebug( "WeakZeroDestinationSIVTest proved independence through distance " "escaping the loop bounds."); PrintDebug( "Bound values were as follows\n" "\tlower bound value: " + ToString(lower_bound_value) + "\n\tupper bound value: " + ToString(upper_bound_value) + "\n\tdistance value: " + ToString(distance)); distance_entry->dependence_information = DistanceEntry::DependenceInformation::DISTANCE; distance_entry->direction = DistanceEntry::Directions::NONE; distance_entry->distance = distance; return true; } } else { PrintDebug( "WeakZeroDestinationSIVTest was unable to find lower and upper bound " "as SEConstantNodes."); } // Now we want to see if we can detect to peel the first or last iterations. // We get the FirstTripValue as GetFirstTripInductionNode() + // GetConstantTerm(source) SENode* first_trip_SENode = scalar_evolution_.SimplifyExpression( scalar_evolution_.CreateAddNode(GetFirstTripInductionNode(subscript_loop), GetConstantTerm(subscript_loop, source))); // If destination == FirstTripValue, peel_first. if (first_trip_SENode) { PrintDebug("WeakZeroDestinationSIVTest built first_trip_SENode."); if (first_trip_SENode->AsSEConstantNode()) { PrintDebug( "WeakZeroDestinationSIVTest has found first_trip_SENode as an " "SEConstantNode with value: " + ToString(first_trip_SENode->AsSEConstantNode()->FoldToSingleValue()) + "\n"); } if (destination == first_trip_SENode) { // We have found that peeling the first iteration will break dependency. PrintDebug( "WeakZeroDestinationSIVTest has found peeling first iteration will " "break dependency"); distance_entry->dependence_information = DistanceEntry::DependenceInformation::PEEL; distance_entry->peel_first = true; return false; } } else { PrintDebug( "WeakZeroDestinationSIVTest was unable to build first_trip_SENode"); } // We get the LastTripValue as GetFinalTripInductionNode(coefficient) + // GetConstantTerm(source) SENode* final_trip_SENode = scalar_evolution_.SimplifyExpression(scalar_evolution_.CreateAddNode( GetFinalTripInductionNode(subscript_loop, coefficient), GetConstantTerm(subscript_loop, source))); // If destination == LastTripValue, peel_last. if (final_trip_SENode) { PrintDebug("WeakZeroDestinationSIVTest built final_trip_SENode."); if (final_trip_SENode->AsSEConstantNode()) { PrintDebug( "WeakZeroDestinationSIVTest has found final_trip_SENode as an " "SEConstantNode with value: " + ToString(final_trip_SENode->AsSEConstantNode()->FoldToSingleValue()) + "\n"); } if (destination == final_trip_SENode) { // We have found that peeling the last iteration will break dependency. PrintDebug( "WeakZeroDestinationSIVTest has found peeling final iteration will " "break dependency"); distance_entry->dependence_information = DistanceEntry::DependenceInformation::PEEL; distance_entry->peel_last = true; return false; } } else { PrintDebug( "WeakZeroDestinationSIVTest was unable to build final_trip_SENode"); } // We were unable to prove independence or discern any additional information. // Must assume <=> direction. PrintDebug( "WeakZeroDestinationSIVTest was unable to determine any dependence " "information."); distance_entry->direction = DistanceEntry::Directions::ALL; return false; } bool LoopDependenceAnalysis::WeakCrossingSIVTest( SENode* source, SENode* destination, SENode* coefficient, DistanceEntry* distance_entry) { PrintDebug("Performing WeakCrossingSIVTest."); // We currently can't handle symbolic WeakCrossingSIVTests. If either source // or destination are not SERecurrentNodes we must exit. if (!source->AsSERecurrentNode() || !destination->AsSERecurrentNode()) { PrintDebug( "WeakCrossingSIVTest found source or destination != SERecurrentNode. " "Exiting"); distance_entry->direction = DistanceEntry::Directions::ALL; return false; } // Build an SENode for distance. SENode* offset_delta = scalar_evolution_.SimplifyExpression(scalar_evolution_.CreateSubtraction( destination->AsSERecurrentNode()->GetOffset(), source->AsSERecurrentNode()->GetOffset())); // Scalar evolution doesn't perform division, so we must fold to constants and // do it manually. int64_t distance = 0; SEConstantNode* delta_constant = offset_delta->AsSEConstantNode(); SEConstantNode* coefficient_constant = coefficient->AsSEConstantNode(); if (delta_constant && coefficient_constant) { PrintDebug( "WeakCrossingSIVTest folding offset_delta and coefficient to " "constants."); int64_t delta_value = delta_constant->FoldToSingleValue(); int64_t coefficient_value = coefficient_constant->FoldToSingleValue(); // Check if the distance is not integral or if it has a non-integral part // equal to 1/2. if (delta_value % (2 * coefficient_value) != 0 && static_cast<float>(delta_value % (2 * coefficient_value)) / static_cast<float>(2 * coefficient_value) != 0.5) { PrintDebug( "WeakCrossingSIVTest proved independence through distance escaping " "the loop bounds."); distance_entry->dependence_information = DistanceEntry::DependenceInformation::DIRECTION; distance_entry->direction = DistanceEntry::Directions::NONE; return true; } else { distance = delta_value / (2 * coefficient_value); } if (distance == 0) { PrintDebug("WeakCrossingSIVTest found EQ dependence."); distance_entry->dependence_information = DistanceEntry::DependenceInformation::DISTANCE; distance_entry->direction = DistanceEntry::Directions::EQ; distance_entry->distance = 0; return false; } } else { PrintDebug( "WeakCrossingSIVTest was unable to fold offset_delta and coefficient " "to constants."); } // We were unable to prove independence or discern any additional information. // Must assume <=> direction. PrintDebug( "WeakCrossingSIVTest was unable to determine any dependence " "information."); distance_entry->direction = DistanceEntry::Directions::ALL; return false; } // Perform the GCD test if both, the source and the destination nodes, are in // the form a0*i0 + a1*i1 + ... an*in + c. bool LoopDependenceAnalysis::GCDMIVTest( const std::pair<SENode*, SENode*>& subscript_pair) { auto source = std::get<0>(subscript_pair); auto destination = std::get<1>(subscript_pair); // Bail out if source/destination is in an unexpected form. if (!IsInCorrectFormForGCDTest(source) || !IsInCorrectFormForGCDTest(destination)) { return false; } auto source_recurrences = GetAllTopLevelRecurrences(source); auto dest_recurrences = GetAllTopLevelRecurrences(destination); // Bail out if all offsets and coefficients aren't constant. if (!AreOffsetsAndCoefficientsConstant(source_recurrences) || !AreOffsetsAndCoefficientsConstant(dest_recurrences)) { return false; } // Calculate the GCD of all coefficients. auto source_constants = GetAllTopLevelConstants(source); int64_t source_constant = CalculateConstantTerm(source_recurrences, source_constants); auto dest_constants = GetAllTopLevelConstants(destination); int64_t destination_constant = CalculateConstantTerm(dest_recurrences, dest_constants); int64_t delta = std::abs(source_constant - destination_constant); int64_t running_gcd = 0; running_gcd = CalculateGCDFromCoefficients(source_recurrences, running_gcd); running_gcd = CalculateGCDFromCoefficients(dest_recurrences, running_gcd); return delta % running_gcd != 0; } using PartitionedSubscripts = std::vector<std::set<std::pair<Instruction*, Instruction*>>>; PartitionedSubscripts LoopDependenceAnalysis::PartitionSubscripts( const std::vector<Instruction*>& source_subscripts, const std::vector<Instruction*>& destination_subscripts) { PartitionedSubscripts partitions{}; auto num_subscripts = source_subscripts.size(); // Create initial partitions with one subscript pair per partition. for (size_t i = 0; i < num_subscripts; ++i) { partitions.push_back({{source_subscripts[i], destination_subscripts[i]}}); } // Iterate over the loops to create all partitions for (auto loop : loops_) { int64_t k = -1; for (size_t j = 0; j < partitions.size(); ++j) { auto& current_partition = partitions[j]; // Does |loop| appear in |current_partition| auto it = std::find_if( current_partition.begin(), current_partition.end(), [loop, this](const std::pair<Instruction*, Instruction*>& elem) -> bool { auto source_recurrences = scalar_evolution_.AnalyzeInstruction(std::get<0>(elem)) ->CollectRecurrentNodes(); auto destination_recurrences = scalar_evolution_.AnalyzeInstruction(std::get<1>(elem)) ->CollectRecurrentNodes(); source_recurrences.insert(source_recurrences.end(), destination_recurrences.begin(), destination_recurrences.end()); auto loops_in_pair = CollectLoops(source_recurrences); auto end_it = loops_in_pair.end(); return std::find(loops_in_pair.begin(), end_it, loop) != end_it; }); auto has_loop = it != current_partition.end(); if (has_loop) { if (k == -1) { k = j; } else { // Add |partitions[j]| to |partitions[k]| and discard |partitions[j]| partitions[static_cast<size_t>(k)].insert(current_partition.begin(), current_partition.end()); current_partition.clear(); } } } } // Remove empty (discarded) partitions partitions.erase( std::remove_if( partitions.begin(), partitions.end(), [](const std::set<std::pair<Instruction*, Instruction*>>& partition) { return partition.empty(); }), partitions.end()); return partitions; } Constraint* LoopDependenceAnalysis::IntersectConstraints( Constraint* constraint_0, Constraint* constraint_1, const SENode* lower_bound, const SENode* upper_bound) { if (constraint_0->AsDependenceNone()) { return constraint_1; } else if (constraint_1->AsDependenceNone()) { return constraint_0; } // Both constraints are distances. Either the same distance or independent. if (constraint_0->AsDependenceDistance() && constraint_1->AsDependenceDistance()) { auto dist_0 = constraint_0->AsDependenceDistance(); auto dist_1 = constraint_1->AsDependenceDistance(); if (*dist_0->GetDistance() == *dist_1->GetDistance()) { return constraint_0; } else { return make_constraint<DependenceEmpty>(); } } // Both constraints are points. Either the same point or independent. if (constraint_0->AsDependencePoint() && constraint_1->AsDependencePoint()) { auto point_0 = constraint_0->AsDependencePoint(); auto point_1 = constraint_1->AsDependencePoint(); if (*point_0->GetSource() == *point_1->GetSource() && *point_0->GetDestination() == *point_1->GetDestination()) { return constraint_0; } else { return make_constraint<DependenceEmpty>(); } } // Both constraints are lines/distances. if ((constraint_0->AsDependenceDistance() || constraint_0->AsDependenceLine()) && (constraint_1->AsDependenceDistance() || constraint_1->AsDependenceLine())) { auto is_distance_0 = constraint_0->AsDependenceDistance() != nullptr; auto is_distance_1 = constraint_1->AsDependenceDistance() != nullptr; auto a0 = is_distance_0 ? scalar_evolution_.CreateConstant(1) : constraint_0->AsDependenceLine()->GetA(); auto b0 = is_distance_0 ? scalar_evolution_.CreateConstant(-1) : constraint_0->AsDependenceLine()->GetB(); auto c0 = is_distance_0 ? scalar_evolution_.SimplifyExpression( scalar_evolution_.CreateNegation( constraint_0->AsDependenceDistance()->GetDistance())) : constraint_0->AsDependenceLine()->GetC(); auto a1 = is_distance_1 ? scalar_evolution_.CreateConstant(1) : constraint_1->AsDependenceLine()->GetA(); auto b1 = is_distance_1 ? scalar_evolution_.CreateConstant(-1) : constraint_1->AsDependenceLine()->GetB(); auto c1 = is_distance_1 ? scalar_evolution_.SimplifyExpression( scalar_evolution_.CreateNegation( constraint_1->AsDependenceDistance()->GetDistance())) : constraint_1->AsDependenceLine()->GetC(); if (a0->AsSEConstantNode() && b0->AsSEConstantNode() && c0->AsSEConstantNode() && a1->AsSEConstantNode() && b1->AsSEConstantNode() && c1->AsSEConstantNode()) { auto constant_a0 = a0->AsSEConstantNode()->FoldToSingleValue(); auto constant_b0 = b0->AsSEConstantNode()->FoldToSingleValue(); auto constant_c0 = c0->AsSEConstantNode()->FoldToSingleValue(); auto constant_a1 = a1->AsSEConstantNode()->FoldToSingleValue(); auto constant_b1 = b1->AsSEConstantNode()->FoldToSingleValue(); auto constant_c1 = c1->AsSEConstantNode()->FoldToSingleValue(); // a & b can't both be zero, otherwise it wouldn't be line. if (NormalizeAndCompareFractions(constant_a0, constant_b0, constant_a1, constant_b1)) { // Slopes are equal, either parallel lines or the same line. if (constant_b0 == 0 && constant_b1 == 0) { if (NormalizeAndCompareFractions(constant_c0, constant_a0, constant_c1, constant_a1)) { return constraint_0; } return make_constraint<DependenceEmpty>(); } else if (NormalizeAndCompareFractions(constant_c0, constant_b0, constant_c1, constant_b1)) { // Same line. return constraint_0; } else { // Parallel lines can't intersect, report independence. return make_constraint<DependenceEmpty>(); } } else { // Lines are not parallel, therefore, they must intersect. // Calculate intersection. if (upper_bound->AsSEConstantNode() && lower_bound->AsSEConstantNode()) { auto constant_lower_bound = lower_bound->AsSEConstantNode()->FoldToSingleValue(); auto constant_upper_bound = upper_bound->AsSEConstantNode()->FoldToSingleValue(); auto up = constant_b1 * constant_c0 - constant_b0 * constant_c1; // Both b or both a can't be 0, so down is never 0 // otherwise would have entered the parallel line section. auto down = constant_b1 * constant_a0 - constant_b0 * constant_a1; auto x_coord = up / down; int64_t y_coord = 0; int64_t arg1 = 0; int64_t const_b_to_use = 0; if (constant_b1 != 0) { arg1 = constant_c1 - constant_a1 * x_coord; y_coord = arg1 / constant_b1; const_b_to_use = constant_b1; } else if (constant_b0 != 0) { arg1 = constant_c0 - constant_a0 * x_coord; y_coord = arg1 / constant_b0; const_b_to_use = constant_b0; } if (up % down == 0 && arg1 % const_b_to_use == 0 && // Coordinates are integers. constant_lower_bound <= x_coord && // x_coord is within loop bounds. x_coord <= constant_upper_bound && constant_lower_bound <= y_coord && // y_coord is within loop bounds. y_coord <= constant_upper_bound) { // Lines intersect at integer coordinates. return make_constraint<DependencePoint>( scalar_evolution_.CreateConstant(x_coord), scalar_evolution_.CreateConstant(y_coord), constraint_0->GetLoop()); } else { return make_constraint<DependenceEmpty>(); } } else { // Not constants, bail out. return make_constraint<DependenceNone>(); } } } else { // Not constants, bail out. return make_constraint<DependenceNone>(); } } // One constraint is a line/distance and the other is a point. if ((constraint_0->AsDependencePoint() && (constraint_1->AsDependenceLine() || constraint_1->AsDependenceDistance())) || (constraint_1->AsDependencePoint() && (constraint_0->AsDependenceLine() || constraint_0->AsDependenceDistance()))) { auto point_0 = constraint_0->AsDependencePoint() != nullptr; auto point = point_0 ? constraint_0->AsDependencePoint() : constraint_1->AsDependencePoint(); auto line_or_distance = point_0 ? constraint_1 : constraint_0; auto is_distance = line_or_distance->AsDependenceDistance() != nullptr; auto a = is_distance ? scalar_evolution_.CreateConstant(1) : line_or_distance->AsDependenceLine()->GetA(); auto b = is_distance ? scalar_evolution_.CreateConstant(-1) : line_or_distance->AsDependenceLine()->GetB(); auto c = is_distance ? scalar_evolution_.SimplifyExpression( scalar_evolution_.CreateNegation( line_or_distance->AsDependenceDistance()->GetDistance())) : line_or_distance->AsDependenceLine()->GetC(); auto x = point->GetSource(); auto y = point->GetDestination(); if (a->AsSEConstantNode() && b->AsSEConstantNode() && c->AsSEConstantNode() && x->AsSEConstantNode() && y->AsSEConstantNode()) { auto constant_a = a->AsSEConstantNode()->FoldToSingleValue(); auto constant_b = b->AsSEConstantNode()->FoldToSingleValue(); auto constant_c = c->AsSEConstantNode()->FoldToSingleValue(); auto constant_x = x->AsSEConstantNode()->FoldToSingleValue(); auto constant_y = y->AsSEConstantNode()->FoldToSingleValue(); auto left_hand_side = constant_a * constant_x + constant_b * constant_y; if (left_hand_side == constant_c) { // Point is on line, return point return point_0 ? constraint_0 : constraint_1; } else { // Point not on line, report independence (empty constraint). return make_constraint<DependenceEmpty>(); } } else { // Not constants, bail out. return make_constraint<DependenceNone>(); } } return nullptr; } // Propagate constraints function as described in section 5 of Practical // Dependence Testing, Goff, Kennedy, Tseng, 1991. SubscriptPair LoopDependenceAnalysis::PropagateConstraints( const SubscriptPair& subscript_pair, const std::vector<Constraint*>& constraints) { SENode* new_first = subscript_pair.first; SENode* new_second = subscript_pair.second; for (auto& constraint : constraints) { // In the paper this is a[k]. We're extracting the coefficient ('a') of a // recurrent expression with respect to the loop 'k'. SENode* coefficient_of_recurrent = scalar_evolution_.GetCoefficientFromRecurrentTerm( new_first, constraint->GetLoop()); // In the paper this is a'[k]. SENode* coefficient_of_recurrent_prime = scalar_evolution_.GetCoefficientFromRecurrentTerm( new_second, constraint->GetLoop()); if (constraint->GetType() == Constraint::Distance) { DependenceDistance* as_distance = constraint->AsDependenceDistance(); // In the paper this is a[k]*d SENode* rhs = scalar_evolution_.CreateMultiplyNode( coefficient_of_recurrent, as_distance->GetDistance()); // In the paper this is a[k] <- 0 SENode* zeroed_coefficient = scalar_evolution_.BuildGraphWithoutRecurrentTerm( new_first, constraint->GetLoop()); // In the paper this is e <- e - a[k]*d. new_first = scalar_evolution_.CreateSubtraction(zeroed_coefficient, rhs); new_first = scalar_evolution_.SimplifyExpression(new_first); // In the paper this is a'[k] - a[k]. SENode* new_child = scalar_evolution_.SimplifyExpression( scalar_evolution_.CreateSubtraction(coefficient_of_recurrent_prime, coefficient_of_recurrent)); // In the paper this is a'[k]'i[k]. SERecurrentNode* prime_recurrent = scalar_evolution_.GetRecurrentTerm(new_second, constraint->GetLoop()); if (!prime_recurrent) continue; // As we hash the nodes we need to create a new node when we update a // child. SENode* new_recurrent = scalar_evolution_.CreateRecurrentExpression( constraint->GetLoop(), prime_recurrent->GetOffset(), new_child); // In the paper this is a'[k] <- a'[k] - a[k]. new_second = scalar_evolution_.UpdateChildNode( new_second, prime_recurrent, new_recurrent); } } new_second = scalar_evolution_.SimplifyExpression(new_second); return std::make_pair(new_first, new_second); } bool LoopDependenceAnalysis::DeltaTest( const std::vector<SubscriptPair>& coupled_subscripts, DistanceVector* dv_entry) { std::vector<Constraint*> constraints(loops_.size()); std::vector<bool> loop_appeared(loops_.size()); std::generate(std::begin(constraints), std::end(constraints), [this]() { return make_constraint<DependenceNone>(); }); // Separate SIV and MIV subscripts std::vector<SubscriptPair> siv_subscripts{}; std::vector<SubscriptPair> miv_subscripts{}; for (const auto& subscript_pair : coupled_subscripts) { if (IsSIV(subscript_pair)) { siv_subscripts.push_back(subscript_pair); } else { miv_subscripts.push_back(subscript_pair); } } // Delta Test while (!siv_subscripts.empty()) { std::vector<bool> results(siv_subscripts.size()); std::vector<DistanceVector> current_distances( siv_subscripts.size(), DistanceVector(loops_.size())); // Apply SIV test to all SIV subscripts, report independence if any of them // is independent std::transform( std::begin(siv_subscripts), std::end(siv_subscripts), std::begin(current_distances), std::begin(results), [this](SubscriptPair& p, DistanceVector& d) { return SIVTest(p, &d); }); if (std::accumulate(std::begin(results), std::end(results), false, std::logical_or<bool>{})) { return true; } // Derive new constraint vector. std::vector<std::pair<Constraint*, size_t>> all_new_constrants{}; for (size_t i = 0; i < siv_subscripts.size(); ++i) { auto loop = GetLoopForSubscriptPair(siv_subscripts[i]); auto loop_id = std::distance(std::begin(loops_), std::find(std::begin(loops_), std::end(loops_), loop)); loop_appeared[loop_id] = true; auto distance_entry = current_distances[i].GetEntries()[loop_id]; if (distance_entry.dependence_information == DistanceEntry::DependenceInformation::DISTANCE) { // Construct a DependenceDistance. auto node = scalar_evolution_.CreateConstant(distance_entry.distance); all_new_constrants.push_back( {make_constraint<DependenceDistance>(node, loop), loop_id}); } else { // Construct a DependenceLine. const auto& subscript_pair = siv_subscripts[i]; SENode* source_node = std::get<0>(subscript_pair); SENode* destination_node = std::get<1>(subscript_pair); int64_t source_induction_count = CountInductionVariables(source_node); int64_t destination_induction_count = CountInductionVariables(destination_node); SENode* a = nullptr; SENode* b = nullptr; SENode* c = nullptr; if (destination_induction_count != 0) { a = destination_node->AsSERecurrentNode()->GetCoefficient(); c = scalar_evolution_.CreateNegation( destination_node->AsSERecurrentNode()->GetOffset()); } else { a = scalar_evolution_.CreateConstant(0); c = scalar_evolution_.CreateNegation(destination_node); } if (source_induction_count != 0) { b = scalar_evolution_.CreateNegation( source_node->AsSERecurrentNode()->GetCoefficient()); c = scalar_evolution_.CreateAddNode( c, source_node->AsSERecurrentNode()->GetOffset()); } else { b = scalar_evolution_.CreateConstant(0); c = scalar_evolution_.CreateAddNode(c, source_node); } a = scalar_evolution_.SimplifyExpression(a); b = scalar_evolution_.SimplifyExpression(b); c = scalar_evolution_.SimplifyExpression(c); all_new_constrants.push_back( {make_constraint<DependenceLine>(a, b, c, loop), loop_id}); } } // Calculate the intersection between the new and existing constraints. std::vector<Constraint*> intersection = constraints; for (const auto& constraint_to_intersect : all_new_constrants) { auto loop_id = std::get<1>(constraint_to_intersect); auto loop = loops_[loop_id]; intersection[loop_id] = IntersectConstraints( intersection[loop_id], std::get<0>(constraint_to_intersect), GetLowerBound(loop), GetUpperBound(loop)); } // Report independence if an empty constraint (DependenceEmpty) is found. auto first_empty = std::find_if(std::begin(intersection), std::end(intersection), [](Constraint* constraint) { return constraint->AsDependenceEmpty() != nullptr; }); if (first_empty != std::end(intersection)) { return true; } std::vector<SubscriptPair> new_siv_subscripts{}; std::vector<SubscriptPair> new_miv_subscripts{}; auto equal = std::equal(std::begin(constraints), std::end(constraints), std::begin(intersection), [](Constraint* a, Constraint* b) { return *a == *b; }); // If any constraints have changed, propagate them into the rest of the // subscripts possibly creating new ZIV/SIV subscripts. if (!equal) { std::vector<SubscriptPair> new_subscripts(miv_subscripts.size()); // Propagate constraints into MIV subscripts std::transform(std::begin(miv_subscripts), std::end(miv_subscripts), std::begin(new_subscripts), [this, &intersection](SubscriptPair& subscript_pair) { return PropagateConstraints(subscript_pair, intersection); }); // If a ZIV subscript is returned, apply test, otherwise, update untested // subscripts. for (auto& subscript : new_subscripts) { if (IsZIV(subscript) && ZIVTest(subscript)) { return true; } else if (IsSIV(subscript)) { new_siv_subscripts.push_back(subscript); } else { new_miv_subscripts.push_back(subscript); } } } // Set new constraints and subscripts to test. std::swap(siv_subscripts, new_siv_subscripts); std::swap(miv_subscripts, new_miv_subscripts); std::swap(constraints, intersection); } // Create the dependence vector from the constraints. for (size_t i = 0; i < loops_.size(); ++i) { // Don't touch entries for loops that weren't tested. if (loop_appeared[i]) { auto current_constraint = constraints[i]; auto& current_distance_entry = (*dv_entry).GetEntries()[i]; if (auto dependence_distance = current_constraint->AsDependenceDistance()) { if (auto constant_node = dependence_distance->GetDistance()->AsSEConstantNode()) { current_distance_entry.dependence_information = DistanceEntry::DependenceInformation::DISTANCE; current_distance_entry.distance = constant_node->FoldToSingleValue(); if (current_distance_entry.distance == 0) { current_distance_entry.direction = DistanceEntry::Directions::EQ; } else if (current_distance_entry.distance < 0) { current_distance_entry.direction = DistanceEntry::Directions::GT; } else { current_distance_entry.direction = DistanceEntry::Directions::LT; } } } else if (auto dependence_point = current_constraint->AsDependencePoint()) { auto source = dependence_point->GetSource(); auto destination = dependence_point->GetDestination(); if (source->AsSEConstantNode() && destination->AsSEConstantNode()) { current_distance_entry = DistanceEntry( source->AsSEConstantNode()->FoldToSingleValue(), destination->AsSEConstantNode()->FoldToSingleValue()); } } } } // Test any remaining MIV subscripts and report independence if found. std::vector<bool> results(miv_subscripts.size()); std::transform(std::begin(miv_subscripts), std::end(miv_subscripts), std::begin(results), [this](const SubscriptPair& p) { return GCDMIVTest(p); }); return std::accumulate(std::begin(results), std::end(results), false, std::logical_or<bool>{}); } } // namespace opt } // namespace spvtools