codegen
Optimizing LLVM's bump allocator
BumpPtrAllocator is LLVM's bump allocator (arena allocator): each allocation bumps a pointer within a slab, and everything is freed at once when the allocator dies.
It backs Clang's ASTContext, lld's make<T> object pools, TableGen records, and many other arenas.
Here is the fast path before three recent changes:
__attribute__((returns_nonnull)) void *Allocate(size_t Size, Align Alignment) {
BytesAllocated += Size; // (3) accounting RMW
uintptr_t AlignedPtr = alignAddr(CurPtr, Alignment); // (1) always realign
size_t SizeToAllocate = Size;
#if LLVM_ADDRESS_SANITIZER_BUILD
SizeToAllocate += RedZoneSize;
#endif
uintptr_t AllocEndPtr = AlignedPtr + SizeToAllocate;
if (LLVM_LIKELY(AllocEndPtr <= uintptr_t(End)
&& CurPtr != nullptr)) { // (2) bound + null check
CurPtr = reinterpret_cast<char *>(AllocEndPtr);
...
return reinterpret_cast<char *>(AlignedPtr);
}
return AllocateSlow(Size, SizeToAllocate, Alignment);
}…
A deep dive into SmallVector::push_back
tl;dr This blog post describes a recent SmallVector::push_back optimization for approximately trivially copyable element types.
SmallVector is LLVM's most-used container, and push_back its hot operation.
For the trivially-copyable specialization the fast path should be fast.
#include <llvm/ADT/SmallVector.h>
void f(llvm::SmallVectorImpl<int> &v, int x) { v.push_back(x); }
clang -S --target=x86_64 -O2 -DNDEBUG a.cc generates:
push rbp # callee-saved spills + a stack realignment,
push rbx # all on the fast path
push rax
mov eax, [rdi + 8] # size
cmp eax, [rdi + 12] # vs capacity
jae .Lgrow
.Lstore: # reached from the fast path AND from .Lgrow
mov rcx, [rdi]
mov [rcx + rax*4], esi
inc dword ptr [rdi + 8]
add rsp, 8
pop rbx
pop rbp
ret
.Lgrow:
mov rbx, rdi # keep `this`/`x` alive across the call
mov ebp, esi
call SmallVectorBase<unsigned>::grow_pod
...
jmp .Lstore…