Skip to content

Commit dcda1dc

Browse files
authored
Merge pull request #13822 from slavapestov/protocol-dispatch-thunks
Protocol dispatch thunks
2 parents 8b886f7 + e849ba5 commit dcda1dc

19 files changed

+332
-54
lines changed

include/swift/IRGen/Linking.h

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,18 @@ class LinkEntity {
108108
#define LINKENTITY_GET_FIELD(value, field) ((value & field##Mask) >> field##Shift)
109109

110110
enum class Kind {
111+
/// A method dispatch thunk. The pointer is a FuncDecl* inside a protocol
112+
/// or a class.
113+
DispatchThunk,
114+
115+
/// A method dispatch thunk for an initializing constructor. The pointer
116+
/// is a ConstructorDecl* inside a class.
117+
DispatchThunkInitializer,
118+
119+
/// A method dispatch thunk for an allocating constructor. The pointer is a
120+
/// ConstructorDecl* inside a protocol or a class.
121+
DispatchThunkAllocator,
122+
111123
/// A field offset. The pointer is a VarDecl*.
112124
FieldOffset,
113125

@@ -227,15 +239,6 @@ class LinkEntity {
227239
};
228240
friend struct llvm::DenseMapInfo<LinkEntity>;
229241

230-
static bool isFunction(ValueDecl *decl) {
231-
return (isa<FuncDecl>(decl) || isa<EnumElementDecl>(decl) ||
232-
isa<ConstructorDecl>(decl));
233-
}
234-
235-
static bool hasGetterSetter(ValueDecl *decl) {
236-
return (isa<VarDecl>(decl) || isa<SubscriptDecl>(decl));
237-
}
238-
239242
Kind getKind() const {
240243
return Kind(LINKENTITY_GET_FIELD(Data, Kind));
241244
}
@@ -362,6 +365,36 @@ class LinkEntity {
362365
LinkEntity() = default;
363366

364367
public:
368+
static LinkEntity forDispatchThunk(SILDeclRef declRef) {
369+
assert(!declRef.isForeign &&
370+
!declRef.isDirectReference &&
371+
!declRef.isCurried);
372+
373+
LinkEntity::Kind kind;
374+
375+
auto *decl = declRef.getDecl();
376+
assert(isa<ClassDecl>(decl->getDeclContext()) ||
377+
isa<ProtocolDecl>(decl->getDeclContext()));
378+
379+
switch (declRef.kind) {
380+
case SILDeclRef::Kind::Func:
381+
kind = Kind::DispatchThunk;
382+
break;
383+
case SILDeclRef::Kind::Initializer:
384+
kind = Kind::DispatchThunkInitializer;
385+
break;
386+
case SILDeclRef::Kind::Allocator:
387+
kind = Kind::DispatchThunkAllocator;
388+
break;
389+
default:
390+
llvm_unreachable("Bad SILDeclRef for dispatch thunk");
391+
}
392+
393+
LinkEntity entity;
394+
entity.setForDecl(kind, decl);
395+
return entity;
396+
}
397+
365398
static LinkEntity forFieldOffset(VarDecl *decl, bool isIndirect) {
366399
LinkEntity entity;
367400
entity.Pointer = decl;

lib/IRGen/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ add_swift_library(swiftIRGen STATIC
2828
GenProto.cpp
2929
GenReflection.cpp
3030
GenStruct.cpp
31+
GenThunk.cpp
3132
GenTuple.cpp
3233
GenType.cpp
3334
GenValueWitness.cpp

lib/IRGen/GenDecl.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1213,6 +1213,18 @@ SILLinkage LinkEntity::getLinkage(ForDefinition_t forDefinition) const {
12131213
};
12141214

12151215
switch (getKind()) {
1216+
case Kind::DispatchThunk:
1217+
case Kind::DispatchThunkInitializer:
1218+
case Kind::DispatchThunkAllocator: {
1219+
auto *decl = getDecl();
1220+
1221+
// Protocol requirements don't have their own access control
1222+
if (auto *proto = dyn_cast<ProtocolDecl>(decl->getDeclContext()))
1223+
decl = proto;
1224+
1225+
return getSILLinkage(getDeclLinkage(decl), forDefinition);
1226+
}
1227+
12161228
// Most type metadata depend on the formal linkage of their type.
12171229
case Kind::ValueWitnessTable: {
12181230
auto type = getType();
@@ -1363,6 +1375,11 @@ static bool isAvailableExternally(IRGenModule &IGM, Type type) {
13631375

13641376
bool LinkEntity::isAvailableExternally(IRGenModule &IGM) const {
13651377
switch (getKind()) {
1378+
case Kind::DispatchThunk:
1379+
case Kind::DispatchThunkInitializer:
1380+
case Kind::DispatchThunkAllocator:
1381+
return ::isAvailableExternally(IGM, getDecl());
1382+
13661383
case Kind::ValueWitnessTable:
13671384
case Kind::TypeMetadata:
13681385
return ::isAvailableExternally(IGM, getType());
@@ -1485,7 +1502,7 @@ getIRLinkage(const UniversalLinkageInfo &info, SILLinkage linkage,
14851502

14861503
/// Given that we're going to define a global value but already have a
14871504
/// forward-declaration of it, update its linkage.
1488-
static void updateLinkageForDefinition(IRGenModule &IGM,
1505+
void irgen::updateLinkageForDefinition(IRGenModule &IGM,
14891506
llvm::GlobalValue *global,
14901507
const LinkEntity &entity) {
14911508
// TODO: there are probably cases where we can avoid redoing the

lib/IRGen/GenDecl.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#define SWIFT_IRGEN_GENDECL_H
1919

2020
#include "swift/Basic/OptimizationMode.h"
21+
#include "swift/SIL/SILLocation.h"
2122
#include "llvm/IR/CallingConv.h"
2223
#include "DebugTypeInfo.h"
2324
#include "IRGen.h"
@@ -30,9 +31,14 @@ namespace llvm {
3031
namespace swift {
3132
namespace irgen {
3233
class IRGenModule;
34+
class LinkEntity;
3335
class LinkInfo;
3436
class Signature;
3537

38+
void updateLinkageForDefinition(IRGenModule &IGM,
39+
llvm::GlobalValue *global,
40+
const LinkEntity &entity);
41+
3642
llvm::Function *createFunction(IRGenModule &IGM,
3743
LinkInfo &linkInfo,
3844
const Signature &signature,

lib/IRGen/GenMeta.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5524,6 +5524,18 @@ void IRGenModule::emitProtocolDecl(ProtocolDecl *protocol) {
55245524

55255525
// Note that we emitted this protocol.
55265526
SwiftProtocols.push_back(protocol);
5527+
5528+
// If the protocol is resilient, emit dispatch thunks.
5529+
if (isResilient(protocol, ResilienceExpansion::Minimal)) {
5530+
for (auto *member : protocol->getMembers()) {
5531+
if (auto *funcDecl = dyn_cast<FuncDecl>(member)) {
5532+
emitDispatchThunk(SILDeclRef(funcDecl));
5533+
}
5534+
if (auto *ctorDecl = dyn_cast<ConstructorDecl>(member)) {
5535+
emitDispatchThunk(SILDeclRef(ctorDecl, SILDeclRef::Kind::Allocator));
5536+
}
5537+
}
5538+
}
55275539
}
55285540

55295541
/// \brief Load a reference to the protocol descriptor for the given protocol.
@@ -5555,5 +5567,5 @@ llvm::Value *irgen::emitMetatypeInstanceType(IRGenFunction &IGF,
55555567
// The instance type field of MetatypeMetadata is immediately after
55565568
// the isa field.
55575569
return emitInvariantLoadFromMetadataAtIndex(IGF, metatypeMetadata, 1,
5558-
IGF.IGM.TypeMetadataPtrTy);
5570+
IGF.IGM.TypeMetadataPtrTy);
55595571
}

lib/IRGen/GenProto.cpp

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,8 @@ PolymorphicConvention::enumerateRequirements(const RequirementCallback &callback
266266
return enumerateGenericSignatureRequirements(Generics, callback);
267267
}
268268

269-
void PolymorphicConvention::enumerateUnfulfilledRequirements(const RequirementCallback &callback) {
269+
void PolymorphicConvention::
270+
enumerateUnfulfilledRequirements(const RequirementCallback &callback) {
270271
enumerateRequirements([&](GenericRequirement requirement) {
271272
if (requirement.Protocol) {
272273
if (!Fulfillments.getWitnessTable(requirement.TypeParameter,
@@ -589,8 +590,9 @@ void EmitPolymorphicParameters::bindParameterSources(const GetParameterFn &getPa
589590
}
590591
}
591592

592-
void EmitPolymorphicParameters::bindParameterSource(SILParameterInfo param, unsigned paramIndex,
593-
const GetParameterFn &getParameter) {
593+
void EmitPolymorphicParameters::
594+
bindParameterSource(SILParameterInfo param, unsigned paramIndex,
595+
const GetParameterFn &getParameter) {
594596
// Ignore indirect parameters for now. This is potentially dumb.
595597
if (IGF.IGM.silConv.isSILIndirect(param))
596598
return;
@@ -3077,33 +3079,39 @@ void irgen::expandTrailingWitnessSignature(IRGenModule &IGM,
30773079

30783080
FunctionPointer
30793081
irgen::emitWitnessMethodValue(IRGenFunction &IGF,
3080-
CanType baseTy,
3081-
llvm::Value **baseMetadataCache,
3082-
SILDeclRef member,
3083-
ProtocolConformanceRef conformance,
3084-
CanSILFunctionType fnType) {
3085-
auto fn = cast<AbstractFunctionDecl>(member.getDecl());
3086-
auto fnProto = cast<ProtocolDecl>(fn->getDeclContext());
3087-
3088-
assert(conformance.getRequirement() == fnProto);
3082+
llvm::Value *wtable,
3083+
SILDeclRef member) {
3084+
auto *fn = cast<AbstractFunctionDecl>(member.getDecl());
3085+
auto proto = cast<ProtocolDecl>(fn->getDeclContext());
30893086

3090-
// Find the witness table.
3091-
llvm::Value *wtable = emitWitnessTableRef(IGF, baseTy, baseMetadataCache,
3092-
conformance);
3087+
assert(!IGF.IGM.isResilient(proto, ResilienceExpansion::Maximal));
30933088

30943089
// Find the witness we're interested in.
3095-
auto &fnProtoInfo = IGF.IGM.getProtocolInfo(conformance.getRequirement());
3090+
auto &fnProtoInfo = IGF.IGM.getProtocolInfo(proto);
30963091
auto index = fnProtoInfo.getFunctionIndex(fn);
30973092
llvm::Value *witnessFnPtr =
30983093
emitInvariantLoadOfOpaqueWitness(IGF, wtable, index);
30993094

3095+
auto fnType = IGF.IGM.getSILTypes().getConstantFunctionType(member);
31003096
Signature signature = IGF.IGM.getSignature(fnType);
31013097
witnessFnPtr = IGF.Builder.CreateBitCast(witnessFnPtr,
3102-
signature.getType()->getPointerTo());
3098+
signature.getType()->getPointerTo());
31033099

31043100
return FunctionPointer(witnessFnPtr, signature);
31053101
}
31063102

3103+
FunctionPointer
3104+
irgen::emitWitnessMethodValue(IRGenFunction &IGF,
3105+
CanType baseTy,
3106+
llvm::Value **baseMetadataCache,
3107+
SILDeclRef member,
3108+
ProtocolConformanceRef conformance) {
3109+
llvm::Value *wtable = emitWitnessTableRef(IGF, baseTy, baseMetadataCache,
3110+
conformance);
3111+
3112+
return emitWitnessMethodValue(IGF, wtable, member);
3113+
}
3114+
31073115
Signature IRGenModule::getAssociatedTypeMetadataAccessFunctionSignature() {
31083116
auto &fnType = AssociatedTypeMetadataAccessFunctionTy;
31093117
if (!fnType) {

lib/IRGen/GenProto.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,20 @@ namespace irgen {
5252
/// Set an LLVM value name for the given protocol witness table.
5353
void setProtocolWitnessTableName(IRGenModule &IGM, llvm::Value *value,
5454
CanType type, ProtocolDecl *protocol);
55-
55+
56+
/// Extract the method pointer from the given witness table
57+
/// as a function value.
58+
FunctionPointer emitWitnessMethodValue(IRGenFunction &IGF,
59+
llvm::Value *wtable,
60+
SILDeclRef member);
61+
5662
/// Extract the method pointer from an archetype's witness table
5763
/// as a function value.
5864
FunctionPointer emitWitnessMethodValue(IRGenFunction &IGF,
5965
CanType baseTy,
6066
llvm::Value **baseMetadataCache,
6167
SILDeclRef member,
62-
ProtocolConformanceRef conformance,
63-
CanSILFunctionType fnType);
68+
ProtocolConformanceRef conformance);
6469

6570
/// Given a type T and an associated type X of some protocol P to
6671
/// which T conforms, return the type metadata for T.X.

lib/IRGen/GenThunk.cpp

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//===--- GenThunk.cpp - IR Generation for Method Dispatch Thunks ----------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 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+
// This file implements IR generation for class and protocol method dispatch
14+
// thunks, which are used in resilient builds to hide vtable and witness table
15+
// offsets from clients.
16+
//
17+
//===----------------------------------------------------------------------===//
18+
19+
#include "Callee.h"
20+
#include "Explosion.h"
21+
#include "GenDecl.h"
22+
#include "GenOpaque.h"
23+
#include "GenProto.h"
24+
#include "IRGenFunction.h"
25+
#include "IRGenModule.h"
26+
#include "ProtocolInfo.h"
27+
#include "Signature.h"
28+
#include "swift/IRGen/Linking.h"
29+
#include "llvm/IR/Function.h"
30+
31+
using namespace swift;
32+
using namespace irgen;
33+
34+
/// Find the entry point for a method dispatch thunk.
35+
llvm::Function *
36+
IRGenModule::getAddrOfDispatchThunk(SILDeclRef declRef,
37+
ForDefinition_t forDefinition) {
38+
LinkEntity entity = LinkEntity::forDispatchThunk(declRef);
39+
40+
llvm::Function *&entry = GlobalFuncs[entity];
41+
if (entry) {
42+
if (forDefinition) updateLinkageForDefinition(*this, entry, entity);
43+
return entry;
44+
}
45+
46+
auto fnType = getSILModule().Types.getConstantFunctionType(declRef);
47+
Signature signature = getSignature(fnType);
48+
LinkInfo link = LinkInfo::get(*this, entity, forDefinition);
49+
50+
return createFunction(*this, link, signature);
51+
}
52+
53+
static FunctionPointer lookupMethod(IRGenFunction &IGF,
54+
SILDeclRef declRef) {
55+
// Find the witness table.
56+
llvm::Value *wtable = (IGF.CurFn->arg_end() - 1);
57+
58+
// Find the witness we're interested in.
59+
return emitWitnessMethodValue(IGF, wtable, declRef);
60+
}
61+
62+
llvm::Function *IRGenModule::emitDispatchThunk(SILDeclRef declRef) {
63+
auto *f = getAddrOfDispatchThunk(declRef, ForDefinition);
64+
65+
IRGenFunction IGF(*this, f);
66+
67+
// Look up the method.
68+
auto fn = lookupMethod(IGF, declRef);
69+
70+
// Call the witness, forwarding all of the parameters.
71+
auto params = IGF.collectParameters();
72+
auto result = IGF.Builder.CreateCall(fn, params.claimAll());
73+
74+
// Return the result, if we have one.
75+
if (result->getType()->isVoidTy())
76+
IGF.Builder.CreateRetVoid();
77+
else
78+
IGF.Builder.CreateRet(result);
79+
80+
return f;
81+
}

lib/IRGen/IRGenModule.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,6 +1046,11 @@ private: \
10461046
/// Cast the given constant to i8*.
10471047
llvm::Constant *getOpaquePtr(llvm::Constant *pointer);
10481048

1049+
llvm::Function *getAddrOfDispatchThunk(SILDeclRef declRef,
1050+
ForDefinition_t forDefinition);
1051+
1052+
llvm::Function *emitDispatchThunk(SILDeclRef declRef);
1053+
10491054
Address getAddrOfFieldOffset(VarDecl *D, bool isIndirect,
10501055
ForDefinition_t forDefinition);
10511056
llvm::Function *getAddrOfValueWitness(CanType concreteType,

lib/IRGen/IRGenSIL.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5243,13 +5243,22 @@ void IRGenSILFunction::visitWitnessMethodInst(swift::WitnessMethodInst *i) {
52435243
ProtocolConformanceRef conformance = i->getConformance();
52445244
SILDeclRef member = i->getMember();
52455245

5246-
auto fnType = i->getType().castTo<SILFunctionType>();
5246+
if (IGM.isResilient(conformance.getRequirement(),
5247+
ResilienceExpansion::Maximal)) {
5248+
auto *fnPtr = IGM.getAddrOfDispatchThunk(member, NotForDefinition);
5249+
auto fnType = IGM.getSILTypes().getConstantFunctionType(member);
5250+
auto sig = IGM.getSignature(fnType);
5251+
auto fn = FunctionPointer::forDirect(fnPtr, sig);
5252+
5253+
setLoweredFunctionPointer(i, fn);
5254+
return;
5255+
}
52475256

52485257
// It would be nice if this weren't discarded.
52495258
llvm::Value *baseMetadataCache = nullptr;
52505259

52515260
auto fn = emitWitnessMethodValue(*this, baseTy, &baseMetadataCache,
5252-
member, conformance, fnType);
5261+
member, conformance);
52535262

52545263
setLoweredFunctionPointer(i, fn);
52555264
}

0 commit comments

Comments
 (0)