@@ -238,14 +238,11 @@ struct RefCountBitOffsets;
238
238
// 32-bit out of line
239
239
template <>
240
240
struct RefCountBitOffsets <8 > {
241
- // We reserve 1 bit (which we likely be using in future) to make the
242
- // unowned field 31 bit. The reason is that unowned overflow checking does
243
- // not work with 32 bit in the current implementation.
244
- static const size_t ReservedShift = 0 ;
245
- static const size_t ReservedBitCount = 1 ;
246
- static const uint32_t ReservedMask = maskForField(Reserved);
247
-
248
- static const size_t UnownedRefCountShift = shiftAfterField(Reserved);
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);
249
246
static const size_t UnownedRefCountBitCount = 31 ;
250
247
static const uint64_t UnownedRefCountMask = maskForField(UnownedRefCount);
251
248
@@ -256,7 +253,7 @@ struct RefCountBitOffsets<8> {
256
253
static const size_t StrongExtraRefCountShift = shiftAfterField(IsDeiniting);
257
254
static const size_t StrongExtraRefCountBitCount = 30 ;
258
255
static const uint64_t StrongExtraRefCountMask = maskForField(StrongExtraRefCount);
259
-
256
+
260
257
static const size_t UseSlowRCShift = shiftAfterField(StrongExtraRefCount);
261
258
static const size_t UseSlowRCBitCount = 1 ;
262
259
static const uint64_t UseSlowRCMask = maskForField(UseSlowRC);
@@ -274,22 +271,22 @@ struct RefCountBitOffsets<8> {
274
271
// 32-bit inline
275
272
template <>
276
273
struct RefCountBitOffsets <4 > {
277
- static const size_t ReservedShift = 0 ;
278
- static const size_t ReservedBitCount = 0 ;
279
- static const uint32_t ReservedMask = maskForField(Reserved );
280
-
281
- static const size_t UnownedRefCountShift = shiftAfterField(Reserved );
282
- static const size_t UnownedRefCountBitCount = 8 ;
274
+ static const size_t IsImmortalShift = 0 ;
275
+ static const size_t IsImmortalBitCount = 1 ;
276
+ static const uint64_t IsImmortalMask = maskForField(IsImmortal );
277
+
278
+ static const size_t UnownedRefCountShift = shiftAfterField(IsImmortal );
279
+ static const size_t UnownedRefCountBitCount = 7 ;
283
280
static const uint32_t UnownedRefCountMask = maskForField(UnownedRefCount);
284
281
285
282
static const size_t IsDeinitingShift = shiftAfterField(UnownedRefCount);
286
283
static const size_t IsDeinitingBitCount = 1 ;
287
284
static const uint32_t IsDeinitingMask = maskForField(IsDeiniting);
288
-
285
+
289
286
static const size_t StrongExtraRefCountShift = shiftAfterField(IsDeiniting);
290
287
static const size_t StrongExtraRefCountBitCount = 22 ;
291
288
static const uint32_t StrongExtraRefCountMask = maskForField(StrongExtraRefCount);
292
-
289
+
293
290
static const size_t UseSlowRCShift = shiftAfterField(StrongExtraRefCount);
294
291
static const size_t UseSlowRCBitCount = 1 ;
295
292
static const uint32_t UseSlowRCMask = maskForField(UseSlowRC);
@@ -307,7 +304,7 @@ struct RefCountBitOffsets<4> {
307
304
308
305
// FIXME: reinstate these assertions
309
306
#if 0
310
- static_assert(StrongExtraRefCountShift == IsDeinitingShift + 1,
307
+ static_assert(StrongExtraRefCountShift == IsDeinitingShift + 1,
311
308
"IsDeiniting must be LSB-wards of StrongExtraRefCount");
312
309
static_assert(UseSlowRCShift + UseSlowRCBitCount == sizeof(bits)*8,
313
310
"UseSlowRC must be MSB");
@@ -368,7 +365,20 @@ class RefCountBitsT {
368
365
}
369
366
370
367
public:
368
+
369
+ enum Immortal_t { Immortal };
371
370
371
+ LLVM_ATTRIBUTE_ALWAYS_INLINE
372
+ bool isImmortal () const {
373
+ return bool (getField (IsImmortal));
374
+ }
375
+
376
+ LLVM_ATTRIBUTE_ALWAYS_INLINE
377
+ void setIsImmortal (bool value) {
378
+ setField (IsImmortal, value);
379
+ setField (UseSlowRC, value);
380
+ }
381
+
372
382
LLVM_ATTRIBUTE_ALWAYS_INLINE
373
383
RefCountBitsT () = default;
374
384
@@ -378,6 +388,15 @@ class RefCountBitsT {
378
388
: bits((BitsType(strongExtraCount) << Offsets::StrongExtraRefCountShift) |
379
389
(BitsType(unownedCount) << Offsets::UnownedRefCountShift))
380
390
{ }
391
+
392
+ LLVM_ATTRIBUTE_ALWAYS_INLINE
393
+ constexpr
394
+ 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))
399
+ { }
381
400
382
401
LLVM_ATTRIBUTE_ALWAYS_INLINE
383
402
RefCountBitsT (HeapObjectSideTableEntry* side)
@@ -414,8 +433,7 @@ class RefCountBitsT {
414
433
415
434
LLVM_ATTRIBUTE_ALWAYS_INLINE
416
435
bool hasSideTable () const {
417
- // FIXME: change this when introducing immutable RC objects
418
- bool hasSide = getUseSlowRC ();
436
+ bool hasSide = getUseSlowRC () && !isImmortal ();
419
437
420
438
// Side table refcount must not point to another side table.
421
439
assert ((refcountIsInline || !hasSide) &&
@@ -505,7 +523,7 @@ class RefCountBitsT {
505
523
LLVM_NODISCARD LLVM_ATTRIBUTE_ALWAYS_INLINE
506
524
bool decrementStrongExtraRefCount (uint32_t dec) {
507
525
#ifndef NDEBUG
508
- if (!hasSideTable ()) {
526
+ if (!hasSideTable () && ! isImmortal () ) {
509
527
// Can't check these assertions with side table present.
510
528
511
529
if (getIsDeiniting ())
@@ -537,10 +555,10 @@ class RefCountBitsT {
537
555
538
556
LLVM_ATTRIBUTE_ALWAYS_INLINE
539
557
bool isUniquelyReferenced () {
540
- static_assert (Offsets::ReservedBitCount +
541
- Offsets::UnownedRefCountBitCount +
558
+ static_assert (Offsets::UnownedRefCountBitCount +
542
559
Offsets::IsDeinitingBitCount +
543
560
Offsets::StrongExtraRefCountBitCount +
561
+ Offsets::IsImmortalBitCount +
544
562
Offsets::UseSlowRCBitCount == sizeof (bits)*8 ,
545
563
" inspect isUniquelyReferenced after adding fields" );
546
564
@@ -663,6 +681,7 @@ class RefCounts {
663
681
664
682
public:
665
683
enum Initialized_t { Initialized };
684
+ enum Immortal_t { Immortal };
666
685
667
686
// RefCounts must be trivially constructible to avoid ObjC++
668
687
// destruction overhead at runtime. Use RefCounts(Initialized)
@@ -673,6 +692,10 @@ class RefCounts {
673
692
constexpr RefCounts (Initialized_t)
674
693
: refCounts(RefCountBits(0 , 1 )) {}
675
694
695
+ // Refcount of an immortal object has top and bottom bits set
696
+ constexpr RefCounts (Immortal_t)
697
+ : refCounts(RefCountBits(RefCountBits::Immortal)) {}
698
+
676
699
void init () {
677
700
refCounts.store (RefCountBits (0 , 1 ), std::memory_order_relaxed);
678
701
}
@@ -684,6 +707,24 @@ class RefCounts {
684
707
void initForNotFreeing () {
685
708
refCounts.store (RefCountBits (0 , 2 ), std::memory_order_relaxed);
686
709
}
710
+
711
+ // Initialize for an object which will never deallocate.
712
+ void initImmortal () {
713
+ refCounts.store (RefCountBits (RefCountBits::Immortal), std::memory_order_relaxed);
714
+ }
715
+
716
+ void setIsImmortal (bool immortal) {
717
+ auto oldbits = refCounts.load (SWIFT_MEMORY_ORDER_CONSUME);
718
+ if (oldbits.isImmortal ()) {
719
+ return ;
720
+ }
721
+ RefCountBits newbits;
722
+ do {
723
+ newbits = oldbits;
724
+ newbits.setIsImmortal (immortal);
725
+ } while (!refCounts.compare_exchange_weak (oldbits, newbits,
726
+ std::memory_order_relaxed));
727
+ }
687
728
688
729
// Initialize from another refcount bits.
689
730
// Only inline -> out-of-line is allowed (used for new side table entries).
@@ -698,8 +739,11 @@ class RefCounts {
698
739
do {
699
740
newbits = oldbits;
700
741
bool fast = newbits.incrementStrongExtraRefCount (inc);
701
- if (!fast)
742
+ if (__builtin_expect (!fast, 0 )) {
743
+ if (oldbits.isImmortal ())
744
+ return ;
702
745
return incrementSlow (oldbits, inc);
746
+ }
703
747
} while (!refCounts.compare_exchange_weak (oldbits, newbits,
704
748
std::memory_order_relaxed));
705
749
}
@@ -708,8 +752,11 @@ class RefCounts {
708
752
auto oldbits = refCounts.load (SWIFT_MEMORY_ORDER_CONSUME);
709
753
auto newbits = oldbits;
710
754
bool fast = newbits.incrementStrongExtraRefCount (inc);
711
- if (!fast)
755
+ if (__builtin_expect (!fast, 0 )) {
756
+ if (oldbits.isImmortal ())
757
+ return ;
712
758
return incrementNonAtomicSlow (oldbits, inc);
759
+ }
713
760
refCounts.store (newbits, std::memory_order_relaxed);
714
761
}
715
762
@@ -723,8 +770,11 @@ class RefCounts {
723
770
724
771
newbits = oldbits;
725
772
bool fast = newbits.incrementStrongExtraRefCount (1 );
726
- if (!fast)
773
+ if (__builtin_expect (!fast, 0 )) {
774
+ if (oldbits.isImmortal ())
775
+ return true ;
727
776
return tryIncrementSlow (oldbits);
777
+ }
728
778
} while (!refCounts.compare_exchange_weak (oldbits, newbits,
729
779
std::memory_order_relaxed));
730
780
return true ;
@@ -737,8 +787,11 @@ class RefCounts {
737
787
738
788
auto newbits = oldbits;
739
789
bool fast = newbits.incrementStrongExtraRefCount (1 );
740
- if (!fast)
790
+ if (__builtin_expect (!fast, 0 )) {
791
+ if (oldbits.isImmortal ())
792
+ return true ;
741
793
return tryIncrementNonAtomicSlow (oldbits);
794
+ }
742
795
refCounts.store (newbits, std::memory_order_relaxed);
743
796
return true ;
744
797
}
@@ -771,6 +824,9 @@ class RefCounts {
771
824
// Precondition: the reference count must be 1
772
825
void decrementFromOneNonAtomic () {
773
826
auto bits = refCounts.load (SWIFT_MEMORY_ORDER_CONSUME);
827
+ if (bits.isImmortal ()) {
828
+ return ;
829
+ }
774
830
if (bits.hasSideTable ())
775
831
return bits.getSideTable ()->decrementFromOneNonAtomic ();
776
832
@@ -866,7 +922,9 @@ class RefCounts {
866
922
// Decrement completed normally. New refcount is not zero.
867
923
deinitNow = false ;
868
924
}
869
- else if (oldbits.hasSideTable ()) {
925
+ else if (oldbits.isImmortal ()) {
926
+ return false ;
927
+ } else if (oldbits.hasSideTable ()) {
870
928
// Decrement failed because we're on some other slow path.
871
929
return doDecrementSideTable<performDeinit>(oldbits, dec);
872
930
}
@@ -903,6 +961,9 @@ class RefCounts {
903
961
// Decrement completed normally. New refcount is not zero.
904
962
deinitNow = false ;
905
963
}
964
+ else if (oldbits.isImmortal ()) {
965
+ return false ;
966
+ }
906
967
else if (oldbits.hasSideTable ()) {
907
968
// Decrement failed because we're on some other slow path.
908
969
return doDecrementNonAtomicSideTable<performDeinit>(oldbits, dec);
@@ -939,9 +1000,13 @@ class RefCounts {
939
1000
newbits = oldbits;
940
1001
bool fast =
941
1002
newbits.decrementStrongExtraRefCount (dec);
942
- if (!fast)
1003
+ if (__builtin_expect (!fast, 0 )) {
1004
+ if (oldbits.isImmortal ()) {
1005
+ return false ;
1006
+ }
943
1007
// Slow paths include side table; deinit; underflow
944
1008
return doDecrementSlow<performDeinit>(oldbits, dec);
1009
+ }
945
1010
} while (!refCounts.compare_exchange_weak (oldbits, newbits,
946
1011
std::memory_order_release,
947
1012
std::memory_order_relaxed));
@@ -960,6 +1025,8 @@ class RefCounts {
960
1025
// Increment the unowned reference count.
961
1026
void incrementUnowned (uint32_t inc) {
962
1027
auto oldbits = refCounts.load (SWIFT_MEMORY_ORDER_CONSUME);
1028
+ if (oldbits.isImmortal ())
1029
+ return ;
963
1030
RefCountBits newbits;
964
1031
do {
965
1032
if (oldbits.hasSideTable ())
@@ -979,6 +1046,8 @@ class RefCounts {
979
1046
980
1047
void incrementUnownedNonAtomic (uint32_t inc) {
981
1048
auto oldbits = refCounts.load (SWIFT_MEMORY_ORDER_CONSUME);
1049
+ if (oldbits.isImmortal ())
1050
+ return ;
982
1051
if (oldbits.hasSideTable ())
983
1052
return oldbits.getSideTable ()->incrementUnownedNonAtomic (inc);
984
1053
@@ -997,6 +1066,8 @@ class RefCounts {
997
1066
// Return true if the caller should free the object.
998
1067
bool decrementUnownedShouldFree (uint32_t dec) {
999
1068
auto oldbits = refCounts.load (SWIFT_MEMORY_ORDER_CONSUME);
1069
+ if (oldbits.isImmortal ())
1070
+ return false ;
1000
1071
RefCountBits newbits;
1001
1072
1002
1073
bool performFree;
@@ -1023,7 +1094,8 @@ class RefCounts {
1023
1094
1024
1095
bool decrementUnownedShouldFreeNonAtomic (uint32_t dec) {
1025
1096
auto oldbits = refCounts.load (SWIFT_MEMORY_ORDER_CONSUME);
1026
-
1097
+ if (oldbits.isImmortal ())
1098
+ return false ;
1027
1099
if (oldbits.hasSideTable ())
1028
1100
return oldbits.getSideTable ()->decrementUnownedShouldFreeNonAtomic (dec);
1029
1101
@@ -1312,8 +1384,12 @@ inline bool RefCounts<InlineRefCountBits>::doDecrementNonAtomic(uint32_t dec) {
1312
1384
1313
1385
auto newbits = oldbits;
1314
1386
bool fast = newbits.decrementStrongExtraRefCount (dec);
1315
- if (!fast)
1387
+ if (!fast) {
1388
+ if (oldbits.isImmortal ()) {
1389
+ return false ;
1390
+ }
1316
1391
return doDecrementNonAtomicSlow<performDeinit>(oldbits, dec);
1392
+ }
1317
1393
1318
1394
refCounts.store (newbits, std::memory_order_relaxed);
1319
1395
return false ; // don't deinit
0 commit comments