Skip to content

Commit b0fdadb

Browse files
author
Joe Shajrawi
committed
Devirtualizer: disable the “effectively final” optimization if a function is inlinable.
The devirtualizer performs two optimizations: - If a value is known to have an exact class type, ie it is the result of an alloc_ref, we can devirtualize calls of *non-final* methods, because we know we’re calling that specific method and not an override. - If a method is known to be “effectively final” (it is not open, and there are no overrides inside the module) we can devirtualize it. However the second optimization needs to be disabled if a function is inlinable (F->getResilienceExpansion() == ResilienceExpansion::Minimal).
1 parent 7e387b7 commit b0fdadb

File tree

5 files changed

+87
-17
lines changed

5 files changed

+87
-17
lines changed

include/swift/SILOptimizer/Utils/Devirtualize.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,17 @@ DevirtualizationResult tryDevirtualizeApply(ApplySite AI,
7070
bool canDevirtualizeApply(FullApplySite AI, ClassHierarchyAnalysis *CHA);
7171
bool isNominalTypeWithUnboundGenericParameters(SILType Ty, SILModule &M);
7272
bool canDevirtualizeClassMethod(FullApplySite AI, SILType ClassInstanceType,
73-
OptRemark::Emitter *ORE = nullptr);
73+
OptRemark::Emitter *ORE = nullptr,
74+
bool isEffectivelyFinalMethod = false);
7475
SILFunction *getTargetClassMethod(SILModule &M, SILType ClassOrMetatypeType,
7576
MethodInst *MI);
7677
DevirtualizationResult devirtualizeClassMethod(FullApplySite AI,
7778
SILValue ClassInstance,
7879
OptRemark::Emitter *ORE);
79-
DevirtualizationResult tryDevirtualizeClassMethod(FullApplySite AI,
80-
SILValue ClassInstance,
81-
OptRemark::Emitter *ORE);
80+
DevirtualizationResult
81+
tryDevirtualizeClassMethod(FullApplySite AI, SILValue ClassInstance,
82+
OptRemark::Emitter *ORE,
83+
bool isEffectivelyFinalMethod = false);
8284
DevirtualizationResult
8385
tryDevirtualizeWitnessMethod(ApplySite AI, OptRemark::Emitter *ORE);
8486
}

