Skip to content

Commit d1206e4

Browse files
committed
[GSB] Replace depth-first search connected components with union-find.
When computing connected components for the graph of derived same-type constraints within an equivalence, replace the depth-first search with union-find. The latter does not require us to have an out-edges list for each node in the graph.
1 parent e8c9b3f commit d1206e4

File tree

1 file changed

+100
-93
lines changed

1 file changed

+100
-93
lines changed

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 100 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -5381,61 +5381,57 @@ void GenericSignatureBuilder::checkConformanceConstraints(
53815381
}
53825382
}
53835383

5384-
/// Perform a depth-first search from the given potential archetype through
5385-
/// the *implicit* same-type constraints.
5386-
///
5387-
/// \param pa The potential archetype to visit.
5388-
/// \param paToComponent A mapping from each potential archetype to its
5389-
/// component number.
5390-
/// \param component The component number we're currently visiting.
5391-
///
5392-
/// \returns the best archetype anchor seen so far.
5393-
static PotentialArchetype *sameTypeDFS(PotentialArchetype *pa,
5394-
unsigned component,
5395-
llvm::SmallDenseMap<PotentialArchetype *, unsigned> &paToComponent) {
5396-
PotentialArchetype *anchor = pa;
5397-
5398-
// If we've already visited this potential archetype, we're done.
5399-
if (!paToComponent.insert({pa, component}).second) return anchor;
5400-
5401-
// Visit its adjacent potential archetypes.
5402-
for (const auto &constraint : pa->getSameTypeConstraints()) {
5403-
// Treat nested-type-name-match constraints specially.
5404-
if (constraint.source->getRoot()->kind ==
5405-
RequirementSource::NestedTypeNameMatch)
5406-
continue;
5407-
5408-
// Skip non-derived constraints.
5409-
if (!constraint.source->isDerivedRequirement()) continue;
5410-
5411-
auto newAnchor =
5412-
sameTypeDFS(constraint.value, component, paToComponent);
5413-
5414-
// If this type is better than the anchor, use it for the anchor.
5415-
if (compareDependentTypes(&newAnchor, &anchor) < 0)
5416-
anchor = newAnchor;
5417-
}
5418-
5419-
return anchor;
5420-
}
5421-
54225384
namespace swift {
54235385
bool operator<(const DerivedSameTypeComponent &lhs,
54245386
const DerivedSameTypeComponent &rhs) {
54255387
return compareDependentTypes(&lhs.anchor, &rhs.anchor) < 0;
54265388
}
54275389
} // namespace swift
54285390

