IRGen: Make type(of:) behavior consistent in ObjC bridged contexts.

When we use type(of: x) on a class in an ObjC bridged context, the optimizer turns this into a SIL `value_metatype @objc` operation, which is supposed to get the dynamic type of the object as an ObjC class. This was previously lowered by IRGen into a `object_getClass` call, which extracts the isa pointer from the object, but is inconsistent with the `-class` method in ObjC or with the Swift-native behavior, which both look through artificial subclasses, proxies, and so on. This inconsistency led to observably different behavior between debug and release builds and between ObjC-bridged and native entry points, so provide an alternative runtime entry point that replicates the behavior of getting a native Swift class. Fixes SR-7258.
This commit is contained in:
Joe Groff
2018-04-06 13:24:31 -07:00
parent 88b785cadd
commit a4aa054838
10 changed files with 84 additions and 45 deletions

View File

@@ -4154,6 +4154,10 @@ swift_getObjCClassMetadata(const ClassMetadata *theClass);
SWIFT_RUNTIME_EXPORT
const ClassMetadata *
swift_getObjCClassFromMetadata(const Metadata *theClass);
SWIFT_RUNTIME_EXPORT
const ClassMetadata *
swift_getObjCClassFromObject(HeapObject *object);
#endif
/// \brief Fetch a unique type metadata object for a foreign type.

View File

@@ -869,6 +869,12 @@ FUNCTION(GetObjCClassFromMetadata, swift_getObjCClassFromMetadata, C_CC,
ARGS(TypeMetadataPtrTy),
ATTRS(NoUnwind, ReadNone))
// Metadata *swift_getObjCClassFromObject(id object);
FUNCTION(GetObjCClassFromObject, swift_getObjCClassFromObject, C_CC,
RETURNS(ObjCClassPtrTy),
ARGS(ObjCPtrTy),
ATTRS(NoUnwind, ReadNone))
// MetadataResponse swift_getTupleTypeMetadata(MetadataRequest request,
// TupleTypeFlags flags,
// Metadata * const *elts,

View File

@@ -718,7 +718,9 @@ void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF,
}
} else {
// Get the type metadata for the instance.
metadataValue = emitDynamicTypeOfHeapObject(IGF, value, srcType);
metadataValue = emitDynamicTypeOfHeapObject(IGF, value,
MetatypeRepresentation::Thick,
srcType);
}
// Look up witness tables for the protocols that need them.

View File

