Skip to content

Commit f8eee49

Browse files
committed
[Constraint solver] De-priority SIMD operators.
The new SIMD proposal introduced a number of new operators, the presence of which causes more "expression too complex" failures. Route around the problem by de-prioritizing those operators, visiting them only if no other operator could be chosen. This should limit the type checker performance cost of said operators to only those expressions that need them OR that already failed to type-check. Fixes rdar://problem/46541800. (cherry picked from commit 88d34a1)
1 parent 4affa87 commit f8eee49

File tree

4 files changed

+133
-0
lines changed

4 files changed

+133
-0
lines changed

lib/Sema/CSStep.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,24 @@ bool DisjunctionStep::shouldStopAt(const DisjunctionChoice &choice) const {
490490
shortCircuitDisjunctionAt(choice, lastChoice));
491491
}
492492

493+
bool swift::isSIMDOperator(ValueDecl *value) {
494+
auto func = dyn_cast<FuncDecl>(value);
495+
if (!func)
496+
return false;
497+
498+
if (!func->isOperator())
499+
return false;
500+
501+
auto nominal = func->getDeclContext()->getSelfNominalTypeDecl();
502+
if (!nominal)
503+
return false;
504+
505+
if (nominal->getName().empty())
506+
return false;
507+
508+
return nominal->getName().str().startswith_lower("simd");
509+
}
510+
493511
bool DisjunctionStep::shortCircuitDisjunctionAt(
494512
Constraint *currentChoice, Constraint *lastSuccessfulChoice) const {
495513
auto &ctx = CS.getASTContext();
@@ -535,6 +553,18 @@ bool DisjunctionStep::shortCircuitDisjunctionAt(
535553
if (currentChoice->getKind() == ConstraintKind::CheckedCast)
536554
return true;
537555

556+
// If we have an operator from the SIMDOperators module, and the prior
557+
// choice was not from the SIMDOperators module, we're done.
558+
if (currentChoice->getKind() == ConstraintKind::BindOverload &&
559+
currentChoice->getOverloadChoice().isDecl() &&
560+
isSIMDOperator(currentChoice->getOverloadChoice().getDecl()) &&
561+
lastSuccessfulChoice->getKind() == ConstraintKind::BindOverload &&
562+
lastSuccessfulChoice->getOverloadChoice().isDecl() &&
563+
!isSIMDOperator(lastSuccessfulChoice->getOverloadChoice().getDecl()) &&
564+
!ctx.LangOpts.SolverEnableOperatorDesignatedTypes) {
565+
return true;
566+
}
567+
538568
return false;
539569
}
540570

lib/Sema/ConstraintSystem.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1443,6 +1443,41 @@ static void tryOptimizeGenericDisjunction(ConstraintSystem &cs,
14431443
}
14441444
}
14451445

