Skip to content

Commit a53a31c

Browse files
committed
SILGen: Use super_method for native non-final methods in classes
Use the `super_method` instruction for non-final `func` and `class func` declarations in native Swift classes. Previously, we would always emit a static `function_ref` for these, which prevents resilient dynamic dispatch. This is hidden behind a -use-native-super-dispatch flag while I survey the effects on devirtualization and stack promotion. When that's figured out, I'll add more tests and update test cases that still assume static dispatch. rdar://problem/22749732
1 parent 5e25990 commit a53a31c

File tree

7 files changed

+88
-17
lines changed

7 files changed

+88
-17
lines changed

include/swift/AST/SILOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ class SILOptions {
9797

9898
/// Should we use a pass pipeline passed in via a json file? Null by default.
9999
StringRef ExternalPassPipelineFilename;
100+
101+
/// Use super_method for native super method calls instead of function_ref.
102+
bool UseNativeSuperMethod = false;
100103
};
101104

102105
} // end namespace swift

include/swift/Option/FrontendOptions.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,9 @@ def dump_clang_diagnostics : Flag<["-"], "dump-clang-diagnostics">,
187187
def emit_verbose_sil : Flag<["-"], "emit-verbose-sil">,
188188
HelpText<"Emit locations during SIL emission">;
189189

190+
def use_native_super_method : Flag<["-"], "use-native-super-method">,
191+
HelpText<"Use super_method for super calls in native classes">;
192+
190193
def disable_self_type_mangling : Flag<["-"], "disable-self-type-mangling">,
191194
HelpText<"Disable including Self type in method type manglings">;
192195

lib/Frontend/CompilerInvocation.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,8 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args,
10021002

10031003
Opts.GenerateProfile |= Args.hasArg(OPT_profile_generate);
10041004
Opts.EmitProfileCoverageMapping |= Args.hasArg(OPT_profile_coverage_mapping);
1005+
Opts.UseNativeSuperMethod |=
1006+
Args.hasArg(OPT_use_native_super_method);
10051007

10061008
return false;
10071009
}