5391+
/// Find the representative in a simple union-find data structure of
5392+
/// integral values.
5393+
static unsigned findRepresentative(SmallVectorImpl<unsigned> &parents,
5394+
unsigned index) {
5395+
if (parents[index] == index) return index;
5396+
5397+
return parents[index] = findRepresentative(parents, parents[index]);
5398+
}
5399+
5400+
/// Union the same-type components denoted by \c index1 and \c index2.
5401+
///
5402+
/// \param successThreshold Returns true when two sets have been joined
5403+
/// and both representatives are below the threshold. The default of 0
5404+
/// is equivalent to \c successThreshold == parents.size().
5405+
///
5406+
/// \returns \c true if the two components were separate and have now
5407+
/// been joined; \c false if they were already in the same set.
5408+
static bool unionSets(SmallVectorImpl<unsigned> &parents,
5409+
unsigned index1, unsigned index2,
5410+
unsigned successThreshold = 0) {
5411+
// Find the representatives of each component class.
5412+
unsigned rep1 = findRepresentative(parents, index1);
5413+
unsigned rep2 = findRepresentative(parents, index2);
5414+
if (rep1 == rep2) return false;
5415+
5416+
// Point at the lowest-numbered representative.
5417+
if (rep1 < rep2)
5418+
parents[rep2] = rep1;
5419+
else
5420+
parents[rep1] = rep2;
5421+
5422+
return (successThreshold == 0) ||
5423+
(rep1 < successThreshold && rep2 < successThreshold);
5424+
}
5425+
54295426
/// Computes the ordered set of archetype anchors required to form a minimum
54305427
/// spanning tree among the connected components formed by only the derived
5431-
/// same-type requirements within the equivalence class of \c rep.
5428+
/// same-type requirements within the equivalence class \c equivClass.
54325429
///
5433-
/// The equivalence class of the given representative potential archetype
5434-
/// (\c rep) contains all potential archetypes that are made equivalent by
5435-
/// the known set of same-type constraints, which includes both directly-
5436-
/// stated same-type constraints (e.g., \c T.A == T.B) as well as same-type
5437-
/// constraints that are implied either because the names coincide (e.g.,
5438-
/// \c T[.P1].A == T[.P2].A) or due to a requirement in a protocol.
5430+
/// The equivalence class contains all potential archetypes that are made
5431+
/// equivalent by the known set of same-type constraints, which includes both
5432+
/// directly-stated same-type constraints (e.g., \c T.A == T.B) as well as
5433+
/// same-type constraints that are implied either because the names coincide
5434+
/// (e.g., \c T[.P1].A == T[.P2].A) or due to a requirement in a protocol.
54395435
///
54405436
/// The equivalence class of the given representative potential archetype
54415437
/// (\c rep) is formed from a graph whose vertices are the potential archetypes
@@ -5464,21 +5460,68 @@ namespace swift {
54645460
/// canonical edges connects vertex i to vertex i+1 for i in 0..<size-1.
54655461
static void computeDerivedSameTypeComponents(
54665462
GenericSignatureBuilder &builder,
5467-
PotentialArchetype *rep,
5463+
EquivalenceClass *equivClass,
54685464
llvm::SmallDenseMap<PotentialArchetype *, unsigned> &componentOf){
5469-
// Perform a depth-first search to identify the components.
5470-
auto equivClass = rep->getOrCreateEquivalenceClass(builder);
5465+
// Set up the array of "parents" in the union-find data structure.
5466+
llvm::SmallDenseMap<CanType, unsigned> parentIndices;
5467+
SmallVector<unsigned, 4> parents;
5468+
for (unsigned i : indices(equivClass->members)) {
5469+
Type depType = equivClass->members[i]->getDependentType({ });
5470+
parentIndices[depType->getCanonicalType()] = parents.size();
5471+
parents.push_back(i);
5472+
}
5473+
5474+
// Walk all of the same-type constraints, performing a union-find operation.
5475+
for (const auto &entry : equivClass->sameTypeConstraints) {
5476+
for (const auto &constraint : entry.second) {
5477+
// Treat nested-type-name-match constraints specially.
5478+
if (constraint.source->getRoot()->kind ==
5479+
RequirementSource::NestedTypeNameMatch)
5480+
continue;
5481+
5482+
// Skip non-derived constraints.
5483+
if (!constraint.source->isDerivedRequirement()) continue;
5484+
5485+
CanType source =
5486+
constraint.getSubjectDependentType({ })->getCanonicalType();
5487+
CanType target =
5488+
constraint.value->getDependentType({ })->getCanonicalType();
5489+
5490+
assert(parentIndices.count(source) == 1 && "Missing source");
5491+
assert(parentIndices.count(target) == 1 && "Missing target");
5492+
unionSets(parents, parentIndices[source], parentIndices[target]);
5493+
}
5494+
}
5495+
5496+
// Compute and record the components.
54715497
auto &components = equivClass->derivedSameTypeComponents;
5472-
for (auto pa : rep->getEquivalenceClassMembers()) {
5473-
// If we've already seen this potential archetype, there's nothing else to
5474-
// do.
5475-
if (componentOf.count(pa) != 0) continue;
5498+
for (unsigned i : indices(equivClass->members)) {
5499+
auto pa = equivClass->members[i];
5500+
CanType depType = pa->getDependentType({ })->getCanonicalType();
5501+
5502+
// Find the representative of this set.
5503+
assert(parentIndices.count(depType) == 1 && "Unknown member?");
5504+
unsigned index = parentIndices[depType];
5505+
unsigned representative = findRepresentative(parents, index);
5506+
5507+
// If this is the representative, add a component for it.
5508+
if (representative == index) {
5509+
componentOf[pa] = components.size();
5510+
components.push_back(DerivedSameTypeComponent{pa, nullptr});
5511+
continue;
5512+
}
54765513

5477-
// Find all of the potential archetypes within this connected component.
5478-
auto anchor = sameTypeDFS(pa, components.size(), componentOf);
5514+
// This is not the representative; point at the component of the
5515+
// representative.
5516+
auto representativePA = equivClass->members[representative];
5517+
assert(componentOf.count(representativePA) == 1 &&
5518+
"Missing representative component?");
5519+
unsigned componentIndex = componentOf[representativePA];
5520+
componentOf[pa] = componentIndex;
54795521

5480-
// Record the anchor.
5481-
components.push_back({anchor, nullptr});
5522+
// If this is a better anchor, record it.
5523+
if (compareDependentTypes(&pa, &components[componentIndex].anchor) < 0)
5524+
components[componentIndex].anchor = pa;
54825525
}
54835526

54845527
// If there is a concrete type, figure out the best concrete type anchor
@@ -5554,42 +5597,6 @@ void IntercomponentEdge::dump() const {
55545597
llvm::errs() << "\n";
55555598
}
55565599

5557-
/// Find the representative in a simple union-find data structure of
5558-
/// integral values.
5559-
static unsigned findRepresentative(SmallVectorImpl<unsigned> &parents,
5560-
unsigned index) {
5561-
if (parents[index] == index) return index;
5562-
5563-
return parents[index] = findRepresentative(parents, parents[index]);
5564-
}
5565-
5566-
5567-
/// Union the same-type components denoted by \c index1 and \c index2.
5568-
///
5569-
/// \param successThreshold Returns true when two sets have been joined
5570-
/// and both representatives are below the threshold. The default of 0
5571-
/// is equivalent to \c successThreshold == parents.size().
5572-
///
5573-
/// \returns \c true if the two components were separate and have now
5574-
/// been joined; \c false if they were already in the same set.
5575-
static bool unionSets(SmallVectorImpl<unsigned> &parents,
5576-
unsigned index1, unsigned index2,
5577-
unsigned successThreshold = 0) {
5578-
// Find the representatives of each component class.
5579-
unsigned rep1 = findRepresentative(parents, index1);
5580-
unsigned rep2 = findRepresentative(parents, index2);
5581-
if (rep1 == rep2) return false;
5582-
5583-
// Point at the lowest-numbered representative.
5584-
if (rep1 < rep2)
5585-
parents[rep2] = rep1;
5586-
else
5587-
parents[rep1] = rep2;
5588-
5589-
return (successThreshold == 0) ||
5590-
(rep1 < successThreshold && rep2 < successThreshold);
5591-
}
5592-
55935600
/// Determine whether the removal of the given edge will disconnect the
55945601
/// nodes \c from and \c to within the given equivalence class.
55955602
static bool removalDisconnectsEquivalenceClass(
@@ -5883,7 +5890,7 @@ void GenericSignatureBuilder::checkSameTypeConstraints(
58835890
// Compute the components in the subgraph of the same-type constraint graph
58845891
// that includes only derived constraints.
58855892
llvm::SmallDenseMap<PotentialArchetype *, unsigned> componentOf;
5886-
computeDerivedSameTypeComponents(*this, pa, componentOf);
5893+
computeDerivedSameTypeComponents(*this, equivClass, componentOf);
58875894

58885895
// Go through all of the same-type constraints, collecting all of the
58895896
// non-derived constraints to put them into bins: intra-component and

0 commit comments

Comments
 (0)