Skip to content

Commit 5003c15

Browse files
committed
stdlib: Add backward deployment versions for the
dynamic-replacement runtime functions. The recent change of how we do dynamic replacements added 2 new runtime functions. This patch adds those functions to the Compatibility50 static archive. This will allow backward deployment to a swift 5.0 runtime. Patch by Erik Eckstein with a modification to call the standard libraries implementation (marked as weak) when it is available. This ensures we can change the implementation in the future and are not ABI locked. rdar://problem/51601233
1 parent 605a92c commit 5003c15

File tree

8 files changed

+142
-9
lines changed

8 files changed

+142
-9
lines changed

include/swift/Runtime/Exclusivity.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,19 @@ void swift_beginAccess(void *pointer, ValueBuffer *buffer,
4444
/// replacement function if it should be called.
4545
/// Returns null if the original function (which is passed in \p CurrFn) should
4646
/// be called.
47+
#ifdef __APPLE__
48+
__attribute__((weak))
49+
#endif
4750
SWIFT_RUNTIME_EXPORT
4851
char *swift_getFunctionReplacement(char **ReplFnPtr, char *CurrFn);
4952

5053
/// Returns the original function of a replaced function, which is loaded from
5154
/// \p OrigFnPtr.
5255
/// This function is called from a replacement function to call the original
5356
/// function.
57+
#ifdef __APPLE__
58+
__attribute__((weak))
59+
#endif
5460
SWIFT_RUNTIME_EXPORT
5561
char *swift_getOrigOfReplaceable(char **OrigFnPtr);
5662

include/swift/Runtime/RuntimeFunctions.def

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1225,12 +1225,26 @@ FUNCTION(EndAccess, swift_endAccess, C_CC, AlwaysAvailable,
12251225
ARGS(getFixedBufferTy()->getPointerTo()),
12261226
ATTRS(NoUnwind))
12271227

1228-
FUNCTION(GetOrigOfReplaceable, swift_getOrigOfReplaceable, C_CC, AlwaysAvailable,
1228+
FUNCTION(GetOrigOfReplaceable, swift_getOrigOfReplaceable, C_CC,
1229+
GetReplacementAvailability,
12291230
RETURNS(FunctionPtrTy),
12301231
ARGS(FunctionPtrTy->getPointerTo()),
12311232
ATTRS(NoUnwind))
12321233

1233-
FUNCTION(GetReplacement, swift_getFunctionReplacement, C_CC, AlwaysAvailable,
1234+
FUNCTION(GetReplacement, swift_getFunctionReplacement, C_CC,
1235+
GetReplacementAvailability,
1236+
RETURNS(FunctionPtrTy),
1237+
ARGS(FunctionPtrTy->getPointerTo(), FunctionPtrTy),
1238+
ATTRS(NoUnwind))
1239+
1240+
FUNCTION(GetOrigOfReplaceable50, swift_getOrigOfReplaceable50, C_CC,
1241+
AlwaysAvailable,
1242+
RETURNS(FunctionPtrTy),
1243+
ARGS(FunctionPtrTy->getPointerTo()),
1244+
ATTRS(NoUnwind))
1245+
1246+
FUNCTION(GetReplacement50, swift_getFunctionReplacement50, C_CC,
1247+
AlwaysAvailable,
12341248
RETURNS(FunctionPtrTy),
12351249
ARGS(FunctionPtrTy->getPointerTo(), FunctionPtrTy),
12361250
ATTRS(NoUnwind))

lib/IRGen/GenDecl.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2239,8 +2239,12 @@ void IRGenModule::createReplaceableProlog(IRGenFunction &IGF, SILFunction *f) {
22392239
rhs = FnAddr;
22402240
} else {
22412241
// Call swift_getFunctionReplacement to check which function to call.
2242-
auto *callRTFunc = IGF.Builder.CreateCall(getGetReplacementFn(),
2243-
{ ReplAddr, FnAddr });
2242+
auto *getReplacementFn = IRGenModule::isGetReplacementAvailable(Context)
2243+
? getGetReplacementFn()
2244+
: getGetReplacement50Fn();
2245+
2246+
auto *callRTFunc =
2247+
IGF.Builder.CreateCall(getReplacementFn, {ReplAddr, FnAddr});
22442248
callRTFunc->setDoesNotThrow();
22452249
ReplFn = callRTFunc;
22462250
rhs = llvm::ConstantExpr::getNullValue(ReplFn->getType());
@@ -2408,8 +2412,11 @@ void IRGenModule::emitDynamicReplacementOriginalFunctionThunk(SILFunction *f) {
24082412
llvm::ConstantExpr::getInBoundsGetElementPtr(nullptr, linkEntry, indices),
24092413
FunctionPtrTy->getPointerTo());
24102414

2411-
auto *OrigFn = IGF.Builder.CreateCall(getGetOrigOfReplaceableFn(),
2412-
{ fnPtrAddr });
2415+
auto *getOrigOfReplaceableFn = IRGenModule::isGetReplacementAvailable(Context)
2416+
? getGetOrigOfReplaceableFn()
2417+
: getGetOrigOfReplaceable50Fn();
2418+
auto *OrigFn = IGF.Builder.CreateCall(getOrigOfReplaceableFn, {fnPtrAddr});
2419+
24132420
OrigFn->setDoesNotThrow();
24142421

24152422
auto *typeFnPtr =

lib/IRGen/IRGenModule.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,31 @@ IRGenModule::~IRGenModule() {
527527

528528
static bool isReturnAttribute(llvm::Attribute::AttrKind Attr);
529529

530+
static AvailabilityContext
531+
getGetReplacementAvailability(ASTContext &context) {
532+
auto target = context.LangOpts.Target;
533+
534+
if (target.isMacOSX()) {
535+
return AvailabilityContext(
536+
VersionRange::allGTE(llvm::VersionTuple(10, 15, 0)));
537+
} else if (target.isiOS()) {
538+
return AvailabilityContext(
539+
VersionRange::allGTE(llvm::VersionTuple(13, 0, 0)));
540+
} else if (target.isWatchOS()) {
541+
return AvailabilityContext(
542+
VersionRange::allGTE(llvm::VersionTuple(6, 0, 0)));
543+
} else {
544+
return AvailabilityContext::alwaysAvailable();
545+
}
546+
}
547+
548+
bool IRGenModule::isGetReplacementAvailable(ASTContext &Context) {
549+
auto deploymentAvailability =
550+
AvailabilityContext::forDeploymentTarget(Context);
551+
auto featureAvailability = getGetReplacementAvailability(Context);
552+
return deploymentAvailability.isContainedIn(featureAvailability);
553+
}
554+
530555
// Explicitly listing these constants is an unfortunate compromise for
531556
// making the database file much more compact.
532557
//
@@ -551,6 +576,10 @@ namespace RuntimeConstants {
551576

552577
return !deploymentAvailability.isContainedIn(featureAvailability);
553578
}
579+
580+
bool GetReplacementAvailability(ASTContext &Context) {
581+
return !IRGenModule::isGetReplacementAvailable(Context);
582+
}
554583
} // namespace RuntimeConstants
555584

556585
// We don't use enough attributes to justify generalizing the

lib/IRGen/IRGenModule.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1441,6 +1441,7 @@ private: \
14411441

14421442
void emitOpaqueTypeDescriptorAccessor(OpaqueTypeDecl *);
14431443

1444+
static bool isGetReplacementAvailable(ASTContext &context);
14441445
private:
14451446
llvm::Constant *
14461447
getAddrOfSharedContextDescriptor(LinkEntity entity,

stdlib/public/Compatibility50/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ set(swift_runtime_linker_flags ${SWIFT_RUNTIME_CORE_LINK_FLAGS})
33

44
add_swift_target_library(swiftCompatibility50 TARGET_LIBRARY STATIC INSTALL_WITH_SHARED
55
ProtocolConformance.cpp
6+
DynamicReplaceable.cpp
67
Overrides.cpp
78
C_COMPILE_FLAGS ${swift_runtime_library_compile_flags}
89
LINK_FLAGS ${swift_runtime_linker_flags}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//===--- ProtocolConformance.cpp - Swift protocol conformance checking ----===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Runtime support for dynamic replaceable functions.
14+
//
15+
// This implementation is intended to be backward-deployed into Swift 5.0
16+
// runtimes.
17+
//
18+
//===----------------------------------------------------------------------===//
19+
20+
#include "Overrides.h"
21+
#include "swift/Runtime/Once.h"
22+
#include "swift/Runtime/Exclusivity.h"
23+
#include "../runtime/ThreadLocalStorage.h"
24+
25+
using namespace swift;
26+
27+
// The thread-local key must be weak so that it is shared between different
28+
// binaries (e.g. shared libraries).
29+
__attribute__((weak)) __swift_thread_key_t _swift_dr_key;
30+
__attribute__((weak)) swift_once_t _swift_dr_Predicate;
31+
32+
static void createSwiftThreadKey(void *) {
33+
int result = SWIFT_THREAD_KEY_CREATE(&_swift_dr_key, nullptr);
34+
if (result != 0)
35+
abort();
36+
}
37+
38+
static __swift_thread_key_t &getTLSKey() {
39+
swift_once(&_swift_dr_Predicate, createSwiftThreadKey, nullptr);
40+
return _swift_dr_key;
41+
}
42+
43+
__attribute__((visibility("hidden"), weak))
44+
extern "C" char *swift_getFunctionReplacement50(char **ReplFnPtr, char *CurrFn) {
45+
// Call the current implementation if it is available.
46+
if (swift_getFunctionReplacement)
47+
return swift_getFunctionReplacement(ReplFnPtr, CurrFn);
48+
49+
char *ReplFn = *ReplFnPtr;
50+
char *RawReplFn = ReplFn;
51+
52+
if (RawReplFn == CurrFn)
53+
return nullptr;
54+
55+
__swift_thread_key_t key = getTLSKey();
56+
if ((intptr_t)SWIFT_THREAD_GETSPECIFIC(key) != 0) {
57+
SWIFT_THREAD_SETSPECIFIC(key, (void *)0);
58+
return nullptr;
59+
}
60+
return ReplFn;
61+
}
62+
63+
__attribute__((visibility("hidden"), weak))
64+
extern "C" char *swift_getOrigOfReplaceable50(char **OrigFnPtr) {
65+
// Call the current implementation if it is available.
66+
if (swift_getOrigOfReplaceable)
67+
return swift_getOrigOfReplaceable(OrigFnPtr);
68+
69+
char *OrigFn = *OrigFnPtr;
70+
SWIFT_THREAD_SETSPECIFIC(getTLSKey(), (void *)-1);
71+
return OrigFn;
72+
}

test/IRGen/dynamic_replaceable.sil

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
// RUN: %target-swift-frontend %s -emit-ir -disable-objc-interop | %FileCheck %s
1+
// RUN: %target-swift-frontend %s -emit-ir -disable-objc-interop | %FileCheck %s --check-prefix=CHECK --check-prefix=COMPAT
2+
// RUN: %target-swift-frontend %s -emit-ir -disable-objc-interop -target x86_64-apple-macosx10.15 | %FileCheck %s --check-prefix=CHECK --check-prefix=NONCOMPAT
23

34
// REQUIRES: objc_interop
45

@@ -23,7 +24,8 @@
2324

2425
// CHECK-LABEL: define swiftcc void @test_dynamically_replaceable()
2526
// CHECK-NEXT: entry:
26-
// CHECK-NEXT: [[FUN_PTR:%.*]] = call i8* @swift_getFunctionReplacement(i8** getelementptr inbounds (%swift.dyn_repl_link_entry, %swift.dyn_repl_link_entry* @test_dynamically_replaceableTX, i32 0, i32 0), i8* bitcast (void ()* @test_dynamically_replaceable to i8*))
27+
// COMPAT-NEXT: [[FUN_PTR:%.*]] = call i8* @swift_getFunctionReplacement50(i8** getelementptr inbounds (%swift.dyn_repl_link_entry, %swift.dyn_repl_link_entry* @test_dynamically_replaceableTX, i32 0, i32 0), i8* bitcast (void ()* @test_dynamically_replaceable to i8*))
28+
// NONCOMPAT-NEXT: [[FUN_PTR:%.*]] = call i8* @swift_getFunctionReplacement(i8** getelementptr inbounds (%swift.dyn_repl_link_entry, %swift.dyn_repl_link_entry* @test_dynamically_replaceableTX, i32 0, i32 0), i8* bitcast (void ()* @test_dynamically_replaceable to i8*))
2729
// CHECK-NEXT: [[CMP:%.*]] = icmp eq i8* [[FUN_PTR]], null
2830
// CHECK-NEXT: br i1 [[CMP]], label %[[ORIGBB:[a-z_]*]], label %[[FWBB:[a-z_]*]]
2931
// CHECK: [[FWBB]]:
@@ -49,7 +51,8 @@ bb0:
4951
// The thunk that implement the prev_dynamic_function_ref lookup.
5052
// CHECK-LABEL: define swiftcc void @test_replacementTI()
5153
// CHECK: entry:
52-
// CHECK: [[FUN_PTR:%.*]] = call i8* @swift_getOrigOfReplaceable(i8** getelementptr inbounds (%swift.dyn_repl_link_entry, %swift.dyn_repl_link_entry* @test_replacementTX, i32 0, i32 0))
54+
// COMPAT: [[FUN_PTR:%.*]] = call i8* @swift_getOrigOfReplaceable50(i8** getelementptr inbounds (%swift.dyn_repl_link_entry, %swift.dyn_repl_link_entry* @test_replacementTX, i32 0, i32 0))
55+
// NONCOMPAT: [[FUN_PTR:%.*]] = call i8* @swift_getOrigOfReplaceable(i8** getelementptr inbounds (%swift.dyn_repl_link_entry, %swift.dyn_repl_link_entry* @test_replacementTX, i32 0, i32 0))
5356
// CHECK: [[TYPED_PTR:%.*]] = bitcast i8* [[FUN_PTR]] to void ()*
5457
// CHECK: call swiftcc void [[TYPED_PTR]]()
5558
// CHECK: ret void

0 commit comments

Comments
 (0)