@@ -1881,15 +1881,8 @@ void irgen::emitMetatypeOfClassExistential(IRGenFunction &IGF, Explosion &value,
assert((IGF.IGM.ObjCInterop || repr != MetatypeRepresentation::ObjC) &&
"Class metatypes should not have ObjC representation without runtime");
if (repr == MetatypeRepresentation::Thick) {
auto dynamicType = emitDynamicTypeOfOpaqueHeapObject(IGF, instance);
out.add(dynamicType);
} else if (repr == MetatypeRepresentation::ObjC) {
auto dynamicType = emitHeapMetadataRefForUnknownHeapObject(IGF, instance);
out.add(dynamicType);
} else {
llvm_unreachable("Unknown metatype representation");
}
auto dynamicType = emitDynamicTypeOfOpaqueHeapObject(IGF, instance, repr);
out.add(dynamicType);
// Get the witness tables.
out.add(tablesAndValue.first);
@@ -1926,7 +1919,8 @@ irgen::emitClassExistentialProjection(IRGenFunction &IGF,
ArrayRef<llvm::Value*> wtables;
llvm::Value *value;
std::tie(wtables, value) = baseTI.getWitnessTablesAndValue(base);
auto metadata = emitDynamicTypeOfOpaqueHeapObject(IGF, value);
auto metadata = emitDynamicTypeOfOpaqueHeapObject(IGF, value,
MetatypeRepresentation::Thick);
IGF.bindArchetype(openedArchetype, metadata, MetadataState::Complete,
wtables);
@@ -2265,7 +2259,8 @@ Address irgen::emitOpaqueBoxedExistentialProjection(
IGF.getTypeInfo(existentialTy).as<ClassExistentialTypeInfo>();
auto valueAddr = baseTI.projectValue(IGF, base);
auto value = IGF.Builder.CreateLoad(valueAddr);
auto metadata = emitDynamicTypeOfOpaqueHeapObject(IGF, value);
auto metadata = emitDynamicTypeOfOpaqueHeapObject(IGF, value,
MetatypeRepresentation::Thick);
// If we are projecting into an opened archetype, capture the
// witness tables.

View File

@@ -1796,7 +1796,8 @@ llvm::Value *IRGenFunction::getLocalSelfMetadata() {
case ObjCMetatype:
return emitObjCMetadataRefForMetadata(*this, LocalSelf);
case ObjectReference:
return emitDynamicTypeOfOpaqueHeapObject(*this, LocalSelf);
return emitDynamicTypeOfOpaqueHeapObject(*this, LocalSelf,
MetatypeRepresentation::Thick);
}
llvm_unreachable("Not a valid LocalSelfKind.");
@@ -1916,11 +1917,26 @@ llvm::Value *irgen::emitHeapMetadataRefForHeapObject(IRGenFunction &IGF,
/// Given an opaque class instance pointer, produce the type metadata reference
/// as a %type*.
llvm::Value *irgen::emitDynamicTypeOfOpaqueHeapObject(IRGenFunction &IGF,
llvm::Value *object) {
llvm::Value *object,
MetatypeRepresentation repr) {
object = IGF.Builder.CreateBitCast(object, IGF.IGM.ObjCPtrTy);
auto metadata = IGF.Builder.CreateCall(IGF.IGM.getGetObjectTypeFn(),
object,
object->getName() + ".Type");
llvm::CallInst *metadata;
switch (repr) {
case MetatypeRepresentation::ObjC:
metadata = IGF.Builder.CreateCall(IGF.IGM.getGetObjCClassFromObjectFn(),
object,
object->getName() + ".Type");
break;
case MetatypeRepresentation::Thick:
metadata = IGF.Builder.CreateCall(IGF.IGM.getGetObjectTypeFn(),
object,
object->getName() + ".Type");
break;
case MetatypeRepresentation::Thin:
llvm_unreachable("class metadata can't be thin");
}
metadata->setDoesNotThrow();
metadata->setOnlyReadsMemory();
return metadata;
@@ -1944,18 +1960,18 @@ emitHeapMetadataRefForUnknownHeapObject(IRGenFunction &IGF,
/// as a %type*.
llvm::Value *irgen::emitDynamicTypeOfHeapObject(IRGenFunction &IGF,
llvm::Value *object,
MetatypeRepresentation repr,
SILType objectType,
bool suppressCast) {
// If it is known to have swift metadata, just load.
// If it is known to have swift metadata, just load. A swift class is both
// heap metadata and type metadata.
if (hasKnownSwiftMetadata(IGF.IGM, objectType.getSwiftRValueType())) {
return emitLoadOfHeapMetadataRef(IGF, object,
getIsaEncodingForType(IGF.IGM, objectType.getSwiftRValueType()),
suppressCast);
}
// Okay, ask the runtime for the type metadata of this
// potentially-ObjC object.
return emitDynamicTypeOfOpaqueHeapObject(IGF, object);
return emitDynamicTypeOfOpaqueHeapObject(IGF, object, repr);
}
static ClassDecl *getRootClass(ClassDecl *theClass) {

View File

@@ -143,12 +143,14 @@ emitAllocateExistentialBoxInBuffer(IRGenFunction &IGF, SILType boxedType,
/// Given an opaque class instance pointer, produce the type
/// metadata reference as a %type*.
llvm::Value *emitDynamicTypeOfOpaqueHeapObject(IRGenFunction &IGF,
llvm::Value *object);
llvm::Value *object,
MetatypeRepresentation rep);
/// Given a heap-object instance, with some heap-object type,
/// produce a reference to its type metadata.
llvm::Value *emitDynamicTypeOfHeapObject(IRGenFunction &IGF,
llvm::Value *object,
MetatypeRepresentation rep,
SILType objectType,
bool suppressCast = false);

View File

@@ -627,7 +627,9 @@ bindParameterSource(SILParameterInfo param, unsigned paramIndex,
llvm::Value *instanceRef = getParameter(paramIndex);
SILType instanceType = SILType::getPrimitiveObjectType(paramType);
llvm::Value *metadata =
emitDynamicTypeOfHeapObject(IGF, instanceRef, instanceType);
emitDynamicTypeOfHeapObject(IGF, instanceRef,
MetatypeRepresentation::Thick,
instanceType);
IGF.bindLocalTypeDataFromTypeMetadata(paramType, IsInexact, metadata,
MetadataState::Complete);
return;
@@ -686,7 +688,9 @@ void BindPolymorphicParameter::emit(Explosion &nativeParam, unsigned paramIndex)
llvm::Value *instanceRef = nativeParam.getAll()[0];
SILType instanceType = SILType::getPrimitiveObjectType(paramType);
llvm::Value *metadata =
emitDynamicTypeOfHeapObject(IGF, instanceRef, instanceType);
emitDynamicTypeOfHeapObject(IGF, instanceRef,
MetatypeRepresentation::Thick,
instanceType);
IGF.bindLocalTypeDataFromTypeMetadata(paramType, IsInexact, metadata,
MetadataState::Complete);
}

View File

@@ -1914,24 +1914,6 @@ static llvm::Value *getClassBaseValue(IRGenSILFunction &IGF,
return e.claimNext();
}
static llvm::Value *getClassMetatype(IRGenFunction &IGF,
llvm::Value *baseValue,
MetatypeRepresentation repr,
SILType instanceType) {
switch (repr) {
case MetatypeRepresentation::Thin:
llvm_unreachable("Class metatypes are never thin");
case MetatypeRepresentation::Thick:
return emitDynamicTypeOfHeapObject(IGF, baseValue, instanceType);
case MetatypeRepresentation::ObjC:
return emitHeapMetadataRefForHeapObject(IGF, baseValue, instanceType);
}
llvm_unreachable("Not a valid MetatypeRepresentation.");
}
void IRGenSILFunction::visitValueMetatypeInst(swift::ValueMetatypeInst *i) {
SILType instanceTy = i->getOperand()->getType();
auto metaTy = i->getType().castTo<MetatypeType>();
@@ -1945,12 +1927,12 @@ void IRGenSILFunction::visitValueMetatypeInst(swift::ValueMetatypeInst *i) {
Explosion e;
if (instanceTy.getClassOrBoundGenericClass()) {
e.add(getClassMetatype(*this,
e.add(emitDynamicTypeOfHeapObject(*this,
getClassBaseValue(*this, i->getOperand()),
metaTy->getRepresentation(), instanceTy));
} else if (auto arch = instanceTy.getAs<ArchetypeType>()) {
if (arch->requiresClass()) {
e.add(getClassMetatype(*this,
e.add(emitDynamicTypeOfHeapObject(*this,
getClassBaseValue(*this, i->getOperand()),
metaTy->getRepresentation(), instanceTy));
} else {

View File

@@ -96,6 +96,34 @@ static Class _swift_getObjCClassOfAllocated(const void *object) {
return class_const_cast(_swift_getClassOfAllocated(object));
}
/// \brief Fetch the ObjC class object associated with the formal dynamic
/// type of the given (possibly Objective-C) object. The formal
/// dynamic type ignores dynamic subclasses such as those introduced
/// by KVO.
///
/// The object pointer may be a tagged pointer, but cannot be null.
const ClassMetadata *swift::swift_getObjCClassFromObject(HeapObject *object) {
auto classAsMetadata = _swift_getClass(object);
// Walk up the superclass chain skipping over artifical Swift classes.
// If we find a non-Swift class use the result of [object class] instead.
while (classAsMetadata && classAsMetadata->isTypeMetadata()) {
if (!classAsMetadata->isArtificialSubclass())
return classAsMetadata;
classAsMetadata = classAsMetadata->Superclass;
}
id objcObject = reinterpret_cast<id>(object);
Class objcClass = [objcObject class];
if (objcObjectIsClass(objcObject)) {
// Original object is a class. We want a
// metaclass but +class doesn't give that to us.
objcClass = object_getClass(objcClass);
}
classAsMetadata = reinterpret_cast<const ClassMetadata *>(objcClass);
return classAsMetadata;
}
#endif
/// \brief Fetch the type metadata associated with the formal dynamic

View File

@@ -67,7 +67,7 @@ entry:
// CHECK-LABEL: define{{( protected)?}} swiftcc %objc_class* @existential_objc_metatype(%objc_object*) {{.*}} {
// CHECK: entry:
// CHECK-NEXT: [[METATYPE:%.*]] = call %objc_class* @object_getClass(%objc_object* %0) {{#[0-9]+}}
// CHECK-NEXT: [[METATYPE:%.*]] = call %objc_class* @swift_getObjCClassFromObject(%objc_object* %0) {{#[0-9]+}}
// CHECK-NEXT: ret %objc_class* [[METATYPE]]
// CHECK-NEXT: }
sil @existential_objc_metatype : $@convention(thin) (AnyObject) -> (@objc_metatype AnyObject.Type) {