[Constraint graph] Reinstate the adjacencies of constraint graph nodes.

Reinstate the list of adjacencies in each constraint graph node,
effectively reverting
dfdd352d3d. Exclude one-way constraints
from this computation; we'll handle them separately.
This commit is contained in:
Doug Gregor
2019-08-22 09:38:21 -07:00
parent 1857a37e3b
commit cf1732cce2
2 changed files with 248 additions and 27 deletions

View File

@@ -128,6 +128,74 @@ void ConstraintGraphNode::removeConstraint(Constraint *constraint) {
Constraints.pop_back();
}
ConstraintGraphNode::Adjacency &
ConstraintGraphNode::getAdjacency(TypeVariableType *typeVar) {
assert(typeVar != TypeVar && "Cannot be adjacent to oneself");
// Look for existing adjacency information.
auto pos = AdjacencyInfo.find(typeVar);
if (pos != AdjacencyInfo.end())
return pos->second;
// If we weren't already adjacent to this type variable, add it to the
// list of adjacencies.
pos = AdjacencyInfo.insert(
{ typeVar, { static_cast<unsigned>(Adjacencies.size()), 0 } })
.first;
Adjacencies.push_back(typeVar);
return pos->second;
}
void ConstraintGraphNode::modifyAdjacency(
TypeVariableType *typeVar,
llvm::function_ref<void(Adjacency& adj)> modify) {
// Find the adjacency information.
auto pos = AdjacencyInfo.find(typeVar);
assert(pos != AdjacencyInfo.end() && "Type variables not adjacent");
assert(Adjacencies[pos->second.Index] == typeVar && "Mismatched adjacency");
// Perform the modification .
modify(pos->second);
// If the adjacency is not empty, leave the information in there.
if (!pos->second.empty())
return;
// Remove this adjacency from the mapping.
unsigned index = pos->second.Index;
AdjacencyInfo.erase(pos);
// If this adjacency is last in the vector, just pop it off.
unsigned lastIndex = Adjacencies.size()-1;
if (index == lastIndex) {
Adjacencies.pop_back();
return;
}
// This adjacency is somewhere in the middle; swap it with the last
// adjacency so we can remove the adjacency from the vector in O(1) time
// rather than O(n) time.
auto lastTypeVar = Adjacencies[lastIndex];
Adjacencies[index] = lastTypeVar;
AdjacencyInfo[lastTypeVar].Index = index;
Adjacencies.pop_back();
}
void ConstraintGraphNode::addAdjacency(TypeVariableType *typeVar) {
auto &adjacency = getAdjacency(typeVar);
// Bump the degree of the adjacency.
++adjacency.NumConstraints;
}
void ConstraintGraphNode::removeAdjacency(TypeVariableType *typeVar) {
modifyAdjacency(typeVar, [](Adjacency &adj) {
assert(adj.NumConstraints > 0 && "No adjacency to remove?");
--adj.NumConstraints;
});
}
void ConstraintGraphNode::addToEquivalenceClass(
ArrayRef<TypeVariableType *> typeVars) {
assert(TypeVar == TypeVar->getImpl().getRepresentative(nullptr) &&
@@ -258,52 +326,80 @@ void ConstraintGraph::removeNode(TypeVariableType *typeVar) {
TypeVariables.pop_back();
}
void ConstraintGraph::addConstraint(Constraint *constraint) {
// For the nodes corresponding to each type variable...
/// Enumerate the adjacency edges for the given constraint.
static void enumerateAdjacencies(
Constraint *constraint,
llvm::function_ref<void(TypeVariableType *, TypeVariableType *)> visitor) {
// Don't record adjacencies for one-way constraints.
if (constraint->isOneWayConstraint())
return;
// O(N^2) update for all of the adjacent type variables.
auto referencedTypeVars = constraint->getTypeVariables();
for (auto typeVar : referencedTypeVars) {
// Find the node for this type variable.
auto &node = (*this)[typeVar];
for (auto otherTypeVar : referencedTypeVars) {
if (typeVar == otherTypeVar)
continue;
// Note the constraint within the node for that type variable.
node.addConstraint(constraint);
}
// If the constraint doesn't reference any type variables, it's orphaned;
// track it as such.
if (referencedTypeVars.empty()) {
OrphanedConstraints.push_back(constraint);
visitor(typeVar, otherTypeVar);
}
}
}
void ConstraintGraph::addConstraint(Constraint *constraint) {
// Record the change, if there are active scopes.
if (ActiveScope)
if (ActiveScope) {
Changes.push_back(Change::addedConstraint(constraint));
}
if (constraint->getTypeVariables().empty()) {
// A constraint that doesn't reference any type variables is orphaned;
// track it as such.
OrphanedConstraints.push_back(constraint);
return;
}
// Record this constraint in each type variable.
for (auto typeVar : constraint->getTypeVariables()) {
(*this)[typeVar].addConstraint(constraint);
}
// Record adjacencies.
enumerateAdjacencies(constraint,
[&](TypeVariableType *lhs, TypeVariableType *rhs) {
assert(lhs != rhs);
(*this)[lhs].addAdjacency(rhs);
});
}
void ConstraintGraph::removeConstraint(Constraint *constraint) {
// For the nodes corresponding to each type variable...
auto referencedTypeVars = constraint->getTypeVariables();
for (auto typeVar : referencedTypeVars) {
// Find the node for this type variable.
auto &node = (*this)[typeVar];
// Record the change, if there are active scopes.
if (ActiveScope)
Changes.push_back(Change::removedConstraint(constraint));
// Remove the constraint.
node.removeConstraint(constraint);
}
// If this is an orphaned constraint, remove it from the list.
if (referencedTypeVars.empty()) {
if (constraint->getTypeVariables().empty()) {
// A constraint that doesn't reference any type variables is orphaned;
// remove it from the list of orphaned constraints.
auto known = std::find(OrphanedConstraints.begin(),
OrphanedConstraints.end(),
constraint);
assert(known != OrphanedConstraints.end() && "missing orphaned constraint");
*known = OrphanedConstraints.back();
OrphanedConstraints.pop_back();
return;
}
// Record the change, if there are active scopes.
if (ActiveScope)
Changes.push_back(Change::removedConstraint(constraint));
// Remove the constraint from each type variable.
for (auto typeVar : constraint->getTypeVariables()) {
(*this)[typeVar].removeConstraint(constraint);
}
// Remove all adjacencies for all type variables.
enumerateAdjacencies(constraint,
[&](TypeVariableType *lhs, TypeVariableType *rhs) {
assert(lhs != rhs);
(*this)[lhs].removeAdjacency(rhs);
});
}
void ConstraintGraph::mergeNodes(TypeVariableType *typeVar1,
@@ -1228,6 +1324,28 @@ void ConstraintGraphNode::print(llvm::raw_ostream &out, unsigned indent) {
}
}
if (!Adjacencies.empty()) {
out.indent(indent + 2);
out << "Adjacencies:";
SmallVector<TypeVariableType *, 4> sortedAdjacencies(Adjacencies.begin(),
Adjacencies.end());
std::sort(sortedAdjacencies.begin(), sortedAdjacencies.end(),
[](TypeVariableType *lhs, TypeVariableType *rhs) {
return lhs->getID() < rhs->getID();
});
for (auto adj : sortedAdjacencies) {
out << ' ';
adj->print(out);
auto &info = AdjacencyInfo[adj];
auto degree = info.NumConstraints;
if (degree > 1) {
out << " (" << degree << ")";
}
}
out << "\n";
}
// Print fixed bindings.
if (!FixedBindings.empty()) {
out.indent(indent + 2);
@@ -1396,6 +1514,67 @@ void ConstraintGraphNode::verify(ConstraintGraph &cg) {
requireSameValue(info.first, Constraints[info.second],
"constraint map provides wrong index into vector");
}
// Verify that the adjacency map/vector haven't gotten out of sync.
requireSameValue(Adjacencies.size(), AdjacencyInfo.size(),
"adjacency vector and map have different sizes");
for (auto info : AdjacencyInfo) {
require(info.second.Index < Adjacencies.size(),
"adjacency index out-of-range");
requireSameValue(info.first, Adjacencies[info.second.Index],
"adjacency map provides wrong index into vector");
require(!info.second.empty(),
"adjacency information should have been removed");
require(info.second.NumConstraints <= Constraints.size(),
"adjacency information has higher degree than # of constraints");
}
// Based on the constraints we have, build up a representation of what
// we expect the adjacencies to look like.
llvm::DenseMap<TypeVariableType *, unsigned> expectedAdjacencies;
for (auto constraint : Constraints) {
if (constraint->isOneWayConstraint())
continue;
for (auto adjTypeVar : constraint->getTypeVariables()) {
if (adjTypeVar == TypeVar)
continue;
++expectedAdjacencies[adjTypeVar];
}
}
// Make sure that the adjacencies we expect are the adjacencies we have.
for (auto adj : expectedAdjacencies) {
auto knownAdj = AdjacencyInfo.find(adj.first);
requireWithContext(knownAdj != AdjacencyInfo.end(),
"missing adjacency information for type variable",
[&] {
llvm::dbgs() << " type variable=" << adj.first->getString() << 'n';
});
requireWithContext(adj.second == knownAdj->second.NumConstraints,
"wrong number of adjacencies for type variable",
[&] {
llvm::dbgs() << " type variable=" << adj.first->getString()
<< " (" << adj.second << " vs. "
<< knownAdj->second.NumConstraints
<< ")\n";
});
}
if (AdjacencyInfo.size() != expectedAdjacencies.size()) {
// The adjacency information has something extra in it. Find the
// extraneous type variable.
for (auto adj : AdjacencyInfo) {
requireWithContext(AdjacencyInfo.count(adj.first) > 0,
"extraneous adjacency info for type variable",
[&] {
llvm::dbgs() << " type variable=" << adj.first->getString() << '\n';
});
}
}
#undef requireSameValue
#undef requireWithContext
#undef require