Skip to content

Commit e021691

Browse files
committed
[ConstraintGraph] Fix contractEdges to gather constraints only once
Currently we have this non-optimal behavior in `contractEdges` where for every type variable it gathers constraints for its whole equivalence class before checking if any are "contractable", instead constraints could be gathered/filtered once which removes a lot of useless work.
1 parent a95a4b9 commit e021691

File tree

2 files changed

+83
-73
lines changed

2 files changed

+83
-73
lines changed

lib/Sema/ConstraintGraph.cpp

Lines changed: 67 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -649,93 +649,87 @@ static bool shouldContractEdge(ConstraintKind kind) {
649649
}
650650

651651
bool ConstraintGraph::contractEdges() {
652-
llvm::SetVector<std::pair<TypeVariableType *,
653-
TypeVariableType *>> contractions;
654-
655-
auto tyvars = getTypeVariables();
656-
auto didContractEdges = false;
652+
SmallVector<Constraint *, 16> constraints;
653+
CS.findConstraints(constraints, [](const Constraint &constraint) {
654+
return shouldContractEdge(constraint.getKind());
655+
});
657656

658-
for (auto tyvar : tyvars) {
659-
SmallVector<Constraint *, 4> constraints;
660-
gatherConstraints(tyvar, constraints,
661-
ConstraintGraph::GatheringKind::EquivalenceClass);
657+
bool didContractEdges = false;
658+
for (auto *constraint : constraints) {
659+
auto kind = constraint->getKind();
662660

663-
for (auto constraint : constraints) {
664-
auto kind = constraint->getKind();
665-
// Contract binding edges between type variables.
666-
if (shouldContractEdge(kind)) {
667-
auto t1 = constraint->getFirstType()->getDesugaredType();
668-
auto t2 = constraint->getSecondType()->getDesugaredType();
661+
// Contract binding edges between type variables.
662+
assert(shouldContractEdge(kind));
669663

670-
auto tyvar1 = t1->getAs<TypeVariableType>();
671-
auto tyvar2 = t2->getAs<TypeVariableType>();
664+
auto t1 = constraint->getFirstType()->getDesugaredType();
665+
auto t2 = constraint->getSecondType()->getDesugaredType();
672666

673-
if (!(tyvar1 && tyvar2))
674-
continue;
667+
auto tyvar1 = t1->getAs<TypeVariableType>();
668+
auto tyvar2 = t2->getAs<TypeVariableType>();
675669

676-
auto isParamBindingConstraint = kind == ConstraintKind::BindParam;
677-
678-
// If the argument is allowed to bind to `inout`, in general,
679-
// it's invalid to contract the edge between argument and parameter,
680-
// but if we can prove that there are no possible bindings
681-
// which result in attempt to bind `inout` type to argument
682-
// type variable, we should go ahead and allow (temporary)
683-
// contraction, because that greatly helps with performance.
684-
// Such action is valid because argument type variable can
685-
// only get its bindings from related overload, which gives
686-
// us enough information to decided on l-valueness.
687-
if (isParamBindingConstraint && tyvar1->getImpl().canBindToInOut()) {
688-
bool isNotContractable = true;
689-
if (auto bindings = CS.getPotentialBindings(tyvar1)) {
690-
for (auto &binding : bindings.Bindings) {
691-
auto type = binding.BindingType;
692-
isNotContractable = type.findIf([&](Type nestedType) -> bool {
693-
if (auto tv = nestedType->getAs<TypeVariableType>()) {
694-
if (!tv->getImpl().mustBeMaterializable())
695-
return true;
696-
}
697-
698-
return nestedType->is<InOutType>();
699-
});
670+
if (!(tyvar1 && tyvar2))
671+
continue;
700672

701-
// If there is at least one non-contractable binding, let's
702-
// not risk contracting this edge.
703-
if (isNotContractable)
704-
break;
673+
auto isParamBindingConstraint = kind == ConstraintKind::BindParam;
674+
675+
// If the argument is allowed to bind to `inout`, in general,
676+
// it's invalid to contract the edge between argument and parameter,
677+
// but if we can prove that there are no possible bindings
678+
// which result in attempt to bind `inout` type to argument
679+
// type variable, we should go ahead and allow (temporary)
680+
// contraction, because that greatly helps with performance.
681+
// Such action is valid because argument type variable can
682+
// only get its bindings from related overload, which gives
683+
// us enough information to decided on l-valueness.
684+
if (isParamBindingConstraint && tyvar1->getImpl().canBindToInOut()) {
685+
bool isNotContractable = true;
686+
if (auto bindings = CS.getPotentialBindings(tyvar1)) {
687+
for (auto &binding : bindings.Bindings) {
688+
auto type = binding.BindingType;
689+
isNotContractable = type.findIf([&](Type nestedType) -> bool {
690+
if (auto tv = nestedType->getAs<TypeVariableType>()) {
691+
if (!tv->getImpl().mustBeMaterializable())
692+
return true;
705693
}
706-
}
707694

695+
return nestedType->is<InOutType>();
696+
});
697+
698+
// If there is at least one non-contractable binding, let's
699+
// not risk contracting this edge.
708700
if (isNotContractable)
709-
continue;
701+
break;
710702
}
703+
}
711704

712-
auto rep1 = CS.getRepresentative(tyvar1);
713-
auto rep2 = CS.getRepresentative(tyvar2);
714-
715-
if (((rep1->getImpl().canBindToLValue() ==
716-
rep2->getImpl().canBindToLValue()) ||
717-
// Allow l-value contractions when binding parameter types.
718-
isParamBindingConstraint)) {
719-
if (CS.TC.getLangOpts().DebugConstraintSolver) {
720-
auto &log = CS.getASTContext().TypeCheckerDebug->getStream();
721-
if (CS.solverState)
722-
log.indent(CS.solverState->depth * 2);
723-
724-
log << "Contracting constraint ";
725-
constraint->print(log, &CS.getASTContext().SourceMgr);
726-
log << "\n";
727-
}
728-
729-
// Merge the edges and remove the constraint.
730-
removeEdge(constraint);
731-
if (rep1 != rep2)
732-
CS.mergeEquivalenceClasses(rep1, rep2, /*updateWorkList*/ false);
733-
didContractEdges = true;
734-
}
705+
if (isNotContractable)
706+
continue;
707+
}
708+
709+
auto rep1 = CS.getRepresentative(tyvar1);
710+
auto rep2 = CS.getRepresentative(tyvar2);
711+
712+
if (((rep1->getImpl().canBindToLValue() ==
713+
rep2->getImpl().canBindToLValue()) ||
714+
// Allow l-value contractions when binding parameter types.
715+
isParamBindingConstraint)) {
716+
if (CS.TC.getLangOpts().DebugConstraintSolver) {
717+
auto &log = CS.getASTContext().TypeCheckerDebug->getStream();
718+
if (CS.solverState)
719+
log.indent(CS.solverState->depth * 2);
720+
721+
log << "Contracting constraint ";
722+
constraint->print(log, &CS.getASTContext().SourceMgr);
723+
log << "\n";
735724
}
725+
726+
// Merge the edges and remove the constraint.
727+
removeEdge(constraint);
728+
if (rep1 != rep2)
729+
CS.mergeEquivalenceClasses(rep1, rep2, /*updateWorkList*/ false);
730+
didContractEdges = true;
736731
}
737732
}
738-
739733
return didContractEdges;
740734
}
741735

lib/Sema/ConstraintSystem.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1909,6 +1909,12 @@ class ConstraintSystem {
19091909
/// due to a change.
19101910
ConstraintList &getActiveConstraints() { return ActiveConstraints; }
19111911

1912+
void findConstraints(SmallVectorImpl<Constraint *> &found,
1913+
llvm::function_ref<bool(const Constraint &)> pred) {
1914+
filterConstraints(ActiveConstraints, pred, found);
1915+
filterConstraints(InactiveConstraints, pred, found);
1916+
}
1917+
19121918
/// \brief Retrieve the representative of the equivalence class containing
19131919
/// this type variable.
19141920
TypeVariableType *getRepresentative(TypeVariableType *typeVar) {
@@ -2074,6 +2080,16 @@ class ConstraintSystem {
20742080
/// into the worklist.
20752081
void addTypeVariableConstraintsToWorkList(TypeVariableType *typeVar);
20762082

2083+
static void
2084+
filterConstraints(ConstraintList &constraints,
2085+
llvm::function_ref<bool(const Constraint &)> pred,
2086+
SmallVectorImpl<Constraint *> &found) {
2087+
for (auto &constraint : constraints) {
2088+
if (pred(constraint))
2089+
found.push_back(&constraint);
2090+
}
2091+
}
2092+
20772093
public:
20782094

20792095
/// \brief Coerce the given expression to an rvalue, if it isn't already.

0 commit comments

Comments
 (0)