1446+
/// If there are any SIMD operators in the overload set, partition the set so
1447+
/// that the SIMD operators come at the end.
1448+
static ArrayRef<OverloadChoice> partitionSIMDOperators(
1449+
ArrayRef<OverloadChoice> choices,
1450+
SmallVectorImpl<OverloadChoice> &scratch) {
1451+
// If the first element isn't an operator, none of them are.
1452+
if (!choices[0].isDecl() ||
1453+
!isa<FuncDecl>(choices[0].getDecl()) ||
1454+
!cast<FuncDecl>(choices[0].getDecl())->isOperator() ||
1455+
choices[0].getDecl()->getASTContext().LangOpts
1456+
.SolverEnableOperatorDesignatedTypes)
1457+
return choices;
1458+
1459+
// Check whether we have any SIMD operators.
1460+
bool foundSIMDOperator = false;
1461+
for (const auto &choice : choices) {
1462+
if (choice.isDecl() && isSIMDOperator(choice.getDecl())) {
1463+
foundSIMDOperator = true;
1464+
break;
1465+
}
1466+
}
1467+
1468+
if (!foundSIMDOperator)
1469+
return choices;
1470+
1471+
scratch.assign(choices.begin(), choices.end());
1472+
std::stable_partition(scratch.begin(), scratch.end(),
1473+
[](const OverloadChoice &choice) {
1474+
return !choice.isDecl() ||
1475+
!isSIMDOperator(choice.getDecl());
1476+
});
1477+
1478+
return scratch;
1479+
}
1480+
14461481
void ConstraintSystem::addOverloadSet(Type boundType,
14471482
ArrayRef<OverloadChoice> choices,
14481483
DeclContext *useDC,
@@ -1459,6 +1494,9 @@ void ConstraintSystem::addOverloadSet(Type boundType,
14591494

14601495
tryOptimizeGenericDisjunction(*this, choices, favoredChoice);
14611496

1497+
SmallVector<OverloadChoice, 4> scratchChoices;
1498+
choices = partitionSIMDOperators(choices, scratchChoices);
1499+
14621500
SmallVector<Constraint *, 4> overloads;
14631501

14641502
// As we do for other favored constraints, if a favored overload has been

lib/Sema/ConstraintSystem.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3830,6 +3830,9 @@ bool exprNeedsParensOutsideFollowingOperator(
38303830
TypeChecker &TC, DeclContext *DC, Expr *expr, Expr *rootExpr,
38313831
PrecedenceGroupDecl *followingPG);
38323832

3833+
/// Determine whether this is a SIMD operator.
3834+
bool isSIMDOperator(ValueDecl *value);
3835+
38333836
} // end namespace swift
38343837

38353838
#endif // LLVM_SWIFT_SEMA_CONSTRAINT_SYSTEM_H
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// RUN: %target-typecheck-verify-swift
2+
// RUN: %target-typecheck-verify-swift -solver-enable-operator-designated-types
3+
// REQUIRES: OS=macosx
4+
5+
import simd
6+
import CoreGraphics
7+
import Foundation
8+
9+
class SomeView {
10+
func layoutSubviews() {
11+
let descriptionTextViewFrame = CGRect.zero
12+
let availableBounds = CGRect()
13+
let descriptionLabelProperties = SomeView.descriptionTextViewLabelProperties()
14+
let textSize = descriptionTextView.sizeThatFits(availableBounds.size)
15+
let textInset = descriptionTextView.textInset(forBounds: CGRect(origin: .zero, size: textSize))
16+
let descriptionTextBaselineOffset: CGFloat = CGFloat()
17+
let displayScale: CGFloat = CGFloat()
18+
let _ = (descriptionTextViewFrame.height
19+
+ (-descriptionTextView.lastBaselineOffsetFromBottom - textInset.bottom + descriptionLabelProperties.lastBaselineOffsetFromBottom)
20+
+ (-descriptionTextView.firstBaselineOffsetFromTop - textInset.top + descriptionTextBaselineOffset).ceilingValue(scale: displayScale)
21+
)
22+
}
23+
24+
static func descriptionTextViewLabelProperties() -> FontDescriptorBaselineProperties {
25+
fatalError()
26+
}
27+
28+
lazy var descriptionTextView: SomeOtherView = SomeOtherView()
29+
}
30+
31+
class SomeOtherView {
32+
init() { }
33+
func sizeThatFits(_ size: CGSize) -> CGSize { return size }
34+
}
35+
36+
37+
struct FontDescriptorBaselineProperties {
38+
// let fontDescriptor: MPUFontDescriptor
39+
let defaultFirstBaselineOffsetFromTop: CGFloat
40+
let defaultLastBaselineOffsetFromBottom: CGFloat
41+
var firstBaselineOffsetFromTop: CGFloat { fatalError() }
42+
var lastBaselineOffsetFromBottom: CGFloat {
43+
fatalError()
44+
}
45+
}
46+
47+
struct EdgeInsets {
48+
var top: CGFloat
49+
var bottom: CGFloat
50+
}
51+
52+
extension SomeOtherView {
53+
func textInset(forBounds bounds: CGRect) -> EdgeInsets { fatalError() }
54+
var firstBaselineOffsetFromTop: CGFloat { fatalError() }
55+
var lastBaselineOffsetFromBottom: CGFloat {
56+
fatalError()
57+
}
58+
}
59+
60+
extension CGFloat {
61+
public func ceilingValue(scale: CGFloat) -> CGFloat { fatalError() }
62+
}

0 commit comments

Comments
 (0)