summaryrefslogtreecommitdiffstats
path: root/compiler-rt/lib/xray/xray_allocator.h
diff options
context:
space:
mode:
Diffstat (limited to 'compiler-rt/lib/xray/xray_allocator.h')
-rw-r--r--compiler-rt/lib/xray/xray_allocator.h149
1 files changed, 149 insertions, 0 deletions
diff --git a/compiler-rt/lib/xray/xray_allocator.h b/compiler-rt/lib/xray/xray_allocator.h
new file mode 100644
index 00000000000..8f8e13a7952
--- /dev/null
+++ b/compiler-rt/lib/xray/xray_allocator.h
@@ -0,0 +1,149 @@
+//===-- xray_allocator.h ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of XRay, a dynamic runtime instrumentation system.
+//
+// Defines the allocator interface for an arena allocator, used primarily for
+// the profiling runtime.
+//
+//===----------------------------------------------------------------------===//
+#ifndef XRAY_ALLOCATOR_H
+#define XRAY_ALLOCATOR_H
+
+#include "sanitizer_common/sanitizer_allocator_internal.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_mutex.h"
+#include <cstddef>
+#include <cstdint>
+
+#include "sanitizer_common/sanitizer_internal_defs.h"
+
+namespace __xray {
+
+/// The Allocator type hands out fixed-sized chunks of memory that are
+/// cache-line aligned and sized. This is useful for placement of
+/// performance-sensitive data in memory that's frequently accessed. The
+/// allocator also self-limits the peak memory usage to a dynamically defined
+/// maximum.
+///
+/// N is the lower-bound size of the block of memory to return from the
+/// allocation function. N is used to compute the size of a block, which is
+/// cache-line-size multiples worth of memory. We compute the size of a block by
+/// determining how many cache lines worth of memory is required to subsume N.
+template <size_t N> struct Allocator {
+ // The Allocator returns memory as Block instances.
+ struct Block {
+ /// Compute the minimum cache-line size multiple that is >= N.
+ static constexpr auto Size =
+ kCacheLineSize * ((N / kCacheLineSize) + (N % kCacheLineSize ? 1 : 0));
+ void *Data = nullptr;
+ };
+
+private:
+ // A BlockLink will contain a fixed number of blocks, each with an identifier
+ // to specify whether it's been handed out or not. We keep track of BlockLink
+ // iterators, which are basically a pointer to the link and an offset into
+ // the fixed set of blocks associated with a link. The iterators are
+ // bidirectional.
+ //
+ // We're calling it a "link" in the context of seeing these as a chain of
+ // block pointer containers (i.e. links in a chain).
+ struct BlockLink {
+ static_assert(kCacheLineSize % sizeof(void *) == 0,
+ "Cache line size is not divisible by size of void*; none of "
+ "the assumptions of the BlockLink will hold.");
+
+ // We compute the number of pointers to areas in memory where we consider as
+ // individual blocks we've allocated. To ensure that instances of the
+ // BlockLink object are cache-line sized, we deduct one additional
+ // pointers worth representing the pointer to the previous link.
+ //
+ // This structure corresponds to the following layout:
+ //
+ // Blocks [ 0, 1, 2, .., BlockPtrCount - 1]
+ //
+ static constexpr auto BlockPtrCount =
+ (kCacheLineSize / sizeof(Block *)) - 1;
+
+ // FIXME: Align this to cache-line address boundaries?
+ Block Blocks[BlockPtrCount]{};
+ BlockLink *Prev = nullptr;
+ };
+
+ static_assert(sizeof(BlockLink) == kCacheLineSize,
+ "BlockLink instances must be cache-line-sized.");
+
+ static BlockLink NullLink;
+
+ // FIXME: Implement a freelist, in case we actually do intend to return memory
+ // to the allocator, as opposed to just de-allocating everything in one go?
+
+ size_t MaxMemory;
+ SpinMutex Mutex{};
+ BlockLink *Tail = &NullLink;
+ size_t Counter = 0;
+
+ BlockLink *NewChainLink() {
+ auto NewChain = reinterpret_cast<BlockLink *>(
+ InternalAlloc(sizeof(BlockLink), nullptr, kCacheLineSize));
+ auto BackingStore = reinterpret_cast<char *>(InternalAlloc(
+ BlockLink::BlockPtrCount * Block::Size, nullptr, kCacheLineSize));
+ size_t Offset = 0;
+ DCHECK_NE(NewChain, nullptr);
+ DCHECK_NE(BackingStore, nullptr);
+ for (auto &B : NewChain->Blocks) {
+ B.Data = BackingStore + Offset;
+ Offset += Block::Size;
+ }
+ NewChain->Prev = Tail;
+ return NewChain;
+ }
+
+public:
+ Allocator(size_t M, size_t PreAllocate) : MaxMemory(M) {
+ // FIXME: Implement PreAllocate support!
+ }
+
+ Block Allocate() {
+ SpinMutexLock Lock(&Mutex);
+ // Check whether we're over quota.
+ if (Counter * Block::Size >= MaxMemory)
+ return {};
+
+ size_t ChainOffset = Counter % BlockLink::BlockPtrCount;
+
+ Block B{};
+ BlockLink *Link = Tail;
+ if (UNLIKELY(Counter == 0 || ChainOffset == 0))
+ Tail = Link = NewChainLink();
+
+ B = Link->Blocks[ChainOffset];
+ ++Counter;
+ return B;
+ }
+
+ ~Allocator() NOEXCEPT {
+ // We need to deallocate all the blocks, including the chain links.
+ for (auto *C = Tail; C != &NullLink;) {
+ // We know that the data block is a large contiguous page, we deallocate
+ // that at once.
+ InternalFree(C->Blocks[0].Data);
+ auto Prev = C->Prev;
+ InternalFree(C);
+ C = Prev;
+ }
+ }
+}; // namespace __xray
+
+// Storage for the NullLink sentinel.
+template <size_t N> typename Allocator<N>::BlockLink Allocator<N>::NullLink;
+
+} // namespace __xray
+
+#endif // XRAY_ALLOCATOR_H
OpenPOWER on IntegriCloud