From 18da183f384249814cde13a77d2702443ef52dd7 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 12 Oct 2018 00:15:51 -0700 Subject: [PATCH] [CSBindings] Cache already computed type variable bindings To avoid re-computing same bindings over and over again, let's try to keep a cache of active bindings and invalidate them as needed. Invalidate bindings conservatively when new constraints are introduced (even if such constraints are not going to result in new type bindings), and when type variables are assigned fixed types or merged together. Invalidation also makes sure to erase bindings of all adjacent type variables related to equivalence class of the variables in question. This significantly speeds up use-cases that have a lot of type variables such as collection literals with arbitrary nesting. --- lib/Sema/CSBindings.cpp | 18 +++++++----- lib/Sema/CSSimplify.cpp | 1 - lib/Sema/ConstraintSystem.cpp | 4 +++ lib/Sema/ConstraintSystem.h | 29 +++++++++++++++++++ .../fast/rdar30596744_3.swift.gyb | 15 ++++++++++ .../slow/rdar30596744_2.swift.gyb | 2 +- 6 files changed, 60 insertions(+), 9 deletions(-) create mode 100644 validation-test/Sema/type_checker_perf/fast/rdar30596744_3.swift.gyb diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 7ac46bae1de65..77ae9f31ea653 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -25,22 +25,26 @@ Optional ConstraintSystem::determineBestBindings() { // Look for potential type variable bindings. Optional bestBindings; - llvm::SmallDenseMap cache; - // First, let's collect all of the possible bindings. for (auto *typeVar : getTypeVariables()) { if (typeVar->getImpl().hasRepresentativeOrFixed()) continue; + if (Bindings.count(typeVar) > 0) + continue; + if (auto bindings = getPotentialBindings(typeVar)) - cache.insert({typeVar, std::move(bindings)}); + Bindings.insert({typeVar, std::move(bindings)}); } // Now let's see if we could infer something for related type // variables based on other bindings. for (auto *typeVar : getTypeVariables()) { - auto cachedBindings = cache.find(typeVar); - if (cachedBindings == cache.end()) + if (typeVar->getImpl().hasRepresentativeOrFixed()) + continue; + + auto cachedBindings = Bindings.find(typeVar); + if (cachedBindings == Bindings.end()) continue; auto &bindings = cachedBindings->getSecond(); @@ -62,8 +66,8 @@ ConstraintSystem::determineBestBindings() { if (!tv) continue; - auto relatedBindings = cache.find(tv); - if (relatedBindings == cache.end()) + auto relatedBindings = Bindings.find(tv); + if (relatedBindings == Bindings.end()) continue; for (auto &binding : relatedBindings->getSecond().Bindings) { diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 638f8a6e8ed9c..8eab47b0cf280 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -1561,7 +1561,6 @@ ConstraintSystem::matchTypesBindTypeVar( } assignFixedType(typeVar, type); - return getTypeMatchSuccess(); } diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 73d3f1bed204c..5f609854da603 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -232,6 +232,10 @@ void ConstraintSystem::addTypeVariableConstraintsToWorkList( constraint); constraint->setActive(true); } + + // Invalidate pre-computed bindings associated + // with this type variable (if any). + invalidateBindings(typeVar); } /// Retrieve a dynamic result signature for the given declaration. diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index dd72e8708a4f5..cf8f11ab84afc 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1213,6 +1213,7 @@ class ConstraintSystem { /// \param constraint The newly generated constraint. void addGeneratedConstraint(Constraint *constraint) { generatedConstraints.push_back(constraint); + CS.invalidateBindings(constraint->getTypeVariables()); } /// \brief Erase given constraint from the list of generated constraints @@ -2940,6 +2941,12 @@ class ConstraintSystem { } }; + /// Maps type variables to their bindings. Bindings are invalidated + /// and have to be re-computed if new constraints are introduced or + /// in relation to a particular set of type variables or type variables + /// are assigned fixed types or merged together. + llvm::DenseMap Bindings; + Optional checkTypeOfBinding(TypeVariableType *typeVar, Type type, bool *isNilLiteral = nullptr); Optional determineBestBindings(); @@ -3228,6 +3235,28 @@ class ConstraintSystem { SmallVectorImpl &Ordering, SmallVectorImpl &PartitionBeginning); + void invalidateBindings(ArrayRef typeVars) { + llvm::SmallPtrSet visitedVars; + for (auto *typeVar : typeVars) { + if (typeVar != typeVar->getImpl().getRepresentative(nullptr) || + visitedVars.count(typeVar) > 0) + continue; + + // If there was no binding, there is no need to disturb + // other type variables. + Bindings.erase(typeVar); + visitedVars.insert(typeVar); + + for (auto *eqClassVar : CG[typeVar].getEquivalenceClass()) { + for (auto *adjacentVar : CG[eqClassVar].getAdjacencies()) { + auto *var = getRepresentative(adjacentVar); + if (visitedVars.insert(var).second) + Bindings.erase(var); + } + } + } + } + LLVM_ATTRIBUTE_DEPRECATED( void dump() LLVM_ATTRIBUTE_USED, "only for use within the debugger"); diff --git a/validation-test/Sema/type_checker_perf/fast/rdar30596744_3.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar30596744_3.swift.gyb new file mode 100644 index 0000000000000..0deb3d139b296 --- /dev/null +++ b/validation-test/Sema/type_checker_perf/fast/rdar30596744_3.swift.gyb @@ -0,0 +1,15 @@ +// RUN: %scale-test --begin 1 --end 20 --step 1 --select NumLeafScopes %s +// REQUIRES: OS=macosx +// REQUIRES: asserts + +% array_elements = 20 + +let _ = [ +%for i in range(0, N): + ${i} : [ + %for j in range(array_elements): + "e${j}", + %end + ], +%end +] diff --git a/validation-test/Sema/type_checker_perf/slow/rdar30596744_2.swift.gyb b/validation-test/Sema/type_checker_perf/slow/rdar30596744_2.swift.gyb index f706934b1ef8d..9580d897a00f4 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar30596744_2.swift.gyb +++ b/validation-test/Sema/type_checker_perf/slow/rdar30596744_2.swift.gyb @@ -1,4 +1,4 @@ -// RUN: %scale-test --invert-result --begin 1 --end 5 --step 1 --select NumLeafScopes %s --expected-exit-code 1 +// RUN: %scale-test --invert-result --begin 1 --end 4 --step 1 --select NumLeafScopes %s --expected-exit-code 1 // REQUIRES: OS=macosx // REQUIRES: asserts