Skip to content

Commit 666d6d8

Browse files
committed
Support resilient stored properties in objcImpl
When an @objc @implementation class requires the use of `ClassMetadataStrategy::Update` because some of its stored properties do not have fixed sizes, we adjust the direct field offsets during class realization by: 1. Emitting a customized ObjC metadata update function which does something similar to the Swift metadata completion function. 2. Emitting the OBJC_CLASS symbol with its Swift bit ON (so the ObjC runtime will actually look for and use the ObjC metadata update function). 3. Having the ObjC metadata update function clear the Swift bit before it tells the ObjC runtime to finish realizing the class. This ought to work because other than the realization machinery, nothing in the ObjC runtime ought to look at the class object’s contents until the class has been realized. This commit is a rough proof-of-concept implementation with important limitations: • We’re currently using the field offset vector, which means that field offsets are being emitted into @objc @implementation classes; I suspect that’s going to be unnecessary. • We’re using a new C++ runtime entry point, and one which duplicates a lot of `swift_updateClassMetadata2()`’s implementation, no less; I want to eliminate that so we can back-deploy to OSes without this function. • We will probably add a flag somewhere in the class metadata to mark the bizarre behavior of the class’s Swift bit. • Availability bounds for this feature are TBD and unimplemented. Future commits in this PR will hopefully lift these limitations.
1 parent aae9212 commit 666d6d8

File tree

12 files changed

+807
-139
lines changed

12 files changed

+807
-139
lines changed

include/swift/Runtime/Metadata.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,14 @@ swift_updateClassMetadata2(ClassMetadata *self,
782782
size_t numFields,
783783
const TypeLayout * const *fieldTypes,
784784
size_t *fieldOffsets);
785+
786+
SWIFT_RUNTIME_EXPORT SWIFT_CC(swift)
787+
Class
788+
swift_updatePureObjCClassMetadata(Class self,
789+
ClassLayoutFlags flags,
790+
size_t numFields,
791+
const TypeLayout * const *fieldTypes,
792+
size_t *fieldOffsets);
785793
#endif
786794

787795
/// Given class metadata, a class descriptor and a method descriptor, look up

include/swift/Runtime/RuntimeFunctions.def

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1437,6 +1437,21 @@ FUNCTION(UpdateClassMetadata2,
14371437
EFFECT(MetaData),
14381438
UNKNOWN_MEMEFFECTS)
14391439

1440+
// objc_class *swift_updatePureObjCClassMetadata(objc_class *self,
1441+
// ClassLayoutFlags flags,
1442+
// size_t numFields,
1443+
// TypeLayout * const *fieldTypes,
1444+
// size_t *fieldOffsets);
1445+
FUNCTION(UpdatePureObjCClassMetadata,
1446+
swift_updatePureObjCClassMetadata, SwiftCC, AlwaysAvailable,
1447+
RETURNS(ObjCClassPtrTy),
1448+
ARGS(ObjCClassPtrTy, SizeTy, SizeTy,
1449+
Int8PtrPtrTy->getPointerTo(),
1450+
SizeTy->getPointerTo()),
1451+
ATTRS(NoUnwind),
1452+
EFFECT(MetaData),
1453+
UNKNOWN_MEMEFFECTS)
1454+
14401455
// void *swift_lookUpClassMethod(Metadata *metadata,
14411456
// ClassDescriptor *description,
14421457
// MethodDescriptor *method);

lib/IRGen/GenClass.cpp

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2561,12 +2561,28 @@ static llvm::Function *emitObjCMetadataUpdateFunction(IRGenModule &IGM,
25612561
Explosion params = IGF.collectParameters();
25622562
(void) params.claimAll();
25632563

2564-
// Just directly call our metadata accessor. This should actually
2565-
// return the same metadata; the Objective-C runtime enforces this.
2566-
auto type = D->getDeclaredType()->getCanonicalType();
2567-
auto *metadata = IGF.emitTypeMetadataRef(type,
2568-
MetadataState::Complete)
2569-
.getMetadata();
2564+
llvm::Value *metadata;
2565+
if (D->getObjCImplementationDecl()) {
2566+
// This is an objcImplementation class, so it has no metadata completion
2567+
// function. We need to grab the class's heap address without passing
2568+
// through any runtime, clear the Swift bit and update the field offsets and
2569+
// size, and tell the ObjC runtime to finish realizing it.
2570+
metadata = IGM.getAddrOfObjCClass(D, NotForDefinition);
2571+
auto loweredTy = IGM.getLoweredType(D->getDeclaredTypeInContext());
2572+
2573+
IGF.emitInitializeFieldOffsetVector(loweredTy, metadata,
2574+
/*isVWTMutable=*/false,
2575+
/*collector=*/nullptr);
2576+
} else {
2577+
// Just call our metadata accessor, which will cause the Swift runtime to
2578+
// call the metadata completion function if there is one. This should
2579+
// actually return the same metadata; the Objective-C runtime enforces this.
2580+
auto type = D->getDeclaredType()->getCanonicalType();
2581+
metadata = IGF.emitTypeMetadataRef(type,
2582+
MetadataState::Complete)
2583+
.getMetadata();
2584+
}
2585+
25702586
IGF.Builder.CreateRet(
25712587
IGF.Builder.CreateBitCast(metadata,
25722588
IGM.ObjCClassPtrTy));

lib/IRGen/GenMeta.cpp

Lines changed: 61 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2948,37 +2948,34 @@ IRGenModule::getAddrOfOriginalModuleContextDescriptor(StringRef Name) {
29482948
.first->getValue());
29492949
}
29502950

