Skip to content

Commit 74e27aa

Browse files
author
Dave Abrahams
committed
[stdlib] uniqueness checking for users
Make unique reference checking available to users, making ManagedBuffer a complete facility for building COW value types. Also rationalize the way we name and organize the runtime primitives we ultimately call. Swift SVN r22594
1 parent 9d43ae4 commit 74e27aa

11 files changed

+173
-60
lines changed

lib/SILAnalysis/ARCAnalysis.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ using namespace swift::arc;
3131
static bool isKnownToNotDecrementRefCount(FunctionRefInst *FRI) {
3232
return llvm::StringSwitch<bool>(FRI->getReferencedFunction()->getName())
3333
.Case("swift_keepAlive", true)
34-
.Case("_swift_isUniquelyReferenced", true)
34+
.StartsWith("_swift_isUniquelyReferenced", true)
3535
.Default(false);
3636
}
3737

@@ -146,8 +146,8 @@ bool swift::arc::canDecrementRefCount(SILInstruction *User,
146146
bool swift::arc::canCheckRefCount(SILInstruction *User) {
147147
if (auto *AI = dyn_cast<ApplyInst>(User))
148148
if (auto *FRI = dyn_cast<FunctionRefInst>(AI->getCallee()))
149-
return FRI->getReferencedFunction()->getName() ==
150-
"_swift_isUniquelyReferenced";
149+
return FRI->getReferencedFunction()->getName().startswith(
150+
"_swift_isUniquelyReferenced");
151151
return false;
152152
}
153153

stdlib/core/HeapBuffer.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ public class HeapBufferStorage<Value,Element> {
7070
// we accept everything; unsafeBitCast will at least catch
7171
// inappropriately-sized things at runtime.
7272
public func _isUniquelyReferenced<T>(inout x: T) -> Bool {
73-
return _swift_isUniquelyReferenced(unsafeBitCast(x, UWord.self)) != 0
73+
return _swift_isUniquelyReferenced_native_spareBits(
74+
unsafeBitCast(x, UWord.self)) != 0
7475
}
7576

7677
/// Management API for `HeapBufferStorage<Value, Element>`

stdlib/core/ManagedBuffer.swift

+77-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@
1212

1313
import SwiftShims
1414

15+
/// A common base class for classes that need to be non-\ `@objc`,
16+
/// recognizably in the type system.
17+
///
18+
/// See `isUniquelyReferenced`
19+
public class NonObjectiveCBase {}
20+
1521
/// A base class of `ManagedBuffer<Value,Element>`, used during
1622
/// instance creation.
1723
///
@@ -22,7 +28,7 @@ import SwiftShims
2228
/// `ManagedProtoBuffer` does not offer
2329
/// access to the as-yet uninitialized `value` property of
2430
/// `ManagedBuffer`,
25-
public class ManagedProtoBuffer<Value, Element> {
31+
public class ManagedProtoBuffer<Value, Element> : NonObjectiveCBase {
2632
/// The actual number of elements that can be stored in this object.
2733
///
2834
/// This value may be nontrivial to compute; it is usually a good
@@ -172,3 +178,73 @@ public class ManagedBuffer<Value, Element>
172178
}
173179
}
174180
}
181+
182+
// FIXME: when our calling convention changes to pass self at +0,
183+
// inout should be dropped from the arguments to these functions.
184+
185+
/// Returns `true` iff `object` is a non-\ `@objc` class with a single
186+
/// strong reference.
187+
///
188+
/// * Does *not* modify `object`; the use of `inout` is an
189+
/// implementation artifact.
190+
/// * If `object` is an Objective-C class instance, returns `false`.
191+
/// * Weak references do not affect the result of this function.
192+
///
193+
/// Useful for implementing the copy-on-write optimization for the
194+
/// deep storage of value types::
195+
///
196+
/// mutating func modifyMe(arg: X) {
197+
/// if isUniquelyReferencedNonObjC(&myStorage) {
198+
/// myStorage.modifyInPlace(arg)
199+
/// }
200+
/// else {
201+
/// myStorage = self.createModified(myStorage, arg)
202+
/// }
203+
/// }
204+
///
205+
/// This function is safe to use for `mutating` functions in
206+
/// multithreaded code because a false positive would imply that there
207+
/// is already a user-level data race on the value being mutated.
208+
public func isUniquelyReferencedNonObjC<T: AnyObject>(inout object: T) -> Bool {
209+
210+
// Note: the pointer must be extracted in a separate step or an
211+
// extra reference will be held during the check below
212+
let o = UnsafePointer<Void>(Builtin.bridgeToRawPointer(object))
213+
let result = _swift_isUniquelyReferencedNonObjC_nonNull(o)
214+
Builtin.fixLifetime(object)
215+
return result != 0
216+
}
217+
218+
/// Returns `true` iff `object` is a non-\ `@objc` class with a single
219+
/// strong reference.
220+
///
221+
/// * Does *not* modify `object`; the use of `inout` is an
222+
/// implementation artifact.
223+
/// * Weak references do not affect the result of this function.
224+
///
225+
/// Useful for implementing the copy-on-write optimization for the
226+
/// deep storage of value types::
227+
///
228+
/// mutating func modifyMe(arg: X) {
229+
/// if isUniquelyReferenced(&myStorage) {
230+
/// myStorage.modifyInPlace(arg)
231+
/// }
232+
/// else {
233+
/// myStorage = myStorage.createModified(arg)
234+
/// }
235+
/// }
236+
///
237+
/// This function is safe to use for `mutating` functions in
238+
/// multithreaded code because a false positive would imply that there
239+
/// is already a user-level data race on the value being mutated.
240+
public func isUniquelyReferenced<T: NonObjectiveCBase>(
241+
inout object: T
242+
) -> Bool {
243+
// Note: the pointer must be extracted in a separate step or an
244+
// extra reference will be held during the check below
245+
let o = UnsafePointer<HeapObject>(Builtin.bridgeToRawPointer(object))
246+
let result = _swift_isUniquelyReferenced_nonNull_native(o)
247+
Builtin.fixLifetime(object)
248+
return result != 0
249+
}
250+

stdlib/runtime/HeapObject.cpp

-21
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,6 @@ void swift::swift_deallocObject(HeapObject *object, size_t allocatedSize,
446446
extern "C" void swift_fixLifetime(OpaqueValue* value) {
447447
}
448448

449-
450449
void swift::swift_weakInit(WeakReference *ref, HeapObject *value) {
451450
ref->Value = value;
452451
swift_weakRetain(value);
@@ -523,23 +522,3 @@ void swift::_swift_abortRetainUnowned(const void *object) {
523522
(void)object;
524523
swift::crash("attempted to retain deallocated object");
525524
}
526-
527-
//===----------------------------------------------------------------------===//
528-
// FIXME: this should return bool but it chokes the compiler
529-
// <rdar://problem/18573806>
530-
//===----------------------------------------------------------------------===//
531-
/// Given the bits of a Native swift object reference, or of a
532-
/// word-sized Swift enum containing a Native swift object reference as
533-
/// a payload, return true iff the object's strong reference count is
534-
/// 1.
535-
unsigned char swift::_swift_isUniquelyReferenced(std::uintptr_t bits) {
536-
const auto object = reinterpret_cast<HeapObject*>(
537-
bits & ~heap_object_abi::SwiftSpareBitsMask);
538-
539-
// Sometimes we have a NULL "owner" object, e.g. because the data
540-
// being referenced (usually via UnsafeMutablePointer<T>) has infinite
541-
// lifetime, or lifetime managed outside the Swift object system.
542-
// In these cases we have to assume the data is shared among
543-
// multiple references, and needs to be copied before modification.
544-
return object != nullptr && object->refCount < 2 * RC_INTERVAL;
545-
}

stdlib/runtime/SwiftObject.mm

+40-3
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,9 @@ static void doWeakDestroy(WeakReference *addr, bool valueIsNative) {
683683

684684
// Given a non-nil object reference, return true iff the object uses
685685
// native swift reference counting.
686-
bool swift::_swift_usesNativeSwiftReferenceCounting_nonNull(id object) {
686+
unsigned char swift::_swift_usesNativeSwiftReferenceCounting_nonNull(
687+
const void* object
688+
) {
687689
assert(object != nullptr);
688690
#if SWIFT_OBJC_INTEROP
689691
return !isObjCTaggedPointer(object) &&
@@ -693,13 +695,48 @@ static void doWeakDestroy(WeakReference *addr, bool valueIsNative) {
693695
#endif
694696
}
695697

698+
// Given a non-nil non-@objc object reference, return true iff the
699+
// object has a strong reference count of 1.
700+
unsigned char swift::_swift_isUniquelyReferenced_nonNull_native(
701+
const HeapObject* object
702+
) {
703+
assert(object != nullptr);
704+
return object->refCount < 2 * RC_INTERVAL;
705+
}
706+
696707
// Given a non-nil object reference, return true iff the object is a
697708
// native swift object with strong reference count of 1.
698-
bool swift::_swift_isUniquelyReferencedNative_nonNull(id object) {
709+
unsigned char swift::_swift_isUniquelyReferencedNonObjC_nonNull(
710+
const void* object
711+
) {
699712
assert(object != nullptr);
700713
return
701714
#if SWIFT_OBJC_INTEROP
702715
swift::_swift_usesNativeSwiftReferenceCounting_nonNull(object) &&
703716
#endif
704-
((const HeapObject*)object)->refCount < 2 * RC_INTERVAL;
717+
_swift_isUniquelyReferenced_nonNull_native((HeapObject*)object);
718+
}
719+
720+
//===----------------------------------------------------------------------===//
721+
// FIXME: this should return bool but it chokes the compiler
722+
// <rdar://problem/18573806>
723+
//===----------------------------------------------------------------------===//
724+
/// Given the bits of a possibly-nil Native swift object reference, or of a
725+
/// word-sized Swift enum containing a Native swift object reference as
726+
/// a payload, return true iff the object's strong reference count is
727+
/// 1.
728+
unsigned char swift::_swift_isUniquelyReferenced_native_spareBits(
729+
std::uintptr_t bits
730+
) {
731+
const auto object = reinterpret_cast<HeapObject*>(
732+
bits & ~heap_object_abi::SwiftSpareBitsMask);
733+
734+
// Sometimes we have a NULL "owner" object, e.g. because the data
735+
// being referenced (usually via UnsafeMutablePointer<T>) has infinite
736+
// lifetime, or lifetime managed outside the Swift object system.
737+
// In these cases we have to assume the data is shared among
738+
// multiple references, and needs to be copied before modification.
739+
return object != nullptr && object->refCount < 2 * RC_INTERVAL;
705740
}
741+
742+

stdlib/shims/RuntimeShims.h

+5-7
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,17 @@
1818
#define SWIFT_STDLIB_SHIMS_RUNTIMESHIMS_H_
1919

2020
#include <stddef.h>
21-
#include <stdbool.h>
22-
23-
typedef struct objc_object *id;
2421

2522
#ifdef __cplusplus
2623
namespace swift { extern "C" {
2724
#endif
2825

29-
bool _swift_isUniquelyReferencedNative_nonNull(id);
30-
bool _swift_usesNativeSwiftReferenceCounting_nonNull(id);
31-
unsigned char _swift_isUniquelyReferenced(uintptr_t bits);
26+
unsigned char _swift_isUniquelyReferencedNonObjC_nonNull(const void*);
27+
unsigned char _swift_usesNativeSwiftReferenceCounting_nonNull(const void*);
28+
unsigned char _swift_isUniquelyReferenced_native_spareBits(uintptr_t bits);
29+
unsigned char _swift_isUniquelyReferenced_nonNull_native(
30+
const struct HeapObject*);
3231

33-
3432
#ifdef __cplusplus
3533
}} // extern "C", namespace swift
3634
#endif

test/Prototypes/BridgeObject.swift.gyb

+13-10
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,8 @@ case Cocoa0(CocoaType)
9999
}
100100

101101
mutating func isUniquelyReferenced() -> Bool {
102-
let w = Builtin.ptrtoint_Word(Builtin.bridgeToRawPointer(native!))
103-
if let n = native.map({ Builtin.bridgeToRawPointer($0) }) {
104-
return _swift_isUniquelyReferenced(UWord(Builtin.ptrtoint_Word(n))) != 0
102+
if let raw = native.map({ Builtin.bridgeToRawPointer($0) }) {
103+
return _swift_isUniquelyReferenced_nonNull_native(UnsafePointer(raw)) != 0
105104
}
106105
return false
107106
}
@@ -127,25 +126,29 @@ struct BridgeObject<NativeType: AnyObject, CocoaType: AnyObject> : BridgeStorage
127126
}
128127

129128
mutating func isUniquelyReferenced() -> Bool {
130-
return _swift_isUniquelyReferencedNative_nonNull(object)
129+
return _swift_isUniquelyReferencedNonObjC_nonNull(
130+
UnsafePointer(rawObject)) != 0
131131
}
132132

133133
var native: Native? {
134-
return _swift_usesNativeSwiftReferenceCounting_nonNull(object)
135-
? .Some(Builtin.bridgeFromRawPointer(Builtin.bridgeToRawPointer(object))) : nil
134+
return _swift_usesNativeSwiftReferenceCounting_nonNull(
135+
UnsafePointer(rawObject)) != 0
136+
? .Some(Builtin.bridgeFromRawPointer(rawObject))
137+
: nil
136138
}
137139

138140
var cocoa: Cocoa? {
139-
return _swift_usesNativeSwiftReferenceCounting_nonNull(object)
140-
? nil : .Some(Builtin.bridgeFromRawPointer(Builtin.bridgeToRawPointer(object)))
141+
return _swift_usesNativeSwiftReferenceCounting_nonNull(
142+
UnsafePointer(rawObject)) != 0
143+
? nil : .Some(Builtin.bridgeFromRawPointer(rawObject))
141144
}
142145

143146
var spareBits: Int {
144147
return Int(bits)
145148
}
146149

147-
var address: UnsafePointer<Void> {
148-
return unsafeAddressOf(object)
150+
var rawObject: Builtin.RawPointer {
151+
return Builtin.bridgeToRawPointer(object)
149152
}
150153

151154
let object: AnyObject

test/SILPasses/array_mutable.swift

+5-5
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
// output instead.
77

88
// CHECK-LABEL: sil hidden @_TF13array_mutable8inoutarrFRGSaSi_T_
9-
// CHECK: %[[FR:[0-9]+]] = function_ref @_swift_isUniquelyReferenced
9+
// CHECK: %[[FR:[0-9]+]] = function_ref @_swift_isUniquelyReferenced_native_spareBits
1010
// CHECK-NOT: {{^bb}}
1111
// CHECK: apply %[[FR]]
1212
// CHECK: {{^bb}}
13-
// CHECK-NOT: _swift_isUniquelyReferenced
13+
// CHECK-NOT: _swift_isUniquelyReferenced_native_spareBits
1414
// CHECK: [[VOID:%[^ ]+]] = tuple ()
1515
// CHECK: return [[VOID]]
1616
func inoutarr(inout a: [Int]) {
@@ -24,11 +24,11 @@ struct S {
2424
}
2525

2626
// CHECK-LABEL: sil hidden @_TF13array_mutable6arreltFRVS_1ST_
27-
// CHECK: %[[FR:[0-9]+]] = function_ref @_swift_isUniquelyReferenced
27+
// CHECK: %[[FR:[0-9]+]] = function_ref @_swift_isUniquelyReferenced_native_spareBits
2828
// CHECK-NOT: {{^bb}}
2929
// CHECK: apply %[[FR]]
3030
// CHECK: {{^bb}}
31-
// CHECK-NOT: _swift_isUniquelyReferenced
31+
// CHECK-NOT: _swift_isUniquelyReferenced_native_spareBits
3232
// CHECK: {{^[}]}}
3333
func arrelt(inout s: S) {
3434
for i in 0..<s.a.count {
@@ -39,7 +39,7 @@ func arrelt(inout s: S) {
3939
// Check that we have an explicit retain before calling isUniquelyReferenced.
4040
// <rdar:18109082> ARC: make _isUniquelyReferenced a barrier
4141
// CHECK-LABEL: sil hidden @_TF13array_mutable7arrcopyFRGSaSi_Si
42-
// CHECK: %[[FR:[0-9]+]] = function_ref @_swift_isUniquelyReferenced
42+
// CHECK: %[[FR:[0-9]+]] = function_ref @_swift_isUniquelyReferenced_native_spareBits
4343
// CHECK: retain_value
4444
// CHECK: apply %[[FR]]
4545
// CHECK: {{^bb1}}

test/SILPasses/codemotion.sil

+6-6
Original file line numberDiff line numberDiff line change
@@ -641,15 +641,15 @@ bb3:
641641
}
642642

643643
sil @arc_user : $@thin () -> ()
644-
sil @_swift_isUniquelyReferenced : $@thin (Int) -> Bool
644+
sil @_swift_isUniquelyReferenced_native_spareBits : $@thin (Int) -> Bool
645645

646646
// The retains here aren't able to be moved to a successor BB. But we still want to move them as far
647647
// as possible down the current BB. The first retain should be moved to above the release_value.
648648
// The second retain shouldn't move at all
649649
// The third retain should move below the sadd builtin_function_ref
650650
// The fourth retain should move below the arc_user builtin_function_ref, but above the apply
651651
// The fifth retain should *NOT* move below the apply to
652-
// _swift_isUniquelyReferenced, unless we can prove that the pointers do not
652+
// _swift_isUniquelyReferenced_native_spareBits, unless we can prove that the pointers do not
653653
// alias.
654654
// And the last retain should remain where it is at the end of the BB.
655655
//
@@ -665,8 +665,8 @@ sil @_swift_isUniquelyReferenced : $@thin (Int) -> Bool
665665
// CHECK-NEXT: retain_value %3
666666
// CHECK-NEXT: retain_value %2
667667
// CHECK-NEXT: apply
668-
// CHECK-NEXT: function_ref _swift_isUniquelyReferenced
669-
// CHECK-NEXT: function_ref @_swift_isUniquelyReferenced
668+
// CHECK-NEXT: function_ref _swift_isUniquelyReferenced_native_spareBits
669+
// CHECK-NEXT: function_ref @_swift_isUniquelyReferenced_native_spareBits
670670
// CHECK-NEXT: integer_literal $Builtin.Word, 1
671671
// CHECK-NEXT: struct $Int
672672
// CHECK-NEXT: retain_value %3
@@ -687,8 +687,8 @@ bb0(%0 : $FakeOptional<Builtin.Int32>, %1 : $FakeOptional<Builtin.Int32>, %2 : $
687687
%1000 = function_ref @arc_user : $@thin () -> ()
688688
apply %1000 () : $@thin () -> ()
689689
retain_value %3 : $FakeOptional<Builtin.Int32>
690-
// function_ref _swift_isUniquelyReferenced
691-
%500 = function_ref @_swift_isUniquelyReferenced : $@thin (Int) -> Bool
690+
// function_ref _swift_isUniquelyReferenced_native_spareBits
691+
%500 = function_ref @_swift_isUniquelyReferenced_native_spareBits : $@thin (Int) -> Bool
692692
%501 = integer_literal $Builtin.Word, 1
693693
%502 = struct $Int (%501 : $Builtin.Word)
694694
%503 = apply %500(%502) : $@thin (Int) -> Bool

test/SILPasses/global_arc_unique_check.sil

+3-3
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ bb2:
3434
// CHECK-NOT: strong_retain
3535
// CHECK-NOT: strong_release
3636
strong_retain %0 : $MD5
37-
// function_ref _swift_isUniquelyReferenced
38-
%172 = function_ref @_swift_isUniquelyReferenced : $@thin (Int) -> Bool
37+
// function_ref _swift_isUniquelyReferenced_native_spareBits
38+
%172 = function_ref @_swift_isUniquelyReferenced_native_spareBits : $@thin (Int) -> Bool
3939
%314 = ref_element_addr %0 : $MD5, #MD5.w
4040
%318 = load %314 : $*Array<UInt32>
4141
%319 = alloc_stack $Array<UInt32>
@@ -65,4 +65,4 @@ bb5:
6565
br bb1(%13 : $Builtin.Word)
6666

6767
}
68-
sil @_swift_isUniquelyReferenced : $@thin (Int) -> Bool
68+
sil @_swift_isUniquelyReferenced_native_spareBits : $@thin (Int) -> Bool

0 commit comments

Comments
 (0)