Skip to content

Commit 8284688

Browse files
committed
BasicCalleeAnalysis: fix the handling of allocator class methods.
It can result in a runtime crash. This comes up when using required initializers. fixes rdar://problem/29398625
1 parent 5b3cac2 commit 8284688

File tree

3 files changed

+105
-27
lines changed

3 files changed

+105
-27
lines changed

include/swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ class CalleeList {
7676
class CalleeCache {
7777
typedef llvm::SmallVector<SILFunction *, 16> Callees;
7878
typedef llvm::PointerIntPair<Callees *, 1> CalleesAndCanCallUnknown;
79-
typedef llvm::DenseMap<AbstractFunctionDecl *, CalleesAndCanCallUnknown>
79+
typedef llvm::DenseMap<SILDeclRef, CalleesAndCanCallUnknown>
8080
CacheType;
8181

8282
SILModule &M;
@@ -110,10 +110,11 @@ class CalleeCache {
110110
void sortAndUniqueCallees();
111111
CalleesAndCanCallUnknown &getOrCreateCalleesForMethod(SILDeclRef Decl);
112112
void computeClassMethodCalleesForClass(ClassDecl *CD);
113+
void computeClassMethodCallees(ClassDecl *CD, SILDeclRef Method);
113114
void computeWitnessMethodCalleesForWitnessTable(SILWitnessTable &WT);
114115
void computeMethodCallees();
115116
SILFunction *getSingleCalleeForWitnessMethod(WitnessMethodInst *WMI) const;
116-
CalleeList getCalleeList(AbstractFunctionDecl *Decl) const;
117+
CalleeList getCalleeList(SILDeclRef Decl) const;
117118
CalleeList getCalleeList(WitnessMethodInst *WMI) const;
118119
CalleeList getCalleeList(ClassMethodInst *CMI) const;
119120
CalleeList getCalleeListForCalleeKind(SILValue Callee) const;

lib/SILOptimizer/Analysis/BasicCalleeAnalysis.cpp

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,7 @@ void CalleeCache::sortAndUniqueCallees() {
5454

5555
CalleeCache::CalleesAndCanCallUnknown &
5656
CalleeCache::getOrCreateCalleesForMethod(SILDeclRef Decl) {
57-
auto *AFD = cast<AbstractFunctionDecl>(Decl.getDecl());
58-
auto Found = TheCache.find(AFD);
57+
auto Found = TheCache.find(Decl);
5958
if (Found != TheCache.end())
6059
return Found->second;
6160

@@ -66,7 +65,7 @@ CalleeCache::getOrCreateCalleesForMethod(SILDeclRef Decl) {
6665

6766
bool Inserted;
6867
CacheType::iterator It;
69-
std::tie(It, Inserted) = TheCache.insert(std::make_pair(AFD, Entry));
68+
std::tie(It, Inserted) = TheCache.insert(std::make_pair(Decl, Entry));
7069
assert(Inserted && "Expected new entry to be inserted!");
7170

7271
return It->second;
@@ -82,26 +81,38 @@ void CalleeCache::computeClassMethodCalleesForClass(ClassDecl *CD) {
8281
if (!AFD)
8382
continue;
8483

85-
auto Method = SILDeclRef(AFD);
86-
auto *CalledFn = M.lookUpFunctionInVTable(CD, Method);
87-
if (!CalledFn)
88-
continue;
84+
if (auto *ConstrDecl = dyn_cast<ConstructorDecl>(AFD)) {
85+
computeClassMethodCallees(CD, SILDeclRef(AFD,
86+
SILDeclRef::Kind::Initializer));
87+
if (ConstrDecl->isRequired()) {
88+
computeClassMethodCallees(CD, SILDeclRef(AFD,
89+
SILDeclRef::Kind::Allocator));
90+
}
91+
} else {
92+
computeClassMethodCallees(CD, SILDeclRef(AFD));
93+
}
94+
}
95+
}
8996

90-
bool canCallUnknown = !calleesAreStaticallyKnowable(M, Method);
97+
void CalleeCache::computeClassMethodCallees(ClassDecl *CD, SILDeclRef Method) {
98+
auto *CalledFn = M.lookUpFunctionInVTable(CD, Method);
99+
if (!CalledFn)
100+
return;
91101

92-
// Update the callees for this method and all the methods it
93-
// overrides by adding this function to their lists.
94-
do {
95-
auto &TheCallees = getOrCreateCalleesForMethod(Method);
96-
assert(TheCallees.getPointer() && "Unexpected null callees!");
102+
bool canCallUnknown = !calleesAreStaticallyKnowable(M, Method);
97103

98-
TheCallees.getPointer()->push_back(CalledFn);
99-
if (canCallUnknown)
100-
TheCallees.setInt(true);
104+
// Update the callees for this method and all the methods it
105+
// overrides by adding this function to their lists.
106+
do {
107+
auto &TheCallees = getOrCreateCalleesForMethod(Method);
108+
assert(TheCallees.getPointer() && "Unexpected null callees!");
101109

102-
Method = Method.getNextOverriddenVTableEntry();
103-
} while (Method);
104-
}
110+
TheCallees.getPointer()->push_back(CalledFn);
111+
if (canCallUnknown)
112+
TheCallees.setInt(true);
113+
114+
Method = Method.getNextOverriddenVTableEntry();
115+
} while (Method);
105116
}
106117

107118
void CalleeCache::computeWitnessMethodCalleesForWitnessTable(
@@ -154,7 +165,7 @@ CalleeCache::getSingleCalleeForWitnessMethod(WitnessMethodInst *WMI) const {
154165

155166
// Look up the precomputed callees for an abstract function and
156167
// return it as a CalleeList.
157-
CalleeList CalleeCache::getCalleeList(AbstractFunctionDecl *Decl) const {
168+
CalleeList CalleeCache::getCalleeList(SILDeclRef Decl) const {
158169
auto Found = TheCache.find(Decl);
159170
if (Found == TheCache.end())
160171
return CalleeList();
@@ -172,15 +183,13 @@ CalleeList CalleeCache::getCalleeList(WitnessMethodInst *WMI) const {
172183

173184
// Otherwise see if we previously computed the callees based on
174185
// witness tables.
175-
auto *Decl = cast<AbstractFunctionDecl>(WMI->getMember().getDecl());
176-
return getCalleeList(Decl);
186+
return getCalleeList(WMI->getMember());
177187
}
178188

179189
// Return a callee list for a given class method.
180190
CalleeList CalleeCache::getCalleeList(ClassMethodInst *CMI) const {
181191
// Look for precomputed callees based on vtables.
182-
auto *Decl = cast<AbstractFunctionDecl>(CMI->getMember().getDecl());
183-
return getCalleeList(Decl);
192+
return getCalleeList(CMI->getMember());
184193
}
185194

186195
// Return the list of functions that can be called via the given callee.
@@ -232,6 +241,6 @@ CalleeList CalleeCache::getCalleeList(SILInstruction *I) const {
232241
auto Class = Ty.getSwiftRValueType().getClassOrBoundGenericClass();
233242
if (!Class || Class->hasClangNode() || !Class->hasDestructor())
234243
return CalleeList();
235-
auto Destructor = Class->getDestructor();
244+
SILDeclRef Destructor = SILDeclRef(Class->getDestructor());
236245
return getCalleeList(Destructor);
237246
}

test/SILOptimizer/basic-callee-printer.sil

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,3 +597,71 @@ sil_witness_table private private_proto_public_class: private_proto_3 module wit
597597
sil_witness_table private private_proto_public_class_private_method: private_proto_4 module witness {
598598
method #private_proto_4.theMethod!1: @private_proto_4_public_class_private_method_witness
599599
}
600+
601+
// Test initializer vs allocator
602+
603+
struct InitVal { }
604+
605+
class SomeItem {
606+
required init(ivalreq: InitVal)
607+
convenience init(ival: InitVal)
608+
deinit
609+
}
610+
611+
class SomeChildItem : SomeItem {
612+
deinit
613+
required init(ivalreq: InitVal)
614+
}
615+
616+
sil @test_call_of_allocator_and_initializer : $@convention(thin) (@thick SomeItem.Type, InitVal) -> @owned SomeItem {
617+
bb0(%0 : $@thick SomeItem.Type, %1: $InitVal):
618+
619+
// CHECK: Function call site:
620+
// CHECK: %2 = class_method %0 : $@thick SomeItem.Type, #SomeItem.init!allocator.1 : (SomeItem.Type) -> (InitVal) -> SomeItem , $@convention(method) (InitVal, @thick SomeItem.Type) -> @owned SomeItem
621+
// CHECK: %3 = apply %2(%1, %0) : $@convention(method) (InitVal, @thick SomeItem.Type) -> @owned SomeItem
622+
// CHECK-NOWMO: Incomplete callee list? : Yes
623+
// CHECK-WMO: Incomplete callee list? : No
624+
// CHECK: Known callees:
625+
// CHECK: SomeChildItem_allocator
626+
// CHECK: SomeItem_allocator
627+
628+
%2 = class_method %0 : $@thick SomeItem.Type, #SomeItem.init!allocator.1 : (SomeItem.Type) -> (InitVal) -> SomeItem , $@convention(method) (InitVal, @thick SomeItem.Type) -> @owned SomeItem
629+
%3 = apply %2(%1, %0) : $@convention(method) (InitVal, @thick SomeItem.Type) -> @owned SomeItem
630+
631+
// CHECK: Function call site:
632+
// CHECK: %4 = class_method %3 : $SomeItem, #SomeItem.init!initializer.1 : (SomeItem.Type) -> (InitVal) -> SomeItem , $@convention(method) (InitVal, @owned SomeItem) -> @owned SomeItem
633+
// CHECK: %5 = apply %4(%1, %3) : $@convention(method) (InitVal, @owned SomeItem) -> @owned SomeItem
634+
// CHECK-NOWMO: Incomplete callee list? : Yes
635+
// CHECK-WMO: Incomplete callee list? : No
636+
// CHECK: Known callees:
637+
// CHECK: SomeChildItem_initializer
638+
// CHECK: SomeItem_initializer
639+
640+
%4 = class_method %3 : $SomeItem, #SomeItem.init!initializer.1 : (SomeItem.Type) -> (InitVal) -> SomeItem , $@convention(method) (InitVal, @owned SomeItem) -> @owned SomeItem
641+
%5 = apply %4(%1, %3) : $@convention(method) (InitVal, @owned SomeItem) -> @owned SomeItem
642+
return %5 : $SomeItem
643+
}
644+
645+
sil @SomeItem_allocator : $@convention(method) (InitVal, @thick SomeItem.Type) -> @owned SomeItem
646+
647+
sil @SomeItem_initializer : $@convention(method) (InitVal, @owned SomeItem) -> @owned SomeItem
648+
649+
sil @SomeItem_destructor : $@convention(method) (@owned SomeItem) -> ()
650+
651+
sil @SomeChildItem_allocator : $@convention(method) (InitVal, @thick SomeChildItem.Type) -> @owned SomeChildItem
652+
653+
sil @SomeChildItem_initializer : $@convention(method) (InitVal, @owned SomeChildItem) -> @owned SomeChildItem
654+
655+
sil @SomeChildItem_destructor : $@convention(method) (@owned SomeChildItem) -> ()
656+
657+
sil_vtable SomeItem {
658+
#SomeItem.init!allocator.1: SomeItem_allocator
659+
#SomeItem.init!initializer.1: SomeItem_initializer
660+
#SomeItem.deinit!deallocator: SomeItem_destructor
661+
}
662+
663+
sil_vtable SomeChildItem {
664+
#SomeItem.init!allocator.1: SomeChildItem_allocator
665+
#SomeItem.init!initializer.1: SomeChildItem_initializer
666+
#SomeChildItem.deinit!deallocator: SomeChildItem_destructor
667+
}

0 commit comments

Comments
 (0)