|
18 | 18 | #include "ConstraintGraph.h"
|
19 | 19 | #include "ConstraintGraphScope.h"
|
20 | 20 | #include "ConstraintSystem.h"
|
| 21 | +#include "swift/Basic/Statistic.h" |
21 | 22 | #include "llvm/Support/Debug.h"
|
22 | 23 | #include "llvm/Support/SaveAndRestore.h"
|
23 | 24 | #include <algorithm>
|
|
27 | 28 | using namespace swift;
|
28 | 29 | using namespace constraints;
|
29 | 30 |
|
| 31 | +#define DEBUG_TYPE "ConstraintGraph" |
| 32 | + |
30 | 33 | #pragma mark Graph construction/destruction
|
31 | 34 |
|
32 | 35 | ConstraintGraph::ConstraintGraph(ConstraintSystem &cs) : CS(cs) { }
|
@@ -649,93 +652,89 @@ static bool shouldContractEdge(ConstraintKind kind) {
|
649 | 652 | }
|
650 | 653 |
|
651 | 654 | 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 | + }); |
657 | 661 |
|
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(); |
662 | 665 |
|
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)); |
669 | 668 |
|
670 |
| - auto tyvar1 = t1->getAs<TypeVariableType>(); |
671 |
| - auto tyvar2 = t2->getAs<TypeVariableType>(); |
| 669 | + auto t1 = constraint->getFirstType()->getDesugaredType(); |
| 670 | + auto t2 = constraint->getSecondType()->getDesugaredType(); |
672 | 671 |
|
673 |
| - if (!(tyvar1 && tyvar2)) |
674 |
| - continue; |
| 672 | + auto tyvar1 = t1->getAs<TypeVariableType>(); |
| 673 | + auto tyvar2 = t2->getAs<TypeVariableType>(); |
675 | 674 |
|
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; |
700 | 677 |
|
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; |
705 | 698 | }
|
706 |
| - } |
707 | 699 |
|
| 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. |
708 | 705 | if (isNotContractable)
|
709 |
| - continue; |
| 706 | + break; |
710 | 707 | }
|
| 708 | + } |
711 | 709 |
|
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"; |
735 | 729 | }
|
| 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; |
736 | 736 | }
|
737 | 737 | }
|
738 |
| - |
739 | 738 | return didContractEdges;
|
740 | 739 | }
|
741 | 740 |
|
@@ -773,6 +772,14 @@ void ConstraintGraph::optimize() {
|
773 | 772 | while (contractEdges()) {}
|
774 | 773 | }
|
775 | 774 |
|
| 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 | + |
776 | 783 | #pragma mark Debugging output
|
777 | 784 |
|
778 | 785 | void ConstraintGraphNode::print(llvm::raw_ostream &out, unsigned indent) {
|
|
0 commit comments