Skip to content

Commit c4e04e7

Browse files
authored
Merge pull request #19177 from slavapestov/resilient-super-method-calls
Resilient super method calls
2 parents 6e773a0 + de6e3bd commit c4e04e7

26 files changed

+382
-102
lines changed

docs/ABI/Mangling.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ Globals
5959
global ::= nominal-type 'Ml' // in-place type initialization cache
6060
global ::= nominal-type 'Mm' // class metaclass
6161
global ::= nominal-type 'Mn' // nominal type descriptor
62+
global ::= nominal-type 'Mu' // class method lookup function
6263
global ::= module 'MXM' // module descriptor
6364
global ::= context 'MXE' // extension descriptor
6465
global ::= context 'MXX' // anonymous context descriptor

include/swift/ABI/Metadata.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,8 @@ struct TargetMethodDescriptor {
787787
// TODO: add method types or anything else needed for reflection.
788788
};
789789

790+
using MethodDescriptor = TargetMethodDescriptor<InProcess>;
791+
790792
/// Header for a class vtable descriptor. This is a variable-sized
791793
/// structure that describes how to find and parse a vtable
792794
/// within the type metadata for a class.

include/swift/Demangling/DemangleNodes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ NODE(MergedFunction)
119119
NODE(Metatype)
120120
NODE(MetatypeRepresentation)
121121
NODE(Metaclass)
122+
NODE(MethodLookupFunction)
122123
CONTEXT_NODE(ModifyAccessor)
123124
CONTEXT_NODE(Module)
124125
CONTEXT_NODE(NativeOwningAddressor)

