Skip to content

Commit 1dab584

Browse files
committed
[CSOptimizer] Account for speculative scores only when matching operators vs. non-operators
The problem this is trying to solve is eager selection of operators over unsupported disjunctions, when matching operators let's take speculative information into account because it helps to make better choices in this case.
1 parent 073b48c commit 1dab584

File tree

4 files changed

+66
-47
lines changed

4 files changed

+66
-47
lines changed

lib/Sema/CSOptimizer.cpp

Lines changed: 45 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,20 @@ struct DisjunctionInfo {
4141
/// The score of the disjunction is the highest score from its choices.
4242
/// If the score is nullopt it means that the disjunction is not optimizable.
4343
std::optional<double> Score;
44+
45+
/// Whether the decisions were based on speculative information
46+
/// i.e. literal argument candidates or initializer type inference.
47+
bool IsSpeculative = false;
48+
4449
/// The highest scoring choices that could be favored when disjunction
4550
/// is attempted.
4651
llvm::TinyPtrVector<Constraint *> FavoredChoices;
4752

4853
DisjunctionInfo() = default;
49-
DisjunctionInfo(double score, ArrayRef<Constraint *> favoredChoices = {})
50-
: Score(score), FavoredChoices(favoredChoices) {}
54+
DisjunctionInfo(double score, bool speculative = false,
55+
ArrayRef<Constraint *> favoredChoices = {})
56+
: Score(score), IsSpeculative(speculative),
57+
FavoredChoices(favoredChoices) {}
5158
};
5259

5360
static DeclContext *getDisjunctionDC(Constraint *disjunction) {
@@ -656,7 +663,7 @@ static std::optional<DisjunctionInfo> preserveFavoringOfUnlabeledUnaryArgument(
656663
});
657664

658665
return DisjunctionInfo(/*score=*/favoredChoices.empty() ? 0 : 1,
659-
favoredChoices);
666+
/*speculative=*/false, favoredChoices);
660667
}
661668

662669
} // end anonymous namespace
@@ -720,7 +727,8 @@ static void determineBestChoicesInContext(
720727
return decl &&
721728
!decl->getInterfaceType()->is<AnyFunctionType>();
722729
});
723-
recordResult(disjunction, {/*score=*/1.0, favoredChoices});
730+
recordResult(disjunction, {/*score=*/1.0, /*speculative=*/false,
731+
favoredChoices});
724732
continue;
725733
}
726734

@@ -760,7 +768,8 @@ static void determineBestChoicesInContext(
760768
});
761769

762770
if (!favoredChoices.empty()) {
763-
recordResult(disjunction, {/*score=*/0.01, favoredChoices});
771+
recordResult(disjunction,
772+
{/*score=*/0.01, /*speculative=*/false, favoredChoices});
764773
continue;
765774
}
766775
}
@@ -1577,41 +1586,24 @@ static void determineBestChoicesInContext(
15771586

15781587
bestOverallScore = std::max(bestOverallScore, bestScore);
15791588

1580-
DisjunctionInfo info(/*score=*/bestScore);
1589+
// Determine if the score and favoring decisions here are
1590+
// based only on "speculative" sources i.e. inference from
1591+
// literals.
1592+
//
1593+
// This information is going to be used by the disjunction
1594+
// selection algorithm to prevent over-eager selection of
1595+
// the operators over unsupported non-operator declarations.
1596+
bool isSpeculative = onlySpeculativeArgumentCandidates &&
1597+
(!canUseContextualResultTypes ||
1598+
!anyNonSpeculativeResultTypes(resultTypes));
1599+
1600+
DisjunctionInfo info(/*score=*/bestScore, isSpeculative);
15811601

15821602
for (const auto &choice : favoredChoices) {
15831603
if (choice.second == bestScore)
15841604
info.FavoredChoices.push_back(choice.first);
15851605
}
15861606

1587-
// Reset operator disjunction score but keep favoring
1588-
// choices only available candidates where speculative
1589-
// with no contextual information available, standard
1590-
// comparison operators are a special cases because
1591-
// their return type is fixed to `Bool` unlike that of
1592-
// bitwise, arithmetic, and other operators.
1593-
//
1594-
// This helps to prevent over-eager selection of the
1595-
// operators over unsupported non-operator declarations.
1596-
if (isOperator && onlySpeculativeArgumentCandidates &&
1597-
(!canUseContextualResultTypes ||
1598-
!anyNonSpeculativeResultTypes(resultTypes))) {
1599-
if (cs.isDebugMode()) {
1600-
PrintOptions PO;
1601-
PO.PrintTypesForDebugging = true;
1602-
1603-
llvm::errs().indent(cs.solverState->getCurrentIndent())
1604-
<< "<<< Disjunction "
1605-
<< disjunction->getNestedConstraints()[0]
1606-
->getFirstType()
1607-
->getString(PO)
1608-
<< " score " << bestScore
1609-
<< " was reset due to having only speculative candidates\n";
1610-
}
1611-
1612-
info.Score = 0;
1613-
}
1614-
16151607
recordResult(disjunction, std::move(info));
16161608
}
16171609

@@ -1723,8 +1715,25 @@ ConstraintSystem::selectDisjunction() {
17231715
if (auto preference = isPreferable(*this, first, second))
17241716
return preference.value();
17251717

1726-
auto &[firstScore, firstFavoredChoices] = favorings[first];
1727-
auto &[secondScore, secondFavoredChoices] = favorings[second];
1718+
auto &[firstScore, isFirstSpeculative, firstFavoredChoices] =
1719+
favorings[first];
1720+
auto &[secondScore, isSecondSpeculative, secondFavoredChoices] =
1721+
favorings[second];
1722+
1723+
bool isFirstOperator = isOperatorDisjunction(first);
1724+
bool isSecondOperator = isOperatorDisjunction(second);
1725+
1726+
// Not all of the non-operator disjunctions are supported by the
1727+
// ranking algorithm, so to prevent eager selection of operators
1728+
// when anything concrete is known about them, let's reset the score
1729+
// and compare purely based on number of choices.
1730+
if (isFirstOperator != isSecondOperator) {
1731+
if (isFirstOperator && isFirstSpeculative)
1732+
firstScore = 0;
1733+
1734+
if (isSecondOperator && isSecondSpeculative)
1735+
secondScore = 0;
1736+
}
17281737

17291738
// Rank based on scores only if both disjunctions are supported.
17301739
if (firstScore && secondScore) {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %scale-test --begin 1 --end 10 --step 1 --select NumLeafScopes %s
2+
// REQUIRES: asserts,no_asan
3+
4+
struct Test {
5+
var values: [Int]
6+
}
7+
8+
func test(t: [Test]) {
9+
let _ = 0
10+
+ 1
11+
%for i in range(1, N):
12+
+ 1
13+
%end
14+
+ t.map(\.values.count).reduce(0, +)
15+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// RUN: %target-typecheck-verify-swift -solver-scope-threshold=1000
2+
// REQUIRES: tools-release,no_asan
3+
4+
func rdar31742586() -> Double {
5+
return -(1 + 2) + -(3 + 4) + 5 - (-(1 + 2) + -(3 + 4) + 5)
6+
}

validation-test/Sema/type_checker_perf/slow/rdar31742586.swift

Lines changed: 0 additions & 11 deletions
This file was deleted.

0 commit comments

Comments
 (0)