Skip to content

Commit 4ce5cda

Browse files
committed
[llvm-arc-opts] Implement swift_{retain,release}_n.
rdar://21803771 Swift SVN r30204
1 parent 2315c8f commit 4ce5cda

File tree

5 files changed

+97
-1
lines changed

5 files changed

+97
-1
lines changed

include/swift/Runtime/HeapObject.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ extern "C" void swift_slowDealloc(void *ptr, size_t bytes, size_t alignMask);
150150
extern "C" HeapObject *swift_retain(HeapObject *object);
151151
extern "C" void swift_retain_noresult(HeapObject *object);
152152

153+
extern "C" HeapObject *swift_retain_n(HeapObject *object, uint32_t n);
154+
153155
static inline HeapObject *_swift_retain_inlined(HeapObject *object) {
154156
if (object) {
155157
object->refCount.increment();
@@ -196,6 +198,10 @@ extern "C" void swift_unpin(HeapObject *object);
196198
/// It's unlikely that a custom CC would be beneficial here.
197199
extern "C" void swift_release(HeapObject *object);
198200

201+
/// Atomically decrements the retain count of an object n times. If the retain
202+
/// count reaches zero, the object is destroyed
203+
extern "C" void swift_release_n(HeapObject *object, uint32_t n);
204+
199205
/// ObjC compatibility. Never call this.
200206
extern "C" size_t swift_retainCount(HeapObject *object);
201207

include/swift/Runtime/InstrumentsSupport.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ extern "C" HeapObject *(*_swift_allocObject)(HeapMetadata const *metadata,
2727
extern "C" BoxPair::Return (*_swift_allocBox)(Metadata const *type);
2828

2929
extern "C" HeapObject *(*_swift_retain)(HeapObject *object);
30+
extern "C" HeapObject *(*_swift_retain_n)(HeapObject *object, uint32_t n);
3031
extern "C" HeapObject *(*_swift_tryRetain)(HeapObject *object);
3132
extern "C" bool (*_swift_isDeallocating)(HeapObject *object);
3233
extern "C" void (*_swift_release)(HeapObject *object);
33-
34+
extern "C" void (*_swift_release_n)(HeapObject *object, uint32_t n);
3435

3536
// liboainject on iOS 8 patches the function pointers below if present.
3637
// Do not reuse these names unless you do what oainject expects you to do.

stdlib/public/SwiftShims/RefCount.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ class StrongRefCount {
9595
__atomic_fetch_add(&refCount, RC_ONE, __ATOMIC_RELAXED);
9696
}
9797

98+
// Increment the reference count by n.
99+
void increment(uint32_t n) {
100+
__atomic_fetch_add(&refCount, n << RC_FLAGS_COUNT, __ATOMIC_RELAXED);
101+
}
102+
98103
// Try to simultaneously set the pinned flag and increment the
99104
// reference count. If the flag is already set, don't increment the
100105
// reference count.
@@ -149,6 +154,10 @@ class StrongRefCount {
149154
return doDecrementShouldDeallocate<false>();
150155
}
151156

157+
bool decrementShouldDeallocateN(uint32_t n) {
158+
return doDecrementShouldDeallocateN<false>(n);
159+
}
160+
152161
// Return the reference count.
153162
// During deallocation the reference count is undefined.
154163
uint32_t getCount() const {
@@ -220,6 +229,42 @@ class StrongRefCount {
220229
return __atomic_compare_exchange(&refCount, &oldval, &newval, 0,
221230
__ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
222231
}
232+
233+
template <bool ClearPinnedFlag>
234+
bool doDecrementShouldDeallocateN(uint32_t n) {
235+
// If we're being asked to clear the pinned flag, we can assume
236+
// it's already set.
237+
uint32_t delta = (n << RC_FLAGS_COUNT) + (ClearPinnedFlag ? RC_PINNED_FLAG : 0);
238+
uint32_t newval = __atomic_sub_fetch(&refCount, delta, __ATOMIC_RELEASE);
239+
240+
assert((!ClearPinnedFlag || !(newval & RC_PINNED_FLAG)) &&
241+
"unpinning reference that was not pinned");
242+
assert(newval + delta >= RC_ONE &&
243+
"releasing reference with a refcount of zero");
244+
245+
// If we didn't drop the reference count to zero, or if the
246+
// deallocating flag is already set, we're done; don't start
247+
// deallocation. We can assume that the pinned flag isn't set
248+
// unless the refcount is nonzero, and or'ing it in gives us a
249+
// more efficient mask: the check just becomes "is newval nonzero".
250+
if ((newval & (RC_COUNT_MASK | RC_PINNED_FLAG | RC_DEALLOCATING_FLAG))
251+
!= 0) {
252+
// Refcount is not zero. We definitely do not need to deallocate.
253+
return false;
254+
}
255+
256+
// Refcount is now 0 and is not already deallocating. Try to set
257+
// the deallocating flag. This must be atomic because it can race
258+
// with weak retains.
259+
//
260+
// This also performs the before-deinit acquire barrier if we set the flag.
261+
static_assert(RC_FLAGS_COUNT == 2,
262+
"fix decrementShouldDeallocate() if you add more flags");
263+
uint32_t oldval = 0;
264+
newval = RC_DEALLOCATING_FLAG;
265+
return __atomic_compare_exchange(&refCount, &oldval, &newval, 0,
266+
__ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
267+
}
223268
};
224269

225270

stdlib/public/runtime/HeapObject.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,17 @@ static HeapObject *_swift_retain_(HeapObject *object) {
287287
}
288288
auto swift::_swift_retain = _swift_retain_;
289289

290+
HeapObject *swift::swift_retain_n(HeapObject *object, uint32_t n) {
291+
return _swift_retain_n(object, n);
292+
}
293+
static HeapObject *_swift_retain_n_(HeapObject *object, uint32_t n) {
294+
if (object) {
295+
object->refCount.increment(n);
296+
}
297+
return object;
298+
}
299+
auto swift::_swift_retain_n = _swift_retain_n_;
300+
290301
void swift::swift_release(HeapObject *object) {
291302
SWIFT_RELEASE();
292303
return _swift_release(object);
@@ -298,6 +309,16 @@ static void _swift_release_(HeapObject *object) {
298309
}
299310
auto swift::_swift_release = _swift_release_;
300311

312+
void swift::swift_release_n(HeapObject *object, uint32_t n) {
313+
return _swift_release_n(object, n);
314+
}
315+
static void _swift_release_n_(HeapObject *object, uint32_t n) {
316+
if (object && object->refCount.decrementShouldDeallocateN(n)) {
317+
_swift_release_dealloc(object);
318+
}
319+
}
320+
auto swift::_swift_release_n = _swift_release_n_;
321+
301322
size_t swift::swift_retainCount(HeapObject *object) {
302323
return object->refCount.getCount();
303324
}

unittests/runtime/Refcounting.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,26 @@ TEST(RefcountingTest, pin_pin_unpin_unpin) {
9797
swift_unpin(object);
9898
EXPECT_EQ(1u, value);
9999
}
100+
101+
TEST(RefcountingTest, retain_release_n) {
102+
size_t value = 0;
103+
auto object = allocTestObject(&value, 1);
104+
EXPECT_EQ(0u, value);
105+
auto retainResult = swift_retain_n(object, 32);
106+
EXPECT_EQ(object, retainResult);
107+
retainResult = swift_retain(object);
108+
EXPECT_EQ(object, retainResult);
109+
EXPECT_EQ(0u, value);
110+
EXPECT_EQ(34u, swift_retainCount(object));
111+
swift_release_n(object, 31);
112+
EXPECT_EQ(0u, value);
113+
EXPECT_EQ(3u, swift_retainCount(object));
114+
swift_release(object);
115+
EXPECT_EQ(0u, value);
116+
EXPECT_EQ(2u, swift_retainCount(object));
117+
swift_release_n(object, 1);
118+
EXPECT_EQ(0u, value);
119+
EXPECT_EQ(1u, swift_retainCount(object));
120+
swift_release(object);
121+
EXPECT_EQ(1u, value);
122+
}

0 commit comments

Comments
 (0)