Skip to content

Commit fb7b223

Browse files
committed
Add support to modify chaining behavior of dynamic replacements
Default to not chain dynamic replacements: Only one replacement and the original implementation are active.
1 parent 41d1c6f commit fb7b223

File tree

9 files changed

+95
-3
lines changed

9 files changed

+95
-3
lines changed

include/swift/ABI/Metadata.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4127,6 +4127,8 @@ class DynamicReplacementDescriptor {
41274127
RelativeDirectPointer<DynamicReplacementChainEntry, false> chainEntry;
41284128
uint32_t flags;
41294129

4130+
enum : uint32_t { EnableChainingMask = 0x1 };
4131+
41304132
public:
41314133
/// Enable this replacement by changing the function's replacement chain's
41324134
/// root entry.
@@ -4143,6 +4145,8 @@ class DynamicReplacementDescriptor {
41434145
void disableReplacement() const;
41444146

41454147
uint32_t getFlags() const { return flags; }
4148+
4149+
bool shouldChain() const { return (flags & EnableChainingMask); }
41464150
};
41474151

41484152
/// A collection of dynamic replacement records.

include/swift/AST/IRGenOptions.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,9 @@ class IRGenOptions {
187187
/// Instrument code to generate profiling information.
188188
unsigned GenerateProfile : 1;
189189

190+
/// Enable chaining of dynamic replacements.
191+
unsigned EnableDynamicReplacementChaining : 1;
192+
190193
/// Path to the profdata file to be used for PGO, or the empty string.
191194
std::string UseProfile = "";
192195

@@ -219,7 +222,8 @@ class IRGenOptions {
219222
ValueNames(false), EnableReflectionMetadata(true),
220223
EnableReflectionNames(true), EnableClassResilience(false),
221224
EnableResilienceBypass(false), UseIncrementalLLVMCodeGen(true),
222-
UseSwiftCall(false), GenerateProfile(false), CmdArgs(),
225+
UseSwiftCall(false), GenerateProfile(false),
226+
EnableDynamicReplacementChaining(false), CmdArgs(),
223227
SanitizeCoverage(llvm::SanitizerCoverageOptions()),
224228
TypeInfoFilter(TypeInfoDumpFilter::All) {}
225229

include/swift/Option/FrontendOptions.td

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

365+
def enable_dynamic_replacement_chaining :
366+
Flag<["-"], "enable-dynamic-replacement-chaining">,
367+
Flags<[FrontendOption, NoInteractiveOption, HelpHidden]>,
368+
HelpText<"Enable chaining of dynamic replacements">;
369+
365370
def enable_nskeyedarchiver_diagnostics :
366371
Flag<["-"], "enable-nskeyedarchiver-diagnostics">,
367372
HelpText<"Diagnose classes with unstable mangled names adopting NSCoding">;

lib/Frontend/CompilerInvocation.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,9 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args,
989989

990990
Opts.PrintInlineTree |= Args.hasArg(OPT_print_llvm_inline_tree);
991991

992+
Opts.EnableDynamicReplacementChaining |=
993+
Args.hasArg(OPT_enable_dynamic_replacement_chaining);
994+
992995
Opts.UseSwiftCall = Args.hasArg(OPT_enable_swiftcall);
993996

994997
// This is set to true by default.

lib/IRGen/GenDecl.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1288,7 +1288,7 @@ void IRGenerator::emitDynamicReplacements() {
12881288
// RelativeIndirectablePointer<KeyEntry, false> replacedFunctionKey;
12891289
// RelativeDirectPointer<void> newFunction;
12901290
// RelativeDirectPointer<LinkEntry> replacement;
1291-
// uint32_t flags; // unused.
1291+
// uint32_t flags; // shouldChain.
12921292
// }[0]
12931293
// };
12941294
ConstantInitBuilder builder(IGM);
@@ -1315,7 +1315,8 @@ void IRGenerator::emitDynamicReplacements() {
13151315
replacement.addRelativeAddress(newFnPtr); // direct relative reference.
13161316
replacement.addRelativeAddress(
13171317
replacementLinkEntry); // direct relative reference.
1318-
replacement.addInt32(0); // unused flags.
1318+
replacement.addInt32(
1319+
Opts.EnableDynamicReplacementChaining ? 1 : 0);
13191320
replacement.finishAndAddTo(replacementsArray);
13201321
}
13211322
replacementsArray.finishAndAddTo(replacementScope);

stdlib/public/runtime/MetadataLookup.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1565,6 +1565,13 @@ void DynamicReplacementDescriptor::enableReplacement() const {
15651565
}
15661566
}
15671567

1568+
// Unlink the previous entry if we are not chaining.
1569+
if (!shouldChain() && chainRoot->next) {
1570+
auto *previous = chainRoot->next;
1571+
chainRoot->next = previous->next;
1572+
chainRoot->implementationFunction = previous->implementationFunction;
1573+
}
1574+
15681575
// First populate the current replacement's chain entry.
15691576
auto *currentEntry =
15701577
const_cast<DynamicReplacementChainEntry *>(chainEntry.get());
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
public struct Impl {
2+
public init() {}
3+
dynamic public func foo() -> Int {
4+
return 1
5+
}
6+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import A
2+
3+
extension Impl {
4+
@_dynamicReplacement(for: foo())
5+
func repl() -> Int {
6+
return foo() + 1
7+
}
8+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// First build without chaining.
2+
// RUN: %empty-directory(%t)
3+
// RUN: %target-build-swift-dylib(%t/libA.%target-dylib-extension) -module-name A -emit-module -emit-module-path %t -swift-version 5 %S/Inputs/dynamic_replacement_chaining_A.swift
4+
// RUN: %target-build-swift-dylib(%t/libB.%target-dylib-extension) -I%t -L%t -lA -Xlinker -rpath -Xlinker %t -module-name B -emit-module -emit-module-path %t -swift-version 5 %S/Inputs/dynamic_replacement_chaining_B.swift
5+
// RUN: %target-build-swift-dylib(%t/libC.%target-dylib-extension) -I%t -L%t -lA -Xlinker -rpath -Xlinker %t -module-name C -emit-module -emit-module-path %t -swift-version 5 %S/Inputs/dynamic_replacement_chaining_B.swift
6+
// RUN: %target-build-swift -I%t -L%t -lA -o %t/main -Xlinker -rpath -Xlinker %t %s -swift-version 5
7+
// RUN: %target-codesign %t/main %t/libA.%target-dylib-extension %t/libB.%target-dylib-extension %t/libC.%target-dylib-extension
8+
// RUN: %target-run %t/main %t/libA.%target-dylib-extension %t/libB.%target-dylib-extension %t/libC.%target-dylib-extension
9+
10+
// Now build with chaining enabled.
11+
// RUN: %empty-directory(%t)
12+
// RUN: %target-build-swift-dylib(%t/libA.%target-dylib-extension) -module-name A -emit-module -emit-module-path %t -swift-version 5 %S/Inputs/dynamic_replacement_chaining_A.swift
13+
// RUN: %target-build-swift-dylib(%t/libB.%target-dylib-extension) -I%t -L%t -lA -Xlinker -rpath -Xlinker %t -module-name B -emit-module -emit-module-path %t -swift-version 5 %S/Inputs/dynamic_replacement_chaining_B.swift -Xfrontend -enable-dynamic-replacement-chaining
14+
// RUN: %target-build-swift-dylib(%t/libC.%target-dylib-extension) -I%t -L%t -lA -Xlinker -rpath -Xlinker %t -module-name C -emit-module -emit-module-path %t -swift-version 5 %S/Inputs/dynamic_replacement_chaining_B.swift -Xfrontend -enable-dynamic-replacement-chaining
15+
// RUN: %target-build-swift -I%t -L%t -lA -DCHAINING -o %t/main -Xlinker -rpath -Xlinker %t %s -swift-version 5
16+
// RUN: %target-codesign %t/main %t/libA.%target-dylib-extension %t/libB.%target-dylib-extension %t/libC.%target-dylib-extension
17+
// RUN: %target-run %t/main %t/libA.%target-dylib-extension %t/libB.%target-dylib-extension %t/libC.%target-dylib-extension
18+
19+
20+
import A
21+
22+
import StdlibUnittest
23+
24+
#if os(Linux)
25+
import Glibc
26+
let dylibSuffix = "so"
27+
#else
28+
import Darwin
29+
let dylibSuffix = "dylib"
30+
#endif
31+
32+
var DynamicallyReplaceable = TestSuite("DynamicallyReplaceableChaining")
33+
34+
35+
DynamicallyReplaceable.test("DynamicallyReplaceable") {
36+
var executablePath = CommandLine.arguments[0]
37+
executablePath.removeLast(4)
38+
39+
#if os(Linux)
40+
_ = dlopen("libB."+dylibSuffix, RTLD_NOW)
41+
_ = dlopen("libC."+dylibSuffix, RTLD_NOW)
42+
#else
43+
_ = dlopen(executablePath+"libB."+dylibSuffix, RTLD_NOW)
44+
_ = dlopen(executablePath+"libC."+dylibSuffix, RTLD_NOW)
45+
#endif
46+
47+
#if CHAINING
48+
expectEqual(3, Impl().foo())
49+
#else
50+
expectEqual(2, Impl().foo())
51+
#endif
52+
}
53+
54+
runAllTests()

0 commit comments

Comments
 (0)