Skip to content

Commit 079caaa

Browse files
authored
Merge pull request #4756 from swiftix/assorted-fixes-1
[sil-devirtualizer] Use a more advanced algorithm to figure out the exact types of instances
2 parents 6aac532 + 7d594fb commit 079caaa

File tree

3 files changed

+197
-1
lines changed

3 files changed

+197
-1
lines changed

include/swift/SILOptimizer/Utils/Local.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "swift/Basic/ArrayRefView.h"
1717
#include "swift/SILOptimizer/Analysis/ARCAnalysis.h"
1818
#include "swift/SILOptimizer/Analysis/EpilogueARCAnalysis.h"
19+
#include "swift/SILOptimizer/Analysis/ClassHierarchyAnalysis.h"
1920
#include "swift/SILOptimizer/Analysis/SimplifyInstruction.h"
2021
#include "swift/SIL/SILInstruction.h"
2122
#include "swift/SIL/SILBuilder.h"
@@ -694,6 +695,28 @@ void replaceLoadSequence(SILInstruction *I,
694695
/// be reached by calling the function represented by Decl?
695696
bool calleesAreStaticallyKnowable(SILModule &M, SILDeclRef Decl);
696697

698+
// Attempt to get the instance for S, whose static type is the same as
699+
// its exact dynamic type, returning a null SILValue() if we cannot find it.
700+
// The information that a static type is the same as the exact dynamic,
701+
// can be derived e.g.:
702+
// - from a constructor or
703+
// - from a successful outcome of a checked_cast_br [exact] instruction.
704+
SILValue getInstanceWithExactDynamicType(SILValue S, SILModule &M,
705+
ClassHierarchyAnalysis *CHA);
706+
707+
/// Try to determine the exact dynamic type of an object.
708+
/// returns the exact dynamic type of the object, or an empty type if the exact
709+
/// type could not be determined.
710+
SILType getExactDynamicType(SILValue S, SILModule &M,
711+
ClassHierarchyAnalysis *CHA,
712+
bool ForUnderlyingObject = false);
713+
714+
/// Try to statically determine the exact dynamic type of the underlying object.
715+
/// returns the exact dynamic type of the underlying object, or an empty SILType
716+
/// if the exact type could not be determined.
717+
SILType getExactDynamicTypeOfUnderlyingObject(SILValue S, SILModule &M,
718+
ClassHierarchyAnalysis *CHA);
719+
697720
/// Hoist the address projection rooted in \p Op to \p InsertBefore.
698721
/// Requires the projected value to dominate the insertion point.
699722
///

lib/SILOptimizer/Utils/Devirtualize.cpp

Lines changed: 144 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ static bool isKnownFinalClass(ClassDecl *CD, SILModule &M,
225225
// can be derived e.g.:
226226
// - from a constructor or
227227
// - from a successful outcome of a checked_cast_br [exact] instruction.
228-
static SILValue getInstanceWithExactDynamicType(SILValue S, SILModule &M,
228+
SILValue swift::getInstanceWithExactDynamicType(SILValue S, SILModule &M,
229229
ClassHierarchyAnalysis *CHA) {
230230

231231
while (S) {
@@ -268,6 +268,143 @@ static SILValue getInstanceWithExactDynamicType(SILValue S, SILModule &M,
268268
return SILValue();
269269
}
270270

271+
/// Try to determine the exact dynamic type of an object.
272+
/// returns the exact dynamic type of the object, or an empty type if the exact
273+
/// type could not be determined.
274+
SILType swift::getExactDynamicType(SILValue S, SILModule &M,
275+
ClassHierarchyAnalysis *CHA,
276+
bool ForUnderlyingObject) {
277+
// Set of values to be checked for their exact types.
278+
SmallVector<SILValue, 8> WorkList;
279+
// The detected type of the underlying object.
280+
SILType ResultType;
281+
// Set of processed values.
282+
llvm::SetVector<SILValue> Processed;
283+
WorkList.push_back(S);
284+
285+
while (!WorkList.empty()) {
286+
auto V = WorkList.pop_back_val();
287+
if (!V)
288+
return SILType();
289+
if (Processed.count(V))
290+
continue;
291+
Processed.insert(V);
292+
// For underlying object strip casts and projections.
293+
// For the object itself, simply strip casts.
294+
V = ForUnderlyingObject ? getUnderlyingObject(V) : stripCasts(V);
295+
296+
if (isa<AllocRefInst>(V) || isa<MetatypeInst>(V)) {
297+
if (ResultType && ResultType != V->getType())
298+
return SILType();
299+
ResultType = V->getType();
300+
continue;
301+
}
302+
303+
if (isa<LiteralInst>(V)) {
304+
if (ResultType && ResultType != V->getType())
305+
return SILType();
306+
ResultType = V->getType();
307+
continue;
308+
}
309+
310+
if (isa<StructInst>(V) || isa<TupleInst>(V) || isa<EnumInst>(V)) {
311+
if (ResultType && ResultType != V->getType())
312+
return SILType();
313+
ResultType = V->getType();
314+
continue;
315+
}
316+
317+
if (ForUnderlyingObject) {
318+
if (isa<AllocationInst>(V)) {
319+
if (ResultType && ResultType != V->getType())
320+
return SILType();
321+
ResultType = V->getType();
322+
continue;
323+
}
324+
// Look through strong_pin instructions.
325+
if (isa<StrongPinInst>(V)) {
326+
WorkList.push_back(cast<SILInstruction>(V)->getOperand(0));
327+
continue;
328+
}
329+
}
330+
331+
auto Arg = dyn_cast<SILArgument>(V);
332+
if (!Arg) {
333+
// We don't know what it is.
334+
return SILType();
335+
}
336+
337+
if (Arg->isFunctionArg()) {
338+
// Bail on metatypes for now.
339+
if (Arg->getType().getSwiftRValueType()->is<AnyMetatypeType>()) {
340+
return SILType();
341+
}
342+
auto *CD = Arg->getType().getClassOrBoundGenericClass();
343+
// If it is not class and it is a trivial type, then it
344+
// should be the exact type.
345+
if (!CD && Arg->getType().isTrivial(M)) {
346+
if (ResultType && ResultType != Arg->getType())
347+
return SILType();
348+
ResultType = Arg->getType();
349+
continue;
350+
}
351+
352+
if (!CD) {
353+
// It is not a class or a trivial type, so we don't know what it is.
354+
return SILType();
355+
}
356+
357+
// Check if this class is effectively final.
358+
if (!isKnownFinalClass(CD, M, CHA)) {
359+
return SILType();
360+
}
361+
362+
if (ResultType && ResultType != Arg->getType())
363+
return SILType();
364+
ResultType = Arg->getType();
365+
continue;
366+
}
367+
368+
auto *SinglePred = Arg->getParent()->getSinglePredecessor();
369+
if (SinglePred) {
370+
// If it is a BB argument received on a success branch
371+
// of a checked_cast_br, then we know its exact type.
372+
auto *CCBI = dyn_cast<CheckedCastBranchInst>(SinglePred->getTerminator());
373+
if (CCBI && CCBI->isExact() && CCBI->getSuccessBB() == Arg->getParent()) {
374+
if (ResultType && ResultType != Arg->getType())
375+
return SILType();
376+
ResultType = Arg->getType();
377+
continue;
378+
}
379+
}
380+
381+
// It is a BB argument, look through incoming values. If they all have the
382+
// same exact type, then we consider it to be the type of the BB argument.
383+
SmallVector<SILValue, 4> IncomingValues;
384+
385+
if (Arg->getIncomingValues(IncomingValues)) {
386+
for (auto InValue : IncomingValues) {
387+
WorkList.push_back(InValue);
388+
}
389+
continue;
390+
}
391+
392+
// The exact type is unknown.
393+
return SILType();
394+
}
395+
396+
return ResultType;
397+
}
398+
399+
400+
/// Try to determine the exact dynamic type of the underlying object.
401+
/// returns the exact dynamic type of a value, or an empty type if the exact
402+
/// type could not be determined.
403+
SILType
404+
swift::getExactDynamicTypeOfUnderlyingObject(SILValue S, SILModule &M,
405+
ClassHierarchyAnalysis *CHA) {
406+
return getExactDynamicType(S, M, CHA, /* ForUnderlyingObject */ true);
407+
}
271408

272409
// Start with the substitutions from the apply.
273410
// Try to propagate them to find out the real substitutions required
@@ -873,6 +1010,12 @@ swift::tryDevirtualizeApply(FullApplySite AI, ClassHierarchyAnalysis *CHA) {
8731010
CMI->getModule(),
8741011
CHA))
8751012
return tryDevirtualizeClassMethod(AI, Instance);
1013+
1014+
if (auto ExactTy = getExactDynamicType(CMI->getOperand(), CMI->getModule(),
1015+
CHA)) {
1016+
if (ExactTy == CMI->getOperand()->getType())
1017+
return tryDevirtualizeClassMethod(AI, CMI->getOperand());
1018+
}
8761019
}
8771020

8781021
if (isa<SuperMethodInst>(AI.getCallee())) {

test/SILOptimizer/devirtualize2.sil

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,36 @@ bb6:
7474
return %7 : $()
7575
}
7676

77+
// CHECK-LABEL: sil @function_with_cm_with_dynamic_type_known_from_incoming_bb_args
78+
// CHECK-NOT: class_method
79+
// CHECK: bb3([[SELF:%.*]] : $Bar):
80+
// CHECK: [[REF:%.*]] = function_ref @_TFC4main3Bar4pingfS0_FT_T_
81+
// CHECK: apply [[REF]]([[SELF]])
82+
// CHECK: return
83+
sil @function_with_cm_with_dynamic_type_known_from_incoming_bb_args : $@convention(thin) (Builtin.Int1) -> () {
84+
bb0(%0 : $Builtin.Int1):
85+
cond_br %0, bb1, bb2
86+
87+
bb1:
88+
%1 = alloc_ref $Bar
89+
br bb3(%1 : $Bar)
90+
91+
bb2:
92+
%2 = alloc_ref $Bar
93+
br bb3(%2 : $Bar)
94+
95+
bb3(%4 : $Bar):
96+
// Devirtualizer should be able to figure out that the exact dynamic type of %4 is Bar.
97+
%5 = class_method %4 : $Bar, #Bar.ping!1 : (Bar) -> () -> () , $@convention(method) (@guaranteed Bar) -> ()
98+
%6 = apply %5(%4) : $@convention(method) (@guaranteed Bar) -> ()
99+
br bb4
100+
101+
bb4:
102+
strong_release %4 : $Bar
103+
%7 = tuple ()
104+
return %7 : $()
105+
}
106+
77107

78108
sil @_TFC4main3Bar4pingfS0_FT_T_ : $@convention(method) (@guaranteed Bar) -> ()
79109
sil @_TFC4main3Foo4pingfS0_FT_T_ : $@convention(method) (@guaranteed Foo) -> ()

0 commit comments

Comments
 (0)