lib/SILGen/SILGenApply.cpp

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -627,11 +627,12 @@ class Callee {
627627
case Kind::SuperMethod: {
628628
assert(level <= Constant.uncurryLevel
629629
&& "uncurrying past natural uncurry level of method");
630-
assert(level >= 1
631-
&& "currying 'self' of super method dispatch not yet supported");
630+
assert((level == 0 || level == getNaturalUncurryLevel()) &&
631+
"Can only curry self parameter of super method calls");
632632

633633
constant = Constant.atUncurryLevel(level);
634634
constantInfo = gen.getConstantInfo(*constant);
635+
635636
SILValue methodVal = gen.B.createSuperMethod(Loc,
636637
SelfValue,
637638
*constant,
@@ -735,7 +736,10 @@ class Callee {
735736
return SpecializedEmitter::forDecl(SGM, Constant);
736737
}
737738
case Kind::SuperMethod: {
738-
return SpecializedEmitter(emitPartialSuperMethod);
739+
if (uncurryLevel == 0) {
740+
return SpecializedEmitter(emitPartialSuperMethod);
741+
}
742+
return None;
739743
}
740744
case Kind::EnumElement:
741745
case Kind::IndirectValue:
@@ -1405,15 +1409,21 @@ class SILGenApply : public Lowering::ExprVisitor<SILGenApply> {
14051409
apply);
14061410

14071411
SILValue superMethod;
1408-
if (constant.isForeign) {
1412+
auto *funcDecl = cast<AbstractFunctionDecl>(constant.getDecl());
1413+
1414+
auto Opts = SGF.B.getModule().getOptions();
1415+
if (constant.isForeign ||
1416+
(Opts.UseNativeSuperMethod && !funcDecl->isFinal())) {
1417+
// All Objective-C methods and
1418+
// non-final native Swift methods use dynamic dispatch.
14091419
SILValue Input = super.getValue();
14101420
while (auto *UI = dyn_cast<UpcastInst>(Input))
14111421
Input = UI->getOperand();
14121422
// ObjC super calls require dynamic dispatch.
14131423
setCallee(Callee::forSuperMethod(SGF, Input, constant,
14141424
getSubstFnType(), fn));
14151425
} else {
1416-
// Native Swift super calls are direct.
1426+
// Native Swift super calls to final methods are direct.
14171427
setCallee(Callee::forDirect(SGF, constant, getSubstFnType(), fn));
14181428
}
14191429

@@ -3818,12 +3828,13 @@ static Callee getBaseAccessorFunctionRef(SILGenFunction &gen,
38183828
return Callee::forClassMethod(gen, self, constant, substAccessorType,
38193829
loc);
38203830

3821-
// If this is a "super." dispatch, we either do a direct dispatch in the case
3822-
// of swift classes or an objc super call.
3831+
// If this is a "super." dispatch, we do a dynamic dispatch for objc methods
3832+
// or non-final native Swift methods.
38233833
while (auto *upcast = dyn_cast<UpcastInst>(self))
38243834
self = upcast->getOperand();
38253835

3826-
if (constant.isForeign)
3836+
auto Opts = gen.B.getModule().getOptions();
3837+
if (constant.isForeign || (Opts.UseNativeSuperMethod && !decl->isFinal()))
38273838
return Callee::forSuperMethod(gen, self, constant, substAccessorType,loc);
38283839

38293840
return Callee::forDirect(gen, constant, substAccessorType, loc);

test/SILGen/super_class_method.swift

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,27 @@
1-
// RUN: %target-swift-frontend -emit-silgen -sdk %S/Inputs -I %S/Inputs -enable-source-import %s | FileCheck %s
1+
// RUN: %target-swift-frontend -emit-silgen -sdk %S/Inputs -I %S/Inputs -enable-source-import %s -use-native-super-method | FileCheck %s
22

3-
// REQUIRES: objc_interop
3+
class Parent {
4+
class func onlyInParent() {}
5+
final class func finalOnlyInParent() {}
6+
class func foo() {}
7+
}
8+
9+
class Child : Parent {}
410

5-
import Foundation
6-
class MyFunkyDictionary: NSDictionary {
7-
// CHECK-LABEL: sil hidden @_TZFC18super_class_method17MyFunkyDictionary10initialize
8-
// CHECK: super_method [volatile] %0 : $@thick MyFunkyDictionary.Type, #NSObject.initialize!1.foreign : NSObject.Type -> () -> ()
9-
override class func initialize() {
10-
super.initialize()
11+
class Grandchild : Child {
12+
class func onlyInGrandchild() {
13+
// CHECK-LABEL: sil hidden @_TZFC18super_class_method10Grandchild16onlyInGrandchildfT_T_
14+
// CHECK-NOT: function_ref @_TZFC18super_class_method6Parent12onlyInParentfT_T_
15+
// CHECK: super_method %0 : $@thick Grandchild.Type, #Parent.onlyInParent!1 : Parent.Type -> () -> () , $@convention(thin) (@thick Parent.Type) -> () // user: %5
16+
super.onlyInParent()
17+
// CHECK: function_ref @_TZFC18super_class_method6Parent17finalOnlyInParentfT_T_
18+
super.finalOnlyInParent()
1119
}
12-
}
1320

21+
override class func foo() {
22+
// CHECK: sil hidden @_TZFC18super_class_method10Grandchild3foofT_T_ : $@convention(thin) (@thick Grandchild.Type) -> () {
23+
// CHECK-NOT: function_ref @_TZFC18super_class_method10Grandchild3foofT_T_
24+
// CHECK: super_method %0 : $@thick Grandchild.Type, #Parent.foo!1 : Parent.Type -> () -> () , $@convention(thin) (@thick Parent.Type) -> ()
25+
super.foo()
26+
}
27+
}

test/SILGen/super_method.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %target-swift-frontend -emit-sil -sdk %S/Inputs -I %S/Inputs -enable-source-import %s -use-native-super-method | FileCheck %s
2+
3+
class Parent {
4+
func onlyInParent() {}
5+
final func finalOnlyInParent() {}
6+
func foo() {}
7+
}
8+
9+
class Child : Parent {}
10+
11+
class Grandchild : Child {
12+
// CHECK: sil hidden @_TFC12super_method10Grandchild16onlyInGrandchildfT_T_
13+
func onlyInGrandchild() {
14+
// CHECK: super_method %0 : $Grandchild, #Parent.onlyInParent!1 : Parent -> () -> ()
15+
super.onlyInParent()
16+
// CHECK: function_ref super_method.Parent.finalOnlyInParent
17+
super.finalOnlyInParent()
18+
}
19+
20+
// CHECK: sil hidden @_TFC12super_method10Grandchild3foofT_T_
21+
override func foo() {
22+
// CHECK: super_method %0 : $Grandchild, #Parent.foo!1 : Parent -> () -> ()
23+
super.foo()
24+
}
25+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: %target-swift-frontend -emit-silgen -sdk %S/Inputs -I %S/Inputs -enable-source-import %s -use-native-super-method | FileCheck %s
2+
3+
// REQUIRES: objc_interop
4+
5+
import Foundation
6+
class MyFunkyDictionary: NSDictionary {
7+
// CHECK-LABEL: sil hidden @_TZFC23super_objc_class_method17MyFunkyDictionary10initializefT_T_
8+
// CHECK: super_method [volatile] %0 : $@thick MyFunkyDictionary.Type, #NSObject.initialize!1.foreign : NSObject.Type -> () -> ()
9+
override class func initialize() {
10+
super.initialize()
11+
}
12+
}
13+

0 commit comments

Comments
 (0)