xref: /openbsd-src/gnu/llvm/compiler-rt/lib/tsan/rtl/tsan_dense_alloc.h (revision 810390e339a5425391477d5d41c78d7cab2424ac)
13cab2bb3Spatrick //===-- tsan_dense_alloc.h --------------------------------------*- C++ -*-===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick //
93cab2bb3Spatrick // This file is a part of ThreadSanitizer (TSan), a race detector.
103cab2bb3Spatrick //
113cab2bb3Spatrick // A DenseSlabAlloc is a freelist-based allocator of fixed-size objects.
123cab2bb3Spatrick // DenseSlabAllocCache is a thread-local cache for DenseSlabAlloc.
133cab2bb3Spatrick // The only difference with traditional slab allocators is that DenseSlabAlloc
143cab2bb3Spatrick // allocates/free indices of objects and provide a functionality to map
153cab2bb3Spatrick // the index onto the real pointer. The index is u32, that is, 2 times smaller
163cab2bb3Spatrick // than uptr (hense the Dense prefix).
173cab2bb3Spatrick //===----------------------------------------------------------------------===//
183cab2bb3Spatrick #ifndef TSAN_DENSE_ALLOC_H
193cab2bb3Spatrick #define TSAN_DENSE_ALLOC_H
203cab2bb3Spatrick 
213cab2bb3Spatrick #include "sanitizer_common/sanitizer_common.h"
223cab2bb3Spatrick #include "tsan_defs.h"
233cab2bb3Spatrick 
243cab2bb3Spatrick namespace __tsan {
253cab2bb3Spatrick 
263cab2bb3Spatrick class DenseSlabAllocCache {
273cab2bb3Spatrick   static const uptr kSize = 128;
283cab2bb3Spatrick   typedef u32 IndexT;
293cab2bb3Spatrick   uptr pos;
303cab2bb3Spatrick   IndexT cache[kSize];
31d89ec533Spatrick   template <typename, uptr, uptr, u64>
32d89ec533Spatrick   friend class DenseSlabAlloc;
333cab2bb3Spatrick };
343cab2bb3Spatrick 
35d89ec533Spatrick template <typename T, uptr kL1Size, uptr kL2Size, u64 kReserved = 0>
363cab2bb3Spatrick class DenseSlabAlloc {
373cab2bb3Spatrick  public:
383cab2bb3Spatrick   typedef DenseSlabAllocCache Cache;
393cab2bb3Spatrick   typedef typename Cache::IndexT IndexT;
403cab2bb3Spatrick 
41d89ec533Spatrick   static_assert((kL1Size & (kL1Size - 1)) == 0,
42d89ec533Spatrick                 "kL1Size must be a power-of-two");
43d89ec533Spatrick   static_assert((kL2Size & (kL2Size - 1)) == 0,
44d89ec533Spatrick                 "kL2Size must be a power-of-two");
45d89ec533Spatrick   static_assert((kL1Size * kL2Size) <= (1ull << (sizeof(IndexT) * 8)),
46d89ec533Spatrick                 "kL1Size/kL2Size are too large");
47d89ec533Spatrick   static_assert(((kL1Size * kL2Size - 1) & kReserved) == 0,
48d89ec533Spatrick                 "reserved bits don't fit");
49d89ec533Spatrick   static_assert(sizeof(T) > sizeof(IndexT),
50d89ec533Spatrick                 "it doesn't make sense to use dense alloc");
51d89ec533Spatrick 
DenseSlabAlloc(LinkerInitialized,const char * name)52*810390e3Srobert   DenseSlabAlloc(LinkerInitialized, const char *name) : name_(name) {}
533cab2bb3Spatrick 
DenseSlabAlloc(const char * name)54d89ec533Spatrick   explicit DenseSlabAlloc(const char *name)
55d89ec533Spatrick       : DenseSlabAlloc(LINKER_INITIALIZED, name) {
56d89ec533Spatrick     // It can be very large.
57d89ec533Spatrick     // Don't page it in for linker initialized objects.
58d89ec533Spatrick     internal_memset(map_, 0, sizeof(map_));
59d89ec533Spatrick   }
60d89ec533Spatrick 
~DenseSlabAlloc()613cab2bb3Spatrick   ~DenseSlabAlloc() {
623cab2bb3Spatrick     for (uptr i = 0; i < kL1Size; i++) {
633cab2bb3Spatrick       if (map_[i] != 0)
643cab2bb3Spatrick         UnmapOrDie(map_[i], kL2Size * sizeof(T));
653cab2bb3Spatrick     }
663cab2bb3Spatrick   }
673cab2bb3Spatrick 
Alloc(Cache * c)683cab2bb3Spatrick   IndexT Alloc(Cache *c) {
693cab2bb3Spatrick     if (c->pos == 0)
703cab2bb3Spatrick       Refill(c);
713cab2bb3Spatrick     return c->cache[--c->pos];
723cab2bb3Spatrick   }
733cab2bb3Spatrick 
Free(Cache * c,IndexT idx)743cab2bb3Spatrick   void Free(Cache *c, IndexT idx) {
753cab2bb3Spatrick     DCHECK_NE(idx, 0);
763cab2bb3Spatrick     if (c->pos == Cache::kSize)
773cab2bb3Spatrick       Drain(c);
783cab2bb3Spatrick     c->cache[c->pos++] = idx;
793cab2bb3Spatrick   }
803cab2bb3Spatrick 
Map(IndexT idx)813cab2bb3Spatrick   T *Map(IndexT idx) {
823cab2bb3Spatrick     DCHECK_NE(idx, 0);
833cab2bb3Spatrick     DCHECK_LE(idx, kL1Size * kL2Size);
843cab2bb3Spatrick     return &map_[idx / kL2Size][idx % kL2Size];
853cab2bb3Spatrick   }
863cab2bb3Spatrick 
FlushCache(Cache * c)873cab2bb3Spatrick   void FlushCache(Cache *c) {
88*810390e3Srobert     while (c->pos) Drain(c);
893cab2bb3Spatrick   }
903cab2bb3Spatrick 
InitCache(Cache * c)913cab2bb3Spatrick   void InitCache(Cache *c) {
923cab2bb3Spatrick     c->pos = 0;
933cab2bb3Spatrick     internal_memset(c->cache, 0, sizeof(c->cache));
943cab2bb3Spatrick   }
953cab2bb3Spatrick 
AllocatedMemory()96*810390e3Srobert   uptr AllocatedMemory() const {
97*810390e3Srobert     return atomic_load_relaxed(&fillpos_) * kL2Size * sizeof(T);
98*810390e3Srobert   }
99*810390e3Srobert 
100*810390e3Srobert   template <typename Func>
ForEach(Func func)101*810390e3Srobert   void ForEach(Func func) {
102*810390e3Srobert     Lock lock(&mtx_);
103*810390e3Srobert     uptr fillpos = atomic_load_relaxed(&fillpos_);
104*810390e3Srobert     for (uptr l1 = 0; l1 < fillpos; l1++) {
105*810390e3Srobert       for (IndexT l2 = l1 == 0 ? 1 : 0; l2 < kL2Size; l2++) func(&map_[l1][l2]);
106*810390e3Srobert     }
107*810390e3Srobert   }
108*810390e3Srobert 
1093cab2bb3Spatrick  private:
1103cab2bb3Spatrick   T *map_[kL1Size];
111*810390e3Srobert   Mutex mtx_;
112*810390e3Srobert   // The freelist is organized as a lock-free stack of batches of nodes.
113*810390e3Srobert   // The stack itself uses Block::next links, while the batch within each
114*810390e3Srobert   // stack node uses Block::batch links.
115*810390e3Srobert   // Low 32-bits of freelist_ is the node index, top 32-bits is ABA-counter.
116*810390e3Srobert   atomic_uint64_t freelist_ = {0};
117*810390e3Srobert   atomic_uintptr_t fillpos_ = {0};
118*810390e3Srobert   const char *const name_;
1193cab2bb3Spatrick 
120*810390e3Srobert   struct Block {
121*810390e3Srobert     IndexT next;
122*810390e3Srobert     IndexT batch;
123*810390e3Srobert   };
124*810390e3Srobert 
MapBlock(IndexT idx)125*810390e3Srobert   Block *MapBlock(IndexT idx) { return reinterpret_cast<Block *>(Map(idx)); }
126*810390e3Srobert 
127*810390e3Srobert   static constexpr u64 kCounterInc = 1ull << 32;
128*810390e3Srobert   static constexpr u64 kCounterMask = ~(kCounterInc - 1);
129*810390e3Srobert 
Refill(Cache * c)130*810390e3Srobert   NOINLINE void Refill(Cache *c) {
131*810390e3Srobert     // Pop 1 batch of nodes from the freelist.
132*810390e3Srobert     IndexT idx;
133*810390e3Srobert     u64 xchg;
134*810390e3Srobert     u64 cmp = atomic_load(&freelist_, memory_order_acquire);
135*810390e3Srobert     do {
136*810390e3Srobert       idx = static_cast<IndexT>(cmp);
137*810390e3Srobert       if (!idx)
138*810390e3Srobert         return AllocSuperBlock(c);
139*810390e3Srobert       Block *ptr = MapBlock(idx);
140*810390e3Srobert       xchg = ptr->next | (cmp & kCounterMask);
141*810390e3Srobert     } while (!atomic_compare_exchange_weak(&freelist_, &cmp, xchg,
142*810390e3Srobert                                            memory_order_acq_rel));
143*810390e3Srobert     // Unpack it into c->cache.
144*810390e3Srobert     while (idx) {
145*810390e3Srobert       c->cache[c->pos++] = idx;
146*810390e3Srobert       idx = MapBlock(idx)->batch;
147*810390e3Srobert     }
148*810390e3Srobert   }
149*810390e3Srobert 
Drain(Cache * c)150*810390e3Srobert   NOINLINE void Drain(Cache *c) {
151*810390e3Srobert     // Build a batch of at most Cache::kSize / 2 nodes linked by Block::batch.
152*810390e3Srobert     IndexT head_idx = 0;
153*810390e3Srobert     for (uptr i = 0; i < Cache::kSize / 2 && c->pos; i++) {
154*810390e3Srobert       IndexT idx = c->cache[--c->pos];
155*810390e3Srobert       Block *ptr = MapBlock(idx);
156*810390e3Srobert       ptr->batch = head_idx;
157*810390e3Srobert       head_idx = idx;
158*810390e3Srobert     }
159*810390e3Srobert     // Push it onto the freelist stack.
160*810390e3Srobert     Block *head = MapBlock(head_idx);
161*810390e3Srobert     u64 xchg;
162*810390e3Srobert     u64 cmp = atomic_load(&freelist_, memory_order_acquire);
163*810390e3Srobert     do {
164*810390e3Srobert       head->next = static_cast<IndexT>(cmp);
165*810390e3Srobert       xchg = head_idx | (cmp & kCounterMask) + kCounterInc;
166*810390e3Srobert     } while (!atomic_compare_exchange_weak(&freelist_, &cmp, xchg,
167*810390e3Srobert                                            memory_order_acq_rel));
168*810390e3Srobert   }
169*810390e3Srobert 
AllocSuperBlock(Cache * c)170*810390e3Srobert   NOINLINE void AllocSuperBlock(Cache *c) {
171*810390e3Srobert     Lock lock(&mtx_);
172*810390e3Srobert     uptr fillpos = atomic_load_relaxed(&fillpos_);
173*810390e3Srobert     if (fillpos == kL1Size) {
174*810390e3Srobert       Printf("ThreadSanitizer: %s overflow (%zu*%zu). Dying.\n", name_, kL1Size,
175*810390e3Srobert              kL2Size);
1763cab2bb3Spatrick       Die();
1773cab2bb3Spatrick     }
178*810390e3Srobert     VPrintf(2, "ThreadSanitizer: growing %s: %zu out of %zu*%zu\n", name_,
179*810390e3Srobert             fillpos, kL1Size, kL2Size);
1803cab2bb3Spatrick     T *batch = (T *)MmapOrDie(kL2Size * sizeof(T), name_);
181*810390e3Srobert     map_[fillpos] = batch;
1823cab2bb3Spatrick     // Reserve 0 as invalid index.
183*810390e3Srobert     for (IndexT i = fillpos ? 0 : 1; i < kL2Size; i++) {
1843cab2bb3Spatrick       new (batch + i) T;
185*810390e3Srobert       c->cache[c->pos++] = i + fillpos * kL2Size;
186*810390e3Srobert       if (c->pos == Cache::kSize)
187*810390e3Srobert         Drain(c);
1883cab2bb3Spatrick     }
189*810390e3Srobert     atomic_store_relaxed(&fillpos_, fillpos + 1);
190*810390e3Srobert     CHECK(c->pos);
1913cab2bb3Spatrick   }
1923cab2bb3Spatrick };
1933cab2bb3Spatrick 
1943cab2bb3Spatrick }  // namespace __tsan
1953cab2bb3Spatrick 
1963cab2bb3Spatrick #endif  // TSAN_DENSE_ALLOC_H
197