2951-
static void emitInitializeFieldOffsetVector(IRGenFunction &IGF,
2952-
SILType T,
2951+
void IRGenFunction::emitInitializeFieldOffsetVector(SILType T,
29532952
llvm::Value *metadata,
29542953
bool isVWTMutable,
29552954
MetadataDependencyCollector *collector) {
2956-
auto &IGM = IGF.IGM;
2957-
29582955
auto *target = T.getNominalOrBoundGenericNominal();
29592956
llvm::Value *fieldVector
2960-
= emitAddressOfFieldOffsetVector(IGF, metadata, target)
2957+
= emitAddressOfFieldOffsetVector(*this, metadata, target)
29612958
.getAddress();
29622959

29632960
// Collect the stored properties of the type.
29642961
unsigned numFields = getNumFields(target);
29652962

29662963
// Fill out an array with the field type metadata records.
2967-
Address fields = IGF.createAlloca(
2964+
Address fields = createAlloca(
29682965
llvm::ArrayType::get(IGM.Int8PtrPtrTy, numFields),
29692966
IGM.getPointerAlignment(), "classFields");
2970-
IGF.Builder.CreateLifetimeStart(fields, IGM.getPointerSize() * numFields);
2971-
fields = IGF.Builder.CreateStructGEP(fields, 0, Size(0));
2967+
Builder.CreateLifetimeStart(fields, IGM.getPointerSize() * numFields);
2968+
fields = Builder.CreateStructGEP(fields, 0, Size(0));
29722969

29732970
unsigned index = 0;
29742971
forEachField(IGM, target, [&](Field field) {
29752972
assert(field.isConcrete() &&
29762973
"initializing offset vector for type with missing member?");
29772974
SILType propTy = field.getType(IGM, T);
2978-
llvm::Value *fieldLayout = emitTypeLayoutRef(IGF, propTy, collector);
2975+
llvm::Value *fieldLayout = emitTypeLayoutRef(*this, propTy, collector);
29792976
Address fieldLayoutAddr =
2980-
IGF.Builder.CreateConstArrayGEP(fields, index, IGM.getPointerSize());
2981-
IGF.Builder.CreateStore(fieldLayout, fieldLayoutAddr);
2977+
Builder.CreateConstArrayGEP(fields, index, IGM.getPointerSize());
2978+
Builder.CreateStore(fieldLayout, fieldLayoutAddr);
29822979
++index;
29832980
});
29842981
assert(index == numFields);
@@ -3004,13 +3001,15 @@ static void emitInitializeFieldOffsetVector(IRGenFunction &IGF,
30043001
llvm_unreachable("Emitting metadata init for fixed class metadata?");
30053002
}
30063003

3007-
llvm::Value *dependency;
3008-
3004+
llvm::Value *dependency = nullptr;
3005+
30093006
switch (IGM.getClassMetadataStrategy(classDecl)) {
30103007
case ClassMetadataStrategy::Resilient:
30113008
case ClassMetadataStrategy::Singleton:
30123009
// Call swift_initClassMetadata().
3013-
dependency = IGF.Builder.CreateCall(
3010+
assert(!classDecl->getObjCImplementationDecl()
3011+
&& "Singleton/Resilient strategies not supported for objcImplementation");
3012+
dependency = Builder.CreateCall(
30143013
IGM.getInitClassMetadata2FunctionPointer(),
30153014
{metadata, IGM.getSize(Size(uintptr_t(flags))), numFieldsV,
30163015
fields.getAddress(), fieldVector});
@@ -3023,10 +3022,17 @@ static void emitInitializeFieldOffsetVector(IRGenFunction &IGF,
30233022
// Call swift_updateClassMetadata(). Note that the static metadata
30243023
// already references the superclass in this case, but we still want
30253024
// to ensure the superclass metadata is initialized first.
3026-
dependency = IGF.Builder.CreateCall(
3027-
IGM.getUpdateClassMetadata2FunctionPointer(),
3028-
{metadata, IGM.getSize(Size(uintptr_t(flags))), numFieldsV,
3029-
fields.getAddress(), fieldVector});
3025+
if (classDecl->getObjCImplementationDecl()) {
3026+
Builder.CreateCall(
3027+
IGM.getUpdatePureObjCClassMetadataFunctionPointer(),
3028+
{metadata, IGM.getSize(Size(uintptr_t(flags))), numFieldsV,
3029+
fields.getAddress(), fieldVector});
3030+
} else {
3031+
dependency = Builder.CreateCall(
3032+
IGM.getUpdateClassMetadata2FunctionPointer(),
3033+
{metadata, IGM.getSize(Size(uintptr_t(flags))), numFieldsV,
3034+
fields.getAddress(), fieldVector});
3035+
}
30303036
break;
30313037

30323038
case ClassMetadataStrategy::Fixed:
@@ -3035,8 +3041,8 @@ static void emitInitializeFieldOffsetVector(IRGenFunction &IGF,
30353041

30363042
// Collect any possible dependency from initializing the class; generally
30373043
// this involves the superclass.
3038-
assert(collector);
3039-
collector->collect(IGF, dependency);
3044+
if (collector && dependency)
3045+
collector->collect(*this, dependency);
30403046

30413047
} else {
30423048
assert(isa<StructDecl>(target));
@@ -3047,12 +3053,12 @@ static void emitInitializeFieldOffsetVector(IRGenFunction &IGF,
30473053
flags |= StructLayoutFlags::IsVWTMutable;
30483054

30493055
// Call swift_initStructMetadata().
3050-
IGF.Builder.CreateCall(IGM.getInitStructMetadataFunctionPointer(),
3056+
Builder.CreateCall(IGM.getInitStructMetadataFunctionPointer(),
30513057
{metadata, IGM.getSize(Size(uintptr_t(flags))),
30523058
numFieldsV, fields.getAddress(), fieldVector});
30533059
}
30543060

3055-
IGF.Builder.CreateLifetimeEnd(fields, IGM.getPointerSize() * numFields);
3061+
Builder.CreateLifetimeEnd(fields, IGM.getPointerSize() * numFields);
30563062
}
30573063

30583064
static void emitInitializeFieldOffsetVectorWithLayoutString(
@@ -3314,8 +3320,8 @@ static void emitInitializeValueMetadata(IRGenFunction &IGF,
33143320
emitInitializeFieldOffsetVectorWithLayoutString(IGF, loweredTy, metadata,
33153321
isVWTMutable, collector);
33163322
} else {
3317-
emitInitializeFieldOffsetVector(IGF, loweredTy, metadata, isVWTMutable,
3318-
collector);
3323+
IGF.emitInitializeFieldOffsetVector(loweredTy, metadata, isVWTMutable,
3324+
collector);
33193325
}
33203326
} else {
33213327
assert(isa<EnumDecl>(nominalDecl));
@@ -3347,9 +3353,9 @@ static void emitInitializeClassMetadata(IRGenFunction &IGF,
33473353

33483354
// Set the superclass, fill out the field offset vector, and copy vtable
33493355
// entries, generic requirements and field offsets from superclasses.
3350-
emitInitializeFieldOffsetVector(IGF, loweredTy,
3351-
metadata, /*VWT is mutable*/ false,
3352-
collector);
3356+
IGF.emitInitializeFieldOffsetVector(loweredTy,
3357+
metadata, /*VWT is mutable*/ false,
3358+
collector);
33533359

33543360
// Realizing the class with the ObjC runtime will copy back to the
33553361
// field offset globals for us; but if ObjC interop is disabled, we
@@ -3721,6 +3727,25 @@ createSingletonInitializationMetadataAccessFunction(IRGenModule &IGM,
37213727
[&](IRGenFunction &IGF,
37223728
DynamicMetadataRequest request,
37233729
llvm::Constant *cacheVariable) {
3730+
if (auto CD = dyn_cast<ClassDecl>(typeDecl)) {
3731+
if (CD->getObjCImplementationDecl()) {
3732+
// Use the Objective-C runtime symbol instead of the Swift one.
3733+
llvm::Value *descriptor =
3734+
IGF.IGM.getAddrOfObjCClass(CD, NotForDefinition);
3735+
3736+
// Make the ObjC runtime initialize the class.
3737+
llvm::Value *initializedDescriptor =
3738+
IGF.Builder.CreateCall(IGF.IGM.getFixedClassInitializationFn(),
3739+
{descriptor});
3740+
3741+
// Turn the ObjC class into a valid Swift metadata pointer.
3742+
auto response =
3743+
IGF.Builder.CreateCall(IGF.IGM.getGetObjCClassMetadataFunctionPointer(),
3744+
{initializedDescriptor});
3745+
return MetadataResponse::forComplete(response);
3746+
}
3747+
}
3748+
37243749
llvm::Value *descriptor =
37253750
IGF.IGM.getAddrOfTypeContextDescriptor(typeDecl, RequireMetadata);
37263751
auto responsePair =
@@ -4290,7 +4315,12 @@ namespace {
42904315
// Derive the RO-data.
42914316
llvm::Constant *data = asImpl().getROData();
42924317

4293-
if (!asImpl().getFieldLayout().hasObjCImplementation()) {
4318+
// Fixed-layout objcImpl classes should not have the Swift bit set.
4319+
// Variable-layout objcImpl classes have the Swift bit set *temporarily*
4320+
// so that the ObjC runtime realizes them as though they are Swift
4321+
// classes, but the metadata update callback will clear the Swift bit
4322+
// before the rest of the ObjC runtime starts looking at the class.
4323+
if (!isPureObjC() || !hasFixedLayout()) {
42944324
// Set a low bit to indicate this class has Swift metadata.
42954325
auto bit = llvm::ConstantInt::get(
42964326
IGM.IntPtrTy, asImpl().getClassDataPointerHasSwiftMetadataBits());
@@ -4369,6 +4399,9 @@ namespace {
43694399
if (IGM.getClassMetadataStrategy(Target) == ClassMetadataStrategy::Fixed)
43704400
return;
43714401

4402+
if (isPureObjC())
4403+
return;
4404+
43724405
emitMetadataCompletionFunction(
43734406
IGM, Target,
43744407
[&](IRGenFunction &IGF, llvm::Value *metadata,
@@ -4409,9 +4442,6 @@ namespace {
44094442
: super(IGM, theClass, builder, fieldLayout, vtable) {}
44104443

44114444
void addFieldOffset(VarDecl *var) {
4412-
if (asImpl().getFieldLayout().hasObjCImplementation())
4413-
return;
4414-
44154445
addFixedFieldOffset(IGM, B, var, [](DeclContext *dc) {
44164446
return dc->getDeclaredTypeInContext();
44174447
});
@@ -4443,18 +4473,12 @@ namespace {
44434473
: super(IGM, theClass, builder, fieldLayout) {}
44444474

44454475
void addFieldOffset(VarDecl *var) {
4446-
if (asImpl().getFieldLayout().hasObjCImplementation())
4447-
return;
4448-
44494476
// Field offsets are either copied from the superclass or calculated
44504477
// at runtime.
44514478
B.addInt(IGM.SizeTy, 0);
44524479
}
44534480

44544481
void addFieldOffsetPlaceholders(MissingMemberDecl *placeholder) {
4455-
if (asImpl().getFieldLayout().hasObjCImplementation())
4456-
return;
4457-
44584482
for (unsigned i = 0,
44594483
e = placeholder->getNumberOfFieldOffsetVectorEntries();
44604484
i < e; ++i) {
@@ -4955,9 +4979,6 @@ namespace {
49554979
}
49564980

49574981
void addFieldOffset(VarDecl *var) {
4958-
if (asImpl().getFieldLayout().hasObjCImplementation())
4959-
return;
4960-
49614982
addFixedFieldOffset(IGM, B, var, [&](DeclContext *dc) {
49624983
return dc->mapTypeIntoContext(type);
49634984
});

lib/IRGen/IRGenFunction.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ namespace irgen {
5959
class IRGenModule;
6060
class LinkEntity;
6161
class LocalTypeDataCache;
62+
class MetadataDependencyCollector;
6263
class MetadataResponse;
6364
class Scope;
6465
class TypeInfo;
@@ -405,6 +406,12 @@ class IRGenFunction {
405406
llvm::Value *&metadataSlot,
406407
ValueWitness index);
407408

409+
void emitInitializeFieldOffsetVector(SILType T,
410+
llvm::Value *metadata,
411+
bool isVWTMutable,
412+
MetadataDependencyCollector *collector);
413+
414+
408415
llvm::Value *optionallyLoadFromConditionalProtocolWitnessTable(
409416
llvm::Value *wtable);
410417

0 commit comments

Comments
 (0)