@@ -57,7 +57,15 @@ namespace swift {
5757// /
5858// / SlabMetadataPtr specifies a fake metadata pointer to place at the beginning
5959// / of slab allocations, so analysis tools can identify them.
60- template <size_t SlabCapacity, Metadata *SlabMetadataPtr>
60+ // /
61+ // / SlabAllocatorConfiguration allows customizing behavior. It currently allows
62+ // / one customization point: bool enableSlabAllocator():
63+ // / returns false - the stack allocator directly calls malloc/free
64+ // / returns true - the slab allocator is used
65+ // / This function MUST return the same value throughout the lifetime the stack
66+ // / allocator.
67+ template <size_t SlabCapacity, Metadata *SlabMetadataPtr,
68+ typename SlabAllocatorConfiguration>
6169class StackAllocator {
6270private:
6371
@@ -77,6 +85,8 @@ class StackAllocator {
7785 // / Used for unit testing.
7886 uint32_t numAllocatedSlabs:31 ;
7987
88+ // / The configuration object.
89+ [[no_unique_address]] SlabAllocatorConfiguration configuration;
8090
8191 // / The minimal alignment of allocated memory.
8292 static constexpr size_t alignment = MaximumAlignment;
@@ -227,9 +237,10 @@ class StackAllocator {
227237 };
228238
229239 // Return a slab which is suitable to allocate \p size memory.
240+ SWIFT_ALWAYS_INLINE
230241 Slab *getSlabForAllocation (size_t size) {
231242 Slab *slab = (lastAllocation ? lastAllocation->slab : firstSlab);
232- if (slab) {
243+ if (SWIFT_LIKELY ( slab) ) {
233244 // Is there enough space in the current slab?
234245 if (slab->canAllocate (size))
235246 return slab;
@@ -249,6 +260,12 @@ class StackAllocator {
249260 size = std::max (size, alreadyAllocatedCapacity);
250261 }
251262 }
263+
264+ // This is only checked on the path that allocates a new slab, to minimize
265+ // overhead when the slab allocator is enabled.
266+ if (SWIFT_UNLIKELY (!configuration.enableSlabAllocator ()))
267+ return nullptr ;
268+
252269 size_t capacity = std::max (SlabCapacity,
253270 Allocation::includingHeader (size));
254271 void *slabBuffer = malloc (Slab::includingHeader (capacity));
@@ -281,13 +298,15 @@ class StackAllocator {
281298 // / Construct a StackAllocator without a pre-allocated first slab.
282299 StackAllocator ()
283300 : firstSlab(nullptr ), firstSlabIsPreallocated(false ),
284- numAllocatedSlabs (0 ) {}
301+ numAllocatedSlabs (0 ), configuration() {}
285302
286303 // / Construct a StackAllocator with a pre-allocated first slab.
287304 StackAllocator (void *firstSlabBuffer, size_t bufferCapacity) : StackAllocator() {
288305 // If the pre-allocated buffer can't hold a slab header, ignore it.
289306 if (bufferCapacity <= Slab::headerSize ())
290307 return ;
308+ if (SWIFT_UNLIKELY (!configuration.enableSlabAllocator ()))
309+ return ;
291310 char *start = (char *)llvm::alignAddr (firstSlabBuffer,
292311 llvm::Align (alignment));
293312 char *end = (char *)firstSlabBuffer + bufferCapacity;
@@ -317,6 +336,17 @@ class StackAllocator {
317336 size += sizeof (uintptr_t );
318337 size_t alignedSize = llvm::alignTo (size, llvm::Align (alignment));
319338 Slab *slab = getSlabForAllocation (alignedSize);
339+
340+ // If getSlabForAllocation returns null, that means that the slab allocator
341+ // is disabled, and we should directly call malloc. We get this info
342+ // indirectly rather than directly calling enableSlabAllocator() in order
343+ // to minimize the overhead in the case where the slab allocator is enabled.
344+ // When getSlabForAllocation gets inlined into this code, this ends up
345+ // getting folded into its enableSlabAllocator() call, and the fast path
346+ // where `slab` is non-null ends up with no extra conditionals at all.
347+ if (SWIFT_UNLIKELY (!slab))
348+ return malloc (size);
349+
320350 Allocation *allocation = slab->allocate (alignedSize, lastAllocation);
321351 lastAllocation = allocation;
322352 assert (llvm::isAddrAligned (llvm::Align (alignment),
@@ -326,7 +356,10 @@ class StackAllocator {
326356
327357 // / Deallocate memory \p ptr.
328358 void dealloc (void *ptr) {
329- if (!lastAllocation || lastAllocation->getAllocatedMemory () != ptr) {
359+ if (SWIFT_UNLIKELY (!lastAllocation ||
360+ lastAllocation->getAllocatedMemory () != ptr)) {
361+ if (!configuration.enableSlabAllocator ())
362+ return free (ptr);
330363 SWIFT_FATAL_ERROR (0 , " freed pointer was not the last allocation" );
331364 }
332365
0 commit comments