Skip to content

Commit 2ea531c

Browse files
committed
IRGen/runtime: change the code generation for dynamically replaceable functions
Instead of a thunk insert the dispatch into the original function. If the original function should be executed the prolog just jumps to the "real" code in the function. Otherwise the replacement function is called. There is one little complication here: when the replacement function calls the original function, the original function should not dispatch to the replacement again. To pass this information, we use a flag in thread local storage. The setting and reading of the flag is done in two new runtime functions. rdar://problem/51043781
1 parent 6da1be5 commit 2ea531c

File tree

8 files changed

+152
-47
lines changed

8 files changed

+152
-47
lines changed

include/swift/Runtime/Exclusivity.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,20 @@ SWIFT_RUNTIME_EXPORT
4040
void swift_beginAccess(void *pointer, ValueBuffer *buffer,
4141
ExclusivityFlags flags, void *pc);
4242

43+
/// Loads the replacement function pointer from \p ReplFnPtr and returns the
44+
/// replacement function if it should be called.
45+
/// Returns null if the original function (which is passed in \p CurrFn) should
46+
/// be called.
47+
SWIFT_RUNTIME_EXPORT
48+
char *swift_getFunctionReplacement(char **ReplFnPtr, char *CurrFn);
49+
50+
/// Returns the original function of a replaced function, which is loaded from
51+
/// \p OrigFnPtr.
52+
/// This function is called from a replacement function to call the original
53+
/// function.
54+
SWIFT_RUNTIME_EXPORT
55+
char *swift_getOrigOfReplaceable(char **OrigFnPtr);
56+
4357
/// Stop dynamically tracking an access.
4458
SWIFT_RUNTIME_EXPORT
4559
void swift_endAccess(ValueBuffer *buffer);

include/swift/Runtime/RuntimeFunctions.def

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