lib/SILOptimizer/Utils/Devirtualize.cpp

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,8 @@ SILFunction *swift::getTargetClassMethod(SILModule &M,
496496
/// return true if it is possible to devirtualize, false - otherwise.
497497
bool swift::canDevirtualizeClassMethod(FullApplySite AI,
498498
SILType ClassOrMetatypeType,
499-
OptRemark::Emitter *ORE) {
499+
OptRemark::Emitter *ORE,
500+
bool isEffectivelyFinalMethod) {
500501

501502
DEBUG(llvm::dbgs() << " Trying to devirtualize : " << *AI.getInstruction());
502503

@@ -519,6 +520,15 @@ bool swift::canDevirtualizeClassMethod(FullApplySite AI,
519520
return false;
520521
}
521522

523+
// We need to disable the “effectively final” opt if a function is inlinable
524+
if (isEffectivelyFinalMethod &&
525+
F->getResilienceExpansion() == ResilienceExpansion::Minimal) {
526+
DEBUG(llvm::dbgs() << " FAIL: Could not optimize function because "
527+
"it is an effectively-final inlinable: "
528+
<< F->getName() << "\n");
529+
return false;
530+
}
531+
522532
// Mandatory inlining does class method devirtualization. I'm not sure if this
523533
// is really needed, but some test rely on this.
524534
// So even for Onone functions we have to do it if the SILStage is raw.
@@ -701,10 +711,12 @@ DevirtualizationResult swift::devirtualizeClassMethod(FullApplySite AI,
701711
return std::make_pair(ResultValue, NewAI);
702712
}
703713

704-
DevirtualizationResult swift::tryDevirtualizeClassMethod(FullApplySite AI,
705-
SILValue ClassInstance,
706-
OptRemark::Emitter *ORE) {
707-
if (!canDevirtualizeClassMethod(AI, ClassInstance->getType(), ORE))
714+
DevirtualizationResult
715+
swift::tryDevirtualizeClassMethod(FullApplySite AI, SILValue ClassInstance,
716+
OptRemark::Emitter *ORE,
717+
bool isEffectivelyFinalMethod) {
718+
if (!canDevirtualizeClassMethod(AI, ClassInstance->getType(), ORE,
719+
isEffectivelyFinalMethod))
708720
return std::make_pair(nullptr, FullApplySite());
709721
return devirtualizeClassMethod(AI, ClassInstance, ORE);
710722
}
@@ -1025,7 +1037,8 @@ DevirtualizationResult swift::tryDevirtualizeApply(ApplySite AI,
10251037
auto *CD = ClassType.getClassOrBoundGenericClass();
10261038

10271039
if (isEffectivelyFinalMethod(FAS, ClassType, CD, CHA))
1028-
return tryDevirtualizeClassMethod(FAS, Instance, ORE);
1040+
return tryDevirtualizeClassMethod(FAS, Instance, ORE,
1041+
true /*isEffectivelyFinalMethod*/);
10291042

10301043
// Try to check if the exact dynamic type of the instance is statically
10311044
// known.
@@ -1091,7 +1104,9 @@ bool swift::canDevirtualizeApply(FullApplySite AI, ClassHierarchyAnalysis *CHA)
10911104
auto *CD = ClassType.getClassOrBoundGenericClass();
10921105

10931106
if (isEffectivelyFinalMethod(AI, ClassType, CD, CHA))
1094-
return canDevirtualizeClassMethod(AI, Instance->getType());
1107+
return canDevirtualizeClassMethod(AI, Instance->getType(),
1108+
nullptr /*ORE*/,
1109+
true /*isEffectivelyFinalMethod*/);
10951110

10961111
// Try to check if the exact dynamic type of the instance is statically
10971112
// known.

test/IRGen/big_types_corner_cases.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,6 @@ public class BigClass {
9595
}
9696

9797
// CHECK-LABEL: define{{( protected)?}} hidden swiftcc void @"$S22big_types_corner_cases8BigClassC03useE6Struct0aH0yAA0eH0V_tF"(%T22big_types_corner_cases9BigStructV* noalias nocapture dereferenceable({{.*}}), %T22big_types_corner_cases8BigClassC* swiftself)
98-
// CHECK: getelementptr inbounds %T22big_types_corner_cases8BigClassC, %T22big_types_corner_cases8BigClassC*
99-
// CHECK: call void @"$SSqWy
10098
// CHECK: [[BITCAST:%.*]] = bitcast i8* {{.*}} to void (%T22big_types_corner_cases9BigStructV*, %swift.refcounted*)*
10199
// CHECK: call swiftcc void [[BITCAST]](%T22big_types_corner_cases9BigStructV* noalias nocapture dereferenceable({{.*}}) %0, %swift.refcounted* swiftself
102100
// CHECK: ret void
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all %s -devirtualizer | %FileCheck %s
2+
3+
sil_stage canonical
4+
5+
import Builtin
6+
import Swift
7+
import SwiftShims
8+
9+
class X
10+
{
11+
private func ping() -> Int
12+
@objc deinit
13+
init()
14+
}
15+
16+
class Y : X
17+
{
18+
@objc deinit
19+
override init()
20+
}
21+
22+
sil [serialized] @_TFC14devirt_access21X4pingfS0_FT_Si : $@convention(method) (@guaranteed X) -> Int
23+
sil public_external [transparent] @_TFSi33_convertFromBuiltinIntegerLiteralfMSiFBi2048_Si : $@convention(thin) (Builtin.Int2048, @thin Int.Type) -> Int
24+
sil @_TFC14devirt_access21Xd : $@convention(method) (@guaranteed X) -> @owned Builtin.NativeObject
25+
sil @_TFC14devirt_access21XD : $@convention(method) (@guaranteed X) -> ()
26+
sil @_TFC14devirt_access21XcfMS0_FT_S0_ : $@convention(method) (@owned X) -> @owned X
27+
sil @_TFC14devirt_access21XCfMS0_FT_S0_ : $@convention(thin) (@thick X.Type) -> @owned X
28+
sil @_TFC14devirt_access21Yd : $@convention(method) (@guaranteed Y) -> @owned Builtin.NativeObject
29+
sil @_TFC14devirt_access21YD : $@convention(method) (@guaranteed Y) -> ()
30+
sil @_TFC14devirt_access21YcfMS0_FT_S0_ : $@convention(method) (@owned Y) -> @owned Y
31+
sil @_TFC14devirt_access21YCfMS0_FT_S0_ : $@convention(thin) (@thick Y.Type) -> @owned Y
32+
33+
//CHECK-LABEL: sil @Case
34+
//CHECK-NOT: function_ref @_TFC14devirt_access21X4pingfS0_FT_Si
35+
//CHECK: class_method
36+
//CHECK: apply
37+
//CHECK: return
38+
sil @Case : $@convention(thin) (@owned Y) -> Int {
39+
bb0(%0 : $Y):
40+
debug_value %0 : $Y, let, name "a" // id: %1
41+
strong_retain %0 : $Y // id: %2
42+
%3 = upcast %0 : $Y to $X // users: %4, %5
43+
%4 = class_method %3 : $X, #X.ping!1 : (X) -> () -> Int, $@convention(method) (@guaranteed X) -> Int // user: %5
44+
%5 = apply %4(%3) : $@convention(method) (@guaranteed X) -> Int // user: %7
45+
strong_release %0 : $Y // id: %6
46+
return %5 : $Int // id: %7
47+
}
48+
49+
sil_vtable X {
50+
#X.ping!1: @_TFC14devirt_access21X4pingfS0_FT_Si // devirt_access2.X.ping (devirt_access2.X)() -> Swift.Int
51+
#X.init!initializer.1: @_TFC14devirt_access21XcfMS0_FT_S0_ // devirt_access2.X.init (devirt_access2.X.Type)() -> devirt_access2.X
52+
}
53+
54+
sil_vtable Y {
55+
#X.ping!1: @_TFC14devirt_access21X4pingfS0_FT_Si // devirt_access2.X.ping (devirt_access2.X)() -> Swift.Int
56+
#X.init!initializer.1: @_TFC14devirt_access21YcfMS0_FT_S0_ // devirt_access2.Y.init (devirt_access2.Y.Type)() -> devirt_access2.Y
57+
}

test/sil-func-extractor/basic.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,8 @@
5656

5757
// EXTRACT-NOW-LABEL: sil [serialized] @$S5basic7VehicleC3nowSiyF : $@convention(method) (@guaranteed Vehicle) -> Int {
5858
// EXTRACT-NOW: bb0
59-
// EXTRACT-NOW: ref_element_addr
60-
// EXTRACT-NOW-NEXT: begin_access [read] [dynamic]
61-
// EXTRACT-NOW-NEXT: load
62-
// EXTRACT-NOW-NEXT: end_access
59+
// EXTRACT-NOW: class_method
60+
// EXTRACT-NOW-NEXT: apply
6361
// EXTRACT-NOW-NEXT: return
6462

6563
public struct X {

0 commit comments

Comments
 (0)