From c147cb2a949d2a5c75804613c45e46c1a2ec8ab1 Mon Sep 17 00:00:00 2001 From: Steven Le Rouzic Date: Sat, 20 Apr 2024 01:22:04 +0200 Subject: Temporary allocator --- deimos/core/BUILD | 4 +- deimos/core/allocator.h | 2 +- deimos/core/api_registry.cpp | 2 + deimos/core/atomic.h | 2 +- deimos/core/base.h | 17 +++++++ deimos/core/gsl.h | 4 +- deimos/core/os.h | 4 +- deimos/core/temp_allocator.cpp | 109 +++++++++++++++++++++++++++++++++++++++++ deimos/core/temp_allocator.h | 65 ++++++++++++++++++++++++ 9 files changed, 202 insertions(+), 7 deletions(-) create mode 100644 deimos/core/temp_allocator.cpp (limited to 'deimos/core') diff --git a/deimos/core/BUILD b/deimos/core/BUILD index 84ed375..f81101b 100644 --- a/deimos/core/BUILD +++ b/deimos/core/BUILD @@ -5,6 +5,7 @@ cc_library( "api_registry.h", "atomic.h", "base.h", + "format.h", "gsl.h", "hash.h", "id_name.h", @@ -13,7 +14,7 @@ cc_library( "os.h", "status.h", "std.h", - "format.h", + "temp_allocator.h", ], srcs = [ "allocator.cpp", @@ -23,6 +24,7 @@ cc_library( "log.cpp", "os_win32.cpp", "status.cpp", + "temp_allocator.cpp", ], visibility = ["//:__subpackages__"], ) diff --git a/deimos/core/allocator.h b/deimos/core/allocator.h index e65f316..26374bd 100644 --- a/deimos/core/allocator.h +++ b/deimos/core/allocator.h @@ -29,7 +29,7 @@ class Allocator const MemoryScope m_scope; public: - constexpr explicit Allocator(IAllocator* allocator, MemoryScope scope) : + constexpr Allocator(IAllocator* allocator, MemoryScope scope) : m_allocator{allocator}, m_scope{scope} {} diff --git a/deimos/core/api_registry.cpp b/deimos/core/api_registry.cpp index 41c0257..9d0c93f 100644 --- a/deimos/core/api_registry.cpp +++ b/deimos/core/api_registry.cpp @@ -8,6 +8,7 @@ namespace deimos AllocatorApi* BootstrapAllocatorApi(); void RegisterOsApi(ApiRegistry*); +void RegisterTempAllocatorApi(ApiRegistry*); void RegisterLogApi(ApiRegistry*); void InitializeStatus(ApiRegistry*); @@ -59,6 +60,7 @@ ApiRegistry* InitializeGlobalApiRegistry() InitializeStatus(api_registry); RegisterOsApi(api_registry); + RegisterTempAllocatorApi(api_registry); RegisterLogApi(api_registry); return api_registry; diff --git a/deimos/core/atomic.h b/deimos/core/atomic.h index c3d7947..e4f8569 100644 --- a/deimos/core/atomic.h +++ b/deimos/core/atomic.h @@ -5,7 +5,7 @@ namespace deimos { -enum class MemoryOrder : int +enum class MemoryOrder : int // NOLINT(*-enum-size) { kRelaxed = __ATOMIC_RELAXED, kAcquire = __ATOMIC_ACQUIRE, diff --git a/deimos/core/base.h b/deimos/core/base.h index 1e9b4a9..f24991d 100644 --- a/deimos/core/base.h +++ b/deimos/core/base.h @@ -5,6 +5,8 @@ #define deimos_StaticAssert(...) static_assert(__VA_ARGS__, #__VA_ARGS__) +#define deimos_Panic(MSG) do { __builtin_trap(); } while (0) + #define deimos_NO_COPY(TYPE) \ TYPE(const TYPE&) = delete; \ TYPE& operator=(const TYPE&) = delete; @@ -56,6 +58,21 @@ struct SourceLocation template T Min(T a, T b) { return (a < b) ? a : b; } template T Max(T a, T b) { return (a > b) ? a : b; } +[[maybe_unused]] static constexpr int64_t Kilobytes = 1024; +[[maybe_unused]] static constexpr int64_t Megabytes = 1024 * 1024; +[[maybe_unused]] static constexpr int64_t Gigabytes = 1024 * 1024 * 1024; + +constexpr int64_t AlignUp(int64_t value, int64_t align) +{ + return __builtin_align_up(value, align); +} + +template +constexpr T* OffsetBytes(T* p, int64_t offset) +{ + return std::bit_cast(std::bit_cast(p) + std::bit_cast(offset)); +} + constexpr void MemoryCopy(void* dst, const void* src, int64_t size) { __builtin_memcpy(dst, src, (size_t)size); diff --git a/deimos/core/gsl.h b/deimos/core/gsl.h index b54fc65..d80d746 100644 --- a/deimos/core/gsl.h +++ b/deimos/core/gsl.h @@ -10,5 +10,5 @@ template using owner = T; } // namespace gsl -#define Expects(EXPR) do { if (!(EXPR)) { __builtin_trap(); } } while (0) -#define Ensures(EXPR) do { if (!(EXPR)) { __builtin_trap(); } } while (0) +#define Expects(EXPR) do { if (EXPR) {} else { __builtin_trap(); } } while (0) +#define Ensures(EXPR) do { if (EXPR) {} else { __builtin_trap(); } } while (0) diff --git a/deimos/core/os.h b/deimos/core/os.h index 9aea509..271ecfb 100644 --- a/deimos/core/os.h +++ b/deimos/core/os.h @@ -61,8 +61,8 @@ public: class OsConsoleWriter : public IWriter { - OsConsoleApi* m_api; - OsConsoleType m_type; + OsConsoleApi* m_api; + OsConsoleType m_type; public: constexpr OsConsoleWriter(OsConsoleApi* api, OsConsoleType type) : diff --git a/deimos/core/temp_allocator.cpp b/deimos/core/temp_allocator.cpp new file mode 100644 index 0000000..2f07bcc --- /dev/null +++ b/deimos/core/temp_allocator.cpp @@ -0,0 +1,109 @@ +#include "deimos/core/temp_allocator.h" + +#include "deimos/core/api_registry.h" +#include "deimos/core/allocator.h" +#include "deimos/core/os.h" + +static deimos::OsApi* os_api; + +namespace deimos +{ + +class TempAllocatorImpl : public ITempAllocator +{ + static constexpr int64_t kDefaultAlign = 8; + static constexpr int64_t kPageSize = 1 * Megabytes; + static constexpr int64_t kReserveSize = 256 * Megabytes; + + gsl::owner m_base{}; + void* m_commit_end{}; + void* m_reserve_end{}; + + // @Todo Last allocation optimization + void* m_current{}; + +public: + gsl::owner Reallocate(TempAllocatorTag tag, + gsl::owner old_ptr, int64_t old_size, int64_t new_size) override + { + void* tag_ptr = std::bit_cast(tag.tag); + Expects(tag_ptr >= m_base && tag_ptr <= m_commit_end && tag_ptr <= m_current); + + if (new_size <= old_size) { return (new_size == 0) ? nullptr : old_ptr; } + + void* new_current = OffsetBytes(m_current, AlignUp(new_size, kDefaultAlign)); + if (new_current > m_reserve_end) + { + deimos_Panic("Ran out of temporary memory"); + return nullptr; + } + + if (new_current > m_commit_end) + { + const int64_t new_commit_size = AlignUp( + (uint64_t)(std::bit_cast(new_current) - std::bit_cast(m_base)), // NOLINT + kPageSize); + os_api->virtual_memory->Commit(m_base, new_commit_size); + m_commit_end = OffsetBytes(m_base, new_commit_size); + Ensures(m_commit_end <= m_reserve_end); + } + + if (old_ptr != nullptr) + { + MemoryCopy(m_current, old_ptr, old_size); + } + + return std::exchange(m_current, new_current); + } + + TempAllocator Acquire() + { + if (m_base == nullptr) + { + m_base = os_api->virtual_memory->Reserve(kReserveSize); + m_current = m_base; + m_commit_end = m_base; + m_reserve_end = OffsetBytes(m_base, kReserveSize); + } + + return TempAllocator(this, {std::bit_cast(m_current)}); + } + + void Release(TempAllocatorTag tag) override + { + void* rewind_base = std::bit_cast(tag.tag); + Expects(rewind_base >= m_base && rewind_base <= m_commit_end); + + if (rewind_base < m_current) + { + m_current = rewind_base; + } + else + { + deimos_Panic("Invalid temporary allocator rewind"); + } + } +}; + +static thread_local TempAllocatorImpl g_impl; + +class TempAllocatorApiImpl : public TempAllocatorApi +{ +public: + TempAllocator Acquire() override + { + return g_impl.Acquire(); + } +}; + +void RegisterTempAllocatorApi(ApiRegistry* api_registry) +{ + os_api = api_registry->Get(); + + auto* allocator = api_registry->Get()->system; + gsl::owner temp_allocator_api = allocator->New(); + api_registry->Set(temp_allocator_api); +} + +} // namespace deimos + diff --git a/deimos/core/temp_allocator.h b/deimos/core/temp_allocator.h index 4f80192..6d93a77 100644 --- a/deimos/core/temp_allocator.h +++ b/deimos/core/temp_allocator.h @@ -2,9 +2,74 @@ #include "deimos/core/base.h" #include "deimos/core/id_name.h" +#include "deimos/core/allocator.h" namespace deimos { +struct TempAllocatorTag { uintptr_t tag; }; + +class TempAllocatorApi; + +class ITempAllocator +{ +public: + ITempAllocator() = default; + deimos_NO_COPY_MOVE(ITempAllocator); + virtual ~ITempAllocator() = default; + + virtual gsl::owner Reallocate(TempAllocatorTag tag, + gsl::owner old_ptr, int64_t old_size, int64_t new_size) = 0; + + virtual void Release(TempAllocatorTag tag) = 0; +}; + +class TempAllocator : public IAllocator +{ + ITempAllocator* m_inner; + TempAllocatorTag m_tag; + +public: + TempAllocator(ITempAllocator* inner, TempAllocatorTag tag) : + m_inner{inner}, m_tag{tag} + {} + + deimos_NO_COPY_MOVE(TempAllocator); + + ~TempAllocator() override; + + [[nodiscard]] + gsl::owner Reallocate( + gsl::owner old_ptr, int64_t old_size, int64_t new_size, + MemoryScope scope, const SourceLocation& source_location = {}) override; + + constexpr Allocator allocator() { return Allocator(this, MemoryScope{0}); } +}; + +class TempAllocatorApi +{ +public: + TempAllocatorApi() = default; + deimos_NO_COPY_MOVE(TempAllocatorApi); + virtual ~TempAllocatorApi() = default; + + static constexpr IdName kApiName{"deimos::TempAllocatorApi"}; + + [[nodiscard]] virtual TempAllocator Acquire() = 0; +}; + +[[nodiscard]] +inline gsl::owner TempAllocator::Reallocate( + gsl::owner old_ptr, int64_t old_size, int64_t new_size, + MemoryScope, const SourceLocation&) +{ + return m_inner->Reallocate(m_tag, old_ptr, old_size, new_size); +} + +inline TempAllocator::~TempAllocator() +{ + m_inner->Release(m_tag); +} + } // namespace deimos -- cgit