Skip to content

Commit 95d2510

Browse files
committed
Runtime: Put ObjC class wrapper unwrapping behind a runtime call.
This is a small code size win, and also gives us some abstraction so that future cooperative ObjC compilers/runtimes might be able to interoperate ObjC class objects with Swift type metadata efficiently than they currently are in the fragile Swift runtime. While I'm here, I also noticed that swift_getObjCClassMetadata was unnecessarily getting exposed in non-ObjC-interop runtime builds, so I fixed that as well.
1 parent d53f25c commit 95d2510

File tree

13 files changed

+66
-76
lines changed

13 files changed

+66
-76
lines changed

include/swift/Runtime/Metadata.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2749,13 +2749,18 @@ swift_getBlockTypeMetadata3(const void *arg0,
27492749
SWIFT_RUNTIME_EXPORT
27502750
void
27512751
swift_instantiateObjCClass(const ClassMetadata *theClass);
2752-
#endif
27532752

27542753
/// \brief Fetch a uniqued type metadata for an ObjC class.
27552754
SWIFT_RUNTIME_EXPORT
27562755
const Metadata *
27572756
swift_getObjCClassMetadata(const ClassMetadata *theClass);
27582757

2758+
/// \brief Get the ObjC class object from class type metadata.
2759+
SWIFT_RUNTIME_EXPORT
2760+
const ClassMetadata *
2761+
swift_getObjCClassFromMetadata(const Metadata *theClass);
2762+
#endif
2763+
27592764
/// \brief Fetch a unique type metadata object for a foreign type.
27602765
SWIFT_RUNTIME_EXPORT
27612766
const ForeignTypeMetadata *

include/swift/Runtime/RuntimeFunctions.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,12 @@ FUNCTION(GetObjCClassMetadata, swift_getObjCClassMetadata, DefaultCC,
834834
ARGS(ObjCClassPtrTy),
835835
ATTRS(NoUnwind, ReadNone))
836836

837+
// Metadata *swift_getObjCClassFromMetadata(objc_class *theClass);
838+
FUNCTION(GetObjCClassFromMetadata, swift_getObjCClassFromMetadata, DefaultCC,
839+
RETURNS(ObjCClassPtrTy),
840+
ARGS(TypeMetadataPtrTy),
841+
ATTRS(NoUnwind, ReadNone))
842+
837843
// Metadata *swift_getTupleTypeMetadata(size_t numElements,
838844
// Metadata * const *elts,
839845
// const char *labels,

lib/IRGen/GenMeta.cpp

Lines changed: 11 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ static llvm::Constant *getMangledTypeName(IRGenModule &IGM, CanType type,
108108

109109
llvm::Value *irgen::emitObjCMetadataRefForMetadata(IRGenFunction &IGF,
110110
llvm::Value *classPtr) {
111+
assert(IGF.IGM.Context.LangOpts.EnableObjCInterop);
111112
classPtr = IGF.Builder.CreateBitCast(classPtr, IGF.IGM.ObjCClassPtrTy);
112113

113114
// Fetch the metadata for that class.
@@ -4215,46 +4216,16 @@ llvm::Value *irgen::emitClassHeapMetadataRefForMetatype(IRGenFunction &IGF,
42154216
if (hasKnownSwiftMetadata(IGF.IGM, type))
42164217
return metatype;
42174218

4218-
// Otherwise, we inline a little operation here.
4219-
4220-
// Load the metatype kind.
4221-
auto metatypeKindAddr =
4222-
Address(IGF.Builder.CreateStructGEP(/*Ty=*/nullptr, metatype, 0),
4223-
IGF.IGM.getPointerAlignment());
4224-
auto metatypeKind =
4225-
IGF.Builder.CreateLoad(metatypeKindAddr, metatype->getName() + ".kind");
4226-
4227-
// Compare it with the class wrapper kind.
4228-
auto classWrapperKind =
4229-
llvm::ConstantInt::get(IGF.IGM.MetadataKindTy,
4230-
unsigned(MetadataKind::ObjCClassWrapper));
4231-
auto isObjCClassWrapper =
4232-
IGF.Builder.CreateICmpEQ(metatypeKind, classWrapperKind,
4233-
"isObjCClassWrapper");
4234-
4235-
// Branch based on that.
4236-
llvm::BasicBlock *contBB = IGF.createBasicBlock("metadataForClass.cont");
4237-
llvm::BasicBlock *wrapBB = IGF.createBasicBlock("isWrapper");
4238-
IGF.Builder.CreateCondBr(isObjCClassWrapper, wrapBB, contBB);
4239-
llvm::BasicBlock *origBB = IGF.Builder.GetInsertBlock();
4240-
4241-
// If it's a wrapper, load from the 'Class' field, which is at index 1.
4242-
// TODO: if we guaranteed that this load couldn't crash, we could use
4243-
// a select here instead, which might be profitable.
4244-
IGF.Builder.emitBlock(wrapBB);
4245-
auto classFromWrapper =
4246-
emitInvariantLoadFromMetadataAtIndex(IGF, metatype, 1,
4247-
IGF.IGM.TypeMetadataPtrTy);
4248-
IGF.Builder.CreateBr(contBB);
4249-
4250-
// Continuation block.
4251-
IGF.Builder.emitBlock(contBB);
4252-
auto phi = IGF.Builder.CreatePHI(IGF.IGM.TypeMetadataPtrTy, 2,
4253-
metatype->getName() + ".class");
4254-
phi->addIncoming(metatype, origBB);
4255-
phi->addIncoming(classFromWrapper, wrapBB);
4256-
4257-
return phi;
4219+
// Otherwise, we may have to unwrap an ObjC class wrapper.
4220+
assert(IGF.IGM.Context.LangOpts.EnableObjCInterop);
4221+
metatype = IGF.Builder.CreateBitCast(metatype, IGF.IGM.TypeMetadataPtrTy);
4222+
4223+
// Fetch the metadata for that class.
4224+
auto call = IGF.Builder.CreateCall(IGF.IGM.getGetObjCClassFromMetadataFn(),
4225+
metatype);
4226+
call->setDoesNotThrow();
4227+
call->setDoesNotAccessMemory();
4228+
return call;
42584229
}
42594230

42604231
/// Load the correct virtual function for the given class method.

stdlib/public/runtime/Casting.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3196,7 +3196,7 @@ SWIFT_CC(swift)
31963196
const Metadata *swift::_swift_class_getSuperclass(const Metadata *theClass) {
31973197
if (const ClassMetadata *classType = theClass->getClassObject())
31983198
if (classHasSuperclass(classType))
3199-
return swift_getObjCClassMetadata(classType->SuperClass);
3199+
return getMetadataForClass(classType->SuperClass);
32003200
return nullptr;
32013201
}
32023202

stdlib/public/runtime/Metadata.cpp

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -281,8 +281,6 @@ namespace {
281281
/// The uniquing structure for ObjC class-wrapper metadata.
282282
static SimpleGlobalCache<ObjCClassCacheEntry> ObjCClassWrappers;
283283

284-
#endif
285-
286284
const Metadata *
287285
swift::swift_getObjCClassMetadata(const ClassMetadata *theClass) {
288286
// Make calls resilient against receiving a null Objective-C class. This can
@@ -295,14 +293,24 @@ swift::swift_getObjCClassMetadata(const ClassMetadata *theClass) {
295293
return theClass;
296294
}
297295

298-
#if SWIFT_OBJC_INTEROP
299296
return &ObjCClassWrappers.getOrInsert(theClass).first->Data;
300-
#else
301-
fatalError(/* flags = */ 0,
302-
"swift_getObjCClassMetadata: no Objective-C interop");
303-
#endif
304297
}
305298

299+
const ClassMetadata *
300+
swift::swift_getObjCClassFromMetadata(const Metadata *theMetadata) {
301+
// Unwrap ObjC class wrappers.
302+
if (auto wrapper = dyn_cast<ObjCClassWrapperMetadata>(theMetadata)) {
303+
return wrapper->Class;
304+
}
305+
306+
// Otherwise, the input should already be a Swift class object.
307+
auto theClass = cast<ClassMetadata>(theMetadata);
308+
assert(theClass->isTypeMetadata() && !theClass->isArtificialSubclass());
309+
return theClass;
310+
}
311+
312+
#endif
313+
306314
/***************************************************************************/
307315
/*** Functions *************************************************************/
308316
/***************************************************************************/

stdlib/public/runtime/MetadataLookup.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,9 @@ const Metadata *TypeMetadataRecord::getCanonicalTypeMetadata() const {
137137
}
138138
case TypeMetadataRecordKind::UniqueDirectClass:
139139
if (auto *ClassMetadata =
140-
static_cast<const ::ClassMetadata *>(getDirectType()))
141-
return swift_getObjCClassMetadata(ClassMetadata);
140+
static_cast<const ::ClassMetadata *>(getDirectType())) {
141+
return getMetadataForClass(ClassMetadata);
142+
}
142143
else
143144
return nullptr;
144145
default:

stdlib/public/runtime/Private.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,14 @@ namespace swift {
190190
void (*function)(void *));
191191
#endif
192192

193+
static inline const Metadata *getMetadataForClass(const ClassMetadata *c) {
194+
#if SWIFT_OBJC_INTEROP
195+
return swift_getObjCClassMetadata(c);
196+
#else
197+
return c;
198+
#endif
199+
}
200+
193201
} // end namespace swift
194202

195203
#endif /* SWIFT_RUNTIME_PRIVATE_H */

stdlib/public/runtime/ProtocolConformance.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,14 @@ const Metadata *ProtocolConformanceRecord::getCanonicalTypeMetadata() const {
108108
// metadata. The class additionally may be weak-linked, so we have to check
109109
// for null.
110110
if (auto *ClassMetadata = *getIndirectClass())
111-
return swift_getObjCClassMetadata(ClassMetadata);
111+
return getMetadataForClass(ClassMetadata);
112112
return nullptr;
113113

114114
case TypeMetadataRecordKind::UniqueDirectClass:
115115
// The class may be ObjC, in which case we need to instantiate its Swift
116116
// metadata.
117117
if (auto *ClassMetadata = getDirectClass())
118-
return swift_getObjCClassMetadata(ClassMetadata);
118+
return getMetadataForClass(ClassMetadata);
119119
return nullptr;
120120

121121
case TypeMetadataRecordKind::UniqueNominalTypeDescriptor:
@@ -394,7 +394,7 @@ searchInConformanceCache(const Metadata *type,
394394
// If the type is a class, try its superclass.
395395
if (const ClassMetadata *classType = type->getClassObject()) {
396396
if (classHasSuperclass(classType)) {
397-
type = swift_getObjCClassMetadata(classType->SuperClass);
397+
type = getMetadataForClass(classType->SuperClass);
398398
goto recur;
399399
}
400400
}
@@ -433,7 +433,7 @@ bool isRelatedType(const Metadata *type, const void *candidate,
433433
// If the type is a class, try its superclass.
434434
if (const ClassMetadata *classType = type->getClassObject()) {
435435
if (classHasSuperclass(classType)) {
436-
type = swift_getObjCClassMetadata(classType->SuperClass);
436+
type = getMetadataForClass(classType->SuperClass);
437437
continue;
438438
}
439439
}

stdlib/public/runtime/Reflection.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ void swift_MagicMirrorData_summary(const Metadata *T, String *result) {
295295
const Metadata *type) {
296296
void *object = *reinterpret_cast<void * const *>(value);
297297
auto isa = _swift_getClass(object);
298-
return swift_getObjCClassMetadata(isa);
298+
return getMetadataForClass(isa);
299299
}
300300

301301
static std::tuple<const Metadata *, const OpaqueValue *>

stdlib/public/runtime/SwiftObject.mm

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,7 @@ static Class _swift_getObjCClassOfAllocated(const void *object) {
105105
const Metadata *swift::swift_getObjectType(HeapObject *object) {
106106
auto classAsMetadata = _swift_getClass(object);
107107

108-
#if !SWIFT_OBJC_INTEROP
109-
assert(classAsMetadata &&
110-
classAsMetadata->isTypeMetadata() &&
111-
!classAsMetadata->isArtificialSubclass());
112-
return classAsMetadata;
113-
#else
108+
#if SWIFT_OBJC_INTEROP
114109
// Walk up the superclass chain skipping over artifical Swift classes.
115110
// If we find a non-Swift class use the result of [object class] instead.
116111

@@ -129,6 +124,11 @@ static Class _swift_getObjCClassOfAllocated(const void *object) {
129124
}
130125
classAsMetadata = reinterpret_cast<const ClassMetadata *>(objcClass);
131126
return swift_getObjCClassMetadata(classAsMetadata);
127+
#else
128+
assert(classAsMetadata &&
129+
classAsMetadata->isTypeMetadata() &&
130+
!classAsMetadata->isArtificialSubclass());
131+
return classAsMetadata;
132132
#endif
133133
}
134134

test/ClangImporter/objc_ir.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func propertyAccess(b b: B) {
7373
b.counter = b.counter + 1
7474

7575
// CHECK: call %swift.type* @_T0So1BCMa()
76-
// CHECK: bitcast %swift.type* {{%.+}} to %objc_class*
76+
// CHECK: call %objc_class* @swift_getObjCClassFromMetadata
7777
// CHECK: load i8*, i8** @"\01L_selector(sharedCounter)"
7878
// CHECK: load i8*, i8** @"\01L_selector(setSharedCounter:)"
7979
B.sharedCounter = B.sharedCounter + 1

test/IRGen/abitypes.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,8 @@ class Foo {
214214

215215
// x86_64-macosx: define hidden i8* @_T08abitypes3FooC9copyClass{{[_0-9a-zA-Z]*}}FTo(i8*, i8*, i8*) unnamed_addr {{.*}} {
216216
// x86_64-macosx: [[VALUE:%[0-9]+]] = call swiftcc [[TYPE:%.*]]* @_T08abitypes3FooC9copyClass{{[_0-9a-zA-Z]*}}F
217-
// x86_64-macosx: [[T0:%.*]] = phi [[TYPE]]* [ [[VALUE]],
218-
// x86_64-macosx: [[T1:%.*]] = bitcast [[TYPE]]* [[T0]] to [[OBJC:%objc_class]]*
219-
// x86_64-macosx: [[RESULT:%[0-9]+]] = bitcast [[OBJC]]* [[T1]] to i8*
217+
// x86_64-macosx: [[T0:%.*]] = call [[OBJC:%objc_class]]* @swift_getObjCClassFromMetadata([[TYPE]]* [[VALUE]])
218+
// x86_64-macosx: [[RESULT:%[0-9]+]] = bitcast [[OBJC]]* [[T0]] to i8*
220219
// x86_64-macosx: ret i8* [[RESULT]]
221220
dynamic func copyClass(_ a: AnyClass) -> AnyClass {
222221
return a

test/IRGen/metatype.sil

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,7 @@ bb0(%0 : $@thick X.Type):
3131
// CHECK-LABEL: define{{( protected)?}} swiftcc %objc_class* @foreign_thick_to_objc(%swift.type*)
3232
sil @foreign_thick_to_objc : $@convention(thin) (@thick Gizmo.Type) -> @objc_metatype Gizmo.Type {
3333
bb0(%0 : $@thick Gizmo.Type):
34-
// CHECK: [[KIND_GEP:%[0-9A-Za-z_.]+]] = getelementptr inbounds %swift.type, %swift.type* %0, i32 0, i32 0
35-
// CHECK-NEXT: [[KIND:%[0-9A-Za-z_.]+]] = load i64, i64* [[KIND_GEP]], align 8
36-
// CHECK-NEXT: [[IS_WRAPPER:%[0-9A-Za-z_.]+]] = icmp eq i64 [[KIND]], 14
37-
// CHECK-NEXT: br i1
34+
// CHECK: call %objc_class* @swift_getObjCClassFromMetadata
3835
%1 = thick_to_objc_metatype %0 : $@thick Gizmo.Type to $@objc_metatype Gizmo.Type
3936
// CHECK: ret %objc_class*
4037
return %1 : $@objc_metatype Gizmo.Type
@@ -61,12 +58,7 @@ bb0(%0 : $@objc_metatype Gizmo.Type):
6158
protocol CP: class {}
6259

6360
// CHECK-LABEL: define{{( protected)?}} swiftcc %objc_class* @archetype_objc_metatype(%swift.type* %T, i8** %T.CP)
64-
// CHECK: [[IS_OBJC_WRAPPER:%.*]] = icmp eq i64 {{%.*}}, 14
65-
// CHECK: br i1 [[IS_OBJC_WRAPPER]], label %isWrapper, label %metadataForClass.cont
66-
// CHECK: isWrapper:
67-
// CHECK: [[WRAPPED_CLASS:%.*]] = load %swift.type*, %swift.type**
68-
// CHECK: metadataForClass.cont:
69-
// CHECK: phi %swift.type* [ %T, %entry ], [ [[WRAPPED_CLASS]], %isWrapper ]
61+
// CHECK: call %objc_class* @swift_getObjCClassFromMetadata
7062
sil @archetype_objc_metatype : $@convention(thin) <T: CP> () -> @objc_metatype T.Type {
7163
entry:
7264
%m = metatype $@objc_metatype T.Type

0 commit comments

Comments
 (0)