1228+
FUNCTION(GetOrigOfReplaceable, swift_getOrigOfReplaceable, C_CC, AlwaysAvailable,
1229+
RETURNS(FunctionPtrTy),
1230+
ARGS(FunctionPtrTy->getPointerTo()),
1231+
ATTRS(NoUnwind))
1232+
1233+
FUNCTION(GetReplacement, swift_getFunctionReplacement, C_CC, AlwaysAvailable,
1234+
RETURNS(FunctionPtrTy),
1235+
ARGS(FunctionPtrTy->getPointerTo(), FunctionPtrTy),
1236+
ATTRS(NoUnwind))
1237+
12281238
FUNCTION(InstantiateObjCClass, swift_instantiateObjCClass,
12291239
C_CC, AlwaysAvailable,
12301240
RETURNS(VoidTy),

lib/IRGen/GenDecl.cpp

Lines changed: 74 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2175,6 +2175,71 @@ static llvm::GlobalVariable *createGlobalForDynamicReplacementFunctionKey(
21752175
return key;
21762176
}
21772177

2178+
/// Creates the prolog for a dynamically replaceable function.
2179+
/// It checks if the replaced function or the original function should be called
2180+
/// (by calling the swift_getFunctionReplacement runtime function). In case of
2181+
/// the original function, it just jumps to the "real" code of the function,
2182+
/// otherwise it tail calls the replacement.
2183+
void IRGenModule::createReplaceableProlog(IRGenFunction &IGF, SILFunction *f) {
2184+
LinkEntity varEntity =
2185+
LinkEntity::forDynamicallyReplaceableFunctionVariable(f);
2186+
LinkEntity keyEntity =
2187+
LinkEntity::forDynamicallyReplaceableFunctionKey(f);
2188+
Signature signature = getSignature(f->getLoweredFunctionType());
2189+
2190+
// Create and initialize the first link entry for the chain of replacements.
2191+
// The first implementation is initialized with 'implFn'.
2192+
auto linkEntry = getChainEntryForDynamicReplacement(*this, varEntity, IGF.CurFn);
2193+
2194+
// Create the key data structure. This is used from other modules to refer to
2195+
// the chain of replacements.
2196+
createGlobalForDynamicReplacementFunctionKey(*this, keyEntity, linkEntry);
2197+
2198+
llvm::Constant *indices[] = {llvm::ConstantInt::get(Int32Ty, 0),
2199+
llvm::ConstantInt::get(Int32Ty, 0)};
2200+
2201+
auto *fnPtrAddr =
2202+
llvm::ConstantExpr::getInBoundsGetElementPtr(nullptr, linkEntry, indices);
2203+
2204+
auto *ReplAddr =
2205+
llvm::ConstantExpr::getPointerBitCastOrAddrSpaceCast(fnPtrAddr,
2206+
FunctionPtrTy->getPointerTo());
2207+
2208+
auto *FnAddr = llvm::ConstantExpr::getPointerBitCastOrAddrSpaceCast(
2209+
IGF.CurFn, FunctionPtrTy);
2210+
2211+
// Call swift_getFunctionReplacement to check which function to call.
2212+
auto *ReplFn = IGF.Builder.CreateCall(getGetReplacementFn(),
2213+
{ ReplAddr, FnAddr });
2214+
ReplFn->setDoesNotThrow();
2215+
auto *hasReplFn = IGF.Builder.CreateICmpEQ(ReplFn,
2216+
llvm::ConstantExpr::getNullValue(ReplFn->getType()));
2217+
2218+
auto *replacedBB = IGF.createBasicBlock("forward_to_replaced");
2219+
auto *origEntryBB = IGF.createBasicBlock("original_entry");
2220+
IGF.Builder.CreateCondBr(hasReplFn, origEntryBB, replacedBB);
2221+
2222+
IGF.Builder.emitBlock(replacedBB);
2223+
2224+
// Call the replacement function.
2225+
SmallVector<llvm::Value *, 16> forwardedArgs;
2226+
for (auto &arg : IGF.CurFn->args())
2227+
forwardedArgs.push_back(&arg);
2228+
auto *fnType = signature.getType()->getPointerTo();
2229+
auto *realReplFn = IGF.Builder.CreateBitCast(ReplFn, fnType);
2230+
2231+
auto *Res = IGF.Builder.CreateCall(FunctionPointer(realReplFn, signature),
2232+
forwardedArgs);
2233+
Res->setTailCall();
2234+
if (IGF.CurFn->getReturnType()->isVoidTy())
2235+
IGF.Builder.CreateRetVoid();
2236+
else
2237+
IGF.Builder.CreateRet(Res);
2238+
2239+
IGF.Builder.emitBlock(origEntryBB);
2240+
2241+
}
2242+
21782243
/// Emit the thunk that dispatches to the dynamically replaceable function.
21792244
static void emitDynamicallyReplaceableThunk(IRGenModule &IGM,
21802245
LinkEntity varEntity,
@@ -2303,11 +2368,17 @@ void IRGenModule::emitDynamicReplacementOriginalFunctionThunk(SILFunction *f) {
23032368
llvm::Constant *indices[] = {llvm::ConstantInt::get(Int32Ty, 0),
23042369
llvm::ConstantInt::get(Int32Ty, 0)};
23052370

2306-
auto *fnPtr = IGF.Builder.CreateLoad(
2371+
auto *fnPtrAddr =
2372+
llvm::ConstantExpr::getPointerBitCastOrAddrSpaceCast(
23072373
llvm::ConstantExpr::getInBoundsGetElementPtr(nullptr, linkEntry, indices),
2308-
getPointerAlignment());
2374+
FunctionPtrTy->getPointerTo());
2375+
2376+
auto *OrigFn = IGF.Builder.CreateCall(getGetOrigOfReplaceableFn(),
2377+
{ fnPtrAddr });
2378+
OrigFn->setDoesNotThrow();
2379+
23092380
auto *typeFnPtr =
2310-
IGF.Builder.CreateBitOrPointerCast(fnPtr, implFn->getType());
2381+
IGF.Builder.CreateBitOrPointerCast(OrigFn, implFn->getType());
23112382

23122383
SmallVector<llvm::Value *, 16> forwardedArgs;
23132384
for (auto &arg : implFn->args())
@@ -2338,25 +2409,6 @@ llvm::Function *IRGenModule::getAddrOfSILFunction(
23382409
if (fn) {
23392410
if (forDefinition) {
23402411
updateLinkageForDefinition(*this, fn, entity);
2341-
if (isDynamicallyReplaceableImplementation) {
2342-
// Create the dynamically replacement thunk.
2343-
LinkEntity implEntity = LinkEntity::forSILFunction(f, true);
2344-
auto existingImpl = Module.getFunction(implEntity.mangleAsString());
2345-
assert(!existingImpl);
2346-
(void) existingImpl;
2347-
Signature signature = getSignature(f->getLoweredFunctionType());
2348-
addLLVMFunctionAttributes(f, signature);
2349-
LinkInfo implLink = LinkInfo::get(*this, implEntity, forDefinition);
2350-
auto implFn = createFunction(*this, implLink, signature, fn,
2351-
f->getOptimizationMode());
2352-
LinkEntity varEntity =
2353-
LinkEntity::forDynamicallyReplaceableFunctionVariable(f);
2354-
LinkEntity keyEntity =
2355-
LinkEntity::forDynamicallyReplaceableFunctionKey(f);
2356-
emitDynamicallyReplaceableThunk(*this, varEntity, keyEntity, fn, implFn,
2357-
signature);
2358-
return implFn;
2359-
}
23602412
}
23612413
return fn;
23622414
}
@@ -2431,20 +2483,6 @@ llvm::Function *IRGenModule::getAddrOfSILFunction(
24312483
if (hasOrderNumber) {
24322484
EmittedFunctionsByOrder.insert(orderNumber, fn);
24332485
}
2434-
2435-
if (isDynamicallyReplaceableImplementation && forDefinition) {
2436-
LinkEntity implEntity = LinkEntity::forSILFunction(f, true);
2437-
LinkInfo implLink = LinkInfo::get(*this, implEntity, forDefinition);
2438-
auto implFn = createFunction(*this, implLink, signature, insertBefore,
2439-
f->getOptimizationMode());
2440-
2441-
LinkEntity varEntity =
2442-
LinkEntity::forDynamicallyReplaceableFunctionVariable(f);
2443-
LinkEntity keyEntity = LinkEntity::forDynamicallyReplaceableFunctionKey(f);
2444-
emitDynamicallyReplaceableThunk(*this, varEntity, keyEntity, fn, implFn,
2445-
signature);
2446-
return implFn;
2447-
}
24482486
return fn;
24492487
}
24502488

lib/IRGen/IRGenModule.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1437,6 +1437,8 @@ private: \
14371437
llvm::Function *getAddrOfOpaqueTypeDescriptorAccessFunction(
14381438
OpaqueTypeDecl *decl, ForDefinition_t forDefinition, bool implementation);
14391439

1440+
void createReplaceableProlog(IRGenFunction &IGF, SILFunction *f);
1441+
14401442
void emitOpaqueTypeDescriptorAccessor(OpaqueTypeDecl *);
14411443

14421444
private:

lib/IRGen/IRGenSIL.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1244,6 +1244,10 @@ IRGenSILFunction::IRGenSILFunction(IRGenModule &IGM, SILFunction *f)
12441244
if (f->getDynamicallyReplacedFunction()) {
12451245
IGM.emitDynamicReplacementOriginalFunctionThunk(f);
12461246
}
1247+
1248+
if (f->isDynamicallyReplaceable()) {
1249+
IGM.createReplaceableProlog(*this, f);
1250+
}
12471251
}
12481252

12491253
IRGenSILFunction::~IRGenSILFunction() {
@@ -1646,7 +1650,7 @@ void IRGenSILFunction::emitSILFunction() {
16461650
IGM.DebugInfo->emitFunction(*CurSILFn, CurFn);
16471651

16481652
// Map the entry bb.
1649-
LoweredBBs[&*CurSILFn->begin()] = LoweredBB(&*CurFn->begin(), {});
1653+
LoweredBBs[&*CurSILFn->begin()] = LoweredBB(&CurFn->back(), {});
16501654
// Create LLVM basic blocks for the other bbs.
16511655
for (auto bi = std::next(CurSILFn->begin()), be = CurSILFn->end(); bi != be;
16521656
++bi) {

lib/TBDGen/TBDGen.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,6 @@ void TBDGenVisitor::visitAbstractFunctionDecl(AbstractFunctionDecl *AFD) {
196196
bool useAllocator = shouldUseAllocatorMangling(AFD);
197197
addSymbol(LinkEntity::forDynamicallyReplaceableFunctionVariable(
198198
AFD, useAllocator));
199-
addSymbol(
200-
LinkEntity::forDynamicallyReplaceableFunctionImpl(AFD, useAllocator));
201199
addSymbol(
202200
LinkEntity::forDynamicallyReplaceableFunctionKey(AFD, useAllocator));
203201

stdlib/public/runtime/Exclusivity.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,16 @@ static_assert(sizeof(Access) <= sizeof(ValueBuffer) &&
157157
"Access doesn't fit in a value buffer!");
158158

159159
/// A set of accesses that we're tracking. Just a singly-linked list.
160+
/// TODO: rename this class to something like SwiftTLSContext, because it also
161+
/// contains fields not related to access tracking.
160162
class AccessSet {
161163
Access *Head = nullptr;
164+
165+
// Not related to access tracking: The "implicit" boolean parameter which is
166+
// passed to a dynamically replaceable function.
167+
// If true, the original function should be executed instead of the
168+
// replacement function.
169+
bool CallOriginalOfReplacedFunction = false;
162170
public:
163171
constexpr AccessSet() {}
164172

@@ -221,6 +229,21 @@ class AccessSet {
221229
}
222230
}
223231
#endif
232+
233+
/// Called immediately before a replacement function calls its original
234+
/// function.
235+
void aboutToCallOriginalOfReplacedFunction() {
236+
CallOriginalOfReplacedFunction = true;
237+
}
238+
239+
/// Checked in the prolog of a replaceable function. Returns true if the
240+
/// original function should be called instead of the replacement function.
241+
/// Also clears the CallOriginalOfReplacedFunction flag.
242+
bool shouldCallOriginalOfReplacedFunction() {
243+
bool callOrig = CallOriginalOfReplacedFunction;
244+
CallOriginalOfReplacedFunction = false;
245+
return callOrig;
246+
}
224247
};
225248

226249
} // end anonymous namespace
@@ -327,6 +350,22 @@ void swift::swift_endAccess(ValueBuffer *buffer) {
327350
getAccessSet().remove(access);
328351
}
329352

353+
char *swift::swift_getFunctionReplacement(char **ReplFnPtr, char *CurrFn) {
354+
char *ReplFn = *ReplFnPtr;
355+
if (ReplFn == CurrFn)
356+
return nullptr;
357+
if (getAccessSet().shouldCallOriginalOfReplacedFunction()) {
358+
return nullptr;
359+
}
360+
return ReplFn;
361+
}
362+
363+
char *swift::swift_getOrigOfReplaceable(char **OrigFnPtr) {
364+
char *OrigFn = *OrigFnPtr;
365+
getAccessSet().aboutToCallOriginalOfReplacedFunction();
366+
return OrigFn;
367+
}
368+
330369
#ifndef NDEBUG
331370

332371
// Dump the accesses that are currently being tracked by the runtime.

test/IRGen/dynamic_replaceable.sil

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
// REQUIRES: objc_interop
44

5-
// CHECK: @test_dynamically_replaceableTX = global %swift.dyn_repl_link_entry { i8* bitcast (void ()* @test_dynamically_replaceableTI to i8*), %swift.dyn_repl_link_entry* null }
5+
// CHECK: @test_dynamically_replaceableTX = global %swift.dyn_repl_link_entry { i8* bitcast (void ()* @test_dynamically_replaceable to i8*), %swift.dyn_repl_link_entry* null }
66
// CHECK: @test_dynamically_replaceableTx = constant %swift.dyn_repl_key { i32{{.*}}%swift.dyn_repl_link_entry* @test_dynamically_replaceableTX{{.*}}, i32 0 }, section "__TEXT,__const"
77
// CHECK: @test_replacementTX = global %swift.dyn_repl_link_entry zeroinitializer
88

@@ -23,17 +23,17 @@
2323

2424
// CHECK-LABEL: define swiftcc void @test_dynamically_replaceable()
2525
// CHECK-NEXT: entry:
26-
// CHECK-NEXT: [[FUN_PTR:%.*]] = load i8*, i8** getelementptr inbounds (%swift.dyn_repl_link_entry, %swift.dyn_repl_link_entry* @test_dynamically_replaceableTX, i32 0, i32 0)
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+
// CHECK-NEXT: [[CMP:%.*]] = icmp eq i8* [[FUN_PTR]], null
28+
// CHECK-NEXT: br i1 [[CMP]], label %[[ORIGBB:[a-z_]*]], label %[[FWBB:[a-z_]*]]
29+
// CHECK: [[FWBB]]:
2730
// CHECK-NEXT: [[TYPED_PTR:%.*]] = bitcast i8* [[FUN_PTR]] to void ()*
2831
// CHECK-NEXT: tail call swiftcc void [[TYPED_PTR]]()
2932
// CHECK-NEXT: ret void
33+
// CHECK: [[ORIGBB]]:
34+
// CHECK-NEXT: ret void
3035
// CHECK-NEXT: }
3136

32-
// CHECK-LABEL: define swiftcc void @test_dynamically_replaceableTI()
33-
// CHECK: entry:
34-
// CHECK: ret void
35-
// CHECK: }
36-
3737
sil [dynamically_replacable] @test_dynamically_replaceable : $@convention(thin) () -> () {
3838
bb0:
3939
%0 = tuple ()
@@ -49,7 +49,7 @@ bb0:
4949
// The thunk that implement the prev_dynamic_function_ref lookup.
5050
// CHECK-LABEL: define swiftcc void @test_replacementTI()
5151
// CHECK: entry:
52-
// CHECK: [[FUN_PTR:%.*]] = load i8*, i8** getelementptr inbounds (%swift.dyn_repl_link_entry, %swift.dyn_repl_link_entry* @test_replacementTX, i32 0, i32 0)
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))
5353
// CHECK: [[TYPED_PTR:%.*]] = bitcast i8* [[FUN_PTR]] to void ()*
5454
// CHECK: call swiftcc void [[TYPED_PTR]]()
5555
// CHECK: ret void

0 commit comments

Comments
 (0)