Skip to content

Commit c559655

Browse files
authored
Merge pull request #27833 from eeckstein/fix-basic-callee-analysis
BasicCalleeAnalysis: fix computation of class method callees if an overridden function has a different ABI
2 parents 9d047d9 + d88edad commit c559655

File tree

6 files changed

+148
-58
lines changed

6 files changed

+148
-58
lines changed

include/swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ class CalleeList {
5454
: CalleeFunctions(llvm::makeArrayRef(List.begin(), List.end())),
5555
IsIncomplete(IsIncomplete) {}
5656

57+
LLVM_ATTRIBUTE_DEPRECATED(void dump() const LLVM_ATTRIBUTE_USED,
58+
"Only for use in the debugger");
59+
60+
void print(llvm::raw_ostream &os) const;
61+
5762
/// Return an iterator for the beginning of the list.
5863
ArrayRef<SILFunction *>::iterator begin() const {
5964
return CalleeFunctions.begin();
@@ -67,7 +72,7 @@ class CalleeList {
6772
bool isIncomplete() const { return IsIncomplete; }
6873

6974
/// Returns true if all callees are known and not external.
70-
bool allCalleesVisible();
75+
bool allCalleesVisible() const;
7176
};
7277

7378
/// CalleeCache is a helper class that builds lists of potential
@@ -106,16 +111,16 @@ class CalleeCache {
106111
/// given instruction. E.g. it could be destructors.
107112
CalleeList getCalleeList(SILInstruction *I) const;
108113

114+
CalleeList getCalleeList(SILDeclRef Decl) const;
115+
109116
private:
110117
void enumerateFunctionsInModule();
111118
void sortAndUniqueCallees();
112119
CalleesAndCanCallUnknown &getOrCreateCalleesForMethod(SILDeclRef Decl);
113-
void computeClassMethodCalleesForClass(ClassDecl *CD);
114-
void computeClassMethodCallees(ClassDecl *CD, SILDeclRef Method);
120+
void computeClassMethodCallees();
115121
void computeWitnessMethodCalleesForWitnessTable(SILWitnessTable &WT);
116122
void computeMethodCallees();
117123
SILFunction *getSingleCalleeForWitnessMethod(WitnessMethodInst *WMI) const;
118-
CalleeList getCalleeList(SILDeclRef Decl) const;
119124
CalleeList getCalleeList(WitnessMethodInst *WMI) const;
120125
CalleeList getCalleeList(ClassMethodInst *CMI) const;
121126
CalleeList getCalleeListForCalleeKind(SILValue Callee) const;
@@ -162,17 +167,23 @@ class BasicCalleeAnalysis : public SILAnalysis {
162167
Cache.reset();
163168
}
164169

165-
CalleeList getCalleeList(FullApplySite FAS) {
170+
LLVM_ATTRIBUTE_DEPRECATED(void dump() const LLVM_ATTRIBUTE_USED,
171+
"Only for use in the debugger");
172+
173+
void print(llvm::raw_ostream &os) const;
174+
175+
void updateCache() {
166176
if (!Cache)
167177
Cache = llvm::make_unique<CalleeCache>(M);
178+
}
168179

180+
CalleeList getCalleeList(FullApplySite FAS) {
181+
updateCache();
169182
return Cache->getCalleeList(FAS);
170183
}
171184

172185
CalleeList getCalleeList(SILInstruction *I) {
173-
if (!Cache)
174-
Cache = llvm::make_unique<CalleeCache>(M);
175-
186+
updateCache();
176187
return Cache->getCalleeList(I);
177188
}
178189
};

include/swift/SILOptimizer/Utils/InstOptUtils.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,10 @@ void replaceLoadSequence(SILInstruction *inst, SILValue value);
316316
/// be reached by calling the function represented by Decl?
317317
bool calleesAreStaticallyKnowable(SILModule &module, SILDeclRef decl);
318318

319+
/// Do we have enough information to determine all callees that could
320+
/// be reached by calling the function represented by Decl?
321+
bool calleesAreStaticallyKnowable(SILModule &module, AbstractFunctionDecl *afd);
322+
319323
// Attempt to get the instance for , whose static type is the same as
320324
// its exact dynamic type, returning a null SILValue() if we cannot find it.
321325
// The information that a static type is the same as the exact dynamic,

lib/SILOptimizer/Analysis/BasicCalleeAnalysis.cpp

Lines changed: 74 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,24 @@
2525

2626
using namespace swift;
2727

28-
bool CalleeList::allCalleesVisible() {
28+
void CalleeList::dump() const {
29+
print(llvm::errs());
30+
}
31+
32+
void CalleeList::print(llvm::raw_ostream &os) const {
33+
os << "Incomplete callee list? : "
34+
<< (isIncomplete() ? "Yes" : "No");
35+
if (!allCalleesVisible())
36+
os <<", not all callees visible";
37+
os << '\n';
38+
os << "Known callees:\n";
39+
for (auto *CalleeFn : *this) {
40+
os << " " << CalleeFn->getName() << "\n";
41+
}
42+
os << "\n";
43+
}
44+
45+
bool CalleeList::allCalleesVisible() const {
2946
if (isIncomplete())
3047
return false;
3148

@@ -81,48 +98,45 @@ CalleeCache::getOrCreateCalleesForMethod(SILDeclRef Decl) {
8198
return It->second;
8299
}
83100

84-
/// Update the callees for each method of a given class, along with
85-
/// all the overridden methods from superclasses.
86-
void CalleeCache::computeClassMethodCalleesForClass(ClassDecl *CD) {
87-
assert(!CD->hasClangNode());
88-
89-
for (auto *Member : CD->getMembers()) {
90-
auto *AFD = dyn_cast<AbstractFunctionDecl>(Member);
91-
if (!AFD)
92-
continue;
93-
94-
if (auto *ConstrDecl = dyn_cast<ConstructorDecl>(AFD)) {
95-
computeClassMethodCallees(CD, SILDeclRef(AFD,
96-
SILDeclRef::Kind::Initializer));
97-
if (ConstrDecl->isRequired()) {
98-
computeClassMethodCallees(CD, SILDeclRef(AFD,
99-
SILDeclRef::Kind::Allocator));
101+
/// Update the callees for each method of a given vtable.
102+
void CalleeCache::computeClassMethodCallees() {
103+
SmallPtrSet<AbstractFunctionDecl *, 16> unknownCallees;
104+
105+
// First mark all method declarations which might be overridden in another
106+
// translation unit, i.e. outside the visibility of the optimizer.
107+
// This is a little bit more complicated than to just check the VTable
108+
// entry.Method itself, because an overridden method might be more accessible
109+
// than the base method (e.g. a public method overrides a private method).
110+
for (auto &VTable : M.getVTableList()) {
111+
assert(!VTable.getClass()->hasClangNode());
112+
113+
for (Decl *member : VTable.getClass()->getMembers()) {
114+
if (auto *afd = dyn_cast<AbstractFunctionDecl>(member)) {
115+
// If a method implementation might be overridden in another translation
116+
// unit, also mark all the base methods as 'unknown'.
117+
bool unknown = false;
118+
do {
119+
if (!calleesAreStaticallyKnowable(M, afd))
120+
unknown = true;
121+
if (unknown)
122+
unknownCallees.insert(afd);
123+
afd = afd->getOverriddenDecl();
124+
} while (afd);
100125
}
101-
} else {
102-
computeClassMethodCallees(CD, SILDeclRef(AFD));
103126
}
104127
}
105-
}
106-
107-
void CalleeCache::computeClassMethodCallees(ClassDecl *CD, SILDeclRef Method) {
108-
auto *CalledFn = M.lookUpFunctionInVTable(CD, Method);
109-
if (!CalledFn)
110-
return;
111-
112-
bool canCallUnknown = !calleesAreStaticallyKnowable(M, Method);
113-
114-
// Update the callees for this method and all the methods it
115-
// overrides by adding this function to their lists.
116-
do {
117-
auto &TheCallees = getOrCreateCalleesForMethod(Method);
118-
assert(TheCallees.getPointer() && "Unexpected null callees!");
119-
120-
TheCallees.getPointer()->push_back(CalledFn);
121-
if (canCallUnknown)
122-
TheCallees.setInt(true);
123128

124-
Method = Method.getNextOverriddenVTableEntry();
125-
} while (Method);
129+
// Second step: collect all implementations of a method.
130+
for (auto &VTable : M.getVTableList()) {
131+
for (const SILVTable::Entry &entry : VTable.getEntries()) {
132+
if (auto *afd = entry.Method.getAbstractFunctionDecl()) {
133+
CalleesAndCanCallUnknown &callees = getOrCreateCalleesForMethod(entry.Method);
134+
if (unknownCallees.count(afd) != 0)
135+
callees.setInt(1);
136+
callees.getPointer()->push_back(entry.Implementation);
137+
}
138+
}
139+
}
126140
}
127141

128142
void CalleeCache::computeWitnessMethodCalleesForWitnessTable(
@@ -183,8 +197,8 @@ void CalleeCache::computeWitnessMethodCalleesForWitnessTable(
183197
/// Witness Table.
184198
void CalleeCache::computeMethodCallees() {
185199
SWIFT_FUNC_STAT;
186-
for (auto &VTable : M.getVTableList())
187-
computeClassMethodCalleesForClass(VTable.getClass());
200+
201+
computeClassMethodCallees();
188202

189203
for (auto &WTable : M.getWitnessTableList())
190204
computeWitnessMethodCalleesForWitnessTable(WTable);
@@ -284,3 +298,22 @@ CalleeList CalleeCache::getCalleeList(SILInstruction *I) const {
284298
SILDeclRef Destructor = SILDeclRef(Class->getDestructor());
285299
return getCalleeList(Destructor);
286300
}
301+
302+
void BasicCalleeAnalysis::dump() const {
303+
print(llvm::errs());
304+
}
305+
306+
void BasicCalleeAnalysis::print(llvm::raw_ostream &os) const {
307+
if (!Cache) {
308+
os << "<no cache>\n";
309+
}
310+
llvm::DenseSet<SILDeclRef> printed;
311+
for (auto &VTable : M.getVTableList()) {
312+
for (const SILVTable::Entry &entry : VTable.getEntries()) {
313+
if (printed.insert(entry.Method).second) {
314+
os << "callees for " << entry.Method << ":\n";
315+
Cache->getCalleeList(entry.Method).print(os);
316+
}
317+
}
318+
}
319+
}

lib/SILOptimizer/UtilityPasses/BasicCalleePrinter.cpp

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,7 @@ class BasicCalleePrinterPass : public SILModuleTransform {
4040
llvm::outs() << *FAS.getInstruction();
4141

4242
auto Callees = BCA->getCalleeList(FAS);
43-
llvm::outs() << "Incomplete callee list? : "
44-
<< (Callees.isIncomplete() ? "Yes" : "No") << "\n";
45-
llvm::outs() << "Known callees:\n";
46-
for (auto *CalleeFn : Callees)
47-
llvm::outs() << CalleeFn->getName() << "\n";
48-
llvm::outs() << "\n";
43+
Callees.print(llvm::outs());
4944
}
5045

5146
/// The entry point to the transformation.

lib/SILOptimizer/Utils/InstOptUtils.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1433,13 +1433,19 @@ bool swift::calleesAreStaticallyKnowable(SILModule &module, SILDeclRef decl) {
14331433
if (decl.isForeign)
14341434
return false;
14351435

1436+
auto *afd = decl.getAbstractFunctionDecl();
1437+
assert(afd && "Expected abstract function decl!");
1438+
return calleesAreStaticallyKnowable(module, afd);
1439+
}
1440+
1441+
/// Are the callees that could be called through Decl statically
1442+
/// knowable based on the Decl and the compilation mode?
1443+
bool swift::calleesAreStaticallyKnowable(SILModule &module,
1444+
AbstractFunctionDecl *afd) {
14361445
const DeclContext *assocDC = module.getAssociatedContext();
14371446
if (!assocDC)
14381447
return false;
14391448

1440-
auto *afd = decl.getAbstractFunctionDecl();
1441-
assert(afd && "Expected abstract function decl!");
1442-
14431449
// Only handle members defined within the SILModule's associated context.
14441450
if (!afd->isChildContextOf(assocDC))
14451451
return false;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// RUN: %target-swift-frontend -O %s -emit-ir -o /dev/null
2+
3+
// Check that we don't crash here.
4+
5+
enum E<T> {
6+
case A
7+
case B(T)
8+
}
9+
10+
public protocol P {
11+
associatedtype A
12+
}
13+
14+
internal class Base<T: P> {
15+
func foo() -> E<T.A> {
16+
return .A
17+
}
18+
}
19+
20+
struct Outer<T: P> where T.A == Int {
21+
private class Inner : Base<T> {
22+
23+
// This overridden function has a different ABI than the base implementation.
24+
// The BasicCalleeAnalysis put both methods in the callee list, which let
25+
// some optimizations crash.
26+
override func foo() -> E<T.A> {
27+
return .A
28+
}
29+
}
30+
}
31+
32+
@inline(never)
33+
func getit<T: P>(_ t: T) -> Base<T> {
34+
return Base<T>()
35+
}
36+
37+
public func testit<T: P>(_ t: T) {
38+
let b = getit(t)
39+
b.foo()
40+
}
41+

0 commit comments

Comments
 (0)