Skip to content

Commit 68c0762

Browse files
committed
IRGen: Emit Objective-C resilient class stubs if experimental flag is on
Non-generic classes with resilient ancestry do not have statically-emitted metadata, so we can now emit an Objective-C resilient class stub instead. Also, when emitting an Objective-C category, reference the class stub if the class has resilient ancestry; previously this case would hit an assert. Note that class stubs always start with a zero word, with the address point pointing immediately after. This works around a linker issue, where the linker tries to coalesce categories and gets confused upon encountering a class stub.
1 parent 0446c3e commit 68c0762

File tree

11 files changed

+281
-21
lines changed

11 files changed

+281
-21
lines changed

include/swift/ABI/Metadata.h

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3683,9 +3683,17 @@ struct TargetResilientSuperclass {
36833683
TargetRelativeDirectPointer<Runtime, const void, /*nullable*/true> Superclass;
36843684
};
36853685

3686+
/// A structure that stores a reference to an Objective-C class stub.
3687+
///
3688+
/// This is not the class stub itself; it is part of a class context
3689+
/// descriptor.
36863690
template <typename Runtime>
3687-
struct TargetObjCResilientClassStub {
3688-
/// A relative pointer to an Objective-C resilient class stub:
3691+
struct TargetObjCResilientClassStubInfo {
3692+
/// A relative pointer to an Objective-C resilient class stub.
3693+
///
3694+
/// We do not declare a struct type for class stubs since the Swift runtime
3695+
/// does not need to interpret them. The class stub struct is part of
3696+
/// the Objective-C ABI, and is laid out as follows:
36893697
/// - isa pointer, always 1
36903698
/// - an update callback, of type 'Class (*)(Class *, objc_class_stub *)'
36913699
///
@@ -3710,7 +3718,7 @@ class TargetClassDescriptor final
37103718
TargetMethodDescriptor<Runtime>,
37113719
TargetOverrideTableHeader<Runtime>,
37123720
TargetMethodOverrideDescriptor<Runtime>,
3713-
TargetObjCResilientClassStub<Runtime>> {
3721+
TargetObjCResilientClassStubInfo<Runtime>> {
37143722
private:
37153723
using TrailingGenericContextObjects =
37163724
TrailingGenericContextObjects<TargetClassDescriptor<Runtime>,
@@ -3722,7 +3730,7 @@ class TargetClassDescriptor final
37223730
TargetMethodDescriptor<Runtime>,
37233731
TargetOverrideTableHeader<Runtime>,
37243732
TargetMethodOverrideDescriptor<Runtime>,
3725-
TargetObjCResilientClassStub<Runtime>>;
3733+
TargetObjCResilientClassStubInfo<Runtime>>;
37263734

37273735
using TrailingObjects =
37283736
typename TrailingGenericContextObjects::TrailingObjects;
@@ -3738,8 +3746,8 @@ class TargetClassDescriptor final
37383746
TargetForeignMetadataInitialization<Runtime>;
37393747
using SingletonMetadataInitialization =
37403748
TargetSingletonMetadataInitialization<Runtime>;
3741-
using ObjCResilientClassStub =
3742-
TargetObjCResilientClassStub<Runtime>;
3749+
using ObjCResilientClassStubInfo =
3750+
TargetObjCResilientClassStubInfo<Runtime>;
37433751

37443752
using StoredPointer = typename Runtime::StoredPointer;
37453753
using StoredPointerDifference = typename Runtime::StoredPointerDifference;
@@ -3777,8 +3785,8 @@ class TargetClassDescriptor final
37773785
/// positive size of metadata objects of this class (in words).
37783786
uint32_t MetadataPositiveSizeInWords;
37793787

3780-
/// Classes with resilient ancestry instead use this flags word to
3781-
/// indicate the presence of an Objective-C resilient class stub.
3788+
/// Otherwise, these flags are used to do things like indicating
3789+
/// the presence of an Objective-C resilient class stub.
37823790
ExtraClassDescriptorFlags ExtraClassFlags;
37833791
};
37843792

@@ -3855,7 +3863,7 @@ class TargetClassDescriptor final
38553863
return getOverrideTable()->NumEntries;
38563864
}
38573865

3858-
size_t numTrailingObjects(OverloadToken<ObjCResilientClassStub>) const {
3866+
size_t numTrailingObjects(OverloadToken<ObjCResilientClassStubInfo>) const {
38593867
return hasObjCResilientClassStub() ? 1 : 0;
38603868
}
38613869

@@ -3980,7 +3988,7 @@ class TargetClassDescriptor final
39803988
}
39813989

39823990
/// Whether this context descriptor references an Objective-C resilient
3983-
/// class stub. See the description of TargetObjCResilientClassStub above
3991+
/// class stub. See the above description of TargetObjCResilientClassStubInfo
39843992
/// for details.
39853993
bool hasObjCResilientClassStub() const {
39863994
if (!hasResilientSuperclass())
@@ -3992,7 +4000,7 @@ class TargetClassDescriptor final
39924000
if (!hasObjCResilientClassStub())
39934001
return nullptr;
39944002

3995-
return this->template getTrailingObjects<ObjCResilientClassStub>()
4003+
return this->template getTrailingObjects<ObjCResilientClassStubInfo>()
39964004
->Stub.get();
39974005
}
39984006

include/swift/ABI/MetadataValues.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1315,7 +1315,7 @@ class ExtraClassDescriptorFlags : public FlagSet<uint32_t> {
13151315
enum {
13161316
/// Set if the context descriptor includes a pointer to an Objective-C
13171317
/// resilient class stub structure. See the description of
1318-
/// TargetObjCResilientClassStub in Metadata.h for details.
1318+
/// TargetObjCResilientClassStubInfo in Metadata.h for details.
13191319
///
13201320
/// Only meaningful for class descriptors when Objective-C interop is
13211321
/// enabled.

lib/IRGen/GenCast.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ FailableCastResult irgen::emitClassIdenticalCast(IRGenFunction &IGF,
107107
llvm::Value *targetMetadata;
108108
if ((targetMetadata =
109109
tryEmitConstantHeapMetadataRef(IGF.IGM, toType.getASTType(),
110-
/*allowUninitialized*/ false))) {
110+
/*allowUninitialized*/ false,
111+
/*allowStub*/ false))) {
111112
// ok
112113
} else {
113114
targetMetadata

lib/IRGen/GenClass.cpp

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1207,7 +1207,9 @@ namespace {
12071207
else {
12081208
auto type = getSelfType(getClass()).getASTType();
12091209
llvm::Constant *metadata =
1210-
tryEmitConstantHeapMetadataRef(IGM, type, /*allowUninit*/ true);
1210+
tryEmitConstantHeapMetadataRef(IGM, type,
1211+
/*allowUninit*/ true,
1212+
/*allowStub*/ true);
12111213
assert(metadata &&
12121214
"extended objc class doesn't have constant metadata?");
12131215
fields.add(metadata);
@@ -2048,6 +2050,48 @@ static llvm::Function *emitObjCMetadataUpdateFunction(IRGenModule &IGM,
20482050
return f;
20492051
}
20502052

2053+
/// We emit Objective-C class stubs for non-generic classes with resilient
2054+
/// ancestry. This lets us attach categories to the class even though it
2055+
/// does not have statically-emitted metadata.
2056+
bool irgen::hasObjCResilientClassStub(IRGenModule &IGM, ClassDecl *D) {
2057+
assert(IGM.getClassMetadataStrategy(D) == ClassMetadataStrategy::Resilient);
2058+
return (!D->isGenericContext() &&
2059+
IGM.ObjCInterop &&
2060+
IGM.Context.LangOpts.EnableObjCResilientClassStubs);
2061+
}
2062+
2063+
void irgen::emitObjCResilientClassStub(IRGenModule &IGM, ClassDecl *D) {
2064+
assert(hasObjCResilientClassStub(IGM, D));
2065+
2066+
llvm::Constant *fields[] = {
2067+
llvm::ConstantInt::get(IGM.SizeTy, 0), // reserved
2068+
llvm::ConstantInt::get(IGM.SizeTy, 1), // isa
2069+
IGM.getAddrOfObjCMetadataUpdateFunction(D, NotForDefinition)
2070+
};
2071+
auto init = llvm::ConstantStruct::get(IGM.ObjCFullResilientClassStubTy,
2072+
makeArrayRef(fields));
2073+
2074+
// Define the full stub. This is a private symbol.
2075+
auto fullObjCStub = cast<llvm::GlobalVariable>(
2076+
IGM.getAddrOfObjCResilientClassStub(D, ForDefinition,
2077+
TypeMetadataAddress::FullMetadata));
2078+
fullObjCStub->setInitializer(init);
2079+
2080+
// Emit the metadata update function referenced above.
2081+
emitObjCMetadataUpdateFunction(IGM, D);
2082+
2083+
// Apply the offset.
2084+
auto *objcStub = llvm::ConstantExpr::getBitCast(fullObjCStub, IGM.Int8PtrTy);
2085+
objcStub = llvm::ConstantExpr::getInBoundsGetElementPtr(
2086+
IGM.Int8Ty, objcStub, IGM.getSize(IGM.getPointerSize()));
2087+
objcStub = llvm::ConstantExpr::getPointerCast(objcStub,
2088+
IGM.ObjCResilientClassStubTy->getPointerTo());
2089+
2090+
auto entity = LinkEntity::forObjCResilientClassStub(
2091+
D, TypeMetadataAddress::AddressPoint);
2092+
IGM.defineAlias(entity, objcStub);
2093+
}
2094+
20512095
/// Emit the private data (RO-data) associated with a class.
20522096
llvm::Constant *irgen::emitClassPrivateData(IRGenModule &IGM,
20532097
ClassDecl *cls) {

lib/IRGen/GenClass.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,14 @@ namespace irgen {
170170
llvm::Value *selfValue,
171171
llvm::Value *metadataValue);
172172

173+
/// We emit Objective-C class stubs for non-generic classes with resilient
174+
/// ancestry. This lets us attach categories to the class even though it
175+
/// does not have statically-emitted metadata.
176+
bool hasObjCResilientClassStub(IRGenModule &IGM, ClassDecl *D);
177+
178+
/// Emit a resilient class stub.
179+
void emitObjCResilientClassStub(IRGenModule &IGM, ClassDecl *D);
180+
173181
/// Emit the constant fragile offset of the given property inside an instance
174182
/// of the class.
175183
llvm::Constant *

lib/IRGen/GenMeta.cpp

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1631,6 +1631,7 @@ namespace {
16311631

16321632
// union {
16331633
// uint32_t MetadataPositiveSizeInWords;
1634+
// ExtraClassContextFlags ExtraClassFlags;
16341635
// };
16351636
if (!MetadataLayout) {
16361637
// FIXME: do something meaningful for foreign classes?
@@ -1639,7 +1640,10 @@ namespace {
16391640
B.addInt32(MetadataLayout->getSize().getOffsetToEnd()
16401641
/ IGM.getPointerSize());
16411642
} else {
1642-
B.addInt32(0); // currently unused
1643+
ExtraClassDescriptorFlags flags;
1644+
if (hasObjCResilientClassStub(IGM, getType()))
1645+
flags.setObjCResilientClassStub(true);
1646+
B.addInt32(flags.getOpaqueValue());
16431647
}
16441648

16451649
// uint32_t NumImmediateMembers;
@@ -2552,7 +2556,8 @@ namespace {
25522556
Type type = Target->mapTypeIntoContext(Target->getSuperclass());
25532557
auto *metadata = tryEmitConstantHeapMetadataRef(
25542558
IGM, type->getCanonicalType(),
2555-
/*allowUninit*/ false);
2559+
/*allowUninit*/ false,
2560+
/*allowStub*/ false);
25562561
assert(metadata != nullptr);
25572562
B.add(metadata);
25582563
}
@@ -3137,6 +3142,26 @@ void irgen::emitClassMetadata(IRGenModule &IGM, ClassDecl *classDecl,
31373142
if (IGM.ObjCInterop) {
31383143
switch (strategy) {
31393144
case ClassMetadataStrategy::Resilient:
3145+
// Even non-@objc classes can have Objective-C categories attached, so
3146+
// we always emit a resilient class stub as long as -enable-objc-interop
3147+
// is set.
3148+
if (hasObjCResilientClassStub(IGM, classDecl)) {
3149+
emitObjCResilientClassStub(IGM, classDecl);
3150+
3151+
if (classDecl->isObjC()) {
3152+
auto *stub = IGM.getAddrOfObjCResilientClassStub(
3153+
classDecl, NotForDefinition,
3154+
TypeMetadataAddress::AddressPoint);
3155+
emitObjCClassSymbol(IGM, classDecl, stub);
3156+
3157+
// @_objc_non_lazy_realization is only for use by the standard
3158+
// library, and we cannot support it with Objective-C class
3159+
// stubs (which there are none of in the standard library).
3160+
assert(!classDecl->getAttrs().hasAttribute<ObjCNonLazyRealizationAttr>());
3161+
IGM.addObjCClass(stub, /*eagerInitialization=*/false);
3162+
}
3163+
}
3164+
break;
31403165
case ClassMetadataStrategy::Singleton:
31413166
break;
31423167

lib/IRGen/MetadataRequest.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -508,14 +508,20 @@ irgen::getRuntimeReifiedType(IRGenModule &IGM, CanType type) {
508508
/// Attempts to return a constant heap metadata reference for a
509509
/// class type. This is generally only valid for specific kinds of
510510
/// ObjC reference, like superclasses or category references.
511-
llvm::Constant *irgen::tryEmitConstantHeapMetadataRef(IRGenModule &IGM,
512-
CanType type,
513-
bool allowDynamicUninitialized) {
511+
llvm::Constant *
512+
irgen::tryEmitConstantHeapMetadataRef(IRGenModule &IGM,
513+
CanType type,
514+
bool allowDynamicUninitialized,
515+
bool allowStub) {
514516
auto theDecl = type->getClassOrBoundGenericClass();
515517
assert(theDecl && "emitting constant heap metadata ref for non-class type?");
516518

517519
switch (IGM.getClassMetadataStrategy(theDecl)) {
518520
case ClassMetadataStrategy::Resilient:
521+
if (allowStub && IGM.Context.LangOpts.EnableObjCResilientClassStubs) {
522+
return IGM.getAddrOfObjCResilientClassStub(theDecl, NotForDefinition,
523+
TypeMetadataAddress::AddressPoint);
524+
}
519525
return nullptr;
520526

521527
case ClassMetadataStrategy::Singleton:

lib/IRGen/MetadataRequest.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,8 @@ CanType getRuntimeReifiedType(IRGenModule &IGM, CanType type);
592592
/// by a constant.
593593
llvm::Constant *tryEmitConstantHeapMetadataRef(IRGenModule &IGM,
594594
CanType type,
595-
bool allowUninitialized);
595+
bool allowUninitialized,
596+
bool allowStub);
596597

597598
enum class MetadataValueType { ObjCClass, TypeMetadata };
598599

lib/TBDGen/TBDGen.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,9 +345,16 @@ void TBDGenVisitor::visitClassDecl(ClassDecl *CD) {
345345

346346
// Types with resilient superclasses have some extra symbols.
347347
if (CD->checkAncestry(AncestryFlags::ResilientOther) ||
348-
CD->hasResilientMetadata())
348+
CD->hasResilientMetadata()) {
349349
addSymbol(LinkEntity::forClassMetadataBaseOffset(CD));
350350

351+
auto &Ctx = CD->getASTContext();
352+
if (Ctx.LangOpts.EnableObjCResilientClassStubs) {
353+
addSymbol(LinkEntity::forObjCResilientClassStub(
354+
CD, TypeMetadataAddress::AddressPoint));
355+
}
356+
}
357+
351358
// Emit dispatch thunks for every new vtable entry.
352359
struct VTableVisitor : public SILVTableVisitor<VTableVisitor> {
353360
TBDGenVisitor &TBD;

0 commit comments

Comments
 (0)