Skip to content

Commit abc7ba6

Browse files
committed
---
yaml --- r: 348027 b: refs/heads/master c: c334ff4 h: refs/heads/master i: 348025: f78e9a4 348023: 4536cee
1 parent a4490a9 commit abc7ba6

File tree

5 files changed

+144
-46
lines changed

5 files changed

+144
-46
lines changed

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
refs/heads/master: d71a5be4cf7f8b1fe87a7c8d03fdf8e6f72bdd34
2+
refs/heads/master: c334ff4aab78252fe13f63774ff3fbc0f4f5f0cf
33
refs/heads/master-next: 203b3026584ecad859eb328b2e12490099409cd5
44
refs/tags/osx-passed: b6b74147ef8a386f532cf9357a1bde006e552c54
55
refs/tags/swift-2.2-SNAPSHOT-2015-12-01-a: 6bb18e013c2284f2b45f5f84f2df2887dc0f7dea

trunk/stdlib/public/SwiftShims/RefCount.h

Lines changed: 97 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -238,14 +238,29 @@ struct RefCountBitOffsets;
238238
// 32-bit out of line
239239
template <>
240240
struct RefCountBitOffsets<8> {
241-
static const size_t IsImmortalShift = 0;
242-
static const size_t IsImmortalBitCount = 1;
243-
static const uint64_t IsImmortalMask = maskForField(IsImmortal);
244-
245-
static const size_t UnownedRefCountShift = shiftAfterField(IsImmortal);
241+
/*
242+
The bottom 32 bits (on 64 bit architectures, fewer on 32 bit) of the refcount
243+
field are effectively a union of two different configurations:
244+
245+
---Normal case---
246+
Bit 0: Does this object need to call out to the ObjC runtime for deallocation
247+
Bits 1-31: Unowned refcount
248+
249+
---Immortal case---
250+
All bits set, the object does not deallocate or have a refcount
251+
*/
252+
static const size_t PureSwiftDeallocShift = 0;
253+
static const size_t PureSwiftDeallocBitCount = 1;
254+
static const uint64_t PureSwiftDeallocMask = maskForField(PureSwiftDealloc);
255+
256+
static const size_t UnownedRefCountShift = shiftAfterField(PureSwiftDealloc);
246257
static const size_t UnownedRefCountBitCount = 31;
247258
static const uint64_t UnownedRefCountMask = maskForField(UnownedRefCount);
248259

260+
static const size_t IsImmortalShift = 0; // overlaps PureSwiftDealloc and UnownedRefCount
261+
static const size_t IsImmortalBitCount = 32;
262+
static const uint64_t IsImmortalMask = maskForField(IsImmortal);
263+
249264
static const size_t IsDeinitingShift = shiftAfterField(UnownedRefCount);
250265
static const size_t IsDeinitingBitCount = 1;
251266
static const uint64_t IsDeinitingMask = maskForField(IsDeiniting);
@@ -271,14 +286,18 @@ struct RefCountBitOffsets<8> {
271286
// 32-bit inline
272287
template <>
273288
struct RefCountBitOffsets<4> {
274-
static const size_t IsImmortalShift = 0;
275-
static const size_t IsImmortalBitCount = 1;
276-
static const uint64_t IsImmortalMask = maskForField(IsImmortal);
289+
static const size_t PureSwiftDeallocShift = 0;
290+
static const size_t PureSwiftDeallocBitCount = 1;
291+
static const uint32_t PureSwiftDeallocMask = maskForField(PureSwiftDealloc);
277292

278-
static const size_t UnownedRefCountShift = shiftAfterField(IsImmortal);
293+
static const size_t UnownedRefCountShift = shiftAfterField(PureSwiftDealloc);
279294
static const size_t UnownedRefCountBitCount = 7;
280295
static const uint32_t UnownedRefCountMask = maskForField(UnownedRefCount);
281296

297+
static const size_t IsImmortalShift = 0; // overlaps PureSwiftDealloc and UnownedRefCount
298+
static const size_t IsImmortalBitCount = 8;
299+
static const uint32_t IsImmortalMask = maskForField(IsImmortal);
300+
282301
static const size_t IsDeinitingShift = shiftAfterField(UnownedRefCount);
283302
static const size_t IsDeinitingBitCount = 1;
284303
static const uint32_t IsDeinitingMask = maskForField(IsDeiniting);
@@ -369,33 +388,55 @@ class RefCountBitsT {
369388
enum Immortal_t { Immortal };
370389

371390
LLVM_ATTRIBUTE_ALWAYS_INLINE
372-
bool isImmortal() const {
373-
return bool(getField(IsImmortal));
391+
bool isImmortal(bool checkSlowRCBit) const {
392+
if (checkSlowRCBit) {
393+
return (getField(IsImmortal) == Offsets::IsImmortalMask) &&
394+
bool(getField(UseSlowRC));
395+
} else {
396+
return (getField(IsImmortal) == Offsets::IsImmortalMask);
397+
}
398+
}
399+
400+
LLVM_ATTRIBUTE_ALWAYS_INLINE
401+
bool isOverflowingUnownedRefCount(uint32_t oldValue, uint32_t inc) const {
402+
auto newValue = getUnownedRefCount();
403+
return newValue != oldValue + inc ||
404+
newValue == Offsets::UnownedRefCountMask;
374405
}
375406

376407
LLVM_ATTRIBUTE_ALWAYS_INLINE
377408
void setIsImmortal(bool value) {
378-
setField(IsImmortal, value);
409+
setField(IsImmortal, value ? Offsets::IsImmortalMask : 0);
379410
setField(UseSlowRC, value);
380411
}
381412

413+
LLVM_ATTRIBUTE_ALWAYS_INLINE
414+
bool pureSwiftDeallocation() const {
415+
return bool(getField(PureSwiftDealloc)) && !bool(getField(UseSlowRC));
416+
}
417+
418+
LLVM_ATTRIBUTE_ALWAYS_INLINE
419+
void setPureSwiftDeallocation(bool value) {
420+
setField(PureSwiftDealloc, value);
421+
}
422+
382423
LLVM_ATTRIBUTE_ALWAYS_INLINE
383424
RefCountBitsT() = default;
384425

385426
LLVM_ATTRIBUTE_ALWAYS_INLINE
386427
constexpr
387428
RefCountBitsT(uint32_t strongExtraCount, uint32_t unownedCount)
388429
: bits((BitsType(strongExtraCount) << Offsets::StrongExtraRefCountShift) |
430+
(BitsType(1) << Offsets::PureSwiftDeallocShift) |
389431
(BitsType(unownedCount) << Offsets::UnownedRefCountShift))
390432
{ }
391433

392434
LLVM_ATTRIBUTE_ALWAYS_INLINE
393435
constexpr
394436
RefCountBitsT(Immortal_t immortal)
395-
: bits((BitsType(2) << Offsets::StrongExtraRefCountShift) |
396-
(BitsType(2) << Offsets::UnownedRefCountShift) |
397-
(BitsType(1) << Offsets::IsImmortalShift) |
398-
(BitsType(1) << Offsets::UseSlowRCShift))
437+
: bits((BitsType(2) << Offsets::StrongExtraRefCountShift) |
438+
(BitsType(Offsets::IsImmortalMask)) |
439+
(BitsType(1) << Offsets::UseSlowRCShift))
399440
{ }
400441

401442
LLVM_ATTRIBUTE_ALWAYS_INLINE
@@ -433,7 +474,7 @@ class RefCountBitsT {
433474

434475
LLVM_ATTRIBUTE_ALWAYS_INLINE
435476
bool hasSideTable() const {
436-
bool hasSide = getUseSlowRC() && !isImmortal();
477+
bool hasSide = getUseSlowRC() && !isImmortal(false);
437478

438479
// Side table refcount must not point to another side table.
439480
assert((refcountIsInline || !hasSide) &&
@@ -523,7 +564,7 @@ class RefCountBitsT {
523564
LLVM_NODISCARD LLVM_ATTRIBUTE_ALWAYS_INLINE
524565
bool decrementStrongExtraRefCount(uint32_t dec) {
525566
#ifndef NDEBUG
526-
if (!hasSideTable() && !isImmortal()) {
567+
if (!hasSideTable() && !isImmortal(false)) {
527568
// Can't check these assertions with side table present.
528569

529570
if (getIsDeiniting())
@@ -558,7 +599,7 @@ class RefCountBitsT {
558599
static_assert(Offsets::UnownedRefCountBitCount +
559600
Offsets::IsDeinitingBitCount +
560601
Offsets::StrongExtraRefCountBitCount +
561-
Offsets::IsImmortalBitCount +
602+
Offsets::PureSwiftDeallocBitCount +
562603
Offsets::UseSlowRCBitCount == sizeof(bits)*8,
563604
"inspect isUniquelyReferenced after adding fields");
564605

@@ -715,7 +756,7 @@ class RefCounts {
715756

716757
void setIsImmortal(bool immortal) {
717758
auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME);
718-
if (oldbits.isImmortal()) {
759+
if (oldbits.isImmortal(true)) {
719760
return;
720761
}
721762
RefCountBits newbits;
@@ -725,7 +766,27 @@ class RefCounts {
725766
} while (!refCounts.compare_exchange_weak(oldbits, newbits,
726767
std::memory_order_relaxed));
727768
}
728-
769+
770+
void setPureSwiftDeallocation(bool nonobjc) {
771+
auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME);
772+
//Immortal and no objc complications share a bit, so don't let setting
773+
//the complications one clear the immmortal one
774+
if (oldbits.isImmortal(true) || oldbits.pureSwiftDeallocation() == nonobjc){
775+
return;
776+
}
777+
RefCountBits newbits;
778+
do {
779+
newbits = oldbits;
780+
newbits.setPureSwiftDeallocation(nonobjc);
781+
} while (!refCounts.compare_exchange_weak(oldbits, newbits,
782+
std::memory_order_relaxed));
783+
}
784+
785+
bool getPureSwiftDeallocation() {
786+
auto bits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME);
787+
return bits.pureSwiftDeallocation();
788+
}
789+
729790
// Initialize from another refcount bits.
730791
// Only inline -> out-of-line is allowed (used for new side table entries).
731792
void init(InlineRefCountBits newBits) {
@@ -740,7 +801,7 @@ class RefCounts {
740801
newbits = oldbits;
741802
bool fast = newbits.incrementStrongExtraRefCount(inc);
742803
if (SWIFT_UNLIKELY(!fast)) {
743-
if (oldbits.isImmortal())
804+
if (oldbits.isImmortal(false))
744805
return;
745806
return incrementSlow(oldbits, inc);
746807
}
@@ -753,7 +814,7 @@ class RefCounts {
753814
auto newbits = oldbits;
754815
bool fast = newbits.incrementStrongExtraRefCount(inc);
755816
if (SWIFT_UNLIKELY(!fast)) {
756-
if (oldbits.isImmortal())
817+
if (oldbits.isImmortal(false))
757818
return;
758819
return incrementNonAtomicSlow(oldbits, inc);
759820
}
@@ -771,7 +832,7 @@ class RefCounts {
771832
newbits = oldbits;
772833
bool fast = newbits.incrementStrongExtraRefCount(1);
773834
if (SWIFT_UNLIKELY(!fast)) {
774-
if (oldbits.isImmortal())
835+
if (oldbits.isImmortal(false))
775836
return true;
776837
return tryIncrementSlow(oldbits);
777838
}
@@ -788,7 +849,7 @@ class RefCounts {
788849
auto newbits = oldbits;
789850
bool fast = newbits.incrementStrongExtraRefCount(1);
790851
if (SWIFT_UNLIKELY(!fast)) {
791-
if (oldbits.isImmortal())
852+
if (oldbits.isImmortal(false))
792853
return true;
793854
return tryIncrementNonAtomicSlow(oldbits);
794855
}
@@ -824,7 +885,7 @@ class RefCounts {
824885
// Precondition: the reference count must be 1
825886
void decrementFromOneNonAtomic() {
826887
auto bits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME);
827-
if (bits.isImmortal()) {
888+
if (bits.isImmortal(true)) {
828889
return;
829890
}
830891
if (bits.hasSideTable())
@@ -922,7 +983,7 @@ class RefCounts {
922983
// Decrement completed normally. New refcount is not zero.
923984
deinitNow = false;
924985
}
925-
else if (oldbits.isImmortal()) {
986+
else if (oldbits.isImmortal(false)) {
926987
return false;
927988
} else if (oldbits.hasSideTable()) {
928989
// Decrement failed because we're on some other slow path.
@@ -961,7 +1022,7 @@ class RefCounts {
9611022
// Decrement completed normally. New refcount is not zero.
9621023
deinitNow = false;
9631024
}
964-
else if (oldbits.isImmortal()) {
1025+
else if (oldbits.isImmortal(false)) {
9651026
return false;
9661027
}
9671028
else if (oldbits.hasSideTable()) {
@@ -1001,7 +1062,7 @@ class RefCounts {
10011062
bool fast =
10021063
newbits.decrementStrongExtraRefCount(dec);
10031064
if (SWIFT_UNLIKELY(!fast)) {
1004-
if (oldbits.isImmortal()) {
1065+
if (oldbits.isImmortal(false)) {
10051066
return false;
10061067
}
10071068
// Slow paths include side table; deinit; underflow
@@ -1025,7 +1086,7 @@ class RefCounts {
10251086
// Increment the unowned reference count.
10261087
void incrementUnowned(uint32_t inc) {
10271088
auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME);
1028-
if (oldbits.isImmortal())
1089+
if (oldbits.isImmortal(true))
10291090
return;
10301091
RefCountBits newbits;
10311092
do {
@@ -1037,7 +1098,7 @@ class RefCounts {
10371098
uint32_t oldValue = newbits.incrementUnownedRefCount(inc);
10381099

10391100
// Check overflow and use the side table on overflow.
1040-
if (newbits.getUnownedRefCount() != oldValue + inc)
1101+
if (newbits.isOverflowingUnownedRefCount(oldValue, inc))
10411102
return incrementUnownedSlow(inc);
10421103

10431104
} while (!refCounts.compare_exchange_weak(oldbits, newbits,
@@ -1046,7 +1107,7 @@ class RefCounts {
10461107

10471108
void incrementUnownedNonAtomic(uint32_t inc) {
10481109
auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME);
1049-
if (oldbits.isImmortal())
1110+
if (oldbits.isImmortal(true))
10501111
return;
10511112
if (oldbits.hasSideTable())
10521113
return oldbits.getSideTable()->incrementUnownedNonAtomic(inc);
@@ -1056,7 +1117,7 @@ class RefCounts {
10561117
uint32_t oldValue = newbits.incrementUnownedRefCount(inc);
10571118

10581119
// Check overflow and use the side table on overflow.
1059-
if (newbits.getUnownedRefCount() != oldValue + inc)
1120+
if (newbits.isOverflowingUnownedRefCount(oldValue, inc))
10601121
return incrementUnownedSlow(inc);
10611122

10621123
refCounts.store(newbits, std::memory_order_relaxed);
@@ -1066,7 +1127,7 @@ class RefCounts {
10661127
// Return true if the caller should free the object.
10671128
bool decrementUnownedShouldFree(uint32_t dec) {
10681129
auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME);
1069-
if (oldbits.isImmortal())
1130+
if (oldbits.isImmortal(true))
10701131
return false;
10711132
RefCountBits newbits;
10721133

@@ -1094,7 +1155,7 @@ class RefCounts {
10941155

10951156
bool decrementUnownedShouldFreeNonAtomic(uint32_t dec) {
10961157
auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME);
1097-
if (oldbits.isImmortal())
1158+
if (oldbits.isImmortal(true))
10981159
return false;
10991160
if (oldbits.hasSideTable())
11001161
return oldbits.getSideTable()->decrementUnownedShouldFreeNonAtomic(dec);
@@ -1383,7 +1444,7 @@ inline bool RefCounts<InlineRefCountBits>::doDecrementNonAtomic(uint32_t dec) {
13831444
auto newbits = oldbits;
13841445
bool fast = newbits.decrementStrongExtraRefCount(dec);
13851446
if (!fast) {
1386-
if (oldbits.isImmortal()) {
1447+
if (oldbits.isImmortal(false)) {
13871448
return false;
13881449
}
13891450
return doDecrementNonAtomicSlow<performDeinit>(oldbits, dec);

trunk/stdlib/public/runtime/HeapObject.cpp

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
# include <objc/message.h>
4343
# include <objc/objc.h>
4444
# include "swift/Runtime/ObjCBridge.h"
45+
# include "swift/Runtime/Once.h"
4546
#endif
4647
#include "Leaks.h"
4748

@@ -78,6 +79,32 @@ HeapObject *swift::swift_allocObject(HeapMetadata const *metadata,
7879
return _swift_allocObject(metadata, requiredSize, requiredAlignmentMask);
7980
}
8081

82+
#if OBJC_SETASSOCIATEDOBJECTHOOK_DEFINED
83+
//We interpose objc_setAssociatedObject so that we can set a flag in
84+
//the refcount field of Swift objects to indicate that they have associations,
85+
//since we can't safely skip ObjC dealloc work if they do
86+
static objc_hook_setAssociatedObject originalAssocObjectFunc = nullptr;
87+
88+
static void _swift_setAssociatedObject_hook(
89+
id _Nonnull object,
90+
const void * _Nonnull key,
91+
id _Nullable value,
92+
objc_AssociationPolicy policy
93+
) {
94+
if (!isObjCTaggedPointerOrNull(object) &&
95+
objectUsesNativeSwiftReferenceCounting(object)) {
96+
auto heapObj = reinterpret_cast<HeapObject *>(object);
97+
heapObj->refCounts.setPureSwiftDeallocation(false);
98+
}
99+
originalAssocObjectFunc(object, key, value, policy);
100+
}
101+
102+
static void _interpose_objc_association(void *ctxt) {
103+
objc_setHook_setAssociatedObject(_swift_setAssociatedObject_hook,
104+
&originalAssocObjectFunc);
105+
}
106+
#endif
107+
81108
static HeapObject *_swift_allocObject_(HeapMetadata const *metadata,
82109
size_t requiredSize,
83110
size_t requiredAlignmentMask) {
@@ -90,6 +117,11 @@ static HeapObject *_swift_allocObject_(HeapMetadata const *metadata,
90117
// Linux, and macOS.
91118
new (object) HeapObject(metadata);
92119

120+
#if OBJC_SETASSOCIATEDOBJECTHOOK_DEFINED
121+
static swift_once_t associatedObjectHookOnce;
122+
swift_once(&associatedObjectHookOnce, _interpose_objc_association, nullptr);
123+
#endif
124+
93125
// If leak tracking is enabled, start tracking this object.
94126
SWIFT_LEAKS_START_TRACKING_OBJECT(object);
95127

@@ -594,9 +626,14 @@ void swift::swift_rootObjCDealloc(HeapObject *self) {
594626
void swift::swift_deallocClassInstance(HeapObject *object,
595627
size_t allocatedSize,
596628
size_t allocatedAlignMask) {
597-
#if SWIFT_OBJC_INTEROP
629+
#if OBJC_SETASSOCIATEDOBJECTHOOK_DEFINED
598630
// We need to let the ObjC runtime clean up any associated objects or weak
599631
// references associated with this object.
632+
if (originalAssocObjectFunc == nullptr ||
633+
!object->refCounts.getPureSwiftDeallocation()) {
634+
objc_destructInstance((id)object);
635+
}
636+
#elif SWIFT_OBJC_INTEROP
600637
objc_destructInstance((id)object);
601638
#endif
602639
swift_deallocObject(object, allocatedSize, allocatedAlignMask);

0 commit comments

Comments
 (0)