[Runtime] Add ObjC support to isKnownUniquelyReferenced.

Add code to support detecting uniquely referenced Objective-C and Core
Foundation objects.

rdar://47902425
rdar://66805490
This commit is contained in:
Alastair Houghton
2021-07-08 17:33:02 +01:00
parent 92b7e135b8
commit abec55f432
10 changed files with 134 additions and 11 deletions

View File

@@ -748,6 +748,10 @@ public:
/// tag single payloads. /// tag single payloads.
AvailabilityContext getMultiPayloadEnumTagSinglePayload(); AvailabilityContext getMultiPayloadEnumTagSinglePayload();
/// Get the runtime availability of the Objective-C enabled
/// swift_isUniquelyReferenced functions.
AvailabilityContext getObjCIsUniquelyReferencedAvailability();
/// Get the runtime availability of features introduced in the Swift 5.2 /// Get the runtime availability of features introduced in the Swift 5.2
/// compiler for the target platform. /// compiler for the target platform.
AvailabilityContext getSwift52Availability(); AvailabilityContext getSwift52Availability();

View File

@@ -211,6 +211,18 @@ size_t swift_unownedRetainCount(HeapObject *object);
SWIFT_RUNTIME_EXPORT SWIFT_RUNTIME_EXPORT
size_t swift_weakRetainCount(HeapObject *object); size_t swift_weakRetainCount(HeapObject *object);
/// Is this pointer a non-null unique reference to an object?
SWIFT_RUNTIME_EXPORT
bool swift_isUniquelyReferenced(const void *);
/// Is this non-null pointer a unique reference to an object?
SWIFT_RUNTIME_EXPORT
bool swift_isUniquelyReferenced_nonNull(const void *);
/// Is this non-null BridgeObject a unique reference to an object?
SWIFT_RUNTIME_EXPORT
bool swift_isUniquelyReferenced_nonNull_bridgeObject(uintptr_t bits);
/// Is this pointer a non-null unique reference to an object /// Is this pointer a non-null unique reference to an object
/// that uses Swift reference counting? /// that uses Swift reference counting?
SWIFT_RUNTIME_EXPORT SWIFT_RUNTIME_EXPORT

View File

