+}
+
+void cache_t::setBucketsAndMask(struct bucket_t *newBuckets, mask_t newMask)
+{
+ // objc_msgSend uses mask and buckets with no locks.
+ // It is safe for objc_msgSend to see new buckets but old mask.
+ // (It will get a cache miss but not overrun the buckets' bounds).
+ // It is unsafe for objc_msgSend to see old buckets and new mask.
+ // Therefore we write new buckets, wait a lot, then write new mask.
+ // objc_msgSend reads mask first, then buckets.
+
+ // ensure other threads see buckets contents before buckets pointer
+ mega_barrier();
+
+ _buckets = newBuckets;
+
+ // ensure other threads see new buckets before new mask
+ mega_barrier();
+
+ _mask = newMask;
+ _occupied = 0;
+}
+
+// not arm64
+#endif
+
+struct bucket_t *cache_t::buckets()
+{
+ return _buckets;
+}
+
+mask_t cache_t::mask()
+{
+ return _mask;
+}
+
+mask_t cache_t::occupied()
+{
+ return _occupied;
+}
+
+void cache_t::incrementOccupied()
+{
+ _occupied++;
+}
+
+void cache_t::setEmpty()
+{
+ bzero(this, sizeof(*this));
+ _buckets = (bucket_t *)&_objc_empty_cache;
+}
+
+
+mask_t cache_t::capacity()
+{
+ return mask() ? mask()+1 : 0;
+}
+
+
+#if CACHE_END_MARKER
+
+size_t cache_t::bytesForCapacity(uint32_t cap)
+{
+ // fixme put end marker inline when capacity+1 malloc is inefficient
+ return sizeof(cache_t) * (cap + 1);
+}
+
+bucket_t *cache_t::endMarker(struct bucket_t *b, uint32_t cap)
+{
+ // bytesForCapacity() chooses whether the end marker is inline or not
+ return (bucket_t *)((uintptr_t)b + bytesForCapacity(cap)) - 1;
+}
+
+bucket_t *allocateBuckets(mask_t newCapacity)
+{
+ // Allocate one extra bucket to mark the end of the list.
+ // This can't overflow mask_t because newCapacity is a power of 2.
+ // fixme instead put the end mark inline when +1 is malloc-inefficient
+ bucket_t *newBuckets = (bucket_t *)
+ _calloc_internal(cache_t::bytesForCapacity(newCapacity), 1);
+
+ bucket_t *end = cache_t::endMarker(newBuckets, newCapacity);
+
+#if __arm__
+ // End marker's key is 1 and imp points BEFORE the first bucket.
+ // This saves an instruction in objc_msgSend.
+ end->setKey((cache_key_t)(uintptr_t)1);
+ end->setImp((IMP)(newBuckets - 1));
+#else
+# error unknown architecture
+#endif
+
+ return newBuckets;
+}
+
+#else
+
+bucket_t *allocateBuckets(mask_t newCapacity)
+{
+ return (bucket_t *)_calloc_internal(newCapacity, sizeof(bucket_t));
+}
+
+#endif
+
+
+bool cache_t::canBeFreed()
+{
+ return buckets() != (bucket_t *)&_objc_empty_cache;
+}