Skip to content

Commit 542a8b4

Browse files
committed
Fast-path dynamicCast from a class instance in an existential container to AnyObject
rdar://26981164
1 parent 905e7ba commit 542a8b4

File tree

1 file changed

+49
-11
lines changed

1 file changed

+49
-11
lines changed

stdlib/public/runtime/Casting.cpp

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -663,12 +663,25 @@ static void _maybeDeallocateOpaqueExistential(OpaqueValue *srcExistential,
663663
}
664664
}
665665

666+
static bool
667+
isAnyObjectExistentialType(const ExistentialTypeMetadata *targetType) {
668+
unsigned numProtos = targetType->Protocols.NumProtocols;
669+
if (numProtos != 1)
670+
return false;
671+
const ProtocolDescriptor *protocol = targetType->Protocols[0];
672+
// Assert that AnyObject does not need any witness tables. We rely on this.
673+
assert(!protocol->Flags.needsWitnessTable() &&
674+
"AnyObject should not require witness tables");
675+
return (protocol->Flags.getSpecialProtocol() == SpecialProtocol::AnyObject);
676+
}
677+
666678
/// Given a possibly-existential value, find its dynamic type and the
667679
/// address of its storage.
668-
static void findDynamicValueAndType(OpaqueValue *value, const Metadata *type,
669-
OpaqueValue *&outValue,
670-
const Metadata *&outType,
671-
bool &inoutCanTake) {
680+
static void
681+
findDynamicValueAndType(OpaqueValue *value, const Metadata *type,
682+
OpaqueValue *&outValue, const Metadata *&outType,
683+
bool &inoutCanTake,
684+
bool isTargetTypeAnyObject) {
672685
switch (type->getKind()) {
673686
case MetadataKind::Class:
674687
case MetadataKind::ObjCClassWrapper:
@@ -696,12 +709,23 @@ static void findDynamicValueAndType(OpaqueValue *value, const Metadata *type,
696709

697710
case ExistentialTypeRepresentation::Opaque:
698711
case ExistentialTypeRepresentation::ErrorProtocol: {
712+
const Metadata *innerType = existentialType->getDynamicType(value);
713+
714+
// Short cut class in existential as AnyObject casts.
715+
if (isTargetTypeAnyObject &&
716+
innerType->getKind() == MetadataKind::Class) {
717+
// inline value buffer storage.
718+
outValue = value;
719+
outType = 0;
720+
inoutCanTake= true;
721+
return;
722+
}
699723
OpaqueValue *innerValue
700724
= existentialType->projectValue(value);
701-
const Metadata *innerType = existentialType->getDynamicType(value);
702725
inoutCanTake &= existentialType->mayTakeValue(value);
726+
703727
return findDynamicValueAndType(innerValue, innerType,
704-
outValue, outType, inoutCanTake);
728+
outValue, outType, inoutCanTake, false);
705729
}
706730
}
707731
}
@@ -736,7 +760,7 @@ swift::swift_getDynamicType(OpaqueValue *value, const Metadata *self) {
736760
OpaqueValue *outValue;
737761
const Metadata *outType;
738762
bool canTake = false;
739-
findDynamicValueAndType(value, self, outValue, outType, canTake);
763+
findDynamicValueAndType(value, self, outValue, outType, canTake, false);
740764
return outType;
741765
}
742766

@@ -876,8 +900,17 @@ static bool _dynamicCastToExistential(OpaqueValue *dest,
876900
OpaqueValue *srcDynamicValue;
877901
const Metadata *srcDynamicType;
878902
bool canTake = true;
903+
904+
// Are we casting to AnyObject? In this case we can fast-path casts from
905+
// classes.
906+
bool isTargetTypeAnyObject =
907+
targetType->getKind() == MetadataKind::Existential &&
908+
isAnyObjectExistentialType(targetType);
909+
910+
// We don't care what the target type of a cast from class to AnyObject is.
911+
// srcDynamicType will be set to a nullptr in this case to save a lookup.
879912
findDynamicValueAndType(src, srcType, srcDynamicValue, srcDynamicType,
880-
canTake);
913+
canTake, isTargetTypeAnyObject);
881914

882915
auto maybeDeallocateSourceAfterSuccess = [&] {
883916
if (shouldDeallocateSource(/*succeeded*/ true, flags)) {
@@ -897,10 +930,12 @@ static bool _dynamicCastToExistential(OpaqueValue *dest,
897930
case ExistentialTypeRepresentation::Class: {
898931
auto destExistential =
899932
reinterpret_cast<ClassExistentialContainer*>(dest);
933+
MetadataKind kind =
934+
srcDynamicType ? srcDynamicType->getKind() : MetadataKind::Class;
900935

901936
// If the source type is a value type, it cannot possibly conform
902937
// to a class-bounded protocol.
903-
switch (srcDynamicType->getKind()) {
938+
switch (kind) {
904939
case MetadataKind::ExistentialMetatype:
905940
case MetadataKind::Metatype: {
906941
#if SWIFT_OBJC_INTEROP
@@ -965,8 +1000,11 @@ static bool _dynamicCastToExistential(OpaqueValue *dest,
9651000
return _fail(src, srcType, targetType, flags);
9661001
}
9671002

968-
// Check for protocol conformances and fill in the witness tables.
969-
if (!_conformsToProtocols(srcDynamicValue, srcDynamicType,
1003+
// Check for protocol conformances and fill in the witness tables. If
1004+
// srcDynamicType equals nullptr we have a cast from an existential
1005+
// container with a class instance to AnyObject. In this case no check is
1006+
// necessary.
1007+
if (srcDynamicType && !_conformsToProtocols(srcDynamicValue, srcDynamicType,
9701008
targetType->Protocols,
9711009
destExistential->getWitnessTables())) {
9721010
return _fail(src, srcType, targetType, flags, srcDynamicType);

0 commit comments

Comments
 (0)