[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.
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
/// compiler for the target platform.
AvailabilityContext getSwift52Availability();

View File

@@ -211,6 +211,18 @@ size_t swift_unownedRetainCount(HeapObject *object);
SWIFT_RUNTIME_EXPORT
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
/// that uses Swift reference counting?
SWIFT_RUNTIME_EXPORT

View File

@@ -473,6 +473,30 @@ FUNCTION(IsUniquelyReferencedNonObjC_nonNull_bridgeObject,
ARGS(BridgeObjectPtrTy),
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 *);
FUNCTION(IsUniquelyReferenced_native, swift_isUniquelyReferenced_native,
C_CC, AlwaysAvailable,

View File

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

View File

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

View File

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

View File

@@ -108,7 +108,7 @@ extension _ArrayBuffer {
if !_isClassOrObjCExistential(Element.self) {
return _storage.isUniquelyReferencedUnflaggedNative()
}
return _storage.isUniquelyReferencedNative() && _isNative
return _storage.isUniquelyReferencedNative()
}
/// 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
@inline(__always)
internal mutating func isUniquelyReferencedNative() -> Bool {
return _isUnique(&rawValue)
return isNative && _isUnique(&rawValue)
}
@_alwaysEmitIntoClient

View File

@@ -76,7 +76,6 @@ const ClassMetadata *swift::_swift_getClass(const void *object) {
}
#if SWIFT_OBJC_INTEROP
/// Replacement for ObjC object_isClass(), which is unavailable on
/// deployment targets macOS 10.9 and iOS 7.
static bool objcObjectIsClass(id object) {
@@ -1352,6 +1351,36 @@ bool swift::swift_isUniquelyReferencedNonObjC_nonNull(const void* 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
// to a native swift object with strong reference count of 1.
bool swift::swift_isUniquelyReferencedNonObjC(
@@ -1361,6 +1390,12 @@ bool swift::swift_isUniquelyReferencedNonObjC(
&& 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
/// native swift object whose strong reference count is 1.
bool swift::swift_isUniquelyReferencedNonObjC_nonNull_bridgeObject(
@@ -1386,6 +1421,26 @@ bool swift::swift_isUniquelyReferencedNonObjC_nonNull_bridgeObject(
#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
// object is non-nil and has a strong reference count greather than 1
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: [[CASTED:%.+]] = bitcast {{.+}}* [[ADDR]] to [[UNKNOWN_OBJECT:%objc_object|%swift\.refcounted]]**
// 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-NEXT: ret i1 [[RESULT]]
func isUnique(_ ref: inout Builtin.AnyObject?) -> Bool {
@@ -675,7 +675,7 @@ func isUnique(_ ref: inout Builtin.AnyObject?) -> Bool {
// CHECK-NEXT: entry:
// CHECK-NEXT: [[ADDR:%.+]] = getelementptr inbounds %AnyObject, %AnyObject* %0, i32 0, i32 0
// 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-NEXT: ret i1 [[RESULT]]
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-NEXT: entry:
// 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
func isUnique(_ ref: inout Builtin.BridgeObject) -> Bool {
return Builtin.isUnique(&ref)