Skip to content

Commit a4d11c9

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 emitting a custom metadata update function which calls a new entry point in the Swift runtime. That entry point adjusts field offsets like `swift_updateClassMetadata2()`, but it only assumes that the class has Objective-C metadata, not Swift metadata. This commit introduces an alternative mechanism which does the same thing without using any Swift-only metadata. It’s a rough implementation with important limitations: • We’re currently using the field offset vector, which means that field offsets are being emitted into @objc @implementation classes; these will be removed. • The new Swift runtime entry point duplicates a lot of `swift_updateClassMetadata2()`’s implementation; it will be refactored into something much smaller and more compact. • Availability bounds for this feature have not yet been implemented. Future commits in this PR will correct these issues.
1 parent 9e83099 commit a4d11c9

File tree

12 files changed

+808
-146
lines changed

12 files changed

+808
-146
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, collect field size information, and tell the Swift
2569+
// runtime to update the ObjC metadata and finish realizing the class.
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: 65 additions & 47 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 =
@@ -4275,6 +4300,9 @@ namespace {
42754300
llvm::Constant *getROData() { return emitClassPrivateData(IGM, Target); }
42764301

42774302
uint64_t getClassDataPointerHasSwiftMetadataBits() {
4303+
// objcImpl classes should not have the Swift bit set.
4304+
if (isPureObjC())
4305+
return 0;
42784306
return IGM.UseDarwinPreStableABIBit ? 1 : 2;
42794307
}
42804308

@@ -4290,15 +4318,13 @@ namespace {
42904318
// Derive the RO-data.
42914319
llvm::Constant *data = asImpl().getROData();
42924320

4293-
if (!asImpl().getFieldLayout().hasObjCImplementation()) {
4294-
// Set a low bit to indicate this class has Swift metadata.
4295-
auto bit = llvm::ConstantInt::get(
4296-
IGM.IntPtrTy, asImpl().getClassDataPointerHasSwiftMetadataBits());
4321+
// Set a low bit to indicate this class has Swift metadata.
4322+
auto bit = llvm::ConstantInt::get(
4323+
IGM.IntPtrTy, asImpl().getClassDataPointerHasSwiftMetadataBits());
42974324

4298-
// Emit data + bit.
4299-
data = llvm::ConstantExpr::getPtrToInt(data, IGM.IntPtrTy);
4300-
data = llvm::ConstantExpr::getAdd(data, bit);
4301-
}
4325+
// Emit data + bit.
4326+
data = llvm::ConstantExpr::getPtrToInt(data, IGM.IntPtrTy);
4327+
data = llvm::ConstantExpr::getAdd(data, bit);
43024328

43034329
B.add(data);
43044330
}
@@ -4369,6 +4395,9 @@ namespace {
43694395
if (IGM.getClassMetadataStrategy(Target) == ClassMetadataStrategy::Fixed)
43704396
return;
43714397

4398+
if (isPureObjC())
4399+
return;
4400+
43724401
emitMetadataCompletionFunction(
43734402
IGM, Target,
43744403
[&](IRGenFunction &IGF, llvm::Value *metadata,
@@ -4409,9 +4438,6 @@ namespace {
44094438
: super(IGM, theClass, builder, fieldLayout, vtable) {}
44104439

44114440
void addFieldOffset(VarDecl *var) {
4412-
if (asImpl().getFieldLayout().hasObjCImplementation())
4413-
return;
4414-
44154441
addFixedFieldOffset(IGM, B, var, [](DeclContext *dc) {
44164442
return dc->getDeclaredTypeInContext();
44174443
});
@@ -4443,18 +4469,12 @@ namespace {
44434469
: super(IGM, theClass, builder, fieldLayout) {}
44444470

44454471
void addFieldOffset(VarDecl *var) {
4446-
if (asImpl().getFieldLayout().hasObjCImplementation())
4447-
return;
4448-
44494472
// Field offsets are either copied from the superclass or calculated
44504473
// at runtime.
44514474
B.addInt(IGM.SizeTy, 0);
44524475
}
44534476

44544477
void addFieldOffsetPlaceholders(MissingMemberDecl *placeholder) {
4455-
if (asImpl().getFieldLayout().hasObjCImplementation())
4456-
return;
4457-
44584478
for (unsigned i = 0,
44594479
e = placeholder->getNumberOfFieldOffsetVectorEntries();
44604480
i < e; ++i) {
@@ -4955,9 +4975,6 @@ namespace {
49554975
}
49564976

49574977
void addFieldOffset(VarDecl *var) {
4958-
if (asImpl().getFieldLayout().hasObjCImplementation())
4959-
return;
4960-
49614978
addFixedFieldOffset(IGM, B, var, [&](DeclContext *dc) {
49624979
return dc->mapTypeIntoContext(type);
49634980
});
@@ -4980,6 +4997,7 @@ namespace {
49804997
}
49814998

49824999
uint64_t getClassDataPointerHasSwiftMetadataBits() {
5000+
assert(!isPureObjC());
49835001
return super::getClassDataPointerHasSwiftMetadataBits() | 2;
49845002
}
49855003

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;
@@ -407,6 +408,12 @@ class IRGenFunction {
407408
llvm::Value *&metadataSlot,
408409
ValueWitness index);
409410

411+
void emitInitializeFieldOffsetVector(SILType T,
412+
llvm::Value *metadata,
413+
bool isVWTMutable,
414+
MetadataDependencyCollector *collector);
415+
416+
410417
llvm::Value *optionallyLoadFromConditionalProtocolWitnessTable(
411418
llvm::Value *wtable);
412419

0 commit comments

Comments
 (0)