include/swift/IRGen/Linking.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,9 @@ class LinkEntity {
129129
/// ConstructorDecl* inside a protocol or a class.
130130
MethodDescriptorAllocator,
131131

132+
/// A method lookup function for a class. The pointer is a ClassDecl*.
133+
MethodLookupFunction,
134+
132135
/// A resilient enum tag index. The pointer is a EnumElementDecl*.
133136
EnumCase,
134137

@@ -482,6 +485,12 @@ class LinkEntity {
482485
return entity;
483486
}
484487

488+
static LinkEntity forMethodLookupFunction(ClassDecl *classDecl) {
489+
LinkEntity entity;
490+
entity.setForDecl(Kind::MethodLookupFunction, classDecl);
491+
return entity;
492+
}
493+
485494
static LinkEntity forFieldOffset(VarDecl *decl) {
486495
LinkEntity entity;
487496
entity.setForDecl(Kind::FieldOffset, decl);

include/swift/Runtime/Metadata.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,15 @@ void swift_initClassMetadata(ClassMetadata *self,
595595
const TypeLayout * const *fieldTypes,
596596
size_t *fieldOffsets);
597597

598+
/// Given class metadata, a class descriptor and a method descriptor, look up
599+
/// and load the vtable entry from the given metadata. The metadata must be of
600+
/// the same class or a subclass of the descriptor.
601+
SWIFT_RUNTIME_EXPORT
602+
void *
603+
swift_lookUpClassMethod(ClassMetadata *metadata,
604+
MethodDescriptor *method,
605+
ClassDescriptor *description);
606+
598607
/// \brief Fetch a uniqued metadata for a metatype type.
599608
SWIFT_RUNTIME_EXPORT
600609
const MetatypeMetadata *

include/swift/Runtime/RuntimeFunctions.def

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -773,6 +773,17 @@ FUNCTION(InitClassMetadata,
773773
SizeTy->getPointerTo()),
774774
ATTRS(NoUnwind))
775775

776+
// void *swift_lookUpClassMethod(Metadata *metadata,
777+
// ClassDescriptor *description,
778+
// MethodDescriptor *method);
779+
FUNCTION(LookUpClassMethod,
780+
swift_lookUpClassMethod, C_CC,
781+
RETURNS(Int8PtrTy),
782+
ARGS(TypeMetadataPtrTy,
783+
MethodDescriptorStructTy->getPointerTo(),
784+
TypeContextDescriptorPtrTy),
785+
ATTRS(NoUnwind))
786+
776787
// void swift_initStructMetadata(Metadata *structType,
777788
// StructLayoutFlags flags,
778789
// size_t numFields,

lib/Demangling/Demangler.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1479,6 +1479,8 @@ NodePointer Demangler::demangleMetatype() {
14791479
return createWithPoppedType(Node::Kind::ClassMetadataBaseOffset);
14801480
case 'p':
14811481
return createWithChild(Node::Kind::ProtocolDescriptor, popProtocol());
1482+
case 'u':
1483+
return createWithPoppedType(Node::Kind::MethodLookupFunction);
14821484
case 'B':
14831485
return createWithChild(Node::Kind::ReflectionMetadataBuiltinDescriptor,
14841486
popNode(Node::Kind::Type));

lib/Demangling/NodePrinter.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,7 @@ class NodePrinter {
382382
case Node::Kind::MergedFunction:
383383
case Node::Kind::Metaclass:
384384
case Node::Kind::MethodDescriptor:
385+
case Node::Kind::MethodLookupFunction:
385386
case Node::Kind::ModifyAccessor:
386387
case Node::Kind::NativeOwningAddressor:
387388
case Node::Kind::NativeOwningMutableAddressor:
@@ -912,6 +913,10 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) {
912913
Printer << "method descriptor for ";
913914
print(Node->getChild(0));
914915
return nullptr;
916+
case Node::Kind::MethodLookupFunction:
917+
Printer << "method lookup function for ";
918+
print(Node->getChild(0));
919+
return nullptr;
915920
case Node::Kind::OutlinedBridgedMethod:
916921
Printer << "outlined bridged method (" << Node->getText() << ") of ";
917922
return nullptr;

lib/Demangling/OldRemangler.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1943,6 +1943,10 @@ void Remangler::mangleMethodDescriptor(Node *node) {
19431943
Out << "<method-descriptor>";
19441944
}
19451945

1946+
void Remangler::mangleMethodLookupFunction(Node *node) {
1947+
Out << "<method-lookup-function>";
1948+
}
1949+
19461950
void Remangler::mangleEmptyList(Node *node) {
19471951
Out << "<empty>";
19481952
}

lib/Demangling/Remangler.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1892,6 +1892,11 @@ void Remangler::mangleMethodDescriptor(Node *node) {
18921892
Buffer << "Tq";
18931893
}
18941894

1895+
void Remangler::mangleMethodLookupFunction(Node *node) {
1896+
mangleSingleChildNode(node);
1897+
Buffer << "Mu";
1898+
}
1899+
18951900
void Remangler::mangleThrowsAnnotation(Node *node) {
18961901
Buffer << 'K';
18971902
}

lib/IRGen/ClassMetadataVisitor.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@ template <class Impl> class ClassMetadataVisitor
118118
// This must always be the first item in the immediate members.
119119
asImpl().addGenericFields(theClass, theClass);
120120

121+
// If the class is resilient, we cannot make any assumptions about its
122+
// member layout at all, so skip the rest of this method.
123+
if (IGM.isResilient(theClass, ResilienceExpansion::Maximal))
124+
return;
125+
121126
// Add vtable entries.
122127
asImpl().addVTableEntries(theClass);
123128

lib/IRGen/GenClass.cpp

Lines changed: 12 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2364,49 +2364,27 @@ FunctionPointer irgen::emitVirtualMethodValue(IRGenFunction &IGF,
23642364
SILDeclRef method,
23652365
CanSILFunctionType methodType,
23662366
bool useSuperVTable) {
2367-
AbstractFunctionDecl *methodDecl
2368-
= cast<AbstractFunctionDecl>(method.getDecl());
2369-
2370-
// Find the vtable entry for this method.
2371-
SILDeclRef overridden = method.getOverriddenVTableEntry();
2372-
23732367
// Find the metadata.
23742368
llvm::Value *metadata;
23752369
if (useSuperVTable) {
2376-
auto instanceTy = baseType;
2377-
if (auto metaTy = dyn_cast<MetatypeType>(baseType.getASTType()))
2378-
instanceTy = SILType::getPrimitiveObjectType(metaTy.getInstanceType());
2379-
2380-
if (IGF.IGM.isResilient(instanceTy.getClassOrBoundGenericClass(),
2381-
ResilienceExpansion::Maximal)) {
2382-
// The derived type that is making the super call is resilient,
2383-
// for example we may be in an extension of a class outside of our
2384-
// resilience domain. So, we need to load the superclass metadata
2385-
// dynamically.
2386-
2387-
metadata = emitClassHeapMetadataRef(IGF, instanceTy.getASTType(),
2388-
MetadataValueType::TypeMetadata,
2389-
MetadataState::Complete);
2390-
auto superField = emitAddressOfSuperclassRefInClassMetadata(IGF, metadata);
2391-
metadata = IGF.Builder.CreateLoad(superField);
2392-
} else {
2393-
// Otherwise, we can directly load the statically known superclass's
2394-
// metadata.
2395-
auto superTy = instanceTy.getSuperclass();
2396-
metadata = emitClassHeapMetadataRef(IGF, superTy.getASTType(),
2397-
MetadataValueType::TypeMetadata,
2398-
MetadataState::Complete);
2399-
}
2370+
// For a non-resilient 'super' call, emit a reference to the superclass
2371+
// of the static type of the 'self' value.
2372+
auto instanceTy = baseType.getASTType()->getMetatypeInstanceType();
2373+
auto superTy = instanceTy->getSuperclass();
2374+
metadata = emitClassHeapMetadataRef(IGF,
2375+
superTy->getCanonicalType(),
2376+
MetadataValueType::TypeMetadata,
2377+
MetadataState::Complete);
24002378
} else {
2401-
if ((isa<FuncDecl>(methodDecl) && cast<FuncDecl>(methodDecl)->isStatic()) ||
2402-
(isa<ConstructorDecl>(methodDecl) &&
2403-
method.kind == SILDeclRef::Kind::Allocator)) {
2379+
if (baseType.is<MetatypeType>()) {
2380+
// For a static method call, the 'self' value is already a class metadata.
24042381
metadata = base;
24052382
} else {
2383+
// Otherwise, load the class metadata from the 'self' value's isa pointer.
24062384
metadata = emitHeapMetadataRefForHeapObject(IGF, base, baseType,
24072385
/*suppress cast*/ true);
24082386
}
24092387
}
24102388

2411-
return emitVirtualMethodValue(IGF, metadata, overridden, methodType);
2389+
return emitVirtualMethodValue(IGF, metadata, method, methodType);
24122390
}

lib/IRGen/GenMeta.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,6 +1314,11 @@ namespace {
13141314
if (VTableEntries.empty())
13151315
return;
13161316

1317+
// Only emit a method lookup function if the class is resilient
1318+
// and has a non-empty vtable.
1319+
if (IGM.isResilient(getType(), ResilienceExpansion::Minimal))
1320+
IGM.emitMethodLookupFunction(getType());
1321+
13171322
auto offset = MetadataLayout->hasResilientSuperclass()
13181323
? MetadataLayout->getRelativeVTableOffset()
13191324
: MetadataLayout->getStaticVTableOffset();

lib/IRGen/GenThunk.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,44 @@ IRGenModule::getAddrOfMethodDescriptor(SILDeclRef declRef,
130130
return getAddrOfLLVMVariable(entity, Alignment(4), forDefinition,
131131
MethodDescriptorStructTy, DebugTypeInfo());
132132
}
133+
134+
/// Fetch the method lookup function for a resilient class.
135+
llvm::Function *
136+
IRGenModule::getAddrOfMethodLookupFunction(ClassDecl *classDecl,
137+
ForDefinition_t forDefinition) {
138+
IRGen.noteUseOfTypeMetadata(classDecl);
139+
140+
LinkEntity entity = LinkEntity::forMethodLookupFunction(classDecl);
141+
llvm::Function *&entry = GlobalFuncs[entity];
142+
if (entry) {
143+
if (forDefinition) updateLinkageForDefinition(*this, entry, entity);
144+
return entry;
145+
}
146+
147+
llvm::Type *params[] = {
148+
TypeMetadataPtrTy,
149+
MethodDescriptorStructTy->getPointerTo()
150+
};
151+
auto fnType = llvm::FunctionType::get(Int8PtrTy, params, false);
152+
Signature signature(fnType, llvm::AttributeList(), SwiftCC);
153+
LinkInfo link = LinkInfo::get(*this, entity, forDefinition);
154+
entry = createFunction(*this, link, signature);
155+
return entry;
156+
}
157+
158+
void IRGenModule::emitMethodLookupFunction(ClassDecl *classDecl) {
159+
auto *f = getAddrOfMethodLookupFunction(classDecl, ForDefinition);
160+
161+
IRGenFunction IGF(*this, f);
162+
163+
auto params = IGF.collectParameters();
164+
auto *metadata = params.claimNext();
165+
auto *method = params.claimNext();
166+
167+
auto *description = getAddrOfTypeContextDescriptor(classDecl,
168+
RequireMetadata);
169+
170+
auto *result = IGF.Builder.CreateCall(getLookUpClassMethodFn(),
171+
{metadata, method, description});
172+
IGF.Builder.CreateRet(result);
173+
}

lib/IRGen/IRGenMangler.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ class IRGenMangler : public Mangle::ASTMangler {
6565
return finalize();
6666
}
6767

68+
std::string mangleMethodLookupFunction(const ClassDecl *Decl) {
69+
return mangleNominalTypeSymbol(Decl, "Mu");
70+
}
71+
6872
std::string mangleValueWitness(Type type, ValueWitness witness);
6973

7074
std::string mangleValueWitnessTable(Type type) {

lib/IRGen/IRGenModule.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1144,9 +1144,12 @@ private: \
11441144

11451145
llvm::Function *getAddrOfDispatchThunk(SILDeclRef declRef,
11461146
ForDefinition_t forDefinition);
1147-
11481147
void emitDispatchThunk(SILDeclRef declRef);
11491148

1149+
llvm::Function *getAddrOfMethodLookupFunction(ClassDecl *classDecl,
1150+
ForDefinition_t forDefinition);
1151+
void emitMethodLookupFunction(ClassDecl *classDecl);
1152+
11501153
llvm::GlobalValue *defineAlias(LinkEntity entity,
11511154
llvm::Constant *definition);
11521155

lib/IRGen/IRGenSIL.cpp

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
#include "GenType.h"
7373
#include "IRGenDebugInfo.h"
7474
#include "IRGenModule.h"
75+
#include "MetadataLayout.h"
7576
#include "MetadataRequest.h"
7677
#include "NativeConventionSchema.h"
7778
#include "ReferenceTypeInfo.h"
@@ -5506,9 +5507,75 @@ void IRGenSILFunction::visitSuperMethodInst(swift::SuperMethodInst *i) {
55065507
auto baseType = i->getOperand()->getType();
55075508
llvm::Value *baseValue = base.claimNext();
55085509

5509-
auto method = i->getMember();
5510+
auto method = i->getMember().getOverriddenVTableEntry();
55105511
auto methodType = i->getType().castTo<SILFunctionType>();
55115512

5513+
auto *classDecl = cast<ClassDecl>(method.getDecl()->getDeclContext());
5514+
5515+
// If the class defining the vtable entry is resilient, we cannot assume
5516+
// its offset since methods can be re-ordered resiliently. Instead, we call
5517+
// the class method lookup function, passing in a reference to the
5518+
// method descriptor.
5519+
if (IGM.isResilient(classDecl, ResilienceExpansion::Maximal)) {
5520+
// Load the superclass of the static type of the 'self' value.
5521+
llvm::Value *superMetadata;
5522+
auto instanceTy = CanType(baseType.getASTType()->getMetatypeInstanceType());
5523+
if (!IGM.isResilient(instanceTy.getClassOrBoundGenericClass(),
5524+
ResilienceExpansion::Maximal)) {
5525+
// It's still possible that the static type of 'self' is not resilient, in
5526+
// which case we can assume its superclass.
5527+
//
5528+
// An example is the following hierarchy, where ModuleA is resilient and
5529+
// we're inside ModuleB:
5530+
//
5531+
// ModuleA.Base <-- defines method
5532+
// |
5533+
// \- ModuleB.Middle
5534+
// |
5535+
// \- ModuleB.Derived <-- static type of 'self'
5536+
//
5537+
// It's OK to know that the superclass of Derived is Middle, but the
5538+
// method requires using a resilient access pattern.
5539+
auto superTy = instanceTy->getSuperclass();
5540+
superMetadata = emitClassHeapMetadataRef(*this, superTy->getCanonicalType(),
5541+
MetadataValueType::TypeMetadata,
5542+
MetadataState::Complete);
5543+
} else {
5544+
// Otherwise, we're in the most general case; the superclass might change,
5545+
// so we have to load it dynamically from the metadata of the static type
5546+
// of 'self'.
5547+
auto *metadata = emitClassHeapMetadataRef(*this, instanceTy,
5548+
MetadataValueType::TypeMetadata,
5549+
MetadataState::Complete);
5550+
5551+
auto superField = emitAddressOfSuperclassRefInClassMetadata(*this, metadata);
5552+
superMetadata = Builder.CreateLoad(superField);
5553+
}
5554+
5555+
// Get the method descriptor.
5556+
auto *methodDescriptor =
5557+
IGM.getAddrOfMethodDescriptor(method, NotForDefinition);
5558+
5559+
// Get the method lookup function for the class defining the method.
5560+
auto *lookupFn = IGM.getAddrOfMethodLookupFunction(classDecl,
5561+
NotForDefinition);
5562+
5563+
// Call the lookup function.
5564+
llvm::Value *fnPtr = Builder.CreateCall(lookupFn,
5565+
{superMetadata, methodDescriptor});
5566+
5567+
// The function returns an i8*; cast it to the correct type.
5568+
auto sig = IGM.getSignature(methodType);
5569+
fnPtr = Builder.CreateBitCast(fnPtr, sig.getType()->getPointerTo());
5570+
5571+
FunctionPointer fn(fnPtr, sig);
5572+
5573+
setLoweredFunctionPointer(i, fn);
5574+
return;
5575+
}
5576+
5577+
// Non-resilient case.
5578+
55125579
auto fn = emitVirtualMethodValue(*this, baseValue, baseType,
55135580
method, methodType,
55145581
/*useSuperVTable*/ true);
@@ -5529,14 +5596,12 @@ void IRGenSILFunction::visitClassMethodInst(swift::ClassMethodInst *i) {
55295596
Explosion base = getLoweredExplosion(i->getOperand());
55305597
llvm::Value *baseValue = base.claimNext();
55315598

5532-
SILDeclRef method = i->getMember();
5599+
SILDeclRef method = i->getMember().getOverriddenVTableEntry();
55335600
auto methodType = i->getType().castTo<SILFunctionType>();
55345601

55355602
auto *classDecl = cast<ClassDecl>(method.getDecl()->getDeclContext());
5536-
55375603
if (IGM.isResilient(classDecl,
55385604
ResilienceExpansion::Maximal)) {
5539-
method = method.getOverriddenVTableEntry();
55405605
auto *fnPtr = IGM.getAddrOfDispatchThunk(method, NotForDefinition);
55415606
auto sig = IGM.getSignature(methodType);
55425607
FunctionPointer fn(fnPtr, sig);

0 commit comments

Comments
 (0)