From 356300f9b52b65c5f9775b54564e732ea48e7431 Mon Sep 17 00:00:00 2001 From: Lei Shi Date: Wed, 26 Oct 2016 11:57:16 -0700 Subject: [PATCH] reduce native code data memory usage for in-proc jit In OOP JIT when allocating native code data we record some information about the allocation. At the end of a JIT work item, the allocation information is used to make the pointer fixing up table which then used to fixup the internal pointers after passing native code data to runtime process. After a JIT work item is done, all the native code data including the allocation information are freed in JIT server, only the fixup table and aggregated data buffer is passed to runtime process. The fixup table is freed right after doing fixup in runntime process after the codegen call returned. When doing in-proc jit we don't need these fixup information, and we don't do pointer fixing up and data aggregating/copying, the the fixup information has same life time as the native code data themselves, thus wasting memory. This change bring back the same data structure which before the OOP JIT change and use it for in-proc JIT Note in OOP JIT it uses one pointer size less memory than in-proc JIT because even the next pointer on DataChunk is not kept after data aggregating. --- lib/Backend/NativeCodeData.cpp | 107 ++++++++++++------ lib/Backend/NativeCodeData.h | 77 ++++++++----- .../Language/JavascriptNativeOperators.h | 39 +++---- 3 files changed, 141 insertions(+), 82 deletions(-) diff --git a/lib/Backend/NativeCodeData.cpp b/lib/Backend/NativeCodeData.cpp index 9042c585e08..79f8bc19276 100644 --- a/lib/Backend/NativeCodeData.cpp +++ b/lib/Backend/NativeCodeData.cpp @@ -4,7 +4,8 @@ //------------------------------------------------------------------------------------------------------- #include "Backend.h" -NativeCodeData::NativeCodeData(DataChunk * chunkList) : chunkList(chunkList) +NativeCodeData::NativeCodeData(DataChunk * chunkList) + : chunkList(chunkList) { #ifdef PERF_COUNTERS this->size = 0; @@ -13,7 +14,14 @@ NativeCodeData::NativeCodeData(DataChunk * chunkList) : chunkList(chunkList) NativeCodeData::~NativeCodeData() { - NativeCodeData::DeleteChunkList(this->chunkList); + if (JITManager::GetJITManager()->IsJITServer()) + { + NativeCodeData::DeleteChunkList(this->chunkList); + } + else + { + NativeCodeData::DeleteChunkList(this->noFixupChunkList); + } PERF_COUNTER_SUB(Code, DynamicNativeCodeDataSize, this->size); PERF_COUNTER_SUB(Code, TotalNativeCodeDataSize, this->size); } @@ -171,19 +179,23 @@ NativeCodeData::VerifyExistFixupEntry(void* targetAddr, void* addrToFixup, void* AssertMsg(false, "Data chunk not found"); } +template void -NativeCodeData::DeleteChunkList(DataChunk * chunkList) +NativeCodeData::DeleteChunkList(DataChunkT * chunkList) { - DataChunk * next = chunkList; + DataChunkT * next = chunkList; while (next != nullptr) { - DataChunk * current = next; + DataChunkT * current = next; next = next->next; delete current; } } -NativeCodeData::Allocator::Allocator() : chunkList(nullptr), lastChunkList(nullptr) +NativeCodeData::Allocator::Allocator() + : chunkList(nullptr), + lastChunkList(nullptr), + isOOPJIT(JITManager::GetJITManager()->IsJITServer()) { this->totalSize = 0; this->allocCount = 0; @@ -198,55 +210,73 @@ NativeCodeData::Allocator::Allocator() : chunkList(nullptr), lastChunkList(nullp NativeCodeData::Allocator::~Allocator() { Assert(!finalized || this->chunkList == nullptr); - NativeCodeData::DeleteChunkList(this->chunkList); + if (JITManager::GetJITManager()->IsJITServer()) + { + NativeCodeData::DeleteChunkList(this->chunkList); + } + else + { + NativeCodeData::DeleteChunkList(this->noFixupChunkList); + } PERF_COUNTER_SUB(Code, DynamicNativeCodeDataSize, this->size); PERF_COUNTER_SUB(Code, TotalNativeCodeDataSize, this->size); } char * -NativeCodeData::Allocator::Alloc(size_t requestSize) +NativeCodeData::Allocator::Alloc(DECLSPEC_GUARD_OVERFLOW size_t requestSize) { - char * data = nullptr; Assert(!finalized); + char * data = nullptr; requestSize = Math::Align(requestSize, sizeof(void*)); + if (isOOPJIT) + { + #if DBG - // Always zero out the data for chk build to reduce the chance of false - // positive while verifying missing fixup entries - // Allocation without zeroing out, and with bool field in the structure - // will increase the chance of false positive because of reusing memory - // without zeroing, and the bool field is set to false, makes the garbage - // memory not changed, and the garbage memory might be just pointing to the - // same range of NativeCodeData memory, the checking tool will report false - // poisitive, see NativeCodeData::VerifyExistFixupEntry for more - DataChunk * newChunk = HeapNewStructPlusZ(requestSize, DataChunk); + // Always zero out the data for chk build to reduce the chance of false + // positive while verifying missing fixup entries + // Allocation without zeroing out, and with bool field in the structure + // will increase the chance of false positive because of reusing memory + // without zeroing, and the bool field is set to false, makes the garbage + // memory not changed, and the garbage memory might be just pointing to the + // same range of NativeCodeData memory, the checking tool will report false + // poisitive, see NativeCodeData::VerifyExistFixupEntry for more + DataChunk * newChunk = HeapNewStructPlusZ(requestSize, DataChunk); #else - DataChunk * newChunk = HeapNewStructPlus(requestSize, DataChunk); + DataChunk * newChunk = HeapNewStructPlus(requestSize, DataChunk); #endif #if DBG - newChunk->dataType = nullptr; + newChunk->dataType = nullptr; #endif - newChunk->next = nullptr; - newChunk->allocIndex = this->allocCount++; - newChunk->len = (unsigned int)requestSize; - newChunk->fixupList = nullptr; - newChunk->fixupFunc = nullptr; - newChunk->offset = this->totalSize; - if (this->chunkList == nullptr) - { - this->chunkList = newChunk; - this->lastChunkList = newChunk; + newChunk->next = nullptr; + newChunk->allocIndex = this->allocCount++; + newChunk->len = (unsigned int)requestSize; + newChunk->fixupList = nullptr; + newChunk->fixupFunc = nullptr; + newChunk->offset = this->totalSize; + if (this->chunkList == nullptr) + { + this->chunkList = newChunk; + this->lastChunkList = newChunk; + } + else + { + this->lastChunkList->next = newChunk; + this->lastChunkList = newChunk; + } + this->totalSize += (unsigned int)requestSize; + data = newChunk->data; } else { - this->lastChunkList->next = newChunk; - this->lastChunkList = newChunk; + DataChunkNoFixup * newChunk = HeapNewStructPlus(requestSize, DataChunkNoFixup); + newChunk->next = this->noFixupChunkList; + this->noFixupChunkList = newChunk; + data = newChunk->data; } - data = newChunk->data; - this->totalSize += (unsigned int)requestSize; #ifdef PERF_COUNTERS this->size += requestSize; @@ -258,18 +288,23 @@ NativeCodeData::Allocator::Alloc(size_t requestSize) } char * -NativeCodeData::Allocator::AllocLeaf(size_t requestSize) +NativeCodeData::Allocator::AllocLeaf(DECLSPEC_GUARD_OVERFLOW size_t requestSize) { return Alloc(requestSize); } char * -NativeCodeData::Allocator::AllocZero(size_t requestSize) +NativeCodeData::Allocator::AllocZero(DECLSPEC_GUARD_OVERFLOW size_t requestSize) { char * data = Alloc(requestSize); #if !DBG // Allocated with HeapNewStructPlusZ for chk build memset(data, 0, requestSize); +#else + if (!isOOPJIT) + { + memset(data, 0, requestSize); + } #endif return data; } diff --git a/lib/Backend/NativeCodeData.h b/lib/Backend/NativeCodeData.h index 5591ef6ad0f..eb418ac9601 100644 --- a/lib/Backend/NativeCodeData.h +++ b/lib/Backend/NativeCodeData.h @@ -42,8 +42,15 @@ class NativeCodeData char data[0]; }; + struct DataChunkNoFixup + { + DataChunkNoFixup * next; + char data[0]; + }; + static DataChunk* GetDataChunk(void* data) { + Assert(JITManager::GetJITManager()->IsJITServer()); return (NativeCodeData::DataChunk*)((char*)data - offsetof(NativeCodeData::DataChunk, data)); } @@ -51,11 +58,16 @@ class NativeCodeData static unsigned int GetDataTotalOffset(void* data) { + Assert(JITManager::GetJITManager()->IsJITServer()); return GetDataChunk(data)->offset; } NativeCodeData(DataChunk * chunkList); - DataChunk * chunkList; + union + { + DataChunk * chunkList; + DataChunkNoFixup * noFixupChunkList; + }; #ifdef PERF_COUNTERS size_t size; @@ -66,7 +78,8 @@ class NativeCodeData static void AddFixupEntry(void* targetAddr, void* addrToFixup, void* startAddress, DataChunk * chunkList); static void AddFixupEntry(void* targetAddr, void* targetStartAddr, void* addrToFixup, void* startAddress, DataChunk * chunkList); static void AddFixupEntryForPointerArray(void* startAddress, DataChunk * chunkList); - static void DeleteChunkList(DataChunk * chunkList); + template + static void DeleteChunkList(DataChunkT * chunkList); public: class Allocator { @@ -83,8 +96,12 @@ class NativeCodeData NativeCodeData * Finalize(); void Free(void * buffer, size_t byteSize); - DataChunk * chunkList; - DataChunk * lastChunkList; + union + { + DataChunk * chunkList; + DataChunkNoFixup* noFixupChunkList; + }; + DataChunk * lastChunkList; // used to maintain the allocation order in the list unsigned int totalSize; unsigned int allocCount; @@ -93,8 +110,9 @@ class NativeCodeData Allocator * TrackAllocInfo(TrackAllocData const& data) { return this; } void ClearTrackAllocInfo(TrackAllocData* data = NULL) {} #endif + protected: + bool isOOPJIT; private: - #if DBG bool finalized; #endif @@ -125,12 +143,15 @@ class NativeCodeData { char* dataBlock = __super::Alloc(requestedBytes); #if DBG - DataChunk* chunk = NativeCodeData::GetDataChunk(dataBlock); - chunk->dataType = typeid(T).name(); - if (PHASE_TRACE1(Js::NativeCodeDataPhase)) + if (JITManager::GetJITManager()->IsJITServer()) { - Output::Print(_u("NativeCodeData AllocNoFix: chunk: %p, data: %p, index: %d, len: %x, totalOffset: %x, type: %S\n"), - chunk, (void*)dataBlock, chunk->allocIndex, chunk->len, chunk->offset, chunk->dataType); + DataChunk* chunk = NativeCodeData::GetDataChunk(dataBlock); + chunk->dataType = typeid(T).name(); + if (PHASE_TRACE1(Js::NativeCodeDataPhase)) + { + Output::Print(_u("NativeCodeData AllocNoFix: chunk: %p, data: %p, index: %d, len: %x, totalOffset: %x, type: %S\n"), + chunk, (void*)dataBlock, chunk->allocIndex, chunk->len, chunk->offset, chunk->dataType); + } } #endif @@ -141,12 +162,15 @@ class NativeCodeData char* dataBlock = __super::AllocZero(requestedBytes); #if DBG - DataChunk* chunk = NativeCodeData::GetDataChunk(dataBlock); - chunk->dataType = typeid(T).name(); - if (PHASE_TRACE1(Js::NativeCodeDataPhase)) + if (JITManager::GetJITManager()->IsJITServer()) { - Output::Print(_u("NativeCodeData AllocNoFix: chunk: %p, data: %p, index: %d, len: %x, totalOffset: %x, type: %S\n"), - chunk, (void*)dataBlock, chunk->allocIndex, chunk->len, chunk->offset, chunk->dataType); + DataChunk* chunk = NativeCodeData::GetDataChunk(dataBlock); + chunk->dataType = typeid(T).name(); + if (PHASE_TRACE1(Js::NativeCodeDataPhase)) + { + Output::Print(_u("NativeCodeData AllocNoFix: chunk: %p, data: %p, index: %d, len: %x, totalOffset: %x, type: %S\n"), + chunk, (void*)dataBlock, chunk->allocIndex, chunk->len, chunk->offset, chunk->dataType); + } } #endif @@ -161,22 +185,21 @@ class NativeCodeData template class AllocatorT : public Allocator { -#if DBG - __declspec(noinline) // compiler inline this function even in chk build... maybe because it's in .h file? -#endif char* AddFixup(char* dataBlock) { - DataChunk* chunk = NativeCodeData::GetDataChunk(dataBlock); - chunk->fixupFunc = &Fixup; -#if DBG - chunk->dataType = typeid(T).name(); - if (PHASE_TRACE1(Js::NativeCodeDataPhase)) + if (isOOPJIT) { - Output::Print(_u("NativeCodeData Alloc: chunk: %p, data: %p, index: %d, len: %x, totalOffset: %x, type: %S\n"), - chunk, (void*)dataBlock, chunk->allocIndex, chunk->len, chunk->offset, chunk->dataType); - } + DataChunk* chunk = NativeCodeData::GetDataChunk(dataBlock); + chunk->fixupFunc = &Fixup; +#if DBG + chunk->dataType = typeid(T).name(); + if (PHASE_TRACE1(Js::NativeCodeDataPhase)) + { + Output::Print(_u("NativeCodeData Alloc: chunk: %p, data: %p, index: %d, len: %x, totalOffset: %x, type: %S\n"), + chunk, (void*)dataBlock, chunk->allocIndex, chunk->len, chunk->offset, chunk->dataType); + } #endif - + } return dataBlock; } diff --git a/lib/Runtime/Language/JavascriptNativeOperators.h b/lib/Runtime/Language/JavascriptNativeOperators.h index 4f0c60161eb..2d68615013f 100644 --- a/lib/Runtime/Language/JavascriptNativeOperators.h +++ b/lib/Runtime/Language/JavascriptNativeOperators.h @@ -16,12 +16,15 @@ namespace Js { char* dataBlock = __super::Alloc(requestedBytes); #if DBG - NativeCodeData::DataChunk* chunk = NativeCodeData::GetDataChunk(dataBlock); - chunk->dataType = "BranchDictionary::Bucket"; - if (PHASE_TRACE1(Js::NativeCodeDataPhase)) + if (JITManager::GetJITManager()->IsJITServer()) { - Output::Print(_u("NativeCodeData BranchDictionary::Bucket: chunk: %p, data: %p, index: %d, len: %x, totalOffset: %x, type: %S\n"), - chunk, (void*)dataBlock, chunk->allocIndex, chunk->len, chunk->offset, chunk->dataType); + NativeCodeData::DataChunk* chunk = NativeCodeData::GetDataChunk(dataBlock); + chunk->dataType = "BranchDictionary::Bucket"; + if (PHASE_TRACE1(Js::NativeCodeDataPhase)) + { + Output::Print(_u("NativeCodeData BranchDictionary::Bucket: chunk: %p, data: %p, index: %d, len: %x, totalOffset: %x, type: %S\n"), + chunk, (void*)dataBlock, chunk->allocIndex, chunk->len, chunk->offset, chunk->dataType); + } } #endif return dataBlock; @@ -31,12 +34,15 @@ namespace Js { char* dataBlock = __super::AllocZero(requestedBytes); #if DBG - NativeCodeData::DataChunk* chunk = NativeCodeData::GetDataChunk(dataBlock); - chunk->dataType = "BranchDictionary::Entries"; - if (PHASE_TRACE1(Js::NativeCodeDataPhase)) + if (JITManager::GetJITManager()->IsJITServer()) { - Output::Print(_u("NativeCodeData BranchDictionary::Entries: chunk: %p, data: %p, index: %d, len: %x, totalOffset: %x, type: %S\n"), - chunk, (void*)dataBlock, chunk->allocIndex, chunk->len, chunk->offset, chunk->dataType); + NativeCodeData::DataChunk* chunk = NativeCodeData::GetDataChunk(dataBlock); + chunk->dataType = "BranchDictionary::Entries"; + if (PHASE_TRACE1(Js::NativeCodeDataPhase)) + { + Output::Print(_u("NativeCodeData BranchDictionary::Entries: chunk: %p, data: %p, index: %d, len: %x, totalOffset: %x, type: %S\n"), + chunk, (void*)dataBlock, chunk->allocIndex, chunk->len, chunk->offset, chunk->dataType); + } } #endif return dataBlock; @@ -47,7 +53,6 @@ namespace Js class SimpleDictionaryEntryWithFixUp : public JsUtil::SimpleDictionaryEntry { public: - __declspec(noinline) void FixupWithRemoteKey(void* remoteKey) { this->key = (TKey)remoteKey; @@ -63,7 +68,6 @@ namespace Js : BranchBaseDictionary(allocator, dictionarySize) { } - __declspec(noinline) void Fixup(NativeCodeData::DataChunk* chunkList, void** remoteKeys) { for (int i = 0; i < this->Count(); i++) @@ -84,6 +88,7 @@ namespace Js } else { + Assert(!JITManager::GetJITManager()->IsJITServer()); remoteKeys = nullptr; } } @@ -92,11 +97,6 @@ namespace Js void* defaultTarget; void** remoteKeys; - bool IsOOPJit() - { - return remoteKeys != nullptr; - } - static BranchDictionaryWrapper* New(NativeCodeData::Allocator * allocator, uint dictionarySize, ArenaAllocator* remoteKeyAlloc) { return NativeCodeDataNew(allocator, BranchDictionaryWrapper, allocator, dictionarySize, remoteKeyAlloc); @@ -105,15 +105,16 @@ namespace Js void AddEntry(uint32 offset, T key, void* remoteVar) { int index = dictionary.AddNew(key, (void**)offset); - if (IsOOPJit()) + if (JITManager::GetJITManager()->IsJITServer()) { + Assert(remoteKeys); remoteKeys[index] = remoteVar; } } void Fixup(NativeCodeData::DataChunk* chunkList) { - if (IsOOPJit()) + if (JITManager::GetJITManager()->IsJITServer()) { dictionary.Fixup(chunkList, remoteKeys); }