@@ -473,6 +473,30 @@ FUNCTION(IsUniquelyReferencedNonObjC_nonNull_bridgeObject,
ARGS(BridgeObjectPtrTy), ARGS(BridgeObjectPtrTy),
ATTRS(NoUnwind, ZExt)) ATTRS(NoUnwind, ZExt))
// bool swift_isUniquelyReferenced(const void *);
FUNCTION(IsUniquelyReferenced, swift_isUniquelyReferenced,
C_CC, ObjCIsUniquelyReferencedAvailability,
RETURNS(Int1Ty),
ARGS(UnknownRefCountedPtrTy),
ATTRS(NoUnwind, ZExt))
// bool swift_isUniquelyReferenced_nonNull(const void *);
FUNCTION(IsUniquelyReferenced_nonNull,
swift_isUniquelyReferenced_nonNull,
C_CC, ObjCIsUniquelyReferencedAvailability,
RETURNS(Int1Ty),
ARGS(UnknownRefCountedPtrTy),
ATTRS(NoUnwind, ZExt))
// bool swift_isUniquelyReferenced_nonNull_bridgeObject(
// uintptr_t bits);
FUNCTION(IsUniquelyReferenced_nonNull_bridgeObject,
swift_isUniquelyReferenced_nonNull_bridgeObject,
C_CC, ObjCIsUniquelyReferencedAvailability,
RETURNS(Int1Ty),
ARGS(BridgeObjectPtrTy),
ATTRS(NoUnwind, ZExt))
// bool swift_isUniquelyReferenced_native(const struct HeapObject *); // bool swift_isUniquelyReferenced_native(const struct HeapObject *);
FUNCTION(IsUniquelyReferenced_native, swift_isUniquelyReferenced_native, FUNCTION(IsUniquelyReferenced_native, swift_isUniquelyReferenced_native,
C_CC, AlwaysAvailable, C_CC, AlwaysAvailable,

View File

@@ -338,6 +338,10 @@ AvailabilityContext ASTContext::getMultiPayloadEnumTagSinglePayload() {
return getSwift56Availability(); return getSwift56Availability();
} }
AvailabilityContext ASTContext::getObjCIsUniquelyReferencedAvailability() {
return getSwift56Availability();
}
AvailabilityContext ASTContext::getSwift52Availability() { AvailabilityContext ASTContext::getSwift52Availability() {
auto target = LangOpts.Target; auto target = LangOpts.Target;

View File

@@ -1343,21 +1343,34 @@ llvm::Value *IRGenFunction::emitLoadRefcountedPtr(Address addr,
llvm::Value *IRGenFunction:: llvm::Value *IRGenFunction::
emitIsUniqueCall(llvm::Value *value, SourceLoc loc, bool isNonNull) { emitIsUniqueCall(llvm::Value *value, SourceLoc loc, bool isNonNull) {
llvm::Constant *fn; llvm::Constant *fn;
bool nonObjC = !IGM.getAvailabilityContext().isContainedIn(
IGM.Context.getObjCIsUniquelyReferencedAvailability());
if (value->getType() == IGM.RefCountedPtrTy) { if (value->getType() == IGM.RefCountedPtrTy) {
if (isNonNull) if (isNonNull)
fn = IGM.getIsUniquelyReferenced_nonNull_nativeFn(); fn = IGM.getIsUniquelyReferenced_nonNull_nativeFn();
else else
fn = IGM.getIsUniquelyReferenced_nativeFn(); fn = IGM.getIsUniquelyReferenced_nativeFn();
} else if (value->getType() == IGM.UnknownRefCountedPtrTy) { } else if (value->getType() == IGM.UnknownRefCountedPtrTy) {
if (isNonNull) if (nonObjC) {
fn = IGM.getIsUniquelyReferencedNonObjC_nonNullFn(); if (isNonNull)
else fn = IGM.getIsUniquelyReferencedNonObjC_nonNullFn();
fn = IGM.getIsUniquelyReferencedNonObjCFn(); else
fn = IGM.getIsUniquelyReferencedNonObjCFn();
} else {
if (isNonNull)
fn = IGM.getIsUniquelyReferenced_nonNullFn();
else
fn = IGM.getIsUniquelyReferencedFn();
}
} else if (value->getType() == IGM.BridgeObjectPtrTy) { } else if (value->getType() == IGM.BridgeObjectPtrTy) {
if (!isNonNull) if (!isNonNull)
unimplemented(loc, "optional bridge ref"); unimplemented(loc, "optional bridge ref");
fn = IGM.getIsUniquelyReferencedNonObjC_nonNull_bridgeObjectFn(); if (nonObjC)
fn = IGM.getIsUniquelyReferencedNonObjC_nonNull_bridgeObjectFn();
else
fn = IGM.getIsUniquelyReferenced_nonNull_bridgeObjectFn();
} else { } else {
llvm_unreachable("Unexpected LLVM type for a refcounted pointer."); llvm_unreachable("Unexpected LLVM type for a refcounted pointer.");
} }

View File

@@ -802,6 +802,17 @@ namespace RuntimeConstants {
} }
return RuntimeAvailability::AlwaysAvailable; return RuntimeAvailability::AlwaysAvailable;
} }
RuntimeAvailability
ObjCIsUniquelyReferencedAvailability(ASTContext &context) {
auto featureAvailability =
context.getObjCIsUniquelyReferencedAvailability();
if (!isDeploymentAvailabilityContainedIn(context, featureAvailability)) {
return RuntimeAvailability::ConditionallyAvailable;
}
return RuntimeAvailability::AlwaysAvailable;
}
} // namespace RuntimeConstants } // namespace RuntimeConstants
// We don't use enough attributes to justify generalizing the // We don't use enough attributes to justify generalizing the

View File

@@ -108,7 +108,7 @@ extension _ArrayBuffer {
if !_isClassOrObjCExistential(Element.self) { if !_isClassOrObjCExistential(Element.self) {
return _storage.isUniquelyReferencedUnflaggedNative() return _storage.isUniquelyReferencedUnflaggedNative()
} }
return _storage.isUniquelyReferencedNative() && _isNative return _storage.isUniquelyReferencedNative()
} }
/// Returns `true` and puts the buffer in a mutable state iff the buffer's /// Returns `true` and puts the buffer in a mutable state iff the buffer's

View File

@@ -72,7 +72,7 @@ internal struct _BridgeStorage<NativeClass: AnyObject> {
@inlinable @inlinable
@inline(__always) @inline(__always)
internal mutating func isUniquelyReferencedNative() -> Bool { internal mutating func isUniquelyReferencedNative() -> Bool {
return _isUnique(&rawValue) return isNative && _isUnique(&rawValue)
} }
@_alwaysEmitIntoClient @_alwaysEmitIntoClient

View File

@@ -76,7 +76,6 @@ const ClassMetadata *swift::_swift_getClass(const void *object) {
} }
#if SWIFT_OBJC_INTEROP #if SWIFT_OBJC_INTEROP
/// Replacement for ObjC object_isClass(), which is unavailable on /// Replacement for ObjC object_isClass(), which is unavailable on
/// deployment targets macOS 10.9 and iOS 7. /// deployment targets macOS 10.9 and iOS 7.
static bool objcObjectIsClass(id object) { static bool objcObjectIsClass(id object) {
@@ -1352,6 +1351,36 @@ bool swift::swift_isUniquelyReferencedNonObjC_nonNull(const void* object) {
swift_isUniquelyReferenced_nonNull_native((const HeapObject*)object); swift_isUniquelyReferenced_nonNull_native((const HeapObject*)object);
} }
#if SWIFT_OBJC_INTEROP
// It would be nice to weak link instead of doing this, but we can't do that
// until the new API is in the versions of libobjc that we're linking against.
static bool isUniquelyReferenced(id object) {
#if OBJC_ISUNIQUELYREFERENCED_DEFINED
return objc_isUniquelyReferenced(object);
#else
auto objcIsUniquelyRefd = SWIFT_LAZY_CONSTANT(reinterpret_cast<bool (*)(id)>(
dlsym(RTLD_NEXT, "objc_isUniquelyReferenced")));
return objcIsUniquelyRefd && objcIsUniquelyRefd(object);
#endif /* OBJC_ISUNIQUELYREFERENCED_DEFINED */
}
#endif
bool swift::swift_isUniquelyReferenced_nonNull(const void *object) {
assert(object != nullptr);
#if SWIFT_OBJC_INTEROP
if (isObjCTaggedPointer(object))
return false;
if (!usesNativeSwiftReferenceCounting_nonNull(object)) {
return isUniquelyReferenced(id_const_cast(object));
}
#endif
return swift_isUniquelyReferenced_nonNull_native(
static_cast<const HeapObject *>(object));
}
// Given an object reference, return true iff it is non-nil and refers // Given an object reference, return true iff it is non-nil and refers
// to a native swift object with strong reference count of 1. // to a native swift object with strong reference count of 1.
bool swift::swift_isUniquelyReferencedNonObjC( bool swift::swift_isUniquelyReferencedNonObjC(
@@ -1361,6 +1390,12 @@ bool swift::swift_isUniquelyReferencedNonObjC(
&& swift_isUniquelyReferencedNonObjC_nonNull(object); && swift_isUniquelyReferencedNonObjC_nonNull(object);
} }
// Given an object reference, return true if it is non-nil and refers
// to an ObjC or native swift object with a strong reference count of 1.
bool swift::swift_isUniquelyReferenced(const void *object) {
return object != nullptr && swift_isUniquelyReferenced_nonNull(object);
}
/// Return true if the given bits of a Builtin.BridgeObject refer to a /// Return true if the given bits of a Builtin.BridgeObject refer to a
/// native swift object whose strong reference count is 1. /// native swift object whose strong reference count is 1.
bool swift::swift_isUniquelyReferencedNonObjC_nonNull_bridgeObject( bool swift::swift_isUniquelyReferencedNonObjC_nonNull_bridgeObject(
@@ -1386,6 +1421,26 @@ bool swift::swift_isUniquelyReferencedNonObjC_nonNull_bridgeObject(
#endif #endif
} }
/// Return true if the given bits of a Builtin.BridgeObject refer to
/// an object whose strong reference count is 1.
bool swift::swift_isUniquelyReferenced_nonNull_bridgeObject(uintptr_t bits) {
auto bridgeObject = reinterpret_cast<void *>(bits);
if (isObjCTaggedPointer(bridgeObject))
return false;
const auto object = toPlainObject_unTagged_bridgeObject(bridgeObject);
#if SWIFT_OBJC_INTEROP
return !isNonNative_unTagged_bridgeObject(bridgeObject)
? swift_isUniquelyReferenced_nonNull_native(
(const HeapObject *)object)
: swift_isUniquelyReferenced_nonNull(object);
#else
return swift_isUniquelyReferenced_nonNull_native((const HeapObject *)object);
#endif
}
// Given a non-@objc object reference, return true iff the // Given a non-@objc object reference, return true iff the
// object is non-nil and has a strong reference count greather than 1 // object is non-nil and has a strong reference count greather than 1
bool swift::swift_isEscapingClosureAtFileLocation(const HeapObject *object, bool swift::swift_isEscapingClosureAtFileLocation(const HeapObject *object,

View File

@@ -662,7 +662,7 @@ func acceptsAnyObject(_ ref: inout Builtin.AnyObject?) {}
// CHECK-NEXT: [[ADDR:%.+]] = getelementptr inbounds [[OPTIONAL_ANYOBJECT_TY]], [[OPTIONAL_ANYOBJECT_TY]]* %0, i32 0, i32 0 // CHECK-NEXT: [[ADDR:%.+]] = getelementptr inbounds [[OPTIONAL_ANYOBJECT_TY]], [[OPTIONAL_ANYOBJECT_TY]]* %0, i32 0, i32 0
// CHECK-NEXT: [[CASTED:%.+]] = bitcast {{.+}}* [[ADDR]] to [[UNKNOWN_OBJECT:%objc_object|%swift\.refcounted]]** // CHECK-NEXT: [[CASTED:%.+]] = bitcast {{.+}}* [[ADDR]] to [[UNKNOWN_OBJECT:%objc_object|%swift\.refcounted]]**
// CHECK-NEXT: [[REF:%.+]] = load [[UNKNOWN_OBJECT]]*, [[UNKNOWN_OBJECT]]** [[CASTED]] // CHECK-NEXT: [[REF:%.+]] = load [[UNKNOWN_OBJECT]]*, [[UNKNOWN_OBJECT]]** [[CASTED]]
// CHECK-objc-NEXT: [[RESULT:%.+]] = call i1 @swift_isUniquelyReferencedNonObjC([[UNKNOWN_OBJECT]]* [[REF]]) // CHECK-objc-NEXT: [[RESULT:%.+]] = call i1 @swift_isUniquelyReferenced{{(NonObjC)?}}([[UNKNOWN_OBJECT]]* [[REF]])
// CHECK-native-NEXT: [[RESULT:%.+]] = call i1 @swift_isUniquelyReferenced_native([[UNKNOWN_OBJECT]]* [[REF]]) // CHECK-native-NEXT: [[RESULT:%.+]] = call i1 @swift_isUniquelyReferenced_native([[UNKNOWN_OBJECT]]* [[REF]])
// CHECK-NEXT: ret i1 [[RESULT]] // CHECK-NEXT: ret i1 [[RESULT]]
func isUnique(_ ref: inout Builtin.AnyObject?) -> Bool { func isUnique(_ ref: inout Builtin.AnyObject?) -> Bool {
@@ -675,7 +675,7 @@ func isUnique(_ ref: inout Builtin.AnyObject?) -> Bool {
// CHECK-NEXT: entry: // CHECK-NEXT: entry:
// CHECK-NEXT: [[ADDR:%.+]] = getelementptr inbounds %AnyObject, %AnyObject* %0, i32 0, i32 0 // CHECK-NEXT: [[ADDR:%.+]] = getelementptr inbounds %AnyObject, %AnyObject* %0, i32 0, i32 0
// CHECK-NEXT: [[REF:%.+]] = load [[UNKNOWN_OBJECT]]*, [[UNKNOWN_OBJECT]]** [[ADDR]] // CHECK-NEXT: [[REF:%.+]] = load [[UNKNOWN_OBJECT]]*, [[UNKNOWN_OBJECT]]** [[ADDR]]
// CHECK-objc-NEXT: [[RESULT:%.+]] = call i1 @swift_isUniquelyReferencedNonObjC_nonNull([[UNKNOWN_OBJECT]]* [[REF]]) // CHECK-objc-NEXT: [[RESULT:%.+]] = call i1 @swift_isUniquelyReferenced{{(NonObjC)?}}_nonNull([[UNKNOWN_OBJECT]]* [[REF]])
// CHECK-native-NEXT: [[RESULT:%.+]] = call i1 @swift_isUniquelyReferenced_nonNull_native([[UNKNOWN_OBJECT]]* [[REF]]) // CHECK-native-NEXT: [[RESULT:%.+]] = call i1 @swift_isUniquelyReferenced_nonNull_native([[UNKNOWN_OBJECT]]* [[REF]])
// CHECK-NEXT: ret i1 [[RESULT]] // CHECK-NEXT: ret i1 [[RESULT]]
func isUnique(_ ref: inout Builtin.AnyObject) -> Bool { func isUnique(_ ref: inout Builtin.AnyObject) -> Bool {
@@ -686,7 +686,7 @@ func isUnique(_ ref: inout Builtin.AnyObject) -> Bool {
// CHECK-LABEL: define hidden {{.*}}i1 @"$s8builtins8isUniqueyBi1_BbzF"(%swift.bridge** nocapture dereferenceable({{.*}}) %0) {{.*}} { // CHECK-LABEL: define hidden {{.*}}i1 @"$s8builtins8isUniqueyBi1_BbzF"(%swift.bridge** nocapture dereferenceable({{.*}}) %0) {{.*}} {
// CHECK-NEXT: entry: // CHECK-NEXT: entry:
// CHECK-NEXT: load %swift.bridge*, %swift.bridge** %0 // CHECK-NEXT: load %swift.bridge*, %swift.bridge** %0
// CHECK-NEXT: call i1 @swift_isUniquelyReferencedNonObjC_nonNull_bridgeObject(%swift.bridge* %1) // CHECK-NEXT: call i1 @swift_isUniquelyReferenced{{(NonObjC)?}}_nonNull_bridgeObject(%swift.bridge* %1)
// CHECK-NEXT: ret i1 %2 // CHECK-NEXT: ret i1 %2
func isUnique(_ ref: inout Builtin.BridgeObject) -> Bool { func isUnique(_ ref: inout Builtin.BridgeObject) -> Bool {
return Builtin.isUnique(&ref) return Builtin.isUnique(&ref)