Skip to content

Commit 603e535

Browse files
authored
Merge pull request #13102 from slavapestov/unbound-generic-type-mess
Fix speculative devirtualizer issue with nested generic classes
2 parents 85abdcd + 28d3e05 commit 603e535

File tree

6 files changed

+62
-58
lines changed

6 files changed

+62
-58
lines changed

include/swift/SILOptimizer/Utils/Devirtualize.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,19 @@ namespace swift {
4848
/// between optional types.
4949
typedef std::pair<ValueBase *, ApplySite> DevirtualizationResult;
5050

51+
/// Compute all subclasses of a given class.
52+
///
53+
/// \p CHA class hierarchy analysis
54+
/// \p CD class declaration
55+
/// \p ClassType type of the instance
56+
/// \p M SILModule
57+
/// \p Subs a container to be used for storing the set of subclasses
58+
void getAllSubclasses(ClassHierarchyAnalysis *CHA,
59+
ClassDecl *CD,
60+
SILType ClassType,
61+
SILModule &M,
62+
ClassHierarchyAnalysis::ClassList &Subs);
63+
5164
DevirtualizationResult tryDevirtualizeApply(ApplySite AI,
5265
ClassHierarchyAnalysis *CHA);
5366
bool canDevirtualizeApply(FullApplySite AI, ClassHierarchyAnalysis *CHA);

lib/AST/Type.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,9 @@ static bool isLegalSILType(CanType type) {
433433
// L-values and inouts are not legal.
434434
if (!type->isMaterializable()) return false;
435435

436+
// Unbound generic types are not legal.
437+
if (type->hasUnboundGenericType()) return false;
438+
436439
// Function types must be lowered.
437440
if (isa<AnyFunctionType>(type)) return false;
438441

lib/IRGen/GenMeta.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,6 @@ namespace {
133133
/// nominal metadata reference. The structure produced here is
134134
/// consumed by swift_getGenericMetadata() and must correspond to
135135
/// the fill operations that the compiler emits for the bound decl.
136-
///
137-
/// FIXME: Rework to use GenericSignature instead of AllArchetypes
138136
struct GenericArguments {
139137
/// The values to use to initialize the arguments structure.
140138
SmallVector<llvm::Value *, 8> Values;

lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp

Lines changed: 7 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -378,38 +378,8 @@ static bool tryToSpeculateTarget(FullApplySite AI,
378378
// True if any instructions were changed or generated.
379379
bool Changed = false;
380380

381-
// Collect the direct and indirect subclasses for the class.
382-
// Sort these subclasses in the order they should be tested by the
383-
// speculative devirtualization. Different strategies could be used,
384-
// E.g. breadth-first, depth-first, etc.
385-
// Currently, let's use the breadth-first strategy.
386-
// The exact static type of the instance should be tested first.
387-
auto &DirectSubs = CHA->getDirectSubClasses(CD);
388-
auto &IndirectSubs = CHA->getIndirectSubClasses(CD);
389-
390-
SmallVector<ClassDecl *, 8> Subs(DirectSubs);
391-
Subs.append(IndirectSubs.begin(), IndirectSubs.end());
392-
393-
if (ClassType.is<BoundGenericClassType>()) {
394-
// Filter out any subclasses that do not inherit from this
395-
// specific bound class.
396-
auto RemovedIt = std::remove_if(Subs.begin(),
397-
Subs.end(),
398-
[&ClassType](ClassDecl *Sub){
399-
auto SubCanTy = Sub->getDeclaredType()->getCanonicalType();
400-
// Unbound generic type can override a method from
401-
// a bound generic class, but this unbound generic
402-
// class is not considered to be a subclass of a
403-
// bound generic class in a general case.
404-
if (isa<UnboundGenericType>(SubCanTy))
405-
return false;
406-
// Handle the usual case here: the class in question
407-
// should be a real subclass of a bound generic class.
408-
return !ClassType.isBindableToSuperclassOf(
409-
SILType::getPrimitiveObjectType(SubCanTy));
410-
});
411-
Subs.erase(RemovedIt, Subs.end());
412-
}
381+
SmallVector<ClassDecl *, 8> Subs;
382+
getAllSubclasses(CHA, CD, ClassType, M, Subs);
413383

414384
// Number of subclasses which cannot be handled by checked_cast_br checks.
415385
int NotHandledSubsNum = 0;
@@ -484,15 +454,15 @@ static bool tryToSpeculateTarget(FullApplySite AI,
484454
DEBUG(llvm::dbgs() << "Inserting a speculative call for class "
485455
<< CD->getName() << " and subclass " << S->getName() << "\n");
486456

487-
CanType CanClassType = S->getDeclaredType()->getCanonicalType();
488-
SILType ClassType = SILType::getPrimitiveObjectType(CanClassType);
489-
if (!ClassType.getClassOrBoundGenericClass()) {
490-
// This subclass cannot be handled. This happens e.g. if it is
491-
// a generic class.
457+
// FIXME: Add support for generic subclasses.
458+
if (S->isGenericContext()) {
492459
NotHandledSubsNum++;
493460
continue;
494461
}
495462

463+
CanType CanClassType = S->getDeclaredInterfaceType()->getCanonicalType();
464+
SILType ClassType = SILType::getPrimitiveObjectType(CanClassType);
465+
496466
auto ClassOrMetatypeType = ClassType;
497467
if (auto EMT = SubType.getAs<AnyMetatypeType>()) {
498468
auto InstTy = ClassType.getSwiftRValueType();

lib/SILOptimizer/Utils/Devirtualize.cpp

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,7 @@ STATISTIC(NumWitnessDevirt, "Number of witness_method applies devirtualized");
3939
// Class Method Optimization
4040
//===----------------------------------------------------------------------===//
4141

42-
/// Compute all subclasses of a given class.
43-
///
44-
/// \p CHA class hierarchy analysis
45-
/// \p CD class declaration
46-
/// \p ClassType type of the instance
47-
/// \p M SILModule
48-
/// \p Subs a container to be used for storing the set of subclasses
49-
static void getAllSubclasses(ClassHierarchyAnalysis *CHA,
42+
void swift::getAllSubclasses(ClassHierarchyAnalysis *CHA,
5043
ClassDecl *CD,
5144
SILType ClassType,
5245
SILModule &M,
@@ -61,21 +54,17 @@ static void getAllSubclasses(ClassHierarchyAnalysis *CHA,
6154
auto &IndirectSubs = CHA->getIndirectSubClasses(CD);
6255

6356
Subs.append(DirectSubs.begin(), DirectSubs.end());
64-
//SmallVector<ClassDecl *, 8> Subs(DirectSubs);
6557
Subs.append(IndirectSubs.begin(), IndirectSubs.end());
6658

6759
if (ClassType.is<BoundGenericClassType>()) {
6860
// Filter out any subclasses that do not inherit from this
6961
// specific bound class.
7062
auto RemovedIt = std::remove_if(Subs.begin(), Subs.end(),
7163
[&ClassType](ClassDecl *Sub){
72-
auto SubCanTy = Sub->getDeclaredType()->getCanonicalType();
73-
// Unbound generic type can override a method from
74-
// a bound generic class, but this unbound generic
75-
// class is not considered to be a subclass of a
76-
// bound generic class in a general case.
77-
if (isa<UnboundGenericType>(SubCanTy))
64+
// FIXME: Add support for generic subclasses.
65+
if (Sub->isGenericContext())
7866
return false;
67+
auto SubCanTy = Sub->getDeclaredInterfaceType()->getCanonicalType();
7968
// Handle the usual case here: the class in question
8069
// should be a real subclass of a bound generic class.
8170
return !ClassType.isBindableToSuperclassOf(
@@ -94,10 +83,10 @@ static void getAllSubclasses(ClassHierarchyAnalysis *CHA,
9483
/// \p ClassType type of the instance
9584
/// \p CD static class of the instance whose method is being invoked
9685
/// \p CHA class hierarchy analysis
97-
bool isEffectivelyFinalMethod(FullApplySite AI,
98-
SILType ClassType,
99-
ClassDecl *CD,
100-
ClassHierarchyAnalysis *CHA) {
86+
static bool isEffectivelyFinalMethod(FullApplySite AI,
87+
SILType ClassType,
88+
ClassDecl *CD,
89+
ClassHierarchyAnalysis *CHA) {
10190
if (CD && CD->isFinal())
10291
return true;
10392

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// RUN: %target-swift-frontend %s -parse-as-library -O -emit-sil | %FileCheck %s
2+
// RUN: %target-swift-frontend %s -parse-as-library -Osize -emit-sil
3+
//
4+
// Test speculative devirtualization.
5+
6+
public class Outer<T> {
7+
final class Inner : Base {
8+
@inline(never) override func update() {
9+
}
10+
}
11+
}
12+
13+
public class Base {
14+
@inline(never) func update() { }
15+
}
16+
17+
// FIXME: We don't speculate to the override Outer<T>.Inner.update() here,
18+
// because we cannot express the cast -- the cast "returns" a new archetype
19+
// T, much like an opened existential.
20+
//
21+
// But at least, we shouldn't crash.
22+
23+
// CHECK-LABEL: sil [thunk] [always_inline] @_T025devirt_speculative_nested3fooyAA4BaseC1x_tF : $@convention(thin) (@owned Base) -> ()
24+
// CHECK: checked_cast_br [exact] %0 : $Base to $Base
25+
// CHECK: function_ref @_T025devirt_speculative_nested4BaseC6updateyyF
26+
// CHECK: class_method %0 : $Base, #Base.update!1
27+
// CHECK: return
28+
29+
public func foo(x: Base) {
30+
x.update()
31+
}

0 commit comments

Comments
 (0)