Skip to content

Commit 7e64684

Browse files
committed
SILGen: Add an option to disable the previous implementation inside of dynamic replacement thunks
@_dynamicReplacement(for: selfRec(x: acc:)) func selfRec_r(x: Int, acc: Int) -> Int { if x <= 0 { return acc } // Normally, this will call selfRec(x: acc:)'s implementation. // With the option, this will call to selfRec_r(x: acc:). return selfRec(x: x - 1, acc: acc + 1) } rdar://51229650
1 parent b75f9ee commit 7e64684

8 files changed

+157
-4
lines changed

include/swift/AST/SILOptions.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,16 @@ class SILOptions {
129129
/// Emit extra exclusvity markers for memory access and verify coverage.
130130
bool VerifyExclusivity = false;
131131

132+
/// Calls to the replaced method inside of the replacement method will call
133+
/// the previous implementation.
134+
///
135+
/// @_dynamicReplacement(for: original())
136+
/// func replacement() {
137+
/// if (...)
138+
/// original() // calls original() implementation if true
139+
/// }
140+
bool EnableDynamicReplacementCanCallPreviousImplementation = true;
141+
132142
/// Enable large loadable types IRGen pass.
133143
bool EnableLargeLoadableTypes = true;
134144

include/swift/Option/FrontendOptions.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,11 @@ def enable_implicit_dynamic : Flag<["-"], "enable-implicit-dynamic">,
357357
Flags<[FrontendOption, NoInteractiveOption, HelpHidden]>,
358358
HelpText<"Add 'dynamic' to all declarations">;
359359

360+
def disable_previous_implementation_calls_in_dynamic_replacements :
361+
Flag<["-"], "disable-previous-implementation-calls-in-dynamic-replacements">,
362+
Flags<[FrontendOption, NoInteractiveOption, HelpHidden]>,
363+
HelpText<"Disable calling the previous implementation in dynamic replacements">;
364+
360365
def enable_dynamic_replacement_chaining :
361366
Flag<["-"], "enable-dynamic-replacement-chaining">,
362367
Flags<[FrontendOption, NoInteractiveOption, HelpHidden]>,

lib/Frontend/CompilerInvocation.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,8 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args,
807807
Opts.VerifySILOwnership &= !Args.hasArg(OPT_disable_sil_ownership_verifier);
808808
Opts.EnableLargeLoadableTypes |= Args.hasArg(OPT_enable_large_loadable_types);
809809
Opts.StripOwnershipAfterSerialization |= Args.hasArg(OPT_enable_ownership_stripping_after_serialization);
810+
Opts.EnableDynamicReplacementCanCallPreviousImplementation = !Args.hasArg(
811+
OPT_disable_previous_implementation_calls_in_dynamic_replacements);
810812

811813
if (const Arg *A = Args.getLastArg(OPT_save_optimization_record_path))
812814
Opts.OptRecordFile = A->getValue();

lib/SILGen/SILGenApply.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,7 +1047,9 @@ class SILGenApply : public Lowering::ExprVisitor<SILGenApply> {
10471047
// Directly dispatch to calls of the replaced function inside of
10481048
// '@_dynamicReplacement(for:)' methods.
10491049
bool isObjCReplacementCall = false;
1050-
if (isCallToReplacedInDynamicReplacement(SGF, afd, isObjCReplacementCall) &&
1050+
if (SGF.getOptions()
1051+
.EnableDynamicReplacementCanCallPreviousImplementation &&
1052+
isCallToReplacedInDynamicReplacement(SGF, afd, isObjCReplacementCall) &&
10511053
thisCallSite->getArg()->isSelfExprOf(
10521054
cast<AbstractFunctionDecl>(SGF.FunctionDC->getAsDecl()), false)) {
10531055
auto constant = SILDeclRef(afd).asForeign(
@@ -1118,6 +1120,8 @@ class SILGenApply : public Lowering::ExprVisitor<SILGenApply> {
11181120
ApplyExpr *thisCallSite = callSites.back();
11191121
bool isObjCReplacementSelfCall = false;
11201122
bool isSelfCallToReplacedInDynamicReplacement =
1123+
SGF.getOptions()
1124+
.EnableDynamicReplacementCanCallPreviousImplementation &&
11211125
isCallToReplacedInDynamicReplacement(
11221126
SGF, cast<AbstractFunctionDecl>(constant.getDecl()),
11231127
isObjCReplacementSelfCall) &&
@@ -1452,6 +1456,8 @@ class SILGenApply : public Lowering::ExprVisitor<SILGenApply> {
14521456

14531457
bool isObjCReplacementSelfCall = false;
14541458
bool isSelfCallToReplacedInDynamicReplacement =
1459+
SGF.getOptions()
1460+
.EnableDynamicReplacementCanCallPreviousImplementation &&
14551461
isCallToReplacedInDynamicReplacement(
14561462
SGF, cast<AbstractFunctionDecl>(constant.getDecl()),
14571463
isObjCReplacementSelfCall) &&
@@ -5119,8 +5125,11 @@ static Callee getBaseAccessorFunctionRef(SILGenFunction &SGF,
51195125
auto *decl = cast<AbstractFunctionDecl>(constant.getDecl());
51205126

51215127
bool isObjCReplacementSelfCall = false;
5122-
if (isOnSelfParameter && isCallToReplacedInDynamicReplacement(
5123-
SGF, decl, isObjCReplacementSelfCall)) {
5128+
if (isOnSelfParameter &&
5129+
SGF.getOptions()
5130+
.EnableDynamicReplacementCanCallPreviousImplementation &&
5131+
isCallToReplacedInDynamicReplacement(SGF, decl,
5132+
isObjCReplacementSelfCall)) {
51245133
return Callee::forDirect(
51255134
SGF,
51265135
SILDeclRef(cast<AbstractFunctionDecl>(SGF.FunctionDC->getAsDecl()),
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
public dynamic func selfRec(_ x: Int, _ acc: Int) -> Int {
2+
return 0
3+
}
4+
5+
public class AClass {
6+
public init() {}
7+
public dynamic func selfRec(_ x: Int, _ acc: Int) -> Int {
8+
return 0
9+
}
10+
}
11+
12+
public class AStruct {
13+
public init() {}
14+
public dynamic func selfRec(_ x: Int, _ acc: Int) -> Int {
15+
return 0
16+
}
17+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import Module1
2+
3+
@_dynamicReplacement(for: selfRec(_: _:))
4+
func selfRec_r(_ x: Int, _ acc: Int) -> Int {
5+
if x <= 0 {
6+
return acc
7+
}
8+
return selfRec(x - 1, acc + 1)
9+
}
10+
11+
extension AStruct {
12+
@_dynamicReplacement(for: selfRec(_: _:))
13+
func selfRec_r(_ x: Int, _ acc: Int) -> Int {
14+
if x <= 0 {
15+
return acc
16+
}
17+
return selfRec(x - 1, acc + 1)
18+
}
19+
}
20+
21+
extension AClass {
22+
@_dynamicReplacement(for: selfRec(_: _:))
23+
func selfRec_r(_ x: Int, _ acc: Int) -> Int {
24+
if x <= 0 {
25+
return acc
26+
}
27+
return selfRec(x - 1, acc + 1)
28+
}
29+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift-dylib(%t/%target-library-name(Module1)) -module-name Module1 -emit-module -emit-module-path %t/Module1.swiftmodule -swift-version 5 %S/Inputs/dynamic_replacement_without_previous_calls1.swift
3+
// RUN: %target-build-swift-dylib(%t/%target-library-name(Module2)) -I%t -L%t -lModule1 %target-rpath(%t) -module-name Module2 -emit-module -emit-module-path %t/Module2.swiftmodule -swift-version 5 %S/Inputs/dynamic_replacement_without_previous_calls2.swift -Xfrontend -disable-previous-implementation-calls-in-dynamic-replacements
4+
// RUN: %target-build-swift -I%t -L%t -lModule1 -o %t/main %target-rpath(%t) %s -swift-version 5
5+
// RUN: %target-codesign %t/main %t/%target-library-name(Module1) %t/%target-library-name(Module2)
6+
// RUN: %target-run %t/main %t/%target-library-name(Module1) %t/%target-library-name(Module2)
7+
8+
// REQUIRES: executable_test
9+
10+
import Module1
11+
12+
import StdlibUnittest
13+
14+
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
15+
import Darwin
16+
#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku)
17+
import Glibc
18+
#elseif os(Windows)
19+
import MSVCRT
20+
import WinSDK
21+
#else
22+
#error("Unsupported platform")
23+
#endif
24+
25+
private func target_library_name(_ name: String) -> String {
26+
#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS)
27+
return "lib\(name).dylib"
28+
#elseif os(Windows)
29+
return "\(name).dll"
30+
#else
31+
return "lib\(name).so"
32+
#endif
33+
}
34+
35+
var DynamicallyReplaceable = TestSuite("DynamicallyReplaceable")
36+
37+
DynamicallyReplaceable.test("DynamicallyReplaceable") {
38+
var executablePath = CommandLine.arguments[0]
39+
executablePath.removeLast(4)
40+
41+
// First, test with only the original module.
42+
expectEqual(0, selfRec(2, 0))
43+
expectEqual(0, AClass().selfRec(2, 0))
44+
expectEqual(0, AStruct().selfRec(2, 0))
45+
46+
// Now, test with the module containing the replacements.
47+
#if os(Linux)
48+
_ = dlopen(target_library_name("Module2"), RTLD_NOW)
49+
#elseif os(Windows)
50+
_ = LoadLibraryA(target_library_name("Module2"))
51+
#else
52+
_ = dlopen(executablePath+target_library_name("Module2"), RTLD_NOW)
53+
#endif
54+
expectEqual(2, selfRec(2, 0))
55+
expectEqual(2, AClass().selfRec(2, 0))
56+
expectEqual(2, AStruct().selfRec(2, 0))
57+
}
58+
59+
runAllTests()

test/SILGen/dynamically_replaceable.swift

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// RUN: %target-swift-emit-silgen -swift-version 5 %s | %FileCheck %s
22
// RUN: %target-swift-emit-silgen -swift-version 5 %s -enable-implicit-dynamic | %FileCheck %s --check-prefix=IMPLICIT
3+
// RUN: %target-swift-emit-silgen -swift-version 5 %s -disable-previous-implementation-calls-in-dynamic-replacements | %FileCheck %s --check-prefix=NOPREVIOUS
34

45
// CHECK-LABEL: sil hidden [ossa] @$s23dynamically_replaceable014maybe_dynamic_B0yyF : $@convention(thin) () -> () {
56
// IMPLICIT-LABEL: sil hidden [dynamically_replacable] [ossa] @$s23dynamically_replaceable014maybe_dynamic_B0yyF : $@convention(thin) () -> () {
@@ -122,6 +123,12 @@ extension Klass {
122123
// CHECK: [[METHOD:%.*]] = class_method %0 : $Klass, #Klass.dynamic_replaceable2!1
123124
// CHECK: = apply [[METHOD]](%0) : $@convention(method) (@guaranteed Klass) -> ()
124125
// CHECK: return
126+
// NOPREVIOUS-LABEL: sil hidden [dynamic_replacement_for "$s23dynamically_replaceable5KlassC08dynamic_B0yyF"] [ossa] @$s23dynamically_replaceable5KlassC11replacementyyF : $@convention(method) (@guaranteed Klass) -> () {
127+
// NOPREVIOUS: [[FN:%.*]] = class_method %0 : $Klass, #Klass.dynamic_replaceable
128+
// NOPREVIOUS: apply [[FN]](%0) : $@convention(method) (@guaranteed Klass) -> ()
129+
// NOPREVIOUS: [[METHOD:%.*]] = class_method %0 : $Klass, #Klass.dynamic_replaceable2!1
130+
// NOPREVIOUS: = apply [[METHOD]](%0) : $@convention(method) (@guaranteed Klass) -> ()
131+
// NOPREVIOUS: return
125132
@_dynamicReplacement(for: dynamic_replaceable())
126133
func replacement() {
127134
dynamic_replaceable()
@@ -131,6 +138,9 @@ extension Klass {
131138
// CHECK-LABEL: sil hidden [dynamic_replacement_for "$s23dynamically_replaceable5KlassC1cACSi_tcfC"] [ossa] @$s23dynamically_replaceable5KlassC2crACSi_tcfC : $@convention(method) (Int, @thick Klass.Type) -> @owned Klass {
132139
// CHECK: [[FUN:%.*]] = prev_dynamic_function_ref @$s23dynamically_replaceable5KlassC2crACSi_tcfC
133140
// CHECK: apply [[FUN]]({{.*}}, %1)
141+
// NOPREVIOUS-LABEL: sil hidden [dynamic_replacement_for "$s23dynamically_replaceable5KlassC1cACSi_tcfC"] [ossa] @$s23dynamically_replaceable5KlassC2crACSi_tcfC : $@convention(method) (Int, @thick Klass.Type) -> @owned Klass {
142+
// NOPREVIOUS: [[FUN:%.*]] = dynamic_function_ref @$s23dynamically_replaceable5KlassC1cACSi_tcfC
143+
// NOPREVIOUS: apply [[FUN]]({{.*}}, %1)
134144
@_dynamicReplacement(for: init(c:))
135145
convenience init(cr: Int) {
136146
self.init(c: cr + 1)
@@ -140,7 +150,11 @@ extension Klass {
140150
// CHECK: // dynamic_function_ref Klass.__allocating_init(c:)
141151
// CHECK: [[FUN:%.*]] = dynamic_function_ref @$s23dynamically_replaceable5KlassC1cACSi_tcfC
142152
// CHECK: apply [[FUN]]({{.*}}, %2)
143-
@_dynamicReplacement(for: init(a: b:))
153+
// NOPREVIOUS-LABEL: sil hidden [dynamic_replacement_for "$s23dynamically_replaceable5KlassC1a1bACSi_SitcfC"] [ossa] @$s23dynamically_replaceable5KlassC2ar2brACSi_SitcfC
154+
// NOPREVIOUS: // dynamic_function_ref Klass.__allocating_init(c:)
155+
// NOPREVIOUS: [[FUN:%.*]] = dynamic_function_ref @$s23dynamically_replaceable5KlassC1cACSi_tcfC
156+
// NOPREVIOUS: apply [[FUN]]({{.*}}, %2)
157+
@_dynamicReplacement(for: init(a: b:))
144158
convenience init(ar: Int, br: Int) {
145159
self.init(c: ar + br)
146160
}
@@ -153,11 +167,19 @@ extension Klass {
153167
// CHECK: bb0([[ARG:%.*]] : @guaranteed $Klass):
154168
// CHECK: [[ORIG:%.*]] = prev_dynamic_function_ref @$s23dynamically_replaceable5KlassC1rSivg
155169
// CHECK: apply [[ORIG]]([[ARG]]) : $@convention(method) (@guaranteed Klass) -> Int
170+
// NOPREVIOUS-LABEL: sil hidden [dynamic_replacement_for "$s23dynamically_replaceable5KlassC08dynamic_B4_varSivg"] [ossa] @$s23dynamically_replaceable5KlassC1rSivg : $@convention(method) (@guaranteed Klass) -> Int {
171+
// NOPREVIOUS: bb0([[ARG:%.*]] : @guaranteed $Klass):
172+
// NOPREVIOUS: [[ORIG:%.*]] = class_method [[ARG]] : $Klass, #Klass.dynamic_replaceable_var!getter.1
173+
// NOPREVIOUS: apply [[ORIG]]([[ARG]]) : $@convention(method) (@guaranteed Klass) -> Int
156174

157175
// CHECK-LABEL: sil hidden [dynamic_replacement_for "$s23dynamically_replaceable5KlassC08dynamic_B4_varSivs"] [ossa] @$s23dynamically_replaceable5KlassC1rSivs : $@convention(method) (Int, @guaranteed Klass) -> () {
158176
// CHECK: bb0({{.*}} : $Int, [[SELF:%.*]] : @guaranteed $Klass):
159177
// CHECK: [[ORIG:%.*]] = prev_dynamic_function_ref @$s23dynamically_replaceable5KlassC1rSivs
160178
// CHECK: apply [[ORIG]]({{.*}}, [[SELF]]) : $@convention(method)
179+
// NOPREVIOUS-LABEL: sil hidden [dynamic_replacement_for "$s23dynamically_replaceable5KlassC08dynamic_B4_varSivs"] [ossa] @$s23dynamically_replaceable5KlassC1rSivs : $@convention(method) (Int, @guaranteed Klass) -> () {
180+
// NOPREVIOUS: bb0({{.*}} : $Int, [[SELF:%.*]] : @guaranteed $Klass):
181+
// NOPREVIOUS: [[ORIG:%.*]] = class_method [[SELF]] : $Klass, #Klass.dynamic_replaceable_var!setter
182+
// NOPREVIOUS: apply [[ORIG]]({{.*}}, [[SELF]]) : $@convention(method)
161183
@_dynamicReplacement(for: dynamic_replaceable_var)
162184
var r : Int {
163185
get {

0 commit comments

Comments
 (0)