From 7353f0411074954fc399fbe0f40dcfce6fff829d Mon Sep 17 00:00:00 2001 From: mikedn Date: Sat, 30 Jun 2018 20:05:30 +0300 Subject: [PATCH] Pass CompAllocator by value (dotnet/coreclr#15025) Passing CompAllocator objects by value is advantageous because it no longer needs to be dynamically allocated and cached. CompAllocator instances can now be freely created, copied and stored, which makes adding new CompMemKind values easier. Together with other cleanup this also improves memory allocation performance by removing some extra levels of indirection that were previously required - jitstd::allocator had a pointer to CompAllocator, CompAllocator had a pointer to Compiler and Compiler finally had a pointer to ArenaAllocator. Without MEASURE_MEM_ALLOC enabled, both jitstd::allocator and CompAllocator now just contain a pointer to ArenaAllocator. When MEASURE_MEM_ALLOC is enabled CompAllocator also contains a pointer but to a MemStatsAllocator object that holds the relevant memory kind. This way CompAllocator is always pointer sized so that enabling MEASURE_MEM_ALLOC does not result in increased memory usage due to objects that store a CompAllocator instance. In order to implement this, 2 additional signficant changes have been made: * MemStats has been moved to ArenaAllocator, it's after all the allocator's job to maintain statistics. This also fixes some issues related to memory statistics, such as not tracking the memory allocated by the inlinee compiler (since that one used its own MemStats instance). * Extract the arena page pooling logic out of the allocator. It doesn't make sense to pool an allocator, it has very little state that can actually be reused and everyting else (including MemStats) needs to be reset on reuse. What really needs to be pooled is just a page of memory. Since this was touching allocation code the opportunity has been used to perform additional cleanup: * Remove unnecessary LSRA ListElementAllocator * Remove compGetMem and compGetMemArray * Make CompAllocator and HostAllocator more like the std allocator * Update HashTable to use CompAllocator * Update ArrayStack to use CompAllocator * Move CompAllocator & friends to alloc.h Commit migrated from https://github.com/dotnet/coreclr/commit/c2baf04cd2c2211334949ba12df2e49fd9109728 --- .../clr-jit-coding-conventions.md | 43 ++- src/coreclr/src/jit/alloc.cpp | 428 ++++++++++----------- src/coreclr/src/jit/alloc.h | 235 ++++++++++- src/coreclr/src/jit/arraystack.h | 16 +- src/coreclr/src/jit/bitset.cpp | 18 +- src/coreclr/src/jit/bitset.h | 2 +- src/coreclr/src/jit/block.cpp | 2 +- src/coreclr/src/jit/codegencommon.cpp | 8 +- src/coreclr/src/jit/compiler.cpp | 174 +-------- src/coreclr/src/jit/compiler.h | 185 ++------- src/coreclr/src/jit/compiler.hpp | 88 +---- src/coreclr/src/jit/compilerbitsettraits.hpp | 8 +- src/coreclr/src/jit/copyprop.cpp | 18 +- src/coreclr/src/jit/ee_il_dll.cpp | 2 +- src/coreclr/src/jit/eeinterface.cpp | 2 +- src/coreclr/src/jit/emit.cpp | 2 +- src/coreclr/src/jit/flowgraph.cpp | 16 +- src/coreclr/src/jit/gcencode.cpp | 2 +- src/coreclr/src/jit/gentree.cpp | 10 +- src/coreclr/src/jit/gentree.h | 4 +- src/coreclr/src/jit/hostallocator.cpp | 26 +- src/coreclr/src/jit/hostallocator.h | 38 +- src/coreclr/src/jit/importer.cpp | 6 +- src/coreclr/src/jit/jit.h | 102 ----- src/coreclr/src/jit/jitexpandarray.h | 29 +- src/coreclr/src/jit/jithashtable.h | 22 +- src/coreclr/src/jit/jitstd/allocator.h | 32 +- src/coreclr/src/jit/jitstd/utility.h | 13 - src/coreclr/src/jit/lclvars.cpp | 4 +- src/coreclr/src/jit/lir.cpp | 4 +- src/coreclr/src/jit/loopcloning.cpp | 2 +- src/coreclr/src/jit/loopcloning.h | 10 +- src/coreclr/src/jit/lower.cpp | 2 +- src/coreclr/src/jit/lsra.cpp | 7 +- src/coreclr/src/jit/lsra.h | 62 +-- src/coreclr/src/jit/lsrabuild.cpp | 6 +- src/coreclr/src/jit/morph.cpp | 14 +- src/coreclr/src/jit/rangecheck.cpp | 16 +- src/coreclr/src/jit/rangecheck.h | 8 +- src/coreclr/src/jit/regset.cpp | 2 +- src/coreclr/src/jit/scopeinfo.cpp | 4 +- src/coreclr/src/jit/smallhash.h | 55 ++- src/coreclr/src/jit/ssabuilder.cpp | 21 +- src/coreclr/src/jit/ssarenamestate.cpp | 10 +- src/coreclr/src/jit/ssarenamestate.h | 4 +- src/coreclr/src/jit/stacklevelsetter.cpp | 4 +- src/coreclr/src/jit/unwind.cpp | 10 +- src/coreclr/src/jit/utils.cpp | 10 +- src/coreclr/src/jit/utils.h | 6 +- src/coreclr/src/jit/valuenum.cpp | 10 +- src/coreclr/src/jit/valuenum.h | 8 +- 51 files changed, 741 insertions(+), 1069 deletions(-) diff --git a/docs/coreclr/coding-guidelines/clr-jit-coding-conventions.md b/docs/coreclr/coding-guidelines/clr-jit-coding-conventions.md index 0bab276..4873e1f 100644 --- a/docs/coreclr/coding-guidelines/clr-jit-coding-conventions.md +++ b/docs/coreclr/coding-guidelines/clr-jit-coding-conventions.md @@ -125,7 +125,8 @@ Note that these conventions are different from the CLR C++ Coding Conventions, d * [15.5.9 Global class objects](#15.5.9) * [15.6 Exceptions](#15.6) * [15.7 Code tuning for performance optimization](#15.7) - * [15.8 Obsoleting functions, classes and macros](#15.8) + * [15.8 Memory allocation](#15.8) + * [15.9 Obsoleting functions, classes and macros](#15.9) # 4 Principles @@ -1938,7 +1939,45 @@ In general, code should be written to be readable first, and optimized for perfo In the case of tight loops and code that has been analyzed to be a performance bottleneck, performance optimizations take a higher priority. Talk to the performance team if in doubt. -## 15.8 Obsoleting functions, classes and macros +## 15.8 Memory allocation + +All memory required during the compilation of a method must be allocated using the `Compiler`'s arena allocator. This allocator takes care of deallocating all the memory when compilation ends, avoiding memory leaks and simplifying memory management. + +However, the use of an arena allocator can increase memory usage and it's worth considering its impact when writing JIT code. Simple code changes can have a significant impact on memory usage, such as hoisting a `std::vector` variable out of a loop: +```c++ +std::vector outer; // same memory gets used for all iterations +for (...) +{ + std::vector inner; // this will allocate memory on every iteration + // and previously allocated memory is simply wasted +} +``` +Node based data structures (e.g linked lists) may benefit from retaining and reusing removed nodes, provided that maintaining free lists doesn't add significant cost. + +The arena allocator should not be used directly. `Compiler::getAllocator(CompMemKind)` returns a `CompAllocator` object that wraps the arena allocator and supports memory usage tracking when `MEASURE_MEM_ALLOC` is enabled. It's best to use a meaningful memory kind (e.g. not `CMK_Generic`) but exceptions can be made for small allocations. `CompAllocator` objects are always pointer sized and can be freely copied and stored (useful to avoid repeated `CompMemKind` references). + +The `new (CompAllocator)` operator should be preferred over `CompAllocator::allocate(size_t)`. The later is intended to be used only when constructors must not be run, such as when allocating arrays for containers like `std::vector`. +```c++ +// typical object allocation +RangeCheck* p = new (compiler->getAllocator(CMK_RangeCheck)) RangeCheck(compiler); +// slightly shorter alternative +RangeCheck* p = new (compiler, CMK_RangeCheck) RangeCheck(compiler); +// allocate an array with default initialized elements +LclVarDsc* p = new (compiler->getAllocator(CMK_LvaTable)) LclVarDsc[lvaCount]; +// use list initialization to zero out an array +unsigned* p = new (compiler->getAllocator(CMK_LvaTable)) unsigned[lvaTrackedCount] { }; +// use CompAllocator::allocate to allocate memory without doing any initialization... +LclVarDsc* p = compiler->getAllocator(CMK_LvaTable).allocate(lvaCount); +// ... and construct elements in place as needed +new (&p[i], jitstd::placement_t()) LclVarDsc(compiler) +``` +Note that certain classes (e.g. `GenTree`) provide their own `new` operator overloads, those should be used instead of the general purpose `new (CompAllocator)` operator. + +`jitstd` container classes accept a `CompAllocator` object by implicit conversion from `CompAllocator` to `jitstd::allocator`. + +Debug/checked code that needs to allocate memory outside of method compilation can use the `HostAllocator` class and the associated `new` operator. This is a normal memory allocator that requires manual memory deallocation. + +## 15.9 Obsoleting functions, classes and macros The Visual C++ compiler has support built in for marking various user defined constructs as deprecated. This functionality is accessed via one of two mechanisms: diff --git a/src/coreclr/src/jit/alloc.cpp b/src/coreclr/src/jit/alloc.cpp index 4cde866..8f1e0cc 100644 --- a/src/coreclr/src/jit/alloc.cpp +++ b/src/coreclr/src/jit/alloc.cpp @@ -9,44 +9,108 @@ #endif // defined(_MSC_VER) //------------------------------------------------------------------------ -// PooledAllocator: -// This subclass of `ArenaAllocator` is a singleton that always keeps -// a single default-sized page allocated. We try to use the singleton -// allocator as often as possible (i.e. for all non-concurrent -// method compilations). -class PooledAllocator : public ArenaAllocator +// SinglePagePool: Manage a single, default-sized page pool for ArenaAllocator. +// +// Allocating a page is slightly costly as it involves the JIT host and +// possibly the operating system as well. This pool avoids allocation +// in many cases (i.e. for all non-concurrent method compilations). +// +class ArenaAllocator::SinglePagePool { -private: - enum + // The page maintained by this pool + PageDescriptor* m_page; + // The page available for allocation (either m_page or &m_shutdownPage if shutdown was called) + PageDescriptor* m_availablePage; + // A dummy page that is made available during shutdown + PageDescriptor m_shutdownPage; + +public: + // Attempt to acquire the page managed by this pool. + PageDescriptor* tryAcquirePage(IEEMemoryManager* memoryManager) { - POOLED_ALLOCATOR_NOTINITIALIZED = 0, - POOLED_ALLOCATOR_IN_USE = 1, - POOLED_ALLOCATOR_AVAILABLE = 2, - POOLED_ALLOCATOR_SHUTDOWN = 3, - }; + assert(memoryManager != nullptr); - static PooledAllocator s_pooledAllocator; - static LONG s_pooledAllocatorState; + PageDescriptor* page = InterlockedExchangeT(&m_availablePage, nullptr); + if ((page != nullptr) && (page->m_memoryManager != memoryManager)) + { + // The pool page belongs to a different memory manager, release it. + releasePage(page, page->m_memoryManager); + page = nullptr; + } - PooledAllocator() : ArenaAllocator() + assert((page == nullptr) || isPoolPage(page)); + + return page; + } + + // Attempt to pool the specified page. + void tryPoolPage(PageDescriptor* page) { + assert(page != &m_shutdownPage); + + // Try to pool this page, give up if another thread has already pooled a page. + InterlockedCompareExchangeT(&m_page, page, nullptr); } - PooledAllocator(IEEMemoryManager* memoryManager); - PooledAllocator(const PooledAllocator& other) = delete; - PooledAllocator& operator=(const PooledAllocator& other) = delete; + // Check if a page is pooled. + bool isEmpty() + { + return (m_page == nullptr); + } -public: - PooledAllocator& operator=(PooledAllocator&& other); + // Check if the specified page is pooled. + bool isPoolPage(PageDescriptor* page) + { + return (m_page == page); + } - void destroy() override; + // Release the specified page. + PageDescriptor* releasePage(PageDescriptor* page, IEEMemoryManager* memoryManager) + { + // tryAcquirePage may end up releasing the shutdown page if shutdown was called. + assert((page == &m_shutdownPage) || isPoolPage(page)); + assert((page == &m_shutdownPage) || (memoryManager != nullptr)); + + // Normally m_availablePage should be null when releasePage is called but it can + // be the shutdown page if shutdown is called while the pool page is in use. + assert((m_availablePage == nullptr) || (m_availablePage == &m_shutdownPage)); + + PageDescriptor* next = page->m_next; + // Update the page's memory manager (replaces m_next that's not needed in this state). + page->m_memoryManager = memoryManager; + // Try to make the page available. This will fail if the pool was shutdown + // and then we need to free the page here. + PageDescriptor* shutdownPage = InterlockedCompareExchangeT(&m_availablePage, page, nullptr); + if (shutdownPage != nullptr) + { + assert(shutdownPage == &m_shutdownPage); + freeHostMemory(memoryManager, page); + } + + // Return the next page for caller's convenience. + return next; + } + + // Free the pooled page. + void shutdown() + { + // If the pool page is available then acquire it now so it can be freed. + // Also make the shutdown page available so that: + // - tryAcquirePage won't be return it because it has a null memory manager + // - releasePage won't be able to make the pool page available and instead will free it + PageDescriptor* page = InterlockedExchangeT(&m_availablePage, &m_shutdownPage); - static void shutdown(); + assert(page != &m_shutdownPage); + assert((page == nullptr) || isPoolPage(page)); - static ArenaAllocator* getPooledAllocator(IEEMemoryManager* memoryManager); + if ((page != nullptr) && (page->m_memoryManager != nullptr)) + { + freeHostMemory(page->m_memoryManager, page); + } + } }; -size_t ArenaAllocator::s_defaultPageSize = 0; +ArenaAllocator::SinglePagePool ArenaAllocator::s_pagePool; //------------------------------------------------------------------------ // ArenaAllocator::bypassHostAllocator: @@ -76,7 +140,7 @@ bool ArenaAllocator::bypassHostAllocator() // The default size of an arena page. size_t ArenaAllocator::getDefaultPageSize() { - return s_defaultPageSize; + return DEFAULT_PAGE_SIZE; } //------------------------------------------------------------------------ @@ -89,46 +153,26 @@ ArenaAllocator::ArenaAllocator() , m_nextFreeByte(nullptr) , m_lastFreeByte(nullptr) { + assert(!isInitialized()); } //------------------------------------------------------------------------ -// ArenaAllocator::ArenaAllocator: -// Constructs an arena allocator. +// ArenaAllocator::initialize: +// Initializes the arena allocator. // // Arguments: // memoryManager - The `IEEMemoryManager` instance that will be used to // allocate memory for arena pages. -ArenaAllocator::ArenaAllocator(IEEMemoryManager* memoryManager) - : m_memoryManager(memoryManager) - , m_firstPage(nullptr) - , m_lastPage(nullptr) - , m_nextFreeByte(nullptr) - , m_lastFreeByte(nullptr) -{ - assert(getDefaultPageSize() != 0); - assert(isInitialized()); -} - -//------------------------------------------------------------------------ -// ArenaAllocator::operator=: -// Move-assigns a `ArenaAllocator`. -ArenaAllocator& ArenaAllocator::operator=(ArenaAllocator&& other) +void ArenaAllocator::initialize(IEEMemoryManager* memoryManager) { assert(!isInitialized()); + m_memoryManager = memoryManager; + assert(isInitialized()); - m_memoryManager = other.m_memoryManager; - m_firstPage = other.m_firstPage; - m_lastPage = other.m_lastPage; - m_nextFreeByte = other.m_nextFreeByte; - m_lastFreeByte = other.m_lastFreeByte; - - other.m_memoryManager = nullptr; - other.m_firstPage = nullptr; - other.m_lastPage = nullptr; - other.m_nextFreeByte = nullptr; - other.m_lastFreeByte = nullptr; - - return *this; +#if MEASURE_MEM_ALLOC + memset(&m_stats, 0, sizeof(m_stats)); + memset(&m_statsAllocators, 0, sizeof(m_statsAllocators)); +#endif // MEASURE_MEM_ALLOC } bool ArenaAllocator::isInitialized() @@ -146,7 +190,7 @@ bool ArenaAllocator::isInitialized() // // Return Value: // A pointer to the first usable byte of the newly allocated page. -void* ArenaAllocator::allocateNewPage(size_t size, bool canThrow) +void* ArenaAllocator::allocateNewPage(size_t size) { assert(isInitialized()); @@ -155,11 +199,7 @@ void* ArenaAllocator::allocateNewPage(size_t size, bool canThrow) // Check for integer overflow if (pageSize < size) { - if (canThrow) - { - NOMEM(); - } - + NOMEM(); return nullptr; } @@ -173,34 +213,52 @@ void* ArenaAllocator::allocateNewPage(size_t size, bool canThrow) m_lastPage->m_usedBytes = m_nextFreeByte - m_lastPage->m_contents; } - // Round up to a default-sized page if necessary - if (pageSize <= s_defaultPageSize) - { - pageSize = s_defaultPageSize; - } + PageDescriptor* newPage = nullptr; + bool tryPoolNewPage = false; - // Round to the nearest multiple of OS page size if necessary if (!bypassHostAllocator()) { + // Round to the nearest multiple of OS page size pageSize = roundUp(pageSize, DEFAULT_PAGE_SIZE); + + // If this is the first time we allocate a page then try to use the pool page. + if ((m_firstPage == nullptr) && (pageSize == DEFAULT_PAGE_SIZE)) + { + newPage = s_pagePool.tryAcquirePage(m_memoryManager); + + if (newPage == nullptr) + { + // If there's no pool page yet then try to pool the newly allocated page. + tryPoolNewPage = s_pagePool.isEmpty(); + } + else + { + assert(newPage->m_memoryManager == m_memoryManager); + assert(newPage->m_pageBytes == DEFAULT_PAGE_SIZE); + } + } } - // Allocate the new page - PageDescriptor* newPage = (PageDescriptor*)allocateHostMemory(pageSize); if (newPage == nullptr) { - if (canThrow) + // Allocate the new page + newPage = static_cast(allocateHostMemory(m_memoryManager, pageSize)); + + if (newPage == nullptr) { NOMEM(); + return nullptr; } - return nullptr; + if (tryPoolNewPage) + { + s_pagePool.tryPoolPage(newPage); + } } // Append the new page to the end of the list newPage->m_next = nullptr; newPage->m_pageBytes = pageSize; - newPage->m_previous = m_lastPage; newPage->m_usedBytes = 0; // m_usedBytes is meaningless until a new page is allocated. // Instead of letting it contain garbage (so to confuse us), // set it to zero. @@ -231,11 +289,20 @@ void ArenaAllocator::destroy() { assert(isInitialized()); + PageDescriptor* page = m_firstPage; + + // If the first page is the pool page then return it to the pool. + if ((page != nullptr) && s_pagePool.isPoolPage(page)) + { + page = s_pagePool.releasePage(page, m_memoryManager); + } + // Free all of the allocated pages - for (PageDescriptor *page = m_firstPage, *next; page != nullptr; page = next) + for (PageDescriptor* next; page != nullptr; page = next) { + assert(!s_pagePool.isPoolPage(page)); next = page->m_next; - freeHostMemory(page); + freeHostMemory(m_memoryManager, page); } // Clear out the allocator's fields @@ -244,6 +311,7 @@ void ArenaAllocator::destroy() m_lastPage = nullptr; m_nextFreeByte = nullptr; m_lastFreeByte = nullptr; + assert(!isInitialized()); } // The debug version of the allocator may allocate directly from the @@ -266,9 +334,9 @@ void ArenaAllocator::destroy() // // Return Value: // A pointer to the allocated memory. -void* ArenaAllocator::allocateHostMemory(size_t size) +void* ArenaAllocator::allocateHostMemory(IEEMemoryManager* memoryManager, size_t size) { - assert(isInitialized()); + assert(memoryManager != nullptr); #if defined(DEBUG) if (bypassHostAllocator()) @@ -280,7 +348,7 @@ void* ArenaAllocator::allocateHostMemory(size_t size) return ClrAllocInProcessHeap(0, S_SIZE_T(size)); } #else // defined(DEBUG) - return m_memoryManager->ClrVirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE); + return memoryManager->ClrVirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE); #endif // !defined(DEBUG) } @@ -290,9 +358,9 @@ void* ArenaAllocator::allocateHostMemory(size_t size) // // Arguments: // block - A pointer to the memory to free. -void ArenaAllocator::freeHostMemory(void* block) +void ArenaAllocator::freeHostMemory(IEEMemoryManager* memoryManager, void* block) { - assert(isInitialized()); + assert(memoryManager != nullptr); #if defined(DEBUG) if (bypassHostAllocator()) @@ -304,7 +372,7 @@ void ArenaAllocator::freeHostMemory(void* block) ClrFreeInProcessHeap(0, block); } #else // defined(DEBUG) - m_memoryManager->ClrVirtualFree(block, 0, MEM_RELEASE); + memoryManager->ClrVirtualFree(block, 0, MEM_RELEASE); #endif // !defined(DEBUG) } @@ -362,175 +430,97 @@ size_t ArenaAllocator::getTotalBytesUsed() } //------------------------------------------------------------------------ -// ArenaAllocator::startup: -// Performs any necessary initialization for the arena allocator -// subsystem. -void ArenaAllocator::startup() -{ - s_defaultPageSize = bypassHostAllocator() ? (size_t)MIN_PAGE_SIZE : (size_t)DEFAULT_PAGE_SIZE; -} - -//------------------------------------------------------------------------ // ArenaAllocator::shutdown: // Performs any necessary teardown for the arena allocator subsystem. void ArenaAllocator::shutdown() { - PooledAllocator::shutdown(); + s_pagePool.shutdown(); } -PooledAllocator PooledAllocator::s_pooledAllocator; -LONG PooledAllocator::s_pooledAllocatorState = POOLED_ALLOCATOR_NOTINITIALIZED; +#if MEASURE_MEM_ALLOC +CritSecObject ArenaAllocator::s_statsLock; +ArenaAllocator::AggregateMemStats ArenaAllocator::s_aggStats; +ArenaAllocator::MemStats ArenaAllocator::s_maxStats; -//------------------------------------------------------------------------ -// PooledAllocator::PooledAllocator: -// Constructs a `PooledAllocator`. -PooledAllocator::PooledAllocator(IEEMemoryManager* memoryManager) : ArenaAllocator(memoryManager) -{ -} +const char* ArenaAllocator::MemStats::s_CompMemKindNames[] = { +#define CompMemKindMacro(kind) #kind, +#include "compmemkind.h" +}; -//------------------------------------------------------------------------ -// PooledAllocator::operator=: -// Move-assigns a `PooledAllocator`. -PooledAllocator& PooledAllocator::operator=(PooledAllocator&& other) +void ArenaAllocator::MemStats::Print(FILE* f) { - *((ArenaAllocator*)this) = std::move((ArenaAllocator &&)other); - return *this; + fprintf(f, "count: %10u, size: %10llu, max = %10llu\n", allocCnt, allocSz, allocSzMax); + fprintf(f, "allocateMemory: %10llu, nraUsed: %10llu\n", nraTotalSizeAlloc, nraTotalSizeUsed); + PrintByKind(f); } -//------------------------------------------------------------------------ -// PooledAllocator::shutdown: -// Performs any necessary teardown for the pooled allocator. -// -// Notes: -// If the allocator has been initialized and is in use when this method is called, -// it is up to whatever is using the pooled allocator to call `destroy` in order -// to free its memory. -void PooledAllocator::shutdown() +void ArenaAllocator::MemStats::PrintByKind(FILE* f) { - LONG oldState = InterlockedExchange(&s_pooledAllocatorState, POOLED_ALLOCATOR_SHUTDOWN); - switch (oldState) + fprintf(f, "\nAlloc'd bytes by kind:\n %20s | %10s | %7s\n", "kind", "size", "pct"); + fprintf(f, " %20s-+-%10s-+-%7s\n", "--------------------", "----------", "-------"); + float allocSzF = static_cast(allocSz); + for (int cmk = 0; cmk < CMK_Count; cmk++) { - case POOLED_ALLOCATOR_NOTINITIALIZED: - case POOLED_ALLOCATOR_SHUTDOWN: - case POOLED_ALLOCATOR_IN_USE: - return; - - case POOLED_ALLOCATOR_AVAILABLE: - // The pooled allocator was initialized and not in use; we must destroy it. - s_pooledAllocator.destroy(); - break; + float pct = 100.0f * static_cast(allocSzByKind[cmk]) / allocSzF; + fprintf(f, " %20s | %10llu | %6.2f%%\n", s_CompMemKindNames[cmk], allocSzByKind[cmk], pct); } + fprintf(f, "\n"); } -//------------------------------------------------------------------------ -// PooledAllocator::getPooledAllocator: -// Returns the pooled allocator if it is not already in use. -// -// Arguments: -// memoryManager: The `IEEMemoryManager` instance in use by the caller. -// -// Return Value: -// A pointer to the pooled allocator if it is available or `nullptr` -// if it is already in use. -// -// Notes: -// Calling `destroy` on the returned allocator will return it to the -// pool. -ArenaAllocator* PooledAllocator::getPooledAllocator(IEEMemoryManager* memoryManager) +void ArenaAllocator::AggregateMemStats::Print(FILE* f) { - LONG oldState = InterlockedExchange(&s_pooledAllocatorState, POOLED_ALLOCATOR_IN_USE); - switch (oldState) + fprintf(f, "For %9u methods:\n", nMethods); + if (nMethods == 0) { - case POOLED_ALLOCATOR_IN_USE: - case POOLED_ALLOCATOR_SHUTDOWN: - // Either the allocator is in use or this call raced with a call to `shutdown`. - // Return `nullptr`. - return nullptr; - - case POOLED_ALLOCATOR_AVAILABLE: - if (s_pooledAllocator.m_memoryManager != memoryManager) - { - // The allocator is available, but it was initialized with a different - // memory manager. Release it and return `nullptr`. - InterlockedExchange(&s_pooledAllocatorState, POOLED_ALLOCATOR_AVAILABLE); - return nullptr; - } - - return &s_pooledAllocator; - - case POOLED_ALLOCATOR_NOTINITIALIZED: - { - PooledAllocator allocator(memoryManager); - if (allocator.allocateNewPage(0, false) == nullptr) - { - // Failed to grab the initial memory page. - InterlockedExchange(&s_pooledAllocatorState, POOLED_ALLOCATOR_NOTINITIALIZED); - return nullptr; - } - - s_pooledAllocator = std::move(allocator); - } - - return &s_pooledAllocator; - - default: - assert(!"Unknown pooled allocator state"); - unreached(); + return; } + fprintf(f, " count: %12u (avg %7u per method)\n", allocCnt, allocCnt / nMethods); + fprintf(f, " alloc size : %12llu (avg %7llu per method)\n", allocSz, allocSz / nMethods); + fprintf(f, " max alloc : %12llu\n", allocSzMax); + fprintf(f, "\n"); + fprintf(f, " allocateMemory : %12llu (avg %7llu per method)\n", nraTotalSizeAlloc, nraTotalSizeAlloc / nMethods); + fprintf(f, " nraUsed : %12llu (avg %7llu per method)\n", nraTotalSizeUsed, nraTotalSizeUsed / nMethods); + PrintByKind(f); } -//------------------------------------------------------------------------ -// PooledAllocator::destroy: -// Performs any necessary teardown for an `PooledAllocator` and returns the allocator -// to the pool. -void PooledAllocator::destroy() +ArenaAllocator::MemStatsAllocator* ArenaAllocator::getMemStatsAllocator(CompMemKind kind) { - assert(isInitialized()); - assert(this == &s_pooledAllocator); - assert(s_pooledAllocatorState == POOLED_ALLOCATOR_IN_USE || s_pooledAllocatorState == POOLED_ALLOCATOR_SHUTDOWN); - assert(m_firstPage != nullptr); + assert(kind < CMK_Count); - // Free all but the first allocated page - for (PageDescriptor *page = m_firstPage->m_next, *next; page != nullptr; page = next) + if (m_statsAllocators[kind].m_arena == nullptr) { - next = page->m_next; - freeHostMemory(page); + m_statsAllocators[kind].m_arena = this; + m_statsAllocators[kind].m_kind = kind; } - // Reset the relevant state to point back to the first byte of the first page - m_firstPage->m_next = nullptr; - m_lastPage = m_firstPage; - m_nextFreeByte = m_firstPage->m_contents; - m_lastFreeByte = (BYTE*)m_firstPage + m_firstPage->m_pageBytes; + return &m_statsAllocators[kind]; +} - assert(getTotalBytesAllocated() == s_defaultPageSize); +void ArenaAllocator::finishMemStats() +{ + m_stats.nraTotalSizeAlloc = getTotalBytesAllocated(); + m_stats.nraTotalSizeUsed = getTotalBytesUsed(); - // If we've already been shut down, free the first page. Otherwise, return the allocator to the pool. - if (s_pooledAllocatorState == POOLED_ALLOCATOR_SHUTDOWN) + CritSecHolder statsLock(s_statsLock); + s_aggStats.Add(m_stats); + if (m_stats.allocSz > s_maxStats.allocSz) { - ArenaAllocator::destroy(); - } - else - { - InterlockedExchange(&s_pooledAllocatorState, POOLED_ALLOCATOR_AVAILABLE); + s_maxStats = m_stats; } } -//------------------------------------------------------------------------ -// ArenaAllocator::getPooledAllocator: -// Returns the pooled allocator if it is not already in use. -// -// Arguments: -// memoryManager: The `IEEMemoryManager` instance in use by the caller. -// -// Return Value: -// A pointer to the pooled allocator if it is available or `nullptr` -// if it is already in use. -// -// Notes: -// Calling `destroy` on the returned allocator will return it to the -// pool. -ArenaAllocator* ArenaAllocator::getPooledAllocator(IEEMemoryManager* memoryManager) +void ArenaAllocator::dumpMemStats(FILE* file) +{ + m_stats.Print(file); +} + +void ArenaAllocator::dumpAggregateMemStats(FILE* file) +{ + s_aggStats.Print(file); +} + +void ArenaAllocator::dumpMaxMemStats(FILE* file) { - return PooledAllocator::getPooledAllocator(memoryManager); + s_maxStats.Print(file); } +#endif // MEASURE_MEM_ALLOC diff --git a/src/coreclr/src/jit/alloc.h b/src/coreclr/src/jit/alloc.h index 62e5bc0..7090c7f 100644 --- a/src/coreclr/src/jit/alloc.h +++ b/src/coreclr/src/jit/alloc.h @@ -9,17 +9,32 @@ #include "host.h" #endif // defined(_HOST_H_) +// CompMemKind values are used to tag memory allocations performed via +// the compiler's allocator so that the memory usage of various compiler +// components can be tracked separately (when MEASURE_MEM_ALLOC is defined). + +enum CompMemKind +{ +#define CompMemKindMacro(kind) CMK_##kind, +#include "compmemkind.h" + CMK_Count +}; + class ArenaAllocator { private: ArenaAllocator(const ArenaAllocator& other) = delete; ArenaAllocator& operator=(const ArenaAllocator& other) = delete; + ArenaAllocator& operator=(ArenaAllocator&& other) = delete; -protected: struct PageDescriptor { - PageDescriptor* m_next; - PageDescriptor* m_previous; + union { + // Used when the page is allocated + PageDescriptor* m_next; + // Used by the pooled page when available + IEEMemoryManager* m_memoryManager; + }; size_t m_pageBytes; // # of bytes allocated size_t m_usedBytes; // # of bytes actually used. (This is only valid when we've allocated a new page.) @@ -33,10 +48,11 @@ protected: enum { DEFAULT_PAGE_SIZE = 16 * OS_page_size, - MIN_PAGE_SIZE = sizeof(PageDescriptor) }; - static size_t s_defaultPageSize; + class SinglePagePool; + + static SinglePagePool s_pagePool; IEEMemoryManager* m_memoryManager; @@ -49,15 +65,93 @@ protected: bool isInitialized(); - void* allocateNewPage(size_t size, bool canThrow); + void* allocateNewPage(size_t size); + + static void* allocateHostMemory(IEEMemoryManager* memoryManager, size_t size); + static void freeHostMemory(IEEMemoryManager* memoryManager, void* block); + +#if MEASURE_MEM_ALLOC + struct MemStats + { + unsigned allocCnt; // # of allocs + UINT64 allocSz; // total size of those alloc. + UINT64 allocSzMax; // Maximum single allocation. + UINT64 allocSzByKind[CMK_Count]; // Classified by "kind". + UINT64 nraTotalSizeAlloc; + UINT64 nraTotalSizeUsed; + + static const char* s_CompMemKindNames[]; // Names of the kinds. + + void AddAlloc(size_t sz, CompMemKind cmk) + { + allocCnt += 1; + allocSz += sz; + if (sz > allocSzMax) + { + allocSzMax = sz; + } + allocSzByKind[cmk] += sz; + } + + void Print(FILE* f); // Print these stats to file. + void PrintByKind(FILE* f); // Do just the by-kind histogram part. + }; + + struct AggregateMemStats : public MemStats + { + unsigned nMethods; + + void Add(const MemStats& ms) + { + nMethods++; + allocCnt += ms.allocCnt; + allocSz += ms.allocSz; + allocSzMax = max(allocSzMax, ms.allocSzMax); + for (int i = 0; i < CMK_Count; i++) + { + allocSzByKind[i] += ms.allocSzByKind[i]; + } + nraTotalSizeAlloc += ms.nraTotalSizeAlloc; + nraTotalSizeUsed += ms.nraTotalSizeUsed; + } + + void Print(FILE* f); // Print these stats to file. + }; + +public: + struct MemStatsAllocator + { + ArenaAllocator* m_arena; + CompMemKind m_kind; + + void* allocateMemory(size_t sz) + { + m_arena->m_stats.AddAlloc(sz, m_kind); + return m_arena->allocateMemory(sz); + } + }; + +private: + static CritSecObject s_statsLock; // This lock protects the data structures below. + static MemStats s_maxStats; // Stats for the allocator with the largest amount allocated. + static AggregateMemStats s_aggStats; // Aggregates statistics for all allocators. + + MemStats m_stats; + MemStatsAllocator m_statsAllocators[CMK_Count]; + +public: + MemStatsAllocator* getMemStatsAllocator(CompMemKind kind); + void finishMemStats(); + void dumpMemStats(FILE* file); - void* allocateHostMemory(size_t size); - void freeHostMemory(void* block); + static void dumpMaxMemStats(FILE* file); + static void dumpAggregateMemStats(FILE* file); +#endif // MEASURE_MEM_ALLOC public: ArenaAllocator(); - ArenaAllocator(IEEMemoryManager* memoryManager); - ArenaAllocator& operator=(ArenaAllocator&& other); + + void initialize(IEEMemoryManager* memoryManager); // NOTE: it would be nice to have a destructor on this type to ensure that any value that // goes out of scope is either uninitialized or has been torn down via a call to @@ -65,7 +159,7 @@ public: // revisiting EH in the JIT; such a destructor could be added if SEH is removed // as part of that work. - virtual void destroy(); + void destroy(); inline void* allocateMemory(size_t sz); @@ -75,10 +169,7 @@ public: static bool bypassHostAllocator(); static size_t getDefaultPageSize(); - static void startup(); static void shutdown(); - - static ArenaAllocator* getPooledAllocator(IEEMemoryManager* memoryManager); }; //------------------------------------------------------------------------ @@ -103,7 +194,7 @@ inline void* ArenaAllocator::allocateMemory(size_t size) assert(size != 0); // Ensure that we always allocate in pointer sized increments. - size = (size_t)roundUp(size, sizeof(size_t)); + size = roundUp(size, sizeof(size_t)); #if defined(DEBUG) if (JitConfig.ShouldInjectFault() != 0) @@ -127,7 +218,7 @@ inline void* ArenaAllocator::allocateMemory(size_t size) if (m_nextFreeByte > m_lastFreeByte) { - block = allocateNewPage(size, true); + block = allocateNewPage(size); } #if defined(DEBUG) @@ -137,4 +228,116 @@ inline void* ArenaAllocator::allocateMemory(size_t size) return block; } +// Allows general purpose code (e.g. collection classes) to allocate +// memory of a pre-determined kind via an arena allocator. + +class CompAllocator +{ +#if MEASURE_MEM_ALLOC + ArenaAllocator::MemStatsAllocator* m_arena; +#else + ArenaAllocator* m_arena; +#endif + +public: + CompAllocator(ArenaAllocator* arena, CompMemKind cmk) +#if MEASURE_MEM_ALLOC + : m_arena(arena->getMemStatsAllocator(cmk)) +#else + : m_arena(arena) +#endif + { + } + + // Allocate a block of memory suitable to store `count` objects of type `T`. + // Zero-length allocations are not allowed. + template + T* allocate(size_t count) + { + // Ensure that count * sizeof(T) does not overflow. + if (count > (SIZE_MAX / sizeof(T))) + { + NOMEM(); + } + + void* p = m_arena->allocateMemory(count * sizeof(T)); + + // Ensure that the allocator returned sizeof(size_t) aligned memory. + assert((size_t(p) & (sizeof(size_t) - 1)) == 0); + + return static_cast(p); + } + + // Deallocate a block of memory previously allocated by `allocate`. + // The arena allocator does not release memory so this doesn't do anything. + void deallocate(void* p) + { + } +}; + +// Global operator new overloads that work with CompAllocator + +inline void* __cdecl operator new(size_t n, CompAllocator alloc) +{ + return alloc.allocate(n); +} + +inline void* __cdecl operator new[](size_t n, CompAllocator alloc) +{ + return alloc.allocate(n); +} + +// A CompAllocator wrapper that implements IAllocator and allows zero-length +// memory allocations (the arena allocator does not support zero-length +// allocation). + +class CompIAllocator : public IAllocator +{ + CompAllocator m_alloc; + char m_zeroLenAllocTarg; + +public: + CompIAllocator(CompAllocator alloc) : m_alloc(alloc) + { + } + + // Allocates a block of memory at least `sz` in size. + virtual void* Alloc(size_t sz) override + { + if (sz == 0) + { + return &m_zeroLenAllocTarg; + } + else + { + return m_alloc.allocate(sz); + } + } + + // Allocates a block of memory at least `elems * elemSize` in size. + virtual void* ArrayAlloc(size_t elems, size_t elemSize) override + { + if ((elems == 0) || (elemSize == 0)) + { + return &m_zeroLenAllocTarg; + } + else + { + // Ensure that elems * elemSize does not overflow. + if (elems > (SIZE_MAX / elemSize)) + { + NOMEM(); + } + + return m_alloc.allocate(elems * elemSize); + } + } + + // Frees the block of memory pointed to by p. + virtual void Free(void* p) override + { + m_alloc.deallocate(p); + } +}; + #endif // _ALLOC_H_ diff --git a/src/coreclr/src/jit/arraystack.h b/src/coreclr/src/jit/arraystack.h index c6ac6b2..2565e19 100644 --- a/src/coreclr/src/jit/arraystack.h +++ b/src/coreclr/src/jit/arraystack.h @@ -11,14 +11,12 @@ class ArrayStack static const int builtinSize = 8; public: - ArrayStack(Compiler* comp, int initialSize = builtinSize) + ArrayStack(CompAllocator alloc, int initialSize = builtinSize) : m_alloc(alloc) { - compiler = comp; - if (initialSize > builtinSize) { maxIndex = initialSize; - data = new (compiler, CMK_ArrayStack) T[initialSize]; + data = new (alloc) T[initialSize]; } else { @@ -58,7 +56,7 @@ public: // and copy over T* oldData = data; noway_assert(maxIndex * 2 > maxIndex); - data = new (compiler, CMK_ArrayStack) T[maxIndex * 2]; + data = new (m_alloc) T[maxIndex * 2]; for (int i = 0; i < maxIndex; i++) { data[i] = oldData[i]; @@ -149,10 +147,10 @@ public: } private: - Compiler* compiler; // needed for allocation - int tosIndex; // first free location - int maxIndex; - T* data; + CompAllocator m_alloc; + int tosIndex; // first free location + int maxIndex; + T* data; // initial allocation T builtinData[builtinSize]; }; diff --git a/src/coreclr/src/jit/bitset.cpp b/src/coreclr/src/jit/bitset.cpp index 6802c1a..5771e18 100644 --- a/src/coreclr/src/jit/bitset.cpp +++ b/src/coreclr/src/jit/bitset.cpp @@ -98,30 +98,30 @@ void BitSetSupport::RunTests(Env env) class TestBitSetTraits { public: - static void* Alloc(CompAllocator* alloc, size_t byteSize) + static void* Alloc(CompAllocator alloc, size_t byteSize) { - return alloc->Alloc(byteSize); + return alloc.allocate(byteSize); } - static unsigned GetSize(CompAllocator* alloc) + static unsigned GetSize(CompAllocator alloc) { return 64; } - static unsigned GetArrSize(CompAllocator* alloc, unsigned elemSize) + static unsigned GetArrSize(CompAllocator alloc, unsigned elemSize) { assert(elemSize == sizeof(size_t)); return (64 / 8) / sizeof(size_t); } - static unsigned GetEpoch(CompAllocator* alloc) + static unsigned GetEpoch(CompAllocator alloc) { return 0; } }; -void BitSetSupport::TestSuite(CompAllocator* env) +void BitSetSupport::TestSuite(CompAllocator env) { - BitSetSupport::RunTests(env); - BitSetSupport::RunTests(env); - BitSetSupport::RunTests, BSUInt64Class, CompAllocator*, + BitSetSupport::RunTests(env); + BitSetSupport::RunTests(env); + BitSetSupport::RunTests, BSUInt64Class, CompAllocator, TestBitSetTraits>(env); } #endif diff --git a/src/coreclr/src/jit/bitset.h b/src/coreclr/src/jit/bitset.h index df03dee..a0192e6 100644 --- a/src/coreclr/src/jit/bitset.h +++ b/src/coreclr/src/jit/bitset.h @@ -40,7 +40,7 @@ public: #ifdef DEBUG // This runs the "TestSuite" method for a few important instantiations of BitSet. - static void TestSuite(CompAllocator* env); + static void TestSuite(CompAllocator env); #endif enum Operation diff --git a/src/coreclr/src/jit/block.cpp b/src/coreclr/src/jit/block.cpp index 2653698..00c70af 100644 --- a/src/coreclr/src/jit/block.cpp +++ b/src/coreclr/src/jit/block.cpp @@ -588,7 +588,7 @@ const char* BasicBlock::dspToString(int blockNumPadding /* = 2*/) // Allocation function for MemoryPhiArg. void* BasicBlock::MemoryPhiArg::operator new(size_t sz, Compiler* comp) { - return comp->compGetMem(sz, CMK_MemoryPhiArg); + return comp->getAllocator(CMK_MemoryPhiArg).allocate(sz); } //------------------------------------------------------------------------ diff --git a/src/coreclr/src/jit/codegencommon.cpp b/src/coreclr/src/jit/codegencommon.cpp index fcc0676..56cebe0 100644 --- a/src/coreclr/src/jit/codegencommon.cpp +++ b/src/coreclr/src/jit/codegencommon.cpp @@ -11239,9 +11239,7 @@ void CodeGen::genIPmappingAdd(IL_OFFSETX offsx, bool isLabel) /* Create a mapping entry and append it to the list */ - Compiler::IPmappingDsc* addMapping = - (Compiler::IPmappingDsc*)compiler->compGetMem(sizeof(*addMapping), CMK_DebugInfo); - + Compiler::IPmappingDsc* addMapping = compiler->getAllocator(CMK_DebugInfo).allocate(1); addMapping->ipmdNativeLoc.CaptureLocation(getEmitter()); addMapping->ipmdILoffsx = offsx; addMapping->ipmdIsLabel = isLabel; @@ -11300,9 +11298,7 @@ void CodeGen::genIPmappingAddToFront(IL_OFFSETX offsx) /* Create a mapping entry and prepend it to the list */ - Compiler::IPmappingDsc* addMapping = - (Compiler::IPmappingDsc*)compiler->compGetMem(sizeof(*addMapping), CMK_DebugInfo); - + Compiler::IPmappingDsc* addMapping = compiler->getAllocator(CMK_DebugInfo).allocate(1); addMapping->ipmdNativeLoc.CaptureLocation(getEmitter()); addMapping->ipmdILoffsx = offsx; addMapping->ipmdIsLabel = true; diff --git a/src/coreclr/src/jit/compiler.cpp b/src/coreclr/src/jit/compiler.cpp index 170cf38..4bafdfd 100644 --- a/src/coreclr/src/jit/compiler.cpp +++ b/src/coreclr/src/jit/compiler.cpp @@ -1212,11 +1212,11 @@ struct FileLine FileLine(const char* file, unsigned line, const char* condStr) : m_line(line) { size_t newSize = (strlen(file) + 1) * sizeof(char); - m_file = (char*)HostAllocator::getHostAllocator()->Alloc(newSize); + m_file = HostAllocator::getHostAllocator().allocate(newSize); strcpy_s(m_file, newSize, file); newSize = (strlen(condStr) + 1) * sizeof(char); - m_condStr = (char*)HostAllocator::getHostAllocator()->Alloc(newSize); + m_condStr = HostAllocator::getHostAllocator().allocate(newSize); strcpy_s(m_condStr, newSize, condStr); } @@ -1401,9 +1401,6 @@ void Compiler::compStartup() grossVMsize = grossNCsize = totalNCsize = 0; #endif // DISPLAY_SIZES - // Initialize the JIT's allocator. - ArenaAllocator::startup(); - /* Initialize the table of tree node sizes */ GenTree::InitNodeSize(); @@ -1709,10 +1706,10 @@ void Compiler::compShutdown() if (s_dspMemStats) { fprintf(fout, "\nAll allocations:\n"); - s_aggMemStats.Print(jitstdout); + ArenaAllocator::dumpAggregateMemStats(jitstdout); fprintf(fout, "\nLargest method:\n"); - s_maxCompMemStats.Print(jitstdout); + ArenaAllocator::dumpMaxMemStats(jitstdout); fprintf(fout, "\n"); fprintf(fout, "---------------------------------------------------\n"); @@ -1918,7 +1915,7 @@ void Compiler::compDisplayStaticSizes(FILE* fout) void Compiler::compInit(ArenaAllocator* pAlloc, InlineInfo* inlineInfo) { assert(pAlloc); - compAllocator = pAlloc; + compArenaAllocator = pAlloc; // Inlinee Compile object will only be allocated when needed for the 1st time. InlineeCompiler = nullptr; @@ -1934,32 +1931,11 @@ void Compiler::compInit(ArenaAllocator* pAlloc, InlineInfo* inlineInfo) { m_inlineStrategy = nullptr; compInlineResult = inlineInfo->inlineResult; - - // We shouldn't be using the compAllocatorGeneric for other than the root compiler. - compAllocatorGeneric = nullptr; -#if MEASURE_MEM_ALLOC - compAllocatorBitset = nullptr; - compAllocatorGC = nullptr; - compAllocatorLoopHoist = nullptr; -#ifdef DEBUG - compAllocatorDebugOnly = nullptr; -#endif // DEBUG -#endif // MEASURE_MEM_ALLOC } else { m_inlineStrategy = new (this, CMK_Inlining) InlineStrategy(this); compInlineResult = nullptr; - - compAllocatorGeneric = new (this, CMK_Unknown) CompAllocator(this, CMK_Generic); -#if MEASURE_MEM_ALLOC - compAllocatorBitset = new (this, CMK_Unknown) CompAllocator(this, CMK_bitset); - compAllocatorGC = new (this, CMK_Unknown) CompAllocator(this, CMK_GC); - compAllocatorLoopHoist = new (this, CMK_Unknown) CompAllocator(this, CMK_LoopHoist); -#ifdef DEBUG - compAllocatorDebugOnly = new (this, CMK_Unknown) CompAllocator(this, CMK_DebugOnly); -#endif // DEBUG -#endif // MEASURE_MEM_ALLOC } #ifdef FEATURE_TRACELOGGING @@ -2007,9 +1983,6 @@ void Compiler::compInit(ArenaAllocator* pAlloc, InlineInfo* inlineInfo) optLoopsCloned = 0; -#if MEASURE_MEM_ALLOC - genMemStats.Init(); -#endif // MEASURE_MEM_ALLOC #if LOOP_HOIST_STATS m_loopsConsidered = 0; m_curLoopHasHoistedExpression = false; @@ -2256,47 +2229,6 @@ unsigned char Compiler::compGetJitDefaultFill() #endif // DEBUG -/***************************************************************************** - * - * The central memory allocation routine used by the compiler. Normally this - * is a simple inline method defined in compiler.hpp, but for debugging it's - * often convenient to keep it non-inline. - */ - -#ifdef DEBUG - -void* Compiler::compGetMem(size_t sz, CompMemKind cmk) -{ -#if 0 -#if SMALL_TREE_NODES - if (sz != TREE_NODE_SZ_SMALL && - sz != TREE_NODE_SZ_LARGE && sz > 32) - { - printf("Alloc %3u bytes\n", sz); - } -#else - if (sz != sizeof(GenTree) && sz > 32) - { - printf("Alloc %3u bytes\n", sz); - } -#endif -#endif // 0 - -#if MEASURE_MEM_ALLOC - genMemStats.AddAlloc(sz, cmk); -#endif - - void* ptr = compAllocator->allocateMemory(sz); - - // Verify that the current block is aligned. Only then will the next - // block allocated be on an aligned boundary. - assert((size_t(ptr) & (sizeof(size_t) - 1)) == 0); - - return ptr; -} - -#endif - /*****************************************************************************/ #ifdef DEBUG /*****************************************************************************/ @@ -5239,7 +5171,7 @@ bool Compiler::compQuirkForPPP() assert((varDscExposedStruct->lvExactSize / TARGET_POINTER_SIZE) == 8); BYTE* oldGCPtrs = varDscExposedStruct->lvGcLayout; - BYTE* newGCPtrs = (BYTE*)compGetMem(8, CMK_LvaTable); + BYTE* newGCPtrs = getAllocator(CMK_LvaTable).allocate(8); for (int i = 0; i < 4; i++) { @@ -5432,7 +5364,7 @@ int Compiler::compCompile(CORINFO_METHOD_HANDLE methodHnd, info.compMethodName = eeGetMethodName(methodHnd, &classNamePtr); unsigned len = (unsigned)roundUp(strlen(classNamePtr) + 1); - info.compClassName = (char*)compGetMem(len, CMK_DebugOnly); + info.compClassName = getAllocator(CMK_DebugOnly).allocate(len); strcpy_s((char*)info.compClassName, len, classNamePtr); info.compFullName = eeGetMethodFullName(methodHnd); @@ -5599,26 +5531,16 @@ void Compiler::compCompileFinish() #if MEASURE_MEM_ALLOC { - // Grab the relevant lock. - CritSecHolder statsLock(s_memStatsLock); - - // Make the updates. - genMemStats.nraTotalSizeAlloc = compGetAllocator()->getTotalBytesAllocated(); - genMemStats.nraTotalSizeUsed = compGetAllocator()->getTotalBytesUsed(); - memAllocHist.record((unsigned)((genMemStats.nraTotalSizeAlloc + 1023) / 1024)); - memUsedHist.record((unsigned)((genMemStats.nraTotalSizeUsed + 1023) / 1024)); - s_aggMemStats.Add(genMemStats); - if (genMemStats.allocSz > s_maxCompMemStats.allocSz) - { - s_maxCompMemStats = genMemStats; - } + compArenaAllocator->finishMemStats(); + memAllocHist.record((unsigned)((compArenaAllocator->getTotalBytesAllocated() + 1023) / 1024)); + memUsedHist.record((unsigned)((compArenaAllocator->getTotalBytesUsed() + 1023) / 1024)); } #ifdef DEBUG if (s_dspMemStats || verbose) { printf("\nAllocations for %s (MethodHash=%08x)\n", info.compFullName, info.compMethodHash()); - genMemStats.Print(jitstdout); + compArenaAllocator->dumpMemStats(jitstdout); } #endif // DEBUG #endif // MEASURE_MEM_ALLOC @@ -5646,12 +5568,12 @@ void Compiler::compCompileFinish() (info.compLocalsCount <= 32) && (!opts.MinOpts()) && // We may have too many local variables, etc (getJitStressLevel() == 0) && // We need extra memory for stress !opts.optRepeat && // We need extra memory to repeat opts - !compAllocator->bypassHostAllocator() && // ArenaAllocator::getDefaultPageSize() is artificially low for - // DirectAlloc + !compArenaAllocator->bypassHostAllocator() && // ArenaAllocator::getDefaultPageSize() is artificially low for + // DirectAlloc // Factor of 2x is because data-structures are bigger under DEBUG - (compAllocator->getTotalBytesAllocated() > (2 * ArenaAllocator::getDefaultPageSize())) && + (compArenaAllocator->getTotalBytesAllocated() > (2 * ArenaAllocator::getDefaultPageSize())) && // RyuJIT backend needs memory tuning! TODO-Cleanup: remove this case when memory tuning is complete. - (compAllocator->getTotalBytesAllocated() > (10 * ArenaAllocator::getDefaultPageSize())) && + (compArenaAllocator->getTotalBytesAllocated() > (10 * ArenaAllocator::getDefaultPageSize())) && !verbose) // We allocate lots of memory to convert sets to strings for JitDump { genSmallMethodsNeedingExtraMemoryCnt++; @@ -6754,20 +6676,12 @@ START: if (inlineInfo) { // Use inliner's memory allocator when compiling the inlinee. - pAlloc = inlineInfo->InlinerCompiler->compGetAllocator(); + pAlloc = inlineInfo->InlinerCompiler->compGetArenaAllocator(); } else { - IEEMemoryManager* pMemoryManager = compHnd->getMemoryManager(); - - // Try to reuse the pre-inited allocator - pAlloc = ArenaAllocator::getPooledAllocator(pMemoryManager); - - if (pAlloc == nullptr) - { - alloc = ArenaAllocator(pMemoryManager); - pAlloc = &alloc; - } + alloc.initialize(compHnd->getMemoryManager()); + pAlloc = &alloc; } Compiler* pComp; @@ -6777,7 +6691,6 @@ START: { Compiler* pComp; ArenaAllocator* pAlloc; - ArenaAllocator* alloc; bool jitFallbackCompile; CORINFO_METHOD_HANDLE methodHnd; @@ -6796,7 +6709,6 @@ START: } param; param.pComp = nullptr; param.pAlloc = pAlloc; - param.alloc = &alloc; param.jitFallbackCompile = jitFallbackCompile; param.methodHnd = methodHnd; param.classPtr = classPtr; @@ -8241,7 +8153,7 @@ void JitTimer::PrintCsvMethodStats(Compiler* comp) fprintf(fp, "%Iu,", comp->info.compNativeCodeSize); fprintf(fp, "%Iu,", comp->compInfoBlkSize); - fprintf(fp, "%Iu,", comp->compGetAllocator()->getTotalBytesAllocated()); + fprintf(fp, "%Iu,", comp->compGetArenaAllocator()->getTotalBytesAllocated()); fprintf(fp, "%I64u,", m_info.m_totalCycles); fprintf(fp, "%f\n", CycleTimer::CyclesPerSecond()); fclose(fp); @@ -8259,54 +8171,6 @@ void JitTimer::Terminate(Compiler* comp, CompTimeSummaryInfo& sum, bool includeP } #endif // FEATURE_JIT_METHOD_PERF -#if MEASURE_MEM_ALLOC -// static vars. -CritSecObject Compiler::s_memStatsLock; // Default constructor. -Compiler::AggregateMemStats Compiler::s_aggMemStats; // Default constructor. -Compiler::MemStats Compiler::s_maxCompMemStats; // Default constructor. - -const char* Compiler::MemStats::s_CompMemKindNames[] = { -#define CompMemKindMacro(kind) #kind, -#include "compmemkind.h" -}; - -void Compiler::MemStats::Print(FILE* f) -{ - fprintf(f, "count: %10u, size: %10llu, max = %10llu\n", allocCnt, allocSz, allocSzMax); - fprintf(f, "allocateMemory: %10llu, nraUsed: %10llu\n", nraTotalSizeAlloc, nraTotalSizeUsed); - PrintByKind(f); -} - -void Compiler::MemStats::PrintByKind(FILE* f) -{ - fprintf(f, "\nAlloc'd bytes by kind:\n %20s | %10s | %7s\n", "kind", "size", "pct"); - fprintf(f, " %20s-+-%10s-+-%7s\n", "--------------------", "----------", "-------"); - float allocSzF = static_cast(allocSz); - for (int cmk = 0; cmk < CMK_Count; cmk++) - { - float pct = 100.0f * static_cast(allocSzByKind[cmk]) / allocSzF; - fprintf(f, " %20s | %10llu | %6.2f%%\n", s_CompMemKindNames[cmk], allocSzByKind[cmk], pct); - } - fprintf(f, "\n"); -} - -void Compiler::AggregateMemStats::Print(FILE* f) -{ - fprintf(f, "For %9u methods:\n", nMethods); - if (nMethods == 0) - { - return; - } - fprintf(f, " count: %12u (avg %7u per method)\n", allocCnt, allocCnt / nMethods); - fprintf(f, " alloc size : %12llu (avg %7llu per method)\n", allocSz, allocSz / nMethods); - fprintf(f, " max alloc : %12llu\n", allocSzMax); - fprintf(f, "\n"); - fprintf(f, " allocateMemory : %12llu (avg %7llu per method)\n", nraTotalSizeAlloc, nraTotalSizeAlloc / nMethods); - fprintf(f, " nraUsed : %12llu (avg %7llu per method)\n", nraTotalSizeUsed, nraTotalSizeUsed / nMethods); - PrintByKind(f); -} -#endif // MEASURE_MEM_ALLOC - #if LOOP_HOIST_STATS // Static fields. CritSecObject Compiler::s_loopHoistStatsLock; // Default constructor. diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index 7de9440..2cbf668 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -98,7 +98,7 @@ class Compiler; /*****************************************************************************/ // -// Declare global operator new overloads that use the Compiler::compGetMem() function for allocation. +// Declare global operator new overloads that use the compiler's arena allocator // // I wanted to make the second argument optional, with default = CMK_Unknown, but that @@ -3665,7 +3665,7 @@ public: template T* fgAllocateTypeForEachBlk(CompMemKind cmk = CMK_Unknown) { - return (T*)compGetMem((fgBBNumMax + 1) * sizeof(T), cmk); + return getAllocator(cmk).allocate(fgBBNumMax + 1); } // BlockSets are relative to a specific set of BasicBlock numbers. If that changes @@ -4394,7 +4394,7 @@ public: // The switch block "switchBlk" just had an entry with value "from" modified to the value "to". // Update "this" as necessary: if "from" is no longer an element of the jump table of "switchBlk", // remove it from "this", and ensure that "to" is a member. Use "alloc" to do any required allocation. - void UpdateTarget(CompAllocator* alloc, BasicBlock* switchBlk, BasicBlock* from, BasicBlock* to); + void UpdateTarget(CompAllocator alloc, BasicBlock* switchBlk, BasicBlock* from, BasicBlock* to); }; typedef JitHashTable, SwitchUniqueSuccSet> BlockToSwitchDescMap; @@ -8615,95 +8615,11 @@ public: JitFlags* compileFlags, CorInfoInstantiationVerification instVerInfo); - ArenaAllocator* compGetAllocator(); + ArenaAllocator* compGetArenaAllocator(); #if MEASURE_MEM_ALLOC - static bool s_dspMemStats; // Display per-phase memory statistics for every function - - struct MemStats - { - unsigned allocCnt; // # of allocs - UINT64 allocSz; // total size of those alloc. - UINT64 allocSzMax; // Maximum single allocation. - UINT64 allocSzByKind[CMK_Count]; // Classified by "kind". - UINT64 nraTotalSizeAlloc; - UINT64 nraTotalSizeUsed; - - static const char* s_CompMemKindNames[]; // Names of the kinds. - - MemStats() : allocCnt(0), allocSz(0), allocSzMax(0), nraTotalSizeAlloc(0), nraTotalSizeUsed(0) - { - for (int i = 0; i < CMK_Count; i++) - { - allocSzByKind[i] = 0; - } - } - MemStats(const MemStats& ms) - : allocCnt(ms.allocCnt) - , allocSz(ms.allocSz) - , allocSzMax(ms.allocSzMax) - , nraTotalSizeAlloc(ms.nraTotalSizeAlloc) - , nraTotalSizeUsed(ms.nraTotalSizeUsed) - { - for (int i = 0; i < CMK_Count; i++) - { - allocSzByKind[i] = ms.allocSzByKind[i]; - } - } - - // Until we have ubiquitous constructors. - void Init() - { - this->MemStats::MemStats(); - } - - void AddAlloc(size_t sz, CompMemKind cmk) - { - allocCnt += 1; - allocSz += sz; - if (sz > allocSzMax) - { - allocSzMax = sz; - } - allocSzByKind[cmk] += sz; - } - - void Print(FILE* f); // Print these stats to f. - void PrintByKind(FILE* f); // Do just the by-kind histogram part. - }; - MemStats genMemStats; - - struct AggregateMemStats : public MemStats - { - unsigned nMethods; - - AggregateMemStats() : MemStats(), nMethods(0) - { - } - - void Add(const MemStats& ms) - { - nMethods++; - allocCnt += ms.allocCnt; - allocSz += ms.allocSz; - allocSzMax = max(allocSzMax, ms.allocSzMax); - for (int i = 0; i < CMK_Count; i++) - { - allocSzByKind[i] += ms.allocSzByKind[i]; - } - nraTotalSizeAlloc += ms.nraTotalSizeAlloc; - nraTotalSizeUsed += ms.nraTotalSizeUsed; - } - - void Print(FILE* f); // Print these stats to jitstdout. - }; - - static CritSecObject s_memStatsLock; // This lock protects the data structures below. - static MemStats s_maxCompMemStats; // Stats for the compilation with the largest amount allocated. - static AggregateMemStats s_aggMemStats; // Aggregates statistics for all compilations. - -#endif // MEASURE_MEM_ALLOC +#endif // MEASURE_MEM_ALLOC #if LOOP_HOIST_STATS unsigned m_loopsConsidered; @@ -8722,10 +8638,6 @@ public: static void PrintAggregateLoopHoistStats(FILE* f); #endif // LOOP_HOIST_STATS - void* compGetMemArray(size_t numElem, size_t elemSize, CompMemKind cmk = CMK_Unknown); - void* compGetMem(size_t sz, CompMemKind cmk = CMK_Unknown); - void compFreeMem(void*); - bool compIsForImportOnly(); bool compIsForInlining(); bool compDonotInline(); @@ -8749,7 +8661,7 @@ public: { VarScopeDsc* data; VarScopeListNode* next; - static VarScopeListNode* Create(VarScopeDsc* value, CompAllocator* alloc) + static VarScopeListNode* Create(VarScopeDsc* value, CompAllocator alloc) { VarScopeListNode* node = new (alloc) VarScopeListNode; node->data = value; @@ -8762,7 +8674,7 @@ public: { VarScopeListNode* head; VarScopeListNode* tail; - static VarScopeMapInfo* Create(VarScopeListNode* node, CompAllocator* alloc) + static VarScopeMapInfo* Create(VarScopeListNode* node, CompAllocator alloc) { VarScopeMapInfo* info = new (alloc) VarScopeMapInfo; info->head = node; @@ -8832,19 +8744,9 @@ protected: bool skipMethod(); #endif - ArenaAllocator* compAllocator; + ArenaAllocator* compArenaAllocator; public: - CompAllocator* compAllocatorGeneric; // An allocator that uses the CMK_Generic tracker. -#if MEASURE_MEM_ALLOC - CompAllocator* compAllocatorBitset; // An allocator that uses the CMK_bitset tracker. - CompAllocator* compAllocatorGC; // An allocator that uses the CMK_GC tracker. - CompAllocator* compAllocatorLoopHoist; // An allocator that uses the CMK_LoopHoist tracker. -#ifdef DEBUG - CompAllocator* compAllocatorDebugOnly; // An allocator that uses the CMK_DebugOnly tracker. -#endif // DEBUG -#endif // MEASURE_MEM_ALLOC - void compFunctionTraceStart(); void compFunctionTraceEnd(void* methodCodePtr, ULONG methodCodeSize, bool isNYI); @@ -8882,47 +8784,25 @@ public: // Assumes called as part of process shutdown; does any compiler-specific work associated with that. static void ProcessShutdownWork(ICorStaticInfo* statInfo); - CompAllocator* getAllocator() + CompAllocator getAllocator(CompMemKind cmk = CMK_Generic) { - return compAllocatorGeneric; + return CompAllocator(compArenaAllocator, cmk); } -#if MEASURE_MEM_ALLOC - CompAllocator* getAllocatorBitset() - { - return compAllocatorBitset; - } - CompAllocator* getAllocatorGC() - { - return compAllocatorGC; - } - CompAllocator* getAllocatorLoopHoist() - { - return compAllocatorLoopHoist; - } -#else // !MEASURE_MEM_ALLOC - CompAllocator* getAllocatorBitset() + CompAllocator getAllocatorGC() { - return compAllocatorGeneric; + return getAllocator(CMK_GC); } - CompAllocator* getAllocatorGC() - { - return compAllocatorGeneric; - } - CompAllocator* getAllocatorLoopHoist() + + CompAllocator getAllocatorLoopHoist() { - return compAllocatorGeneric; + return getAllocator(CMK_LoopHoist); } -#endif // !MEASURE_MEM_ALLOC #ifdef DEBUG - CompAllocator* getAllocatorDebugOnly() + CompAllocator getAllocatorDebugOnly() { -#if MEASURE_MEM_ALLOC - return compAllocatorDebugOnly; -#else // !MEASURE_MEM_ALLOC - return compAllocatorGeneric; -#endif // !MEASURE_MEM_ALLOC + return getAllocator(CMK_DebugOnly); } #endif // DEBUG @@ -9293,7 +9173,7 @@ public: if (compRoot->m_fieldSeqStore == nullptr) { // Create a CompAllocator that labels sub-structure with CMK_FieldSeqStore, and use that for allocation. - CompAllocator* ialloc = new (this, CMK_FieldSeqStore) CompAllocator(this, CMK_FieldSeqStore); + CompAllocator ialloc(getAllocator(CMK_FieldSeqStore)); compRoot->m_fieldSeqStore = new (ialloc) FieldSeqStore(ialloc); } return compRoot->m_fieldSeqStore; @@ -9314,8 +9194,8 @@ public: { // Create a CompAllocator that labels sub-structure with CMK_ZeroOffsetFieldMap, and use that for // allocation. - CompAllocator* ialloc = new (this, CMK_ZeroOffsetFieldMap) CompAllocator(this, CMK_ZeroOffsetFieldMap); - m_zeroOffsetFieldMap = new (ialloc) NodeToFieldSeqMap(ialloc); + CompAllocator ialloc(getAllocator(CMK_ZeroOffsetFieldMap)); + m_zeroOffsetFieldMap = new (ialloc) NodeToFieldSeqMap(ialloc); } return m_zeroOffsetFieldMap; } @@ -9341,7 +9221,7 @@ public: if (compRoot->m_arrayInfoMap == nullptr) { // Create a CompAllocator that labels sub-structure with CMK_ArrayInfoMap, and use that for allocation. - CompAllocator* ialloc = new (this, CMK_ArrayInfoMap) CompAllocator(this, CMK_ArrayInfoMap); + CompAllocator ialloc(getAllocator(CMK_ArrayInfoMap)); compRoot->m_arrayInfoMap = new (ialloc) NodeToArrayInfoMap(ialloc); } return compRoot->m_arrayInfoMap; @@ -9396,7 +9276,7 @@ public: if (compRoot->m_memorySsaMap[memoryKind] == nullptr) { // Create a CompAllocator that labels sub-structure with CMK_ArrayInfoMap, and use that for allocation. - CompAllocator* ialloc = new (this, CMK_ArrayInfoMap) CompAllocator(this, CMK_ArrayInfoMap); + CompAllocator ialloc(getAllocator(CMK_ArrayInfoMap)); compRoot->m_memorySsaMap[memoryKind] = new (ialloc) NodeToUnsignedMap(ialloc); } return compRoot->m_memorySsaMap[memoryKind]; @@ -9456,25 +9336,6 @@ public: }; // end of class Compiler -// Inline methods of CompAllocator. -void* CompAllocator::Alloc(size_t sz) -{ -#if MEASURE_MEM_ALLOC - return m_comp->compGetMem(sz, m_cmk); -#else - return m_comp->compGetMem(sz); -#endif -} - -void* CompAllocator::ArrayAlloc(size_t elems, size_t elemSize) -{ -#if MEASURE_MEM_ALLOC - return m_comp->compGetMemArray(elems, elemSize, m_cmk); -#else - return m_comp->compGetMemArray(elems, elemSize); -#endif -} - // LclVarDsc constructor. Uses Compiler, so must come after Compiler definition. inline LclVarDsc::LclVarDsc(Compiler* comp) : // Initialize the ArgRegs to REG_STK. @@ -9564,7 +9425,7 @@ protected: Compiler* m_compiler; ArrayStack m_ancestors; - GenTreeVisitor(Compiler* compiler) : m_compiler(compiler), m_ancestors(compiler) + GenTreeVisitor(Compiler* compiler) : m_compiler(compiler), m_ancestors(compiler->getAllocator(CMK_ArrayStack)) { assert(compiler != nullptr); diff --git a/src/coreclr/src/jit/compiler.hpp b/src/coreclr/src/jit/compiler.hpp index 92ea539..d873ead 100644 --- a/src/coreclr/src/jit/compiler.hpp +++ b/src/coreclr/src/jit/compiler.hpp @@ -888,7 +888,7 @@ void* GenTree::operator new(size_t sz, Compiler* comp, genTreeOps oper) #endif // MEASURE_NODE_SIZE assert(size >= sz); - return comp->compGetMem(size, CMK_ASTNode); + return comp->getAllocator(CMK_ASTNode).allocate(size); } // GenTree constructor @@ -1663,8 +1663,7 @@ inline unsigned Compiler::lvaGrabTemp(bool shortLifetime DEBUGARG(const char* re IMPL_LIMITATION("too many locals"); } - // Note: compGetMemArray might throw. - LclVarDsc* newLvaTable = (LclVarDsc*)compGetMemArray(newLvaTableCnt, sizeof(*lvaTable), CMK_LvaTable); + LclVarDsc* newLvaTable = getAllocator(CMK_LvaTable).allocate(newLvaTableCnt); memcpy(newLvaTable, lvaTable, lvaCount * sizeof(*lvaTable)); memset(newLvaTable + lvaCount, 0, (newLvaTableCnt - lvaCount) * sizeof(*lvaTable)); @@ -1738,8 +1737,7 @@ inline unsigned Compiler::lvaGrabTemps(unsigned cnt DEBUGARG(const char* reason) IMPL_LIMITATION("too many locals"); } - // Note: compGetMemArray might throw. - LclVarDsc* newLvaTable = (LclVarDsc*)compGetMemArray(newLvaTableCnt, sizeof(*lvaTable), CMK_LvaTable); + LclVarDsc* newLvaTable = getAllocator(CMK_LvaTable).allocate(newLvaTableCnt); memcpy(newLvaTable, lvaTable, lvaCount * sizeof(*lvaTable)); memset(newLvaTable + lvaCount, 0, (newLvaTableCnt - lvaCount) * sizeof(*lvaTable)); @@ -4069,75 +4067,9 @@ inline bool Compiler::compStressCompile(compStressArea stressArea, unsigned weig } #endif -inline ArenaAllocator* Compiler::compGetAllocator() -{ - return compAllocator; -} - -/***************************************************************************** - * - * Allocate memory from the no-release allocator. All such memory will be - * freed up simulataneously at the end of the procedure - */ - -#ifndef DEBUG - -inline void* Compiler::compGetMem(size_t sz, CompMemKind cmk) -{ - assert(sz); - -#if MEASURE_MEM_ALLOC - genMemStats.AddAlloc(sz, cmk); -#endif - - return compAllocator->allocateMemory(sz); -} - -#endif - -// Wrapper for Compiler::compGetMem that can be forward-declared for use in template -// types which Compiler depends on but which need to allocate heap memory. -inline void* compGetMem(Compiler* comp, size_t sz) -{ - return comp->compGetMem(sz); -} - -/***************************************************************************** - * - * A common memory allocation for arrays of structures involves the - * multiplication of the number of elements with the size of each element. - * If this computation overflows, then the memory allocation might succeed, - * but not allocate sufficient memory for all the elements. This can cause - * us to overwrite the allocation, and AV or worse, corrupt memory. - * - * This method checks for overflow, and succeeds only when it detects - * that there's no overflow. It should be cheap, because when inlined with - * a constant elemSize, the division should be done in compile time, and so - * at run time we simply have a check of numElem against some number (this - * is why we __forceinline). - */ - -#define MAX_MEMORY_PER_ALLOCATION (512 * 1024 * 1024) - -__forceinline void* Compiler::compGetMemArray(size_t numElem, size_t elemSize, CompMemKind cmk) -{ - if (numElem > (MAX_MEMORY_PER_ALLOCATION / elemSize)) - { - NOMEM(); - } - - return compGetMem(numElem * elemSize, cmk); -} - -/****************************************************************************** - * - * Roundup the allocated size so that if this memory block is aligned, - * then the next block allocated too will be aligned. - * The JIT will always try to keep all the blocks aligned. - */ - -inline void Compiler::compFreeMem(void* ptr) +inline ArenaAllocator* Compiler::compGetArenaAllocator() { + return compArenaAllocator; } inline bool Compiler::compIsProfilerHookNeeded() @@ -4909,18 +4841,18 @@ void GenTree::VisitBinOpOperands(TVisitor visitor) /***************************************************************************** * operator new * - * Note that compGetMem is an arena allocator that returns memory that is + * Note that compiler's allocator is an arena allocator that returns memory that is * not zero-initialized and can contain data from a prior allocation lifetime. */ -inline void* __cdecl operator new(size_t sz, Compiler* context, CompMemKind cmk) +inline void* __cdecl operator new(size_t sz, Compiler* compiler, CompMemKind cmk) { - return context->compGetMem(sz, cmk); + return compiler->getAllocator(cmk).allocate(sz); } -inline void* __cdecl operator new[](size_t sz, Compiler* context, CompMemKind cmk) +inline void* __cdecl operator new[](size_t sz, Compiler* compiler, CompMemKind cmk) { - return context->compGetMem(sz, cmk); + return compiler->getAllocator(cmk).allocate(sz); } inline void* __cdecl operator new(size_t sz, void* p, const jitstd::placement_t& /* syntax_difference */) diff --git a/src/coreclr/src/jit/compilerbitsettraits.hpp b/src/coreclr/src/jit/compilerbitsettraits.hpp index be30564..e6c6b13 100644 --- a/src/coreclr/src/jit/compilerbitsettraits.hpp +++ b/src/coreclr/src/jit/compilerbitsettraits.hpp @@ -17,14 +17,14 @@ // static void* CompAllocBitSetTraits::Alloc(Compiler* comp, size_t byteSize) { - return comp->compGetMem(byteSize, CMK_bitset); + return comp->getAllocator(CMK_bitset).allocate(byteSize); } #ifdef DEBUG // static void* CompAllocBitSetTraits::DebugAlloc(Compiler* comp, size_t byteSize) { - return comp->compGetMem(byteSize, CMK_DebugOnly); + return comp->getAllocator(CMK_DebugOnly).allocate(byteSize); } #endif // DEBUG @@ -141,14 +141,14 @@ BitSetSupport::BitSetOpCounter* BasicBlockBitSetTraits::GetOpCounter(Compiler* c // static void* BitVecTraits::Alloc(BitVecTraits* b, size_t byteSize) { - return b->comp->compGetMem(byteSize, CMK_bitset); + return b->comp->getAllocator(CMK_bitset).allocate(byteSize); } #ifdef DEBUG // static void* BitVecTraits::DebugAlloc(BitVecTraits* b, size_t byteSize) { - return b->comp->compGetMem(byteSize, CMK_DebugOnly); + return b->comp->getAllocator(CMK_DebugOnly).allocate(byteSize); } #endif // DEBUG diff --git a/src/coreclr/src/jit/copyprop.cpp b/src/coreclr/src/jit/copyprop.cpp index 6c21d45..c09fa91 100644 --- a/src/coreclr/src/jit/copyprop.cpp +++ b/src/coreclr/src/jit/copyprop.cpp @@ -21,12 +21,6 @@ #include "ssabuilder.h" #include "treelifeupdater.h" -template -inline static T* allocate_any(jitstd::allocator& alloc, size_t count = 1) -{ - return jitstd::allocator(alloc).allocate(count); -} - /************************************************************************************** * * Corresponding to the live definition pushes, pop the stack as we finish a sub-paths @@ -370,7 +364,7 @@ void Compiler::optBlockCopyProp(BasicBlock* block, LclNumToGenTreePtrStack* curS GenTreePtrStack* stack; if (!curSsaName->Lookup(lclNum, &stack)) { - stack = new (curSsaName->GetAllocator()) GenTreePtrStack(this); + stack = new (curSsaName->GetAllocator()) GenTreePtrStack(curSsaName->GetAllocator()); } stack->Push(tree); curSsaName->Set(lclNum, stack); @@ -383,7 +377,7 @@ void Compiler::optBlockCopyProp(BasicBlock* block, LclNumToGenTreePtrStack* curS GenTreePtrStack* stack; if (!curSsaName->Lookup(lclNum, &stack)) { - stack = new (curSsaName->GetAllocator()) GenTreePtrStack(this); + stack = new (curSsaName->GetAllocator()) GenTreePtrStack(curSsaName->GetAllocator()); stack->Push(tree); curSsaName->Set(lclNum, stack); } @@ -431,10 +425,10 @@ void Compiler::optVnCopyProp() return; } - CompAllocator allocator(this, CMK_CopyProp); + CompAllocator allocator(getAllocator(CMK_CopyProp)); // Compute the domTree to use. - BlkToBlkVectorMap* domTree = new (&allocator) BlkToBlkVectorMap(&allocator); + BlkToBlkVectorMap* domTree = new (allocator) BlkToBlkVectorMap(allocator); domTree->Reallocate(fgBBcount * 3 / 2); // Prime the allocation SsaBuilder::ComputeDominators(this, domTree); @@ -453,9 +447,9 @@ void Compiler::optVnCopyProp() VarSetOps::AssignNoCopy(this, optCopyPropKillSet, VarSetOps::MakeEmpty(this)); // The map from lclNum to its recently live definitions as a stack. - LclNumToGenTreePtrStack curSsaName(&allocator); + LclNumToGenTreePtrStack curSsaName(allocator); - BlockWorkStack* worklist = new (&allocator) BlockWorkStack(&allocator); + BlockWorkStack* worklist = new (allocator) BlockWorkStack(allocator); worklist->push_back(BlockWork(fgFirstBB)); while (!worklist->empty()) diff --git a/src/coreclr/src/jit/ee_il_dll.cpp b/src/coreclr/src/jit/ee_il_dll.cpp index 94cf81c..88612bc 100644 --- a/src/coreclr/src/jit/ee_il_dll.cpp +++ b/src/coreclr/src/jit/ee_il_dll.cpp @@ -733,7 +733,7 @@ void Compiler::eeGetVars() { // Allocate a bit-array for all the variables and initialize to false - bool* varInfoProvided = (bool*)compGetMem(info.compLocalsCount * sizeof(varInfoProvided[0])); + bool* varInfoProvided = getAllocator(CMK_Unknown).allocate(info.compLocalsCount); unsigned i; for (i = 0; i < info.compLocalsCount; i++) { diff --git a/src/coreclr/src/jit/eeinterface.cpp b/src/coreclr/src/jit/eeinterface.cpp index 6581230..5a0e412 100644 --- a/src/coreclr/src/jit/eeinterface.cpp +++ b/src/coreclr/src/jit/eeinterface.cpp @@ -149,7 +149,7 @@ const char* Compiler::eeGetMethodFullName(CORINFO_METHOD_HANDLE hnd) length += param.siglength + 2; - char* retName = (char*)compGetMem(length, CMK_DebugOnly); + char* retName = getAllocator(CMK_DebugOnly).allocate(length); /* Now generate the full signature string in the allocated buffer */ diff --git a/src/coreclr/src/jit/emit.cpp b/src/coreclr/src/jit/emit.cpp index e51c569..3492f2f 100644 --- a/src/coreclr/src/jit/emit.cpp +++ b/src/coreclr/src/jit/emit.cpp @@ -514,7 +514,7 @@ void* emitter::emitGetMem(size_t sz) emitTotMemAlloc += sz; #endif - return emitComp->compGetMem(sz, CMK_InstDesc); + return emitComp->getAllocator(CMK_InstDesc).allocate(sz); } /***************************************************************************** diff --git a/src/coreclr/src/jit/flowgraph.cpp b/src/coreclr/src/jit/flowgraph.cpp index 197af31..7516792 100644 --- a/src/coreclr/src/jit/flowgraph.cpp +++ b/src/coreclr/src/jit/flowgraph.cpp @@ -2337,7 +2337,7 @@ void Compiler::fgDfsInvPostOrderHelper(BasicBlock* block, BlockSet& visited, uns // Allocate a local stack to hold the DFS traversal actions necessary // to compute pre/post-ordering of the control flowgraph. - ArrayStack stack(this); + ArrayStack stack(getAllocator(CMK_ArrayStack)); // Push the first block on the stack to seed the traversal. stack.Push(DfsBlockEntry(DSS_Pre, block)); @@ -2778,7 +2778,7 @@ void Compiler::fgTraverseDomTree(unsigned bbNum, BasicBlockList** domTree, unsig // Allocate a local stack to hold the Dfs traversal actions necessary // to compute pre/post-ordering of the dominator tree. - ArrayStack stack(this); + ArrayStack stack(getAllocator(CMK_ArrayStack)); // Push the first entry number on the stack to seed the traversal. stack.Push(DfsNumEntry(DSS_Pre, bbNum)); @@ -3350,10 +3350,10 @@ Compiler::SwitchUniqueSuccSet Compiler::GetDescriptorForSwitch(BasicBlock* switc } } -void Compiler::SwitchUniqueSuccSet::UpdateTarget(CompAllocator* alloc, - BasicBlock* switchBlk, - BasicBlock* from, - BasicBlock* to) +void Compiler::SwitchUniqueSuccSet::UpdateTarget(CompAllocator alloc, + BasicBlock* switchBlk, + BasicBlock* from, + BasicBlock* to) { assert(switchBlk->bbJumpKind == BBJ_SWITCH); // Precondition. unsigned jmpTabCnt = switchBlk->bbJumpSwt->bbsCount; @@ -19287,7 +19287,7 @@ const char* Compiler::fgProcessEscapes(const char* nameIn, escapeMapping_t* map) if (subsitutionRequired) { - char* newName = (char*)compGetMem(lengthOut, CMK_DebugOnly); + char* newName = getAllocator(CMK_DebugOnly).allocate(lengthOut); char* pDest; pDest = newName; pChar = nameIn; @@ -21159,7 +21159,7 @@ void Compiler::fgDebugCheckFlags(GenTree* tree) case GT_FIELD_LIST: if ((op2 != nullptr) && op2->OperIsAnyList()) { - ArrayStack stack(this); + ArrayStack stack(getAllocator(CMK_DebugOnly)); while ((tree->gtGetOp2() != nullptr) && tree->gtGetOp2()->OperIsAnyList()) { stack.Push(tree); diff --git a/src/coreclr/src/jit/gcencode.cpp b/src/coreclr/src/jit/gcencode.cpp index a05e8eb..9cd478b 100644 --- a/src/coreclr/src/jit/gcencode.cpp +++ b/src/coreclr/src/jit/gcencode.cpp @@ -1906,7 +1906,7 @@ PendingArgsStack::PendingArgsStack(unsigned maxDepth, Compiler* pComp) /* Do we need an array as well as the mask ? */ if (pasMaxDepth > BITS_IN_pasMask) - pasTopArray = (BYTE*)pComp->compGetMem(pasMaxDepth - BITS_IN_pasMask); + pasTopArray = pComp->getAllocator(CMK_Unknown).allocate(pasMaxDepth - BITS_IN_pasMask); } //----------------------------------------------------------------------------- diff --git a/src/coreclr/src/jit/gentree.cpp b/src/coreclr/src/jit/gentree.cpp index bba58cb..08aa2ef 100644 --- a/src/coreclr/src/jit/gentree.cpp +++ b/src/coreclr/src/jit/gentree.cpp @@ -73,7 +73,7 @@ struct IndentStack const char** indents; // Constructor for IndentStack. Uses 'compiler' to determine the mode of printing. - IndentStack(Compiler* compiler) : stack(compiler) + IndentStack(Compiler* compiler) : stack(compiler->getAllocator(CMK_DebugOnly)) { if (compiler->asciiTrees) { @@ -2722,7 +2722,7 @@ unsigned Compiler::gtSetListOrder(GenTree* list, bool isListCallArgs, bool callA assert((list != nullptr) && list->OperIsAnyList()); assert(!callArgsInRegs || isListCallArgs); - ArrayStack listNodes(this); + ArrayStack listNodes(getAllocator(CMK_ArrayStack)); do { @@ -17193,7 +17193,7 @@ void GenTree::LabelIndex(Compiler* comp, bool isConst) FieldSeqNode FieldSeqStore::s_notAField(nullptr, nullptr); // FieldSeqStore methods. -FieldSeqStore::FieldSeqStore(CompAllocator* alloc) : m_alloc(alloc), m_canonMap(new (alloc) FieldSeqNodeCanonMap(alloc)) +FieldSeqStore::FieldSeqStore(CompAllocator alloc) : m_alloc(alloc), m_canonMap(new (alloc) FieldSeqNodeCanonMap(alloc)) { } @@ -17207,7 +17207,7 @@ FieldSeqNode* FieldSeqStore::CreateSingleton(CORINFO_FIELD_HANDLE fieldHnd) } else { - res = reinterpret_cast(m_alloc->Alloc(sizeof(FieldSeqNode))); + res = m_alloc.allocate(1); *res = fsn; m_canonMap->Set(fsn, res); return res; @@ -17250,7 +17250,7 @@ FieldSeqNode* FieldSeqStore::Append(FieldSeqNode* a, FieldSeqNode* b) } else { - res = reinterpret_cast(m_alloc->Alloc(sizeof(FieldSeqNode))); + res = m_alloc.allocate(1); *res = fsn; m_canonMap->Set(fsn, res); return res; diff --git a/src/coreclr/src/jit/gentree.h b/src/coreclr/src/jit/gentree.h index e59b79d..3078b77 100644 --- a/src/coreclr/src/jit/gentree.h +++ b/src/coreclr/src/jit/gentree.h @@ -258,7 +258,7 @@ class FieldSeqStore { typedef JitHashTable FieldSeqNodeCanonMap; - CompAllocator* m_alloc; + CompAllocator m_alloc; FieldSeqNodeCanonMap* m_canonMap; static FieldSeqNode s_notAField; // No value, just exists to provide an address. @@ -268,7 +268,7 @@ class FieldSeqStore static int ConstantIndexPseudoFieldStruct; public: - FieldSeqStore(CompAllocator* alloc); + FieldSeqStore(CompAllocator alloc); // Returns the (canonical in the store) singleton field sequence for the given handle. FieldSeqNode* CreateSingleton(CORINFO_FIELD_HANDLE fieldHnd); diff --git a/src/coreclr/src/jit/hostallocator.cpp b/src/coreclr/src/jit/hostallocator.cpp index b737424..c9afd1a 100644 --- a/src/coreclr/src/jit/hostallocator.cpp +++ b/src/coreclr/src/jit/hostallocator.cpp @@ -5,36 +5,14 @@ #include "jitpch.h" #include "hostallocator.h" -HostAllocator HostAllocator::s_hostAllocator; - -void* HostAllocator::Alloc(size_t size) +void* HostAllocator::allocateHostMemory(size_t size) { assert(g_jitHost != nullptr); return g_jitHost->allocateMemory(size, false); } -void* HostAllocator::ArrayAlloc(size_t elemSize, size_t numElems) -{ - assert(g_jitHost != nullptr); - - ClrSafeInt safeElemSize(elemSize); - ClrSafeInt safeNumElems(numElems); - ClrSafeInt size = safeElemSize * safeNumElems; - if (size.IsOverflow()) - { - return nullptr; - } - - return g_jitHost->allocateMemory(size.Value(), false); -} - -void HostAllocator::Free(void* p) +void HostAllocator::freeHostMemory(void* p) { assert(g_jitHost != nullptr); g_jitHost->freeMemory(p, false); } - -HostAllocator* HostAllocator::getHostAllocator() -{ - return &s_hostAllocator; -} diff --git a/src/coreclr/src/jit/hostallocator.h b/src/coreclr/src/jit/hostallocator.h index 39a32ef..447fc67 100644 --- a/src/coreclr/src/jit/hostallocator.h +++ b/src/coreclr/src/jit/hostallocator.h @@ -7,30 +7,48 @@ class HostAllocator final { private: - static HostAllocator s_hostAllocator; - HostAllocator() { } public: - void* Alloc(size_t size); + template + T* allocate(size_t count) + { + ClrSafeInt safeElemSize(sizeof(T)); + ClrSafeInt safeCount(count); + ClrSafeInt size = safeElemSize * safeCount; + if (size.IsOverflow()) + { + return nullptr; + } + + return static_cast(allocateHostMemory(size.Value())); + } - void* ArrayAlloc(size_t elemSize, size_t numElems); + void deallocate(void* p) + { + freeHostMemory(p); + } - void Free(void* p); + static HostAllocator getHostAllocator() + { + return HostAllocator(); + } - static HostAllocator* getHostAllocator(); +private: + void* allocateHostMemory(size_t size); + void freeHostMemory(void* p); }; // Global operator new overloads that work with HostAllocator -inline void* __cdecl operator new(size_t n, HostAllocator* alloc) +inline void* __cdecl operator new(size_t n, HostAllocator alloc) { - return alloc->Alloc(n); + return alloc.allocate(n); } -inline void* __cdecl operator new[](size_t n, HostAllocator* alloc) +inline void* __cdecl operator new[](size_t n, HostAllocator alloc) { - return alloc->Alloc(n); + return alloc.allocate(n); } diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index 222e787..8ec9303 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -17045,7 +17045,7 @@ void* Compiler::BlockListNode::operator new(size_t sz, Compiler* comp) { if (comp->impBlockListNodeFreeList == nullptr) { - return (BlockListNode*)comp->compGetMem(sizeof(BlockListNode), CMK_BasicBlock); + return comp->getAllocator(CMK_BasicBlock).allocate(1); } else { @@ -17272,7 +17272,7 @@ void Compiler::verInitBBEntryState(BasicBlock* block, EntryState* srcState) return; } - block->bbEntryState = (EntryState*)compGetMem(sizeof(EntryState)); + block->bbEntryState = getAllocator(CMK_Unknown).allocate(1); // block->bbEntryState.esRefcount = 1; @@ -19839,7 +19839,7 @@ CORINFO_CLASS_HANDLE Compiler::impGetSpecialIntrinsicExactReturnType(CORINFO_MET // pointer to token into jit-allocated memory. CORINFO_RESOLVED_TOKEN* Compiler::impAllocateToken(CORINFO_RESOLVED_TOKEN token) { - CORINFO_RESOLVED_TOKEN* memory = (CORINFO_RESOLVED_TOKEN*)compGetMem(sizeof(token)); + CORINFO_RESOLVED_TOKEN* memory = getAllocator(CMK_Unknown).allocate(1); *memory = token; return memory; } diff --git a/src/coreclr/src/jit/jit.h b/src/coreclr/src/jit/jit.h index 0e3ec92..f6a575d 100644 --- a/src/coreclr/src/jit/jit.h +++ b/src/coreclr/src/jit/jit.h @@ -830,110 +830,8 @@ const int MIN_SHORT_AS_INT = -32768; /*****************************************************************************/ -// CompMemKind values are used to tag memory allocations performed via -// the compiler's allocator so that the memory usage of various compiler -// components can be tracked separately (when MEASURE_MEM_ALLOC is defined). - -enum CompMemKind -{ -#define CompMemKindMacro(kind) CMK_##kind, -#include "compmemkind.h" - CMK_Count -}; - class Compiler; -// Allows general purpose code (e.g. collection classes) to allocate memory -// of a pre-determined kind via the compiler's allocator. - -class CompAllocator -{ - Compiler* const m_comp; -#if MEASURE_MEM_ALLOC - CompMemKind const m_cmk; -#endif -public: - CompAllocator(Compiler* comp, CompMemKind cmk) - : m_comp(comp) -#if MEASURE_MEM_ALLOC - , m_cmk(cmk) -#endif - { - } - - // Allocates a block of memory at least `sz` in size. - // Zero-length allocation are not allowed. - inline void* Alloc(size_t sz); - - // Allocates a block of memory at least `elems * elemSize` in size. - // Zero-length allocation are not allowed. - inline void* ArrayAlloc(size_t elems, size_t elemSize); - - // For the compiler's ArenaAllocator, free operations are no-ops. - void Free(void* p) - { - } -}; - -// Global operator new overloads that work with CompAllocator - -inline void* __cdecl operator new(size_t n, CompAllocator* alloc) -{ - return alloc->Alloc(n); -} - -inline void* __cdecl operator new[](size_t n, CompAllocator* alloc) -{ - return alloc->Alloc(n); -} - -// A CompAllocator wrapper that implements IAllocator and allows zero-length -// memory allocations (the compiler's ArenAllocator does not support zero-length -// allocation). - -class CompIAllocator : public IAllocator -{ - CompAllocator* const m_alloc; - char m_zeroLenAllocTarg; - -public: - CompIAllocator(CompAllocator* alloc) : m_alloc(alloc) - { - } - - // Allocates a block of memory at least `sz` in size. - virtual void* Alloc(size_t sz) override - { - if (sz == 0) - { - return &m_zeroLenAllocTarg; - } - else - { - return m_alloc->Alloc(sz); - } - } - - // Allocates a block of memory at least `elems * elemSize` in size. - virtual void* ArrayAlloc(size_t elemSize, size_t numElems) override - { - if ((elemSize == 0) || (numElems == 0)) - { - return &m_zeroLenAllocTarg; - } - else - { - return m_alloc->ArrayAlloc(elemSize, numElems); - } - } - - // Frees the block of memory pointed to by p. - virtual void Free(void* p) override - { - m_alloc->Free(p); - } -}; - class JitTls { #ifdef DEBUG diff --git a/src/coreclr/src/jit/jitexpandarray.h b/src/coreclr/src/jit/jitexpandarray.h index 03d81f5..abe086c 100644 --- a/src/coreclr/src/jit/jitexpandarray.h +++ b/src/coreclr/src/jit/jitexpandarray.h @@ -11,10 +11,10 @@ template class JitExpandArray { protected: - CompAllocator* m_alloc; // The allocator object that should be used to allocate members. - T* m_members; // Pointer to the element array. - unsigned m_size; // The size of the element array. - unsigned m_minSize; // The minimum size of the element array. + CompAllocator m_alloc; // The allocator object that should be used to allocate members. + T* m_members; // Pointer to the element array. + unsigned m_size; // The size of the element array. + unsigned m_minSize; // The minimum size of the element array. // Ensure that the element array is large enough for the specified index to be valid. void EnsureCoversInd(unsigned idx); @@ -54,7 +54,7 @@ public: // time an array element (having index `idx`) is accessed, an array // of size max(`minSize`, `idx`) is allocated. // - JitExpandArray(CompAllocator* alloc, unsigned minSize = 1) + JitExpandArray(CompAllocator alloc, unsigned minSize = 1) : m_alloc(alloc), m_members(nullptr), m_size(0), m_minSize(minSize) { assert(minSize > 0); @@ -71,7 +71,7 @@ public: { if (m_members != nullptr) { - m_alloc->Free(m_members); + m_alloc.deallocate(m_members); } } @@ -86,11 +86,11 @@ public: // This is equivalent to calling the destructor and then constructing // the array again. // - void Init(CompAllocator* alloc, unsigned minSize = 1) + void Init(CompAllocator alloc, unsigned minSize = 1) { if (m_members != nullptr) { - m_alloc->Free(m_members); + m_alloc.deallocate(m_members); } m_alloc = alloc; m_members = nullptr; @@ -220,7 +220,7 @@ public: // Notes: // See JitExpandArray constructor notes. // - JitExpandArrayStack(CompAllocator* alloc, unsigned minSize = 1) : JitExpandArray(alloc, minSize), m_used(0) + JitExpandArrayStack(CompAllocator alloc, unsigned minSize = 1) : JitExpandArray(alloc, minSize), m_used(0) { } @@ -391,18 +391,11 @@ void JitExpandArray::EnsureCoversInd(unsigned idx) unsigned oldSize = m_size; T* oldMembers = m_members; m_size = max(idx + 1, max(m_minSize, m_size * 2)); - if (sizeof(T) < sizeof(int)) - { - m_members = (T*)m_alloc->ArrayAlloc(ALIGN_UP(m_size * sizeof(T), sizeof(int)), sizeof(BYTE)); - } - else - { - m_members = (T*)m_alloc->ArrayAlloc(m_size, sizeof(T)); - } + m_members = m_alloc.allocate(m_size); if (oldMembers != nullptr) { memcpy(m_members, oldMembers, oldSize * sizeof(T)); - m_alloc->Free(oldMembers); + m_alloc.deallocate(oldMembers); } InitializeRange(oldSize, m_size); } diff --git a/src/coreclr/src/jit/jithashtable.h b/src/coreclr/src/jit/jithashtable.h index e47fa04..d411a2b 100644 --- a/src/coreclr/src/jit/jithashtable.h +++ b/src/coreclr/src/jit/jithashtable.h @@ -147,10 +147,8 @@ public: // JitHashTable always starts out empty, with no allocation overhead. // Call Reallocate to prime with an initial size if desired. // - JitHashTable(Allocator* alloc) : m_alloc(alloc), m_table(nullptr), m_tableSizeInfo(), m_tableCount(0), m_tableMax(0) + JitHashTable(Allocator alloc) : m_alloc(alloc), m_table(nullptr), m_tableSizeInfo(), m_tableCount(0), m_tableMax(0) { - assert(m_alloc != nullptr); - #ifndef __GNUC__ // these crash GCC static_assert_no_msg(Behavior::s_growth_factor_numerator > Behavior::s_growth_factor_denominator); static_assert_no_msg(Behavior::s_density_factor_numerator < Behavior::s_density_factor_denominator); @@ -361,7 +359,7 @@ public: pN = pNext; } } - m_alloc->Free(m_table); + m_alloc.deallocate(m_table); m_table = nullptr; m_tableSizeInfo = JitPrimeInfo(); @@ -391,7 +389,7 @@ public: } // Get the allocator used by this hash table. - Allocator* GetAllocator() + Allocator GetAllocator() { return m_alloc; } @@ -513,7 +511,7 @@ public: JitPrimeInfo newPrime = NextPrime(newTableSize); newTableSize = newPrime.prime; - Node** newTable = (Node**)m_alloc->ArrayAlloc(newTableSize, sizeof(Node*)); + Node** newTable = m_alloc.template allocate(newTableSize); for (unsigned i = 0; i < newTableSize; i++) { @@ -539,7 +537,7 @@ public: if (m_table != nullptr) { - m_alloc->Free(m_table); + m_alloc.deallocate(m_table); } m_table = newTable; @@ -763,19 +761,19 @@ private: { } - void* operator new(size_t sz, Allocator* alloc) + void* operator new(size_t sz, Allocator alloc) { - return alloc->Alloc(sz); + return alloc.template allocate(sz); } - void operator delete(void* p, Allocator* alloc) + void operator delete(void* p, Allocator alloc) { - alloc->Free(p); + alloc.deallocate(p); } }; // Instance members - Allocator* m_alloc; // Allocator to use in this table. + Allocator m_alloc; // Allocator to use in this table. Node** m_table; // pointer to table JitPrimeInfo m_tableSizeInfo; // size of table (a prime) and information about it unsigned m_tableCount; // number of elements in table diff --git a/src/coreclr/src/jit/jitstd/allocator.h b/src/coreclr/src/jit/jitstd/allocator.h index f370af8..a6a25de 100644 --- a/src/coreclr/src/jit/jitstd/allocator.h +++ b/src/coreclr/src/jit/jitstd/allocator.h @@ -32,7 +32,7 @@ private: allocator(); public: - inline allocator(CompAllocator* pAlloc); + inline allocator(CompAllocator alloc); template inline allocator(const allocator& alloc); @@ -43,31 +43,31 @@ public: inline allocator& operator=(const allocator& alloc); private: - CompAllocator* m_pAlloc; + CompAllocator m_alloc; template friend class allocator; }; -allocator::allocator(CompAllocator* pAlloc) - : m_pAlloc(pAlloc) +allocator::allocator(CompAllocator alloc) + : m_alloc(alloc) { } allocator::allocator(const allocator& alloc) - : m_pAlloc(alloc.m_pAlloc) + : m_alloc(alloc.m_alloc) { } template allocator::allocator(const allocator& alloc) - : m_pAlloc(alloc.m_pAlloc) + : m_alloc(alloc.m_alloc) { } template allocator& allocator::operator=(const allocator& alloc) { - m_pAlloc = alloc.m_pAlloc; + m_alloc = alloc.m_alloc; return *this; } @@ -86,7 +86,7 @@ public: private: allocator(); public: - allocator(CompAllocator* pAlloc); + allocator(CompAllocator alloc); template allocator(const allocator& alloc); @@ -110,7 +110,7 @@ public: }; private: - CompAllocator* m_pAlloc; + CompAllocator m_alloc; template friend class allocator; }; @@ -122,21 +122,21 @@ namespace jitstd { template -allocator::allocator(CompAllocator* pAlloc) - : m_pAlloc(pAlloc) +allocator::allocator(CompAllocator alloc) + : m_alloc(alloc) { } template template allocator::allocator(const allocator& alloc) - : m_pAlloc(alloc.m_pAlloc) + : m_alloc(alloc.m_alloc) { } template allocator::allocator(const allocator& alloc) - : m_pAlloc(alloc.m_pAlloc) + : m_alloc(alloc.m_alloc) { } @@ -144,7 +144,7 @@ template template allocator& allocator::operator=(const allocator& alloc) { - m_pAlloc = alloc.m_pAlloc; + m_alloc = alloc.m_alloc; return *this; } @@ -163,7 +163,7 @@ typename allocator::const_pointer allocator::address(const_reference val) template T* allocator::allocate(size_type count, allocator::const_pointer hint) { - return (pointer) m_pAlloc->Alloc(sizeof(value_type) * count); + return m_alloc.allocate(count); } template @@ -175,7 +175,7 @@ void allocator::construct(pointer ptr, const_reference val) template void allocator::deallocate(pointer ptr, size_type size) { - // m_pAlloc->Free(ptr); + m_alloc.deallocate(ptr); } template diff --git a/src/coreclr/src/jit/jitstd/utility.h b/src/coreclr/src/jit/jitstd/utility.h index 80ce58e..1930be8 100644 --- a/src/coreclr/src/jit/jitstd/utility.h +++ b/src/coreclr/src/jit/jitstd/utility.h @@ -45,19 +45,6 @@ namespace utility }; - // Helper to allocate objects of any type, given an allocator of void type. - // - // @param alloc An allocator of void type used to create an allocator of type T. - // @param count The number of objects of type T that need to be allocated. - // - // @return A pointer to an object or an array of objects that was allocated. - template - inline - static T* allocate(jitstd::allocator& alloc, size_t count = 1) - { - return jitstd::allocator(alloc).allocate(count); - } - // Ensures that "wset" is the union of the initial state of "wset" and "rset". // Elements from "rset" that were not in "wset" are added to "cset." template diff --git a/src/coreclr/src/jit/lclvars.cpp b/src/coreclr/src/jit/lclvars.cpp index 4b31bd6..0e141fe 100644 --- a/src/coreclr/src/jit/lclvars.cpp +++ b/src/coreclr/src/jit/lclvars.cpp @@ -214,7 +214,7 @@ void Compiler::lvaInitTypeRef() lvaTableCnt = 16; } - lvaTable = (LclVarDsc*)compGetMemArray(lvaTableCnt, sizeof(*lvaTable), CMK_LvaTable); + lvaTable = getAllocator(CMK_LvaTable).allocate(lvaTableCnt); size_t tableSize = lvaTableCnt * sizeof(*lvaTable); memset(lvaTable, 0, tableSize); for (unsigned i = 0; i < lvaTableCnt; i++) @@ -2314,7 +2314,7 @@ void Compiler::lvaSetStruct(unsigned varNum, CORINFO_CLASS_HANDLE typeHnd, bool size_t lvSize = varDsc->lvSize(); assert((lvSize % TARGET_POINTER_SIZE) == 0); // The struct needs to be a multiple of TARGET_POINTER_SIZE bytes for getClassGClayout() to be valid. - varDsc->lvGcLayout = (BYTE*)compGetMem((lvSize / TARGET_POINTER_SIZE) * sizeof(BYTE), CMK_LvaTable); + varDsc->lvGcLayout = getAllocator(CMK_LvaTable).allocate(lvSize / TARGET_POINTER_SIZE); unsigned numGCVars; var_types simdBaseType = TYP_UNKNOWN; varDsc->lvType = impNormStructType(typeHnd, varDsc->lvGcLayout, &numGCVars, &simdBaseType); diff --git a/src/coreclr/src/jit/lir.cpp b/src/coreclr/src/jit/lir.cpp index 2d45af4..0ba18a2 100644 --- a/src/coreclr/src/jit/lir.cpp +++ b/src/coreclr/src/jit/lir.cpp @@ -1435,7 +1435,7 @@ public: CheckLclVarSemanticsHelper(Compiler* compiler, const LIR::Range* range, SmallHashTable& unusedDefs) - : compiler(compiler), range(range), unusedDefs(unusedDefs), unusedLclVarReads(compiler) + : compiler(compiler), range(range), unusedDefs(unusedDefs), unusedLclVarReads(compiler->getAllocator()) { } @@ -1572,7 +1572,7 @@ bool LIR::Range::CheckLIR(Compiler* compiler, bool checkUnusedValues) const slowNode = slowNode->gtNext; } - SmallHashTable unusedDefs(compiler); + SmallHashTable unusedDefs(compiler->getAllocator()); bool pastPhis = false; GenTree* prev = nullptr; diff --git a/src/coreclr/src/jit/loopcloning.cpp b/src/coreclr/src/jit/loopcloning.cpp index cc988b1..f741ff8 100644 --- a/src/coreclr/src/jit/loopcloning.cpp +++ b/src/coreclr/src/jit/loopcloning.cpp @@ -789,7 +789,7 @@ void LC_Deref::DeriveLevelConditions(JitExpandArrayStack* children, unsigned lcl); void DeriveLevelConditions(JitExpandArrayStack*>* len); @@ -560,7 +560,7 @@ struct LC_Deref */ struct LoopCloneContext { - CompAllocator* alloc; // The allocator + CompAllocator alloc; // The allocator JitExpandArrayStack** optInfo; // The array of optimization opportunities found in each loop. (loop x // optimization-opportunities) JitExpandArrayStack** conditions; // The array of conditions that influence which path to take for @@ -572,7 +572,7 @@ struct LoopCloneContext // conditions for // each loop. (loop x level x conditions) - LoopCloneContext(unsigned loopCount, CompAllocator* alloc) : alloc(alloc) + LoopCloneContext(unsigned loopCount, CompAllocator alloc) : alloc(alloc) { optInfo = new (alloc) JitExpandArrayStack*[loopCount]; conditions = new (alloc) JitExpandArrayStack*[loopCount]; diff --git a/src/coreclr/src/jit/lower.cpp b/src/coreclr/src/jit/lower.cpp index b477e20..8859c2f 100644 --- a/src/coreclr/src/jit/lower.cpp +++ b/src/coreclr/src/jit/lower.cpp @@ -1935,7 +1935,7 @@ void Lowering::LowerFastTailCall(GenTreeCall* call) // call could over-write the stack arg that is setup earlier. GenTree* firstPutArgStk = nullptr; GenTreeArgList* args; - ArrayStack putargs(comp); + ArrayStack putargs(comp->getAllocator(CMK_ArrayStack)); for (args = call->gtCallArgs; args; args = args->Rest()) { diff --git a/src/coreclr/src/jit/lsra.cpp b/src/coreclr/src/jit/lsra.cpp index a4ce963..01192b2 100644 --- a/src/coreclr/src/jit/lsra.cpp +++ b/src/coreclr/src/jit/lsra.cpp @@ -605,11 +605,8 @@ LinearScanInterface* getLinearScanAllocator(Compiler* comp) LinearScan::LinearScan(Compiler* theCompiler) : compiler(theCompiler) -#if MEASURE_MEM_ALLOC - , lsraAllocator(nullptr) -#endif // MEASURE_MEM_ALLOC - , intervals(LinearScanMemoryAllocatorInterval(theCompiler)) - , refPositions(LinearScanMemoryAllocatorRefPosition(theCompiler)) + , intervals(theCompiler->getAllocator(CMK_LSRA_Interval)) + , refPositions(theCompiler->getAllocator(CMK_LSRA_RefPosition)) , listNodePool(theCompiler) { #ifdef DEBUG diff --git a/src/coreclr/src/jit/lsra.h b/src/coreclr/src/jit/lsra.h index 84ff2e2..e646294 100644 --- a/src/coreclr/src/jit/lsra.h +++ b/src/coreclr/src/jit/lsra.h @@ -433,48 +433,10 @@ inline bool RefTypeIsDef(RefType refType) typedef regNumberSmall* VarToRegMap; -template -class ListElementAllocator -{ -private: - template - friend class ListElementAllocator; - - Compiler* m_compiler; - -public: - ListElementAllocator(Compiler* compiler) : m_compiler(compiler) - { - } - - template - ListElementAllocator(const ListElementAllocator& other) : m_compiler(other.m_compiler) - { - } - - ElementType* allocate(size_t count) - { - return reinterpret_cast(m_compiler->compGetMem(sizeof(ElementType) * count, MemKind)); - } - - void deallocate(ElementType* pointer, size_t count) - { - } - - template - struct rebind - { - typedef ListElementAllocator allocator; - }; -}; - -typedef ListElementAllocator LinearScanMemoryAllocatorInterval; -typedef ListElementAllocator LinearScanMemoryAllocatorRefPosition; - -typedef jitstd::list IntervalList; -typedef jitstd::list RefPositionList; -typedef jitstd::list::iterator RefPositionIterator; -typedef jitstd::list::reverse_iterator RefPositionReverseIterator; +typedef jitstd::list IntervalList; +typedef jitstd::list RefPositionList; +typedef jitstd::list::iterator RefPositionIterator; +typedef jitstd::list::reverse_iterator RefPositionReverseIterator; class Referenceable { @@ -1399,21 +1361,9 @@ private: Compiler* compiler; private: -#if MEASURE_MEM_ALLOC - CompAllocator* lsraAllocator; -#endif - - CompAllocator* getAllocator(Compiler* comp) + CompAllocator getAllocator(Compiler* comp) { -#if MEASURE_MEM_ALLOC - if (lsraAllocator == nullptr) - { - lsraAllocator = new (comp, CMK_LSRA) CompAllocator(comp, CMK_LSRA); - } - return lsraAllocator; -#else - return comp->getAllocator(); -#endif + return comp->getAllocator(CMK_LSRA); } #ifdef DEBUG diff --git a/src/coreclr/src/jit/lsrabuild.cpp b/src/coreclr/src/jit/lsrabuild.cpp index 0d0efa9..3cd3008 100644 --- a/src/coreclr/src/jit/lsrabuild.cpp +++ b/src/coreclr/src/jit/lsrabuild.cpp @@ -83,9 +83,7 @@ RefInfoListNodePool::RefInfoListNodePool(Compiler* compiler, unsigned preallocat { if (preallocate > 0) { - size_t preallocateSize = sizeof(RefInfoListNode) * preallocate; - RefInfoListNode* preallocatedNodes = - static_cast(compiler->compGetMem(preallocateSize, CMK_LSRA)); + RefInfoListNode* preallocatedNodes = compiler->getAllocator(CMK_LSRA).allocate(preallocate); RefInfoListNode* head = preallocatedNodes; head->m_next = nullptr; @@ -119,7 +117,7 @@ RefInfoListNode* RefInfoListNodePool::GetNode(RefPosition* r, GenTree* t, unsign RefInfoListNode* head = m_freeList; if (head == nullptr) { - head = reinterpret_cast(m_compiler->compGetMem(sizeof(RefInfoListNode))); + head = m_compiler->getAllocator(CMK_LSRA).allocate(1); } else { diff --git a/src/coreclr/src/jit/morph.cpp b/src/coreclr/src/jit/morph.cpp index 4dcc591..6fd800e 100644 --- a/src/coreclr/src/jit/morph.cpp +++ b/src/coreclr/src/jit/morph.cpp @@ -2787,7 +2787,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) ArrayStack args; public: - NonStandardArgs(Compiler* compiler) : args(compiler, 3) // We will have at most 3 non-standard arguments + NonStandardArgs(CompAllocator alloc) : args(alloc, 3) // We will have at most 3 non-standard arguments { } @@ -2874,7 +2874,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) args.IndexRef(index).node = node; } - } nonStandardArgs(this); + } nonStandardArgs(getAllocator(CMK_ArrayStack)); // Count of args. On first morph, this is counted before we've filled in the arg table. // On remorph, we grab it from the arg table. @@ -5122,7 +5122,7 @@ GenTree* Compiler::fgMorphMultiregStructArg(GenTree* arg, fgArgTabEntry* fgEntry // We need to propagate any GTF_ALL_EFFECT flags from the end of the list back to the beginning. // This is verified in fgDebugCheckFlags(). - ArrayStack stack(this); + ArrayStack stack(getAllocator(CMK_ArrayStack)); GenTree* tree; for (tree = newArg; (tree->gtGetOp2() != nullptr) && tree->gtGetOp2()->OperIsFieldList(); tree = tree->gtGetOp2()) { @@ -9872,7 +9872,7 @@ GenTree* Compiler::fgMorphBlkNode(GenTree* tree, bool isDest) addr = tree; GenTree* effectiveVal = tree->gtEffectiveVal(); - GenTreePtrStack commas(this); + GenTreePtrStack commas(getAllocator(CMK_ArrayStack)); for (GenTree* comma = tree; comma != nullptr && comma->gtOper == GT_COMMA; comma = comma->gtGetOp2()) { commas.Push(comma); @@ -13639,7 +13639,7 @@ DONE_MORPHING_CHILDREN: // Perform the transform ADDR(COMMA(x, ..., z)) == COMMA(x, ..., ADDR(z)). // (Be sure to mark "z" as an l-value...) - GenTreePtrStack commas(this); + GenTreePtrStack commas(getAllocator(CMK_ArrayStack)); for (GenTree* comma = op1; comma != nullptr && comma->gtOper == GT_COMMA; comma = comma->gtGetOp2()) { commas.Push(comma); @@ -18540,7 +18540,7 @@ void Compiler::fgMarkAddressExposedLocals() for (stmt = block->bbTreeList; stmt; stmt = stmt->gtNext) { // Call Compiler::fgMarkAddrTakenLocalsCB on each node - AXCStack stk(this); + AXCStack stk(getAllocator(CMK_ArrayStack)); stk.Push(AXC_None); // We start in neither an addr or ind context. fgWalkTree(&stmt->gtStmt.gtStmtExpr, fgMarkAddrTakenLocalsPreCB, fgMarkAddrTakenLocalsPostCB, &stk); } @@ -18746,7 +18746,7 @@ bool Compiler::fgMorphCombineSIMDFieldAssignments(BasicBlock* block, GenTree* st // Since we generated a new address node which didn't exist before, // we should expose this address manually here. - AXCStack stk(this); + AXCStack stk(getAllocator(CMK_ArrayStack)); stk.Push(AXC_None); fgWalkTree(&stmt->gtStmt.gtStmtExpr, fgMarkAddrTakenLocalsPreCB, fgMarkAddrTakenLocalsPostCB, &stk); diff --git a/src/coreclr/src/jit/rangecheck.cpp b/src/coreclr/src/jit/rangecheck.cpp index d80576d..0c1107c 100644 --- a/src/coreclr/src/jit/rangecheck.cpp +++ b/src/coreclr/src/jit/rangecheck.cpp @@ -23,7 +23,7 @@ RangeCheck::RangeCheck(Compiler* pCompiler) , m_pDefTable(nullptr) #endif , m_pCompiler(pCompiler) - , m_alloc(pCompiler, CMK_RangeCheck) + , m_alloc(pCompiler->getAllocator(CMK_RangeCheck)) , m_nVisitBudget(MAX_VISIT_BUDGET) { } @@ -38,7 +38,7 @@ RangeCheck::RangeMap* RangeCheck::GetRangeMap() { if (m_pRangeMap == nullptr) { - m_pRangeMap = new (&m_alloc) RangeMap(&m_alloc); + m_pRangeMap = new (m_alloc) RangeMap(m_alloc); } return m_pRangeMap; } @@ -48,7 +48,7 @@ RangeCheck::OverflowMap* RangeCheck::GetOverflowMap() { if (m_pOverflowMap == nullptr) { - m_pOverflowMap = new (&m_alloc) OverflowMap(&m_alloc); + m_pOverflowMap = new (m_alloc) OverflowMap(m_alloc); } return m_pOverflowMap; } @@ -256,7 +256,7 @@ void RangeCheck::OptimizeRangeCheck(BasicBlock* block, GenTree* stmt, GenTree* t GetRangeMap()->RemoveAll(); GetOverflowMap()->RemoveAll(); - m_pSearchPath = new (&m_alloc) SearchPath(&m_alloc); + m_pSearchPath = new (m_alloc) SearchPath(m_alloc); // Get the range for this index. Range range = GetRange(block, treeIndex, false DEBUGARG(0)); @@ -517,7 +517,7 @@ void RangeCheck::SetDef(UINT64 hash, Location* loc) { if (m_pDefTable == nullptr) { - m_pDefTable = new (&m_alloc) VarToLocMap(&m_alloc); + m_pDefTable = new (m_alloc) VarToLocMap(m_alloc); } #ifdef DEBUG Location* loc2; @@ -1186,7 +1186,7 @@ Range RangeCheck::ComputeRange(BasicBlock* block, GenTree* expr, bool monotonic range = Range(Limit(Limit::keUnknown)); } - GetRangeMap()->Set(expr, new (&m_alloc) Range(range)); + GetRangeMap()->Set(expr, new (m_alloc) Range(range)); m_pSearchPath->Remove(expr); return range; } @@ -1254,7 +1254,7 @@ void RangeCheck::MapStmtDefs(const Location& loc) // To avoid ind(addr) use asgs if (loc.parent->OperIsAssignment()) { - SetDef(HashCode(lclNum, ssaNum), new (&m_alloc) Location(loc)); + SetDef(HashCode(lclNum, ssaNum), new (m_alloc) Location(loc)); } } } @@ -1263,7 +1263,7 @@ void RangeCheck::MapStmtDefs(const Location& loc) { if (loc.parent->OperGet() == GT_ASG) { - SetDef(HashCode(lclNum, ssaNum), new (&m_alloc) Location(loc)); + SetDef(HashCode(lclNum, ssaNum), new (m_alloc) Location(loc)); } } } diff --git a/src/coreclr/src/jit/rangecheck.h b/src/coreclr/src/jit/rangecheck.h index 8b97308..35372a1 100644 --- a/src/coreclr/src/jit/rangecheck.h +++ b/src/coreclr/src/jit/rangecheck.h @@ -175,10 +175,10 @@ struct Limit return false; } #ifdef DEBUG - const char* ToString(CompAllocator* alloc) + const char* ToString(CompAllocator alloc) { unsigned size = 64; - char* buf = (char*)alloc->Alloc(size); + char* buf = alloc.allocate(size); switch (type) { case keUndef: @@ -231,10 +231,10 @@ struct Range } #ifdef DEBUG - char* ToString(CompAllocator* alloc) + char* ToString(CompAllocator alloc) { size_t size = 64; - char* buf = (char*)alloc->Alloc(size); + char* buf = alloc.allocate(size); sprintf_s(buf, size, "<%s, %s>", lLimit.ToString(alloc), uLimit.ToString(alloc)); return buf; } diff --git a/src/coreclr/src/jit/regset.cpp b/src/coreclr/src/jit/regset.cpp index 1ff5ee0..7896fa2 100644 --- a/src/coreclr/src/jit/regset.cpp +++ b/src/coreclr/src/jit/regset.cpp @@ -1065,7 +1065,7 @@ RegSet::SpillDsc* RegSet::SpillDsc::alloc(Compiler* pComp, RegSet* regSet, var_t } else { - spill = (RegSet::SpillDsc*)pComp->compGetMem(sizeof(SpillDsc)); + spill = pComp->getAllocator().allocate(1); } return spill; } diff --git a/src/coreclr/src/jit/scopeinfo.cpp b/src/coreclr/src/jit/scopeinfo.cpp index 5a3f704..366a060 100644 --- a/src/coreclr/src/jit/scopeinfo.cpp +++ b/src/coreclr/src/jit/scopeinfo.cpp @@ -160,7 +160,7 @@ CodeGen::siScope* CodeGen::siNewScope(unsigned LVnum, unsigned varNum) siEndTrackedScope(varIndex); } - siScope* newScope = (siScope*)compiler->compGetMem(sizeof(*newScope), CMK_SiScope); + siScope* newScope = compiler->getAllocator(CMK_SiScope).allocate(1); newScope->scStartLoc.CaptureLocation(getEmitter()); assert(newScope->scStartLoc.Valid()); @@ -825,7 +825,7 @@ void CodeGen::siDispOpenScopes() CodeGen::psiScope* CodeGen::psiNewPrologScope(unsigned LVnum, unsigned slotNum) { - psiScope* newScope = (psiScope*)compiler->compGetMem(sizeof(*newScope), CMK_SiScope); + psiScope* newScope = compiler->getAllocator(CMK_SiScope).allocate(1); newScope->scStartLoc.CaptureLocation(getEmitter()); assert(newScope->scStartLoc.Valid()); diff --git a/src/coreclr/src/jit/smallhash.h b/src/coreclr/src/jit/smallhash.h index 65c5eed..5900d28 100644 --- a/src/coreclr/src/jit/smallhash.h +++ b/src/coreclr/src/jit/smallhash.h @@ -5,11 +5,6 @@ #ifndef _SMALLHASHTABLE_H_ #define _SMALLHASHTABLE_H_ -// Since compiler depends on valuenum which depends on smallhash, forward declare -// a wrapper for comp->compGetMem here (implemented in compiler.hpp) that can be used below. -class Compiler; -void* compGetMem(Compiler* comp, size_t sz); - // genLog2 is defined in compiler.hpp unsigned genLog2(unsigned value); @@ -109,7 +104,7 @@ struct HashTableInfo // TKey - The type of the table's keys. // TValue - The type of the table's values. // TKeyInfo - A type that conforms to the HashTableInfo concept. -template > +template , typename TAllocator = CompAllocator> class HashTableBase { friend class KeyValuePair; @@ -151,10 +146,10 @@ protected: }; private: - Compiler* m_compiler; // The compiler context to use for allocations. - Bucket* m_buckets; // The bucket array. - unsigned m_numBuckets; // The number of buckets in the bucket array. - unsigned m_numFullBuckets; // The number of occupied buckets. + TAllocator m_alloc; // The memory allocator. + Bucket* m_buckets; // The bucket array. + unsigned m_numBuckets; // The number of buckets in the bucket array. + unsigned m_numFullBuckets; // The number of occupied buckets. //------------------------------------------------------------------------ // HashTableBase::Insert: inserts a key-value pair into a bucket array. @@ -302,11 +297,8 @@ private: Bucket* currentBuckets = m_buckets; unsigned newNumBuckets = m_numBuckets == 0 ? InitialNumBuckets : m_numBuckets * 2; - size_t allocSize = sizeof(Bucket) * newNumBuckets; - assert((sizeof(Bucket) * m_numBuckets) < allocSize); - - auto* newBuckets = reinterpret_cast(compGetMem(m_compiler, allocSize)); - memset(newBuckets, 0, allocSize); + Bucket* newBuckets = m_alloc.template allocate(newNumBuckets); + memset(newBuckets, 0, sizeof(Bucket) * newNumBuckets); for (unsigned currentIndex = 0; currentIndex < m_numBuckets; currentIndex++) { @@ -326,11 +318,9 @@ private: } protected: - HashTableBase(Compiler* compiler, Bucket* buckets, unsigned numBuckets) - : m_compiler(compiler), m_buckets(buckets), m_numBuckets(numBuckets), m_numFullBuckets(0) + HashTableBase(TAllocator alloc, Bucket* buckets, unsigned numBuckets) + : m_alloc(alloc), m_buckets(buckets), m_numBuckets(numBuckets), m_numFullBuckets(0) { - assert(compiler != nullptr); - if (numBuckets > 0) { assert((numBuckets & (numBuckets - 1)) == 0); // Size must be a power of 2 @@ -599,10 +589,10 @@ public: //------------------------------------------------------------------------ // HashTable: a simple subclass of `HashTableBase` that always uses heap // storage for its bucket array. -template > -class HashTable final : public HashTableBase +template , typename TAllocator = CompAllocator> +class HashTable final : public HashTableBase { - typedef HashTableBase TBase; + typedef HashTableBase TBase; static unsigned RoundUp(unsigned initialSize) { @@ -610,15 +600,12 @@ class HashTable final : public HashTableBase } public: - HashTable(Compiler* compiler) : TBase(compiler, nullptr, 0) + HashTable(TAllocator alloc) : TBase(alloc, nullptr, 0) { } - HashTable(Compiler* compiler, unsigned initialSize) - : TBase(compiler, - reinterpret_cast( - compGetMem(compiler, RoundUp(initialSize) * sizeof(typename TBase::Bucket))), - RoundUp(initialSize)) + HashTable(TAllocator alloc, unsigned initialSize) + : TBase(alloc, alloc.template allocate(RoundUp(initialSize)), RoundUp(initialSize)) { } }; @@ -630,10 +617,14 @@ public: // the map at any given time falls below a certain // threshold. Switches to heap storage once the initial // inline storage is exhausted. -template > -class SmallHashTable final : public HashTableBase +template , + typename TAllocator = CompAllocator> +class SmallHashTable final : public HashTableBase { - typedef HashTableBase TBase; + typedef HashTableBase TBase; enum : unsigned { @@ -643,7 +634,7 @@ class SmallHashTable final : public HashTableBase typename TBase::Bucket m_inlineBuckets[RoundedNumInlineBuckets]; public: - SmallHashTable(Compiler* compiler) : TBase(compiler, m_inlineBuckets, RoundedNumInlineBuckets) + SmallHashTable(TAllocator alloc) : TBase(alloc, m_inlineBuckets, RoundedNumInlineBuckets) { } }; diff --git a/src/coreclr/src/jit/ssabuilder.cpp b/src/coreclr/src/jit/ssabuilder.cpp index 9845aa9..fbd7d90 100644 --- a/src/coreclr/src/jit/ssabuilder.cpp +++ b/src/coreclr/src/jit/ssabuilder.cpp @@ -134,7 +134,7 @@ void Compiler::fgResetForSsa() */ SsaBuilder::SsaBuilder(Compiler* pCompiler) : m_pCompiler(pCompiler) - , m_allocator(pCompiler, CMK_SSA) + , m_allocator(pCompiler->getAllocator(CMK_SSA)) , m_visitedTraits(0, pCompiler) // at this point we do not know the size, SetupBBRoot can add a block #ifdef SSA_FEATURE_DOMARR , m_pDomPreOrder(nullptr) @@ -193,8 +193,7 @@ int SsaBuilder::TopologicalSort(BasicBlock** postOrder, int count) BasicBlock* block = comp->fgFirstBB; BitVecOps::AddElemD(&m_visitedTraits, m_visited, block->bbNum); - ArrayStack blocks(comp); - + ArrayStack blocks(m_allocator); blocks.Emplace(comp, block); DumpBlockAndSuccessors(comp, block); @@ -538,7 +537,7 @@ void SsaBuilder::ComputeDominanceFrontiers(BasicBlock** postOrder, int count, Bl { DBG_SSA_JITDUMP(" Adding BB%02u to dom frontier of pred dom BB%02u.\n", block->bbNum, b1->bbNum); - BlkVector& b1DF = *mapDF->Emplace(b1, &m_allocator); + BlkVector& b1DF = *mapDF->Emplace(b1, m_allocator); // It's possible to encounter the same DF multiple times, ensure that we don't add duplicates. if (b1DF.empty() || (b1DF.back() != block)) { @@ -692,12 +691,12 @@ void SsaBuilder::InsertPhiFunctions(BasicBlock** postOrder, int count) EndPhase(PHASE_BUILD_SSA_LIVENESS); // Compute dominance frontier. - BlkToBlkVectorMap mapDF(&m_allocator); + BlkToBlkVectorMap mapDF(m_allocator); ComputeDominanceFrontiers(postOrder, count, &mapDF); EndPhase(PHASE_BUILD_SSA_DF); // Use the same IDF vector for all blocks to avoid unnecessary memory allocations - BlkVector blockIDF(&m_allocator); + BlkVector blockIDF(m_allocator); JITDUMP("Inserting phi functions:\n"); @@ -1614,7 +1613,7 @@ void SsaBuilder::RenameVariables(BlkToBlkVectorMap* domTree, SsaRenameState* pRe }; typedef jitstd::vector BlockWorkStack; - BlockWorkStack* blocksToDo = new (&m_allocator) BlockWorkStack(&m_allocator); + BlockWorkStack* blocksToDo = new (m_allocator) BlockWorkStack(m_allocator); blocksToDo->push_back(BlockWork(m_pCompiler->fgFirstBB)); // Probably have to include other roots of dom tree. while (blocksToDo->size() != 0) @@ -1739,7 +1738,7 @@ void SsaBuilder::Build() if (blockCount > DEFAULT_MIN_OPTS_BB_COUNT) { - postOrder = new (&m_allocator) BasicBlock*[blockCount]; + postOrder = new (m_allocator) BasicBlock*[blockCount]; } else { @@ -1758,7 +1757,7 @@ void SsaBuilder::Build() ComputeImmediateDom(postOrder, count); // Compute the dominator tree. - BlkToBlkVectorMap* domTree = new (&m_allocator) BlkToBlkVectorMap(&m_allocator); + BlkToBlkVectorMap* domTree = new (m_allocator) BlkToBlkVectorMap(m_allocator); ComputeDominators(postOrder, count, domTree); EndPhase(PHASE_BUILD_SSA_DOMS); @@ -1766,8 +1765,8 @@ void SsaBuilder::Build() InsertPhiFunctions(postOrder, count); // Rename local variables and collect UD information for each ssa var. - SsaRenameState* pRenameState = new (&m_allocator) - SsaRenameState(&m_allocator, m_pCompiler->lvaCount, m_pCompiler->byrefStatesMatchGcHeapStates); + SsaRenameState* pRenameState = + new (m_allocator) SsaRenameState(m_allocator, m_pCompiler->lvaCount, m_pCompiler->byrefStatesMatchGcHeapStates); RenameVariables(domTree, pRenameState); EndPhase(PHASE_BUILD_SSA_RENAME); diff --git a/src/coreclr/src/jit/ssarenamestate.cpp b/src/coreclr/src/jit/ssarenamestate.cpp index 4ccac05..9ec0770 100644 --- a/src/coreclr/src/jit/ssarenamestate.cpp +++ b/src/coreclr/src/jit/ssarenamestate.cpp @@ -28,9 +28,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * * @params alloc The allocator class used to allocate jitstd data. */ -SsaRenameState::SsaRenameState(const jitstd::allocator& alloc, - unsigned lvaCount, - bool byrefStatesMatchGcHeapStates) +SsaRenameState::SsaRenameState(CompAllocator alloc, unsigned lvaCount, bool byrefStatesMatchGcHeapStates) : counts(nullptr) , stacks(nullptr) , definedLocs(alloc) @@ -51,7 +49,7 @@ void SsaRenameState::EnsureCounts() { if (counts == nullptr) { - counts = jitstd::utility::allocate(m_alloc, lvaCount); + counts = m_alloc.allocate(lvaCount); for (unsigned i = 0; i < lvaCount; ++i) { counts[i] = SsaConfig::FIRST_SSA_NUM; @@ -68,7 +66,7 @@ void SsaRenameState::EnsureStacks() { if (stacks == nullptr) { - stacks = jitstd::utility::allocate(m_alloc, lvaCount); + stacks = m_alloc.allocate(lvaCount); for (unsigned i = 0; i < lvaCount; ++i) { stacks[i] = nullptr; @@ -141,7 +139,7 @@ void SsaRenameState::Push(BasicBlock* bb, unsigned lclNum, unsigned count) if (stack == nullptr) { DBG_SSA_JITDUMP("\tCreating a new stack\n"); - stack = stacks[lclNum] = new (jitstd::utility::allocate(m_alloc), jitstd::placement_t()) Stack(m_alloc); + stack = stacks[lclNum] = new (m_alloc) Stack(m_alloc); } if (stack->empty() || stack->back().m_bb != bb) diff --git a/src/coreclr/src/jit/ssarenamestate.h b/src/coreclr/src/jit/ssarenamestate.h index a8496b6..a17b572 100644 --- a/src/coreclr/src/jit/ssarenamestate.h +++ b/src/coreclr/src/jit/ssarenamestate.h @@ -101,7 +101,7 @@ struct SsaRenameState typedef unsigned* Counts; typedef jitstd::list DefStack; - SsaRenameState(const jitstd::allocator& allocator, unsigned lvaCount, bool byrefStatesMatchGcHeapStates); + SsaRenameState(CompAllocator allocator, unsigned lvaCount, bool byrefStatesMatchGcHeapStates); void EnsureCounts(); void EnsureStacks(); @@ -182,7 +182,7 @@ private: unsigned lvaCount; // Allocator to allocate stacks. - jitstd::allocator m_alloc; + CompAllocator m_alloc; // Indicates whether GcHeap and ByrefExposed use the same state. bool byrefStatesMatchGcHeapStates; diff --git a/src/coreclr/src/jit/stacklevelsetter.cpp b/src/coreclr/src/jit/stacklevelsetter.cpp index b0b6324..393eb25 100644 --- a/src/coreclr/src/jit/stacklevelsetter.cpp +++ b/src/coreclr/src/jit/stacklevelsetter.cpp @@ -13,8 +13,8 @@ StackLevelSetter::StackLevelSetter(Compiler* compiler) : Phase(compiler, "StackLevelSetter", PHASE_STACK_LEVEL_SETTER) , currentStackLevel(0) , maxStackLevel(0) - , memAllocator(compiler, CMK_fgArgInfoPtrArr) - , putArgNumSlots(&memAllocator) + , memAllocator(compiler->getAllocator(CMK_fgArgInfoPtrArr)) + , putArgNumSlots(memAllocator) #if !FEATURE_FIXED_OUT_ARGS , framePointerRequired(compiler->codeGen->isFramePointerRequired()) , throwHelperBlocksUsed(comp->fgUseThrowHelperBlocks() && comp->compUsesThrowHelper) diff --git a/src/coreclr/src/jit/unwind.cpp b/src/coreclr/src/jit/unwind.cpp index 1f090b2..db120c5 100644 --- a/src/coreclr/src/jit/unwind.cpp +++ b/src/coreclr/src/jit/unwind.cpp @@ -160,12 +160,6 @@ void Compiler::unwindPushPopCFI(regNumber reg) } } -template -inline static T* allocate_any(jitstd::allocator& alloc, size_t count = 5) -{ - return jitstd::allocator(alloc).allocate(count); -} - typedef jitstd::vector CFICodeVector; void Compiler::unwindBegPrologCFI() @@ -185,9 +179,7 @@ void Compiler::unwindBegPrologCFI() unwindGetFuncLocations(func, false, &func->coldStartLoc, &func->coldEndLoc); } - jitstd::allocator allocator(getAllocator()); - - func->cfiCodes = new (allocate_any(allocator), jitstd::placement_t()) CFICodeVector(allocator); + func->cfiCodes = new (getAllocator()) CFICodeVector(getAllocator()); #endif // FEATURE_EH_FUNCLETS } diff --git a/src/coreclr/src/jit/utils.cpp b/src/coreclr/src/jit/utils.cpp index 7a8854f..2b120dd 100644 --- a/src/coreclr/src/jit/utils.cpp +++ b/src/coreclr/src/jit/utils.cpp @@ -953,7 +953,7 @@ FixedBitVect* FixedBitVect::bitVectInit(UINT size, Compiler* comp) assert(bitVectMemSize * bitChunkSize() >= size); - bv = (FixedBitVect*)comp->compGetMem(sizeof(FixedBitVect) + bitVectMemSize, CMK_FixedBitVect); + bv = (FixedBitVect*)comp->getAllocator(CMK_FixedBitVect).allocate(sizeof(FixedBitVect) + bitVectMemSize); memset(bv->bitVect, 0, bitVectMemSize); bv->bitVectSize = size; @@ -1491,10 +1491,8 @@ void HelperCallProperties::init() // MyAssembly;mscorlib;System // MyAssembly;mscorlib System -AssemblyNamesList2::AssemblyNamesList2(const wchar_t* list, HostAllocator* alloc) : m_alloc(alloc) +AssemblyNamesList2::AssemblyNamesList2(const wchar_t* list, HostAllocator alloc) : m_alloc(alloc) { - assert(m_alloc != nullptr); - WCHAR prevChar = '?'; // dummy LPWSTR nameStart = nullptr; // start of the name currently being processed. nullptr if no current name AssemblyName** ppPrevLink = &m_pNames; @@ -1561,8 +1559,8 @@ AssemblyNamesList2::~AssemblyNamesList2() AssemblyName* cur = pName; pName = pName->m_next; - m_alloc->Free(cur->m_assemblyName); - m_alloc->Free(cur); + m_alloc.deallocate(cur->m_assemblyName); + m_alloc.deallocate(cur); } } diff --git a/src/coreclr/src/jit/utils.h b/src/coreclr/src/jit/utils.h index 8dadabb..fb6e345 100644 --- a/src/coreclr/src/jit/utils.h +++ b/src/coreclr/src/jit/utils.h @@ -539,12 +539,12 @@ class AssemblyNamesList2 AssemblyName* m_next; }; - AssemblyName* m_pNames; // List of names - HostAllocator* m_alloc; // HostAllocator to use in this class + AssemblyName* m_pNames; // List of names + HostAllocator m_alloc; // HostAllocator to use in this class public: // Take a Unicode string list of assembly names, parse it, and store it. - AssemblyNamesList2(const wchar_t* list, __in HostAllocator* alloc); + AssemblyNamesList2(const wchar_t* list, HostAllocator alloc); ~AssemblyNamesList2(); diff --git a/src/coreclr/src/jit/valuenum.cpp b/src/coreclr/src/jit/valuenum.cpp index 825d6ec..8b70757 100644 --- a/src/coreclr/src/jit/valuenum.cpp +++ b/src/coreclr/src/jit/valuenum.cpp @@ -50,7 +50,7 @@ VNFunc GetVNFuncForOper(genTreeOps oper, bool isUnsigned) } } -ValueNumStore::ValueNumStore(Compiler* comp, CompAllocator* alloc) +ValueNumStore::ValueNumStore(Compiler* comp, CompAllocator alloc) : m_pComp(comp) , m_alloc(alloc) , @@ -60,7 +60,7 @@ ValueNumStore::ValueNumStore(Compiler* comp, CompAllocator* alloc) #endif m_nextChunkBase(0) , m_fixedPointMapSels(alloc, 8) - , m_checkedBoundVNs(comp) + , m_checkedBoundVNs(alloc) , m_chunks(alloc, 8) , m_intCnsMap(nullptr) , m_longCnsMap(nullptr) @@ -672,7 +672,7 @@ bool ValueNumStore::IsSharedStatic(ValueNum vn) return GetVNFunc(vn, &funcAttr) && (s_vnfOpAttribs[funcAttr.m_func] & VNFOA_SharedStatic) != 0; } -ValueNumStore::Chunk::Chunk(CompAllocator* alloc, +ValueNumStore::Chunk::Chunk(CompAllocator alloc, ValueNum* pNextBaseVN, var_types typ, ChunkExtraAttribs attribs, @@ -4538,8 +4538,8 @@ void Compiler::fgValueNumber() assert(fgVNPassesCompleted > 0 || vnStore == nullptr); if (fgVNPassesCompleted == 0) { - CompAllocator* allocator = new (this, CMK_ValueNumber) CompAllocator(this, CMK_ValueNumber); - vnStore = new (this, CMK_ValueNumber) ValueNumStore(this, allocator); + CompAllocator allocator(getAllocator(CMK_ValueNumber)); + vnStore = new (allocator) ValueNumStore(this, allocator); } else { diff --git a/src/coreclr/src/jit/valuenum.h b/src/coreclr/src/jit/valuenum.h index a8aabef..b0c1580 100644 --- a/src/coreclr/src/jit/valuenum.h +++ b/src/coreclr/src/jit/valuenum.h @@ -103,7 +103,7 @@ public: class VNMap : public JitHashTable { public: - VNMap(CompAllocator* alloc) : JitHashTable(alloc) + VNMap(CompAllocator alloc) : JitHashTable(alloc) { } ~VNMap() @@ -128,7 +128,7 @@ private: Compiler* m_pComp; // For allocations. (Other things?) - CompAllocator* m_alloc; + CompAllocator m_alloc; // TODO-Cleanup: should transform "attribs" into a struct with bit fields. That would be simpler... @@ -237,7 +237,7 @@ public: static void InitValueNumStoreStatics(); // Initialize an empty ValueNumStore. - ValueNumStore(Compiler* comp, CompAllocator* allocator); + ValueNumStore(Compiler* comp, CompAllocator allocator); // Returns "true" iff "vnf" (which may have been created by a cast from an integral value) represents // a legal value number function. @@ -916,7 +916,7 @@ private: // Initialize a chunk, starting at "*baseVN", for the given "typ", "attribs", and "loopNum" (using "alloc" for // allocations). // (Increments "*baseVN" by ChunkSize.) - Chunk(CompAllocator* alloc, + Chunk(CompAllocator alloc, ValueNum* baseVN, var_types typ, ChunkExtraAttribs attribs, -- 2.7.4