Skip to content

Commit 8b61c9b

Browse files
authored
Merge pull request swiftlang#77723 from slavapestov/too-complex-stats
Sema: Progress towards an improved "too complex" check
2 parents 55d51b7 + 7fef225 commit 8b61c9b

File tree

10 files changed

+126
-67
lines changed

10 files changed

+126
-67
lines changed

include/swift/Basic/LangOptions.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,16 @@ namespace swift {
847847
/// than this many seconds.
848848
unsigned ExpressionTimeoutThreshold = 600;
849849

850+
/// The upper bound, in bytes, of temporary data that can be
851+
/// allocated by the constraint solver.
852+
unsigned SolverMemoryThreshold = 512 * 1024 * 1024;
853+
854+
/// The maximum number of scopes we explore before giving up.
855+
unsigned SolverScopeThreshold = 1024 * 1024;
856+
857+
/// The maximum number of trail steps we take before giving up.
858+
unsigned SolverTrailThreshold = 64 * 1024 * 1024;
859+
850860
/// If non-zero, abort the switch statement exhaustiveness checker if
851861
/// the Space::minus function is called more than this many times.
852862
///
@@ -896,12 +906,6 @@ namespace swift {
896906
/// is for testing purposes.
897907
std::vector<std::string> DebugForbidTypecheckPrefixes;
898908

899-
/// The upper bound, in bytes, of temporary data that can be
900-
/// allocated by the constraint solver.
901-
unsigned SolverMemoryThreshold = 512 * 1024 * 1024;
902-
903-
unsigned SolverBindingThreshold = 1024 * 1024;
904-
905909
/// The upper bound to number of sub-expressions unsolved
906910
/// before termination of the shrink phrase of the constraint solver.
907911
unsigned SolverShrinkUnsolvedThreshold = 10;

include/swift/Basic/Statistics.def

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,9 +184,14 @@ FRONTEND_STATISTIC(Sema, NumCrossImportsChecked)
184184
/// Number of pairs of modules we've actually found cross-imports for.
185185
FRONTEND_STATISTIC(Sema, NumCrossImportsFound)
186186

187+
/// Number of steps recorded in the trail while solving expression type
188+
/// constraints. A rough proxy for "how much work the expression
189+
/// type checker did".
190+
FRONTEND_STATISTIC(Sema, NumTrailSteps)
191+
187192
/// Number of constraint-solving scopes created in the typechecker, while
188-
/// solving expression type constraints. A rough proxy for "how much work the
189-
/// expression typechecker did".
193+
/// solving expression type constraints. Another rough proxy for "how much
194+
/// work the expression type checker did".
190195
FRONTEND_STATISTIC(Sema, NumConstraintScopes)
191196

192197
/// Number of constraint-solving scopes that were created but which

include/swift/Option/FrontendOptions.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,10 @@ def enable_volatile_modules : Flag<["-"], "enable-volatile-modules">,
827827

828828
def solver_expression_time_threshold_EQ : Joined<["-"], "solver-expression-time-threshold=">;
829829

830+
def solver_scope_threshold_EQ : Joined<["-"], "solver-scope-threshold=">;
831+
832+
def solver_trail_threshold_EQ : Joined<["-"], "solver-trail-threshold=">;
833+
830834
def solver_disable_shrink :
831835
Flag<["-"], "solver-disable-shrink">,
832836
HelpText<"Disable the shrink phase of expression type checking">;

include/swift/Sema/ConstraintSolverStats.def

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ CS_STATISTIC(NumConjunctionTerms, "# of conjunction terms explored")
2626
CS_STATISTIC(NumSimplifiedConstraints, "# of constraints simplified")
2727
CS_STATISTIC(NumUnsimplifiedConstraints, "# of constraints not simplified")
2828
CS_STATISTIC(NumSimplifyIterations, "# of simplification iterations")
29-
CS_STATISTIC(NumStatesExplored, "# of solution states explored")
29+
CS_STATISTIC(NumSolverScopes, "# of solution states explored")
30+
CS_STATISTIC(NumTrailSteps, "# of changes recorded in the trail")
3031
CS_STATISTIC(NumComponentsSplit, "# of connected components split")
3132
#undef CS_STATISTIC

include/swift/Sema/ConstraintSystem.h

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -250,9 +250,6 @@ class ExpressionTimer {
250250
bool PrintWarning;
251251

252252
public:
253-
/// This constructor sets a default threshold defined for all expressions
254-
/// via compiler flag `solver-expression-time-threshold`.
255-
ExpressionTimer(AnchorType Anchor, ConstraintSystem &CS);
256253
ExpressionTimer(AnchorType Anchor, ConstraintSystem &CS,
257254
unsigned thresholdInSecs);
258255

@@ -2170,13 +2167,20 @@ class ConstraintSystem {
21702167
/// Counter for type variables introduced.
21712168
unsigned TypeCounter = 0;
21722169

2170+
/// The number of changes recorded in the trail so far during the
2171+
/// solution of this constraint system.
2172+
///
2173+
/// This is a rough proxy for how much work the solver did.
2174+
unsigned NumTrailSteps = 0;
2175+
21732176
/// The number of scopes created so far during the solution
21742177
/// of this constraint system.
21752178
///
2176-
/// This is a measure of complexity of the solution space. A new
2177-
/// scope is created every time we attempt a type variable binding
2178-
/// or explore an option in a disjunction.
2179-
unsigned CountScopes = 0;
2179+
/// A new scope is created every time we attempt a type variable
2180+
/// binding or explore an option in a disjunction.
2181+
///
2182+
/// This is a measure of complexity of the solution space.
2183+
unsigned NumSolverScopes = 0;
21802184

21812185
/// High-water mark of measured memory usage in any sub-scope we
21822186
/// explored.
@@ -2539,24 +2543,12 @@ class ConstraintSystem {
25392543
}
25402544

25412545
/// Update statistics when a scope begins.
2542-
unsigned beginScope() {
2543-
++depth;
2544-
maxDepth = std::max(maxDepth, depth);
2545-
2546-
CS.incrementScopeCounter();
2547-
2548-
return NumStatesExplored++;
2549-
}
2546+
unsigned beginScope();
25502547

25512548
/// Update statistics when a scope ends.
2552-
void endScope(unsigned scopeNumber) {
2553-
ASSERT(depth > 0);
2554-
--depth;
2555-
2556-
unsigned countScopesExplored = NumStatesExplored - scopeNumber;
2557-
if (countScopesExplored == 1)
2558-
CS.incrementLeafScopes();
2559-
}
2549+
void endScope(unsigned scopeNumber,
2550+
uint64_t startTrailSteps,
2551+
uint64_t endTrailSteps);
25602552

25612553
/// Check whether constraint system is allowed to form solutions
25622554
/// even with unbound type variables present.
@@ -2760,11 +2752,11 @@ class ConstraintSystem {
27602752
struct SolverScope {
27612753
ConstraintSystem &cs;
27622754

2763-
/// The length of \c TypeVariables.
2764-
unsigned numTypeVariables;
2755+
/// The length of \c TypeVariables at the start of the scope.
2756+
unsigned startTypeVariables;
27652757

2766-
/// The length of \c Trail.
2767-
unsigned numTrailChanges;
2758+
/// The length of \c Trail at the start of the scope.
2759+
uint64_t startTrailSteps;
27682760

27692761
/// The scope number of this scope. Set when the scope is registered.
27702762
unsigned scopeNumber : 31;
@@ -5499,7 +5491,9 @@ class ConstraintSystem {
54995491
/// increase the likelihood that a favored constraint will be successfully
55005492
/// resolved before any others.
55015493
void optimizeConstraints(Expr *e);
5502-
5494+
5495+
void startExpressionTimer(ExpressionTimer::AnchorType anchor);
5496+
55035497
/// Determine if we've already explored too many paths in an
55045498
/// attempt to solve this expression.
55055499
std::pair<bool, SourceRange> isAlreadyTooComplex = {false, SourceRange()};
@@ -5538,9 +5532,16 @@ class ConstraintSystem {
55385532
return true;
55395533
}
55405534

5541-
// Bail out once we've looked at a really large number of
5542-
// choices.
5543-
if (CountScopes > getASTContext().TypeCheckerOpts.SolverBindingThreshold) {
5535+
auto &opts = getASTContext().TypeCheckerOpts;
5536+
5537+
// Bail out once we've looked at a really large number of choices.
5538+
if (opts.SolverScopeThreshold && NumSolverScopes > opts.SolverScopeThreshold) {
5539+
isAlreadyTooComplex.first = true;
5540+
return true;
5541+
}
5542+
5543+
// Bail out once we've taken a really large number of steps.
5544+
if (opts.SolverTrailThreshold && NumTrailSteps > opts.SolverTrailThreshold) {
55445545
isAlreadyTooComplex.first = true;
55455546
return true;
55465547
}

lib/Frontend/CompilerInvocation.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1773,6 +1773,10 @@ static bool ParseTypeCheckerArgs(TypeCheckerOptions &Opts, ArgList &Args,
17731773
Opts.DebugConstraintSolverAttempt);
17741774
setUnsignedIntegerArgument(OPT_solver_memory_threshold,
17751775
Opts.SolverMemoryThreshold);
1776+
setUnsignedIntegerArgument(OPT_solver_scope_threshold_EQ,
1777+
Opts.SolverScopeThreshold);
1778+
setUnsignedIntegerArgument(OPT_solver_trail_threshold_EQ,
1779+
Opts.SolverTrailThreshold);
17761780
setUnsignedIntegerArgument(OPT_solver_shrink_unsolved_threshold,
17771781
Opts.SolverShrinkUnsolvedThreshold);
17781782

lib/Sema/CSSolver.cpp

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -682,7 +682,7 @@ ConstraintSystem::SolverState::~SolverState() {
682682
// Update the "largest" statistics if this system is larger than the
683683
// previous one.
684684
// FIXME: This is not at all thread-safe.
685-
if (NumStatesExplored > LargestNumStatesExplored.getValue()) {
685+
if (NumSolverScopes > LargestNumSolverScopes.getValue()) {
686686
LargestSolutionAttemptNumber = SolutionAttempt-1;
687687
++LargestSolutionAttemptNumber;
688688
#define CS_STATISTIC(Name, Description) \
@@ -695,17 +695,17 @@ ConstraintSystem::SolverState::~SolverState() {
695695

696696
ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs)
697697
: cs(cs),
698-
numTypeVariables(cs.TypeVariables.size()),
699-
numTrailChanges(cs.solverState->Trail.size()),
698+
startTypeVariables(cs.TypeVariables.size()),
699+
startTrailSteps(cs.solverState->Trail.size()),
700700
scopeNumber(cs.solverState->beginScope()),
701701
moved(0) {
702702
ASSERT(!cs.failedConstraint && "Unexpected failed constraint!");
703703
}
704704

705705
ConstraintSystem::SolverScope::SolverScope(SolverScope &&other)
706706
: cs(other.cs),
707-
numTypeVariables(other.numTypeVariables),
708-
numTrailChanges(other.numTrailChanges),
707+
startTypeVariables(other.startTypeVariables),
708+
startTrailSteps(other.startTrailSteps),
709709
scopeNumber(other.scopeNumber),
710710
moved(0) {
711711
other.moved = 1;
@@ -720,7 +720,7 @@ ConstraintSystem::SolverScope::~SolverScope() {
720720
return;
721721

722722
// Roll back introduced type variables.
723-
truncate(cs.TypeVariables, numTypeVariables);
723+
truncate(cs.TypeVariables, startTypeVariables);
724724

725725
// Move any remaining active constraints into the inactive list.
726726
if (!cs.ActiveConstraints.empty()) {
@@ -731,16 +731,43 @@ ConstraintSystem::SolverScope::~SolverScope() {
731731
cs.ActiveConstraints);
732732
}
733733

734+
uint64_t endTrailSteps = cs.solverState->Trail.size();
735+
734736
// Roll back changes to the constraint system.
735-
cs.solverState->Trail.undo(numTrailChanges);
737+
cs.solverState->Trail.undo(startTrailSteps);
736738

737739
// Update statistics.
738-
cs.solverState->endScope(scopeNumber);
740+
cs.solverState->endScope(scopeNumber,
741+
startTrailSteps,
742+
endTrailSteps);
739743

740744
// Clear out other "failed" state.
741745
cs.failedConstraint = nullptr;
742746
}
743747

748+
unsigned ConstraintSystem::SolverState::beginScope() {
749+
++depth;
750+
maxDepth = std::max(maxDepth, depth);
751+
752+
CS.incrementScopeCounter();
753+
754+
return NumSolverScopes++;
755+
}
756+
757+
/// Update statistics when a scope ends.
758+
void ConstraintSystem::SolverState::endScope(unsigned scopeNumber,
759+
uint64_t startTrailSteps,
760+
uint64_t endTrailSteps) {
761+
ASSERT(depth > 0);
762+
--depth;
763+
764+
NumTrailSteps += (endTrailSteps - startTrailSteps);
765+
766+
unsigned countSolverScopes = NumSolverScopes - scopeNumber;
767+
if (countSolverScopes == 1)
768+
CS.incrementLeafScopes();
769+
}
770+
744771
/// Solve the system of constraints.
745772
///
746773
/// \param allowFreeTypeVariables How to bind free type variables in
@@ -795,7 +822,7 @@ bool ConstraintSystem::Candidate::solve(
795822
ConstraintSystem cs(DC, std::nullopt);
796823

797824
// Set up expression type checker timer for the candidate.
798-
cs.Timer.emplace(E, cs);
825+
cs.startExpressionTimer(E);
799826

800827
// Generate constraints for the new system.
801828
if (auto generatedExpr = cs.generateConstraints(E, DC)) {
@@ -1494,7 +1521,7 @@ ConstraintSystem::solveImpl(SyntacticElementTarget &target,
14941521

14951522
// Set up the expression type checker timer.
14961523
if (Expr *expr = target.getAsExpr())
1497-
Timer.emplace(expr, *this);
1524+
startExpressionTimer(expr);
14981525

14991526
if (generateConstraints(target, allowFreeTypeVariables))
15001527
return SolutionResult::forError();
@@ -1529,7 +1556,8 @@ bool ConstraintSystem::solve(SmallVectorImpl<Solution> &solutions,
15291556
if (isDebugMode()) {
15301557
auto &log = llvm::errs();
15311558
log << "\n---Solver statistics---\n";
1532-
log << "Total number of scopes explored: " << solverState->NumStatesExplored << "\n";
1559+
log << "Total number of scopes explored: " << solverState->NumSolverScopes << "\n";
1560+
log << "Total number of trail steps: " << solverState->NumTrailSteps << "\n";
15331561
log << "Maximum depth reached while exploring solutions: " << solverState->maxDepth << "\n";
15341562
if (Timer) {
15351563
auto timeInMillis =
@@ -1679,7 +1707,7 @@ bool ConstraintSystem::solveForCodeCompletion(
16791707
setContextualInfo(expr, target.getExprContextualTypeInfo());
16801708

16811709
// Set up the expression type checker timer.
1682-
Timer.emplace(expr, *this);
1710+
startExpressionTimer(expr);
16831711

16841712
shrink(expr);
16851713
}

lib/Sema/CSStep.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -891,16 +891,18 @@ bool ConjunctionStep::attempt(const ConjunctionElement &element) {
891891
// by dropping all scoring information.
892892
CS.clearScore();
893893

894-
// Reset the scope counter to avoid "too complex" failures
895-
// when closure has a lot of elements in the body.
896-
CS.CountScopes = 0;
894+
// Reset the scope and trail counters to avoid "too complex"
895+
// failures when closure has a lot of elements in the body.
896+
CS.NumSolverScopes = 0;
897+
CS.NumTrailSteps = 0;
897898

898899
// If timer is enabled, let's reset it so that each element
899900
// (expression) gets a fresh time slice to get solved. This
900901
// is important for closures with large number of statements
901902
// in them.
902903
if (CS.Timer) {
903-
CS.Timer.emplace(element.getLocator(), CS);
904+
CS.Timer.reset();
905+
CS.startExpressionTimer(element.getLocator());
904906
}
905907

906908
auto success = element.attempt(CS);

lib/Sema/CSStep.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ template <typename P> class BindingStep : public SolverStep {
542542

543543
if (CS.isDebugMode()) {
544544
CS.solverState->Trail.dumpActiveScopeChanges(
545-
llvm::errs(), ActiveChoice->first.numTrailChanges,
545+
llvm::errs(), ActiveChoice->first.startTrailSteps,
546546
CS.solverState->getCurrentIndent());
547547
}
548548

@@ -893,8 +893,12 @@ class ConjunctionStep : public BindingStep<ConjunctionElementProducer> {
893893
std::optional<Score> BestScore;
894894

895895
/// The number of constraint solver scopes already explored
896-
/// before accepting this conjunction.
897-
llvm::SaveAndRestore<unsigned> OuterScopeCount;
896+
/// before attempting this conjunction.
897+
llvm::SaveAndRestore<unsigned> OuterNumSolverScopes;
898+
899+
/// The number of trail steps already recorded before attempting
900+
/// this conjunction.
901+
llvm::SaveAndRestore<unsigned> OuterNumTrailSteps;
898902

899903
/// The number of milliseconds until outer constraint system
900904
/// is considered "too complex" if timer is enabled.
@@ -929,7 +933,9 @@ class ConjunctionStep : public BindingStep<ConjunctionElementProducer> {
929933
: BindingStep(cs, {cs, conjunction},
930934
conjunction->isIsolated() ? IsolatedSolutions : solutions),
931935
BestScore(getBestScore()),
932-
OuterScopeCount(cs.CountScopes, 0), Conjunction(conjunction),
936+
OuterNumSolverScopes(cs.NumSolverScopes, 0),
937+
OuterNumTrailSteps(cs.NumTrailSteps, 0),
938+
Conjunction(conjunction),
933939
OuterSolutions(solutions) {
934940
assert(conjunction->getKind() == ConstraintKind::Conjunction);
935941

0 commit comments

Comments
 (0)