Skip to content

Commit f07743b

Browse files
committed
[sil-generic-specializer] Don't specialize types which are too wide or too deep
This improves the existing logic which is used to stop specialization for types that are too big to handle. It catches some pathological cases which hang the compiler. Fixes rdar://30938882
1 parent 3ab640d commit f07743b

File tree

2 files changed

+153
-16
lines changed

2 files changed

+153
-16
lines changed

lib/SILOptimizer/Utils/Generics.cpp

Lines changed: 121 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,27 +34,134 @@ llvm::cl::opt<bool> SpecializeGenericSubstitutions(
3434
llvm::cl::init(false),
3535
llvm::cl::desc("Enable partial specialization with generic substitutions"));
3636

37-
// Max depth of a bound generic which can be processed by the generic
37+
// Max depth of a type which can be processed by the generic
3838
// specializer.
3939
// E.g. the depth of Array<Array<Array<T>>> is 3.
4040
// No specializations will be produced, if any of generic parameters contains
41-
// a bound generic type with the depth higher than this threshold
42-
static const unsigned BoundGenericDepthThreshold = 50;
43-
44-
static unsigned getBoundGenericDepth(Type t) {
41+
// a bound generic type with the depth higher than this threshold
42+
static const unsigned TypeDepthThreshold = 50;
43+
// Set the width threshold rather high, because some projects uses very wide
44+
// tuples to model fixed size arrays.
45+
static const unsigned TypeWidthThreshold = 2000;
46+
47+
// Compute the width and the depth of a type.
48+
// We compute both, because some pathological test-cases result in very
49+
// wide types and some others result in very deep types. It is important
50+
// to bail as soon as we hit the threshold on any of both dimentions to
51+
// prevent compiler hangs and crashes.
52+
static std::pair<unsigned, unsigned> getTypeDepthAndWidth(Type t) {
4553
unsigned Depth = 0;
46-
if (auto BGT = t->getAs<BoundGenericType>()) {
54+
unsigned Width = 0;
55+
if (auto *BGT = t->getAs<BoundGenericType>()) {
56+
auto *NTD = BGT->getNominalOrBoundGenericNominal();
57+
if (NTD) {
58+
auto StoredProperties = NTD->getStoredProperties();
59+
Width += std::distance(StoredProperties.begin(), StoredProperties.end());
60+
}
4761
Depth++;
62+
unsigned MaxTypeDepth = 0;
4863
auto GenericArgs = BGT->getGenericArgs();
49-
unsigned MaxGenericArgDepth = 0;
50-
for (auto GenericArg : GenericArgs) {
51-
auto ArgDepth = getBoundGenericDepth(GenericArg);
52-
if (ArgDepth > MaxGenericArgDepth)
53-
MaxGenericArgDepth = ArgDepth;
64+
for (auto Ty : GenericArgs) {
65+
unsigned TypeWidth;
66+
unsigned TypeDepth;
67+
std::tie(TypeDepth, TypeWidth) = getTypeDepthAndWidth(Ty);
68+
if (TypeDepth > MaxTypeDepth)
69+
MaxTypeDepth = TypeDepth;
70+
Width += TypeWidth;
71+
}
72+
Depth += MaxTypeDepth;
73+
return std::make_pair(Depth, Width);
74+
}
75+
76+
if (auto *TupleTy = t->getAs<TupleType>()) {
77+
Width += TupleTy->getNumElements();
78+
Depth++;
79+
unsigned MaxTypeDepth = 0;
80+
auto ElementTypes = TupleTy->getElementTypes();
81+
for (auto Ty : ElementTypes) {
82+
unsigned TypeWidth;
83+
unsigned TypeDepth;
84+
std::tie(TypeDepth, TypeWidth) = getTypeDepthAndWidth(Ty);
85+
if (TypeDepth > MaxTypeDepth)
86+
MaxTypeDepth = TypeDepth;
87+
Width += TypeWidth;
88+
}
89+
Depth += MaxTypeDepth;
90+
return std::make_pair(Depth, Width);
91+
}
92+
93+
if (auto *FnTy = t->getAs<SILFunctionType>()) {
94+
Depth++;
95+
unsigned MaxTypeDepth = 0;
96+
auto Params = FnTy->getParameters();
97+
Width += Params.size();
98+
for (auto Param : Params) {
99+
unsigned TypeWidth;
100+
unsigned TypeDepth;
101+
std::tie(TypeDepth, TypeWidth) = getTypeDepthAndWidth(Param.getType());
102+
if (TypeDepth > MaxTypeDepth)
103+
MaxTypeDepth = TypeDepth;
104+
Width += TypeWidth;
54105
}
55-
Depth += MaxGenericArgDepth;
106+
auto Results = FnTy->getResults();
107+
Width += Results.size();
108+
for (auto Result : Results) {
109+
unsigned TypeWidth;
110+
unsigned TypeDepth;
111+
std::tie(TypeDepth, TypeWidth) = getTypeDepthAndWidth(Result.getType());
112+
if (TypeDepth > MaxTypeDepth)
113+
MaxTypeDepth = TypeDepth;
114+
Width += TypeWidth;
115+
}
116+
if (FnTy->hasErrorResult()) {
117+
Width += 1;
118+
unsigned TypeWidth;
119+
unsigned TypeDepth;
120+
std::tie(TypeDepth, TypeWidth) =
121+
getTypeDepthAndWidth(FnTy->getErrorResult().getType());
122+
if (TypeDepth > MaxTypeDepth)
123+
MaxTypeDepth = TypeDepth;
124+
Width += TypeWidth;
125+
}
126+
Depth += MaxTypeDepth;
127+
return std::make_pair(Depth, Width);
128+
}
129+
130+
if (auto *FnTy = t->getAs<FunctionType>()) {
131+
Depth++;
132+
unsigned MaxTypeDepth = 0;
133+
unsigned TypeWidth;
134+
unsigned TypeDepth;
135+
std::tie(TypeDepth, TypeWidth) = getTypeDepthAndWidth(FnTy->getInput());
136+
if (TypeDepth > MaxTypeDepth)
137+
MaxTypeDepth = TypeDepth;
138+
Width += TypeWidth;
139+
std::tie(TypeDepth, TypeWidth) = getTypeDepthAndWidth(FnTy->getResult());
140+
if (TypeDepth > MaxTypeDepth)
141+
MaxTypeDepth = TypeDepth;
142+
Width += TypeWidth;
143+
Depth += MaxTypeDepth;
144+
return std::make_pair(Depth, Width);
56145
}
57-
return Depth;
146+
147+
if (auto *MT = t->getAs<MetatypeType>()) {
148+
Depth += 1;
149+
unsigned TypeWidth;
150+
unsigned TypeDepth;
151+
std::tie(TypeDepth, TypeWidth) = getTypeDepthAndWidth(MT->getInstanceType());
152+
Width += TypeWidth;
153+
Depth += TypeDepth;
154+
return std::make_pair(Depth, Width);
155+
}
156+
157+
return std::make_pair(Depth, Width);
158+
}
159+
160+
static bool isTypeTooComplex(Type t) {
161+
unsigned TypeWidth;
162+
unsigned TypeDepth;
163+
std::tie(TypeDepth, TypeWidth) = getTypeDepthAndWidth(t);
164+
return TypeWidth >= TypeWidthThreshold || TypeDepth >= TypeDepthThreshold;
58165
}
59166

60167
// =============================================================================
@@ -116,9 +223,7 @@ bool ReabstractionInfo::prepareAndCheck(ApplySite Apply, SILFunction *Callee,
116223
// generated specializations.
117224
for (auto Sub : ParamSubs) {
118225
auto Replacement = Sub.getReplacement();
119-
if (Replacement.findIf([](Type ty) -> bool {
120-
return getBoundGenericDepth(ty) >= BoundGenericDepthThreshold;
121-
})) {
226+
if (isTypeTooComplex(Replacement)) {
122227
DEBUG(llvm::dbgs()
123228
<< " Cannot specialize because the generic type is too deep.\n");
124229
return false;

test/SILOptimizer/specialize_deep_generics.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,35 @@ public func testComputeNat() -> Int32 {
3232
return computeNat(8, Zero())
3333
}
3434

35+
// Check that compiler does not hang producing very wide tuples during
36+
// specialization.
37+
@inline(never)
38+
func computeTuple<T>(t: T) {
39+
computeTuple(t: (t, t))
40+
}
41+
42+
// CHECK-LABEL: sil @_T024specialize_deep_generics16testComputeTupleyyF
43+
public func testComputeTuple() {
44+
computeTuple(t: 0)
45+
}
46+
47+
// Check that compiler does not hang producing very deep metatypes.
48+
@inline(never)
49+
public func computeMetatype<T>(t: T) {
50+
computeMetatype(t: T.self)
51+
}
52+
53+
// CHECK-LABEL: sil @_T024specialize_deep_generics19testComputeMetatypeyyF
54+
public func testComputeMetatype() {
55+
computeMetatype(t: 0)
56+
}
57+
58+
// Check that compiler does not hang producing very deep function types.
59+
@inline(never)
60+
public func computeFunctionType<T>(t: [T]) {
61+
computeFunctionType(t: [{ t[0] }])
62+
}
63+
64+
public func testComputeFunctionType() {
65+
computeFunctionType(t: [0])
66+
}

0 commit comments

Comments
 (0)