Skip to content

Commit e6f3e7d

Browse files
authored
Merge pull request #16560 from xedin/rdar-29358447
[ConstraintGraph] Fix `contractEdges` to gather constraints only once
2 parents 2c684e5 + 3e25467 commit e6f3e7d

File tree

5 files changed

+115
-73
lines changed

5 files changed

+115
-73
lines changed

include/swift/Basic/Statistics.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,11 @@ FRONTEND_STATISTIC(Sema, NumConformancesDeserialized)
149149
/// expression typechecker did".
150150
FRONTEND_STATISTIC(Sema, NumConstraintScopes)
151151

152+
/// Number of constraints considered per attempt to
153+
/// contract constraint graph edges.
154+
/// This is a measure of complexity of the contraction algorithm.
155+
FRONTEND_STATISTIC(Sema, NumConstraintsConsideredForEdgeContraction)
156+
152157
/// Number of declarations that were deserialized. A rough proxy for the amount
153158
/// of material loaded from other modules.
154159
FRONTEND_STATISTIC(Sema, NumDeclsDeserialized)

lib/Sema/ConstraintGraph.cpp

Lines changed: 80 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "ConstraintGraph.h"
1919
#include "ConstraintGraphScope.h"
2020
#include "ConstraintSystem.h"
21+
#include "swift/Basic/Statistic.h"
2122
#include "llvm/Support/Debug.h"
2223
#include "llvm/Support/SaveAndRestore.h"
2324
#include <algorithm>
@@ -27,6 +28,8 @@
2728
using namespace swift;
2829
using namespace constraints;
2930

31+
#define DEBUG_TYPE "ConstraintGraph"
32+
3033
#pragma mark Graph construction/destruction
3134

3235
ConstraintGraph::ConstraintGraph(ConstraintSystem &cs) : CS(cs) { }
@@ -649,93 +652,89 @@ static bool shouldContractEdge(ConstraintKind kind) {
649652
}
650653

651654
bool ConstraintGraph::contractEdges() {
652-
llvm::SetVector<std::pair<TypeVariableType *,
653-
TypeVariableType *>> contractions;
654-
655-
auto tyvars = getTypeVariables();
656-
auto didContractEdges = false;
655+
SmallVector<Constraint *, 16> constraints;
656+
CS.findConstraints(constraints, [&](const Constraint &constraint) {
657+
// Track how many constraints did contraction algorithm iterated over.
658+
incrementConstraintsPerContractionCounter();
659+
return shouldContractEdge(constraint.getKind());
660+
});
657661

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

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();
666+
// Contract binding edges between type variables.
667+
assert(shouldContractEdge(kind));
669668

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

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

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-
});
675+
if (!(tyvar1 && tyvar2))
676+
continue;
700677

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

700+
return nestedType->is<InOutType>();
701+
});
702+
703+
// If there is at least one non-contractable binding, let's
704+
// not risk contracting this edge.
708705
if (isNotContractable)
709-
continue;
706+
break;
710707
}
708+
}
711709

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

@@ -773,6 +772,14 @@ void ConstraintGraph::optimize() {
773772
while (contractEdges()) {}
774773
}
775774

775+
void ConstraintGraph::incrementConstraintsPerContractionCounter() {
776+
SWIFT_FUNC_STAT;
777+
auto &context = CS.getASTContext();
778+
if (context.Stats)
779+
context.Stats->getFrontendCounters()
780+
.NumConstraintsConsideredForEdgeContraction++;
781+
}
782+
776783
#pragma mark Debugging output
777784

778785
void ConstraintGraphNode::print(llvm::raw_ostream &out, unsigned indent) {

lib/Sema/ConstraintGraph.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,10 @@ class ConstraintGraph {
332332
/// Constraints that are "orphaned" because they contain no type variables.
333333
SmallVector<Constraint *, 4> OrphanedConstraints;
334334

335+
/// Increment the number of constraints considered per attempt
336+
/// to contract constrant graph edges.
337+
void incrementConstraintsPerContractionCounter();
338+
335339
/// The kind of change made to the graph.
336340
enum class ChangeKind {
337341
/// Added a type variable.

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.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// RUN: %scale-test --begin 0 --end 25000 --step 1000 --typecheck --select incrementConstraintsPerContractionCounter %s
2+
// REQUIRES: OS=macosx
3+
// REQUIRES: asserts
4+
5+
let _: [Int] = [
6+
%for i in range(0, N):
7+
1,
8+
%end
9+
1
10+
]

0 commit comments

Comments
 (0)