Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allocate string literals on frozen segments #49576

Merged
merged 69 commits into from
Sep 13, 2022
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
166ccfe
Naive implementation of FrozenObjectHeap
EgorBo Jul 3, 2021
1cb0927
Add RegisterFrozenSegment
EgorBo Jul 3, 2021
d2b9cbf
Clean up
EgorBo Jul 4, 2021
641365a
Add #include "common.h"
EgorBo Jul 4, 2021
33a126b
Fix build error
EgorBo Jul 4, 2021
bb1b835
Fix build
EgorBo Jul 4, 2021
2ef204a
Add lock
EgorBo Jul 4, 2021
a84a6ee
Clean up
EgorBo Jul 4, 2021
142295a
init memory on first allocation
EgorBo Jul 4, 2021
a446dd6
Clean up
EgorBo Jul 4, 2021
41665c3
revert changes in emitxarch.cpp
EgorBo Jul 4, 2021
66d6106
Merge branch 'main' of github.com:dotnet/runtime into poh-string-lite…
EgorBo Jul 28, 2022
67f32a7
Update branch, fix asserts
EgorBo Jul 28, 2022
9ecf7d7
fix release build
EgorBo Jul 28, 2022
45a454a
Ignore collectible assemblies, fix build
EgorBo Jul 29, 2022
1d0a710
Fix failing tests, address feedback
EgorBo Jul 29, 2022
201565e
Rename bAppDomainWontUnload to bIsCollectible
EgorBo Jul 29, 2022
c7dad85
Address feedback
EgorBo Jul 29, 2022
4202d9f
Clean up
EgorBo Jul 29, 2022
69ab9d2
Merge branch 'main' of github.com:dotnet/runtime into poh2
EgorBo Jul 29, 2022
a144453
Fix assert
EgorBo Jul 29, 2022
2571c9c
Fix more asserts not expecting TYP_REF constants being non zero
EgorBo Jul 29, 2022
286f8ab
Merge branch 'main' of github.com:dotnet/runtime into poh2
EgorBo Jul 29, 2022
28e0331
Test gc fix
EgorBo Jul 29, 2022
6625759
move check to IsEphemeral
EgorBo Jul 30, 2022
89df356
Test commit (do I need to update "ibAllocated"?)
EgorBo Jul 30, 2022
3cb98e9
Implement commit-on-demand
EgorBo Jul 30, 2022
c6df9ed
Clean up
EgorBo Jul 30, 2022
f50b4b9
bump FOH_COMMIT_PAGES
EgorBo Jul 30, 2022
2b55835
Merge branch 'main' of github.com:dotnet/runtime into poh2
EgorBo Jul 30, 2022
6ea2a50
Address feedback, clean up
EgorBo Jul 30, 2022
15ca880
Fix build
EgorBo Jul 30, 2022
193676e
Merge branch 'main' of github.com:dotnet/runtime into poh2
EgorBo Jul 30, 2022
9198043
Unix's ClrVirtualAlloc seems to be non-aligned
EgorBo Jul 30, 2022
7c98ef2
Oops, fix red CI
EgorBo Jul 30, 2022
af6e445
Multiple frozen segments
EgorBo Jul 30, 2022
2183c2f
Merge branch 'main' of github.com:dotnet/runtime into poh2
EgorBo Jul 31, 2022
4ef34dd
Clean up
EgorBo Jul 31, 2022
722eb82
fix typo
EgorBo Jul 31, 2022
0ae890a
Fix assert in assertionprop.cpp
EgorBo Jul 31, 2022
e59d27a
Address feedback
EgorBo Aug 18, 2022
90feb96
Merge branch 'main' of github.com:dotnet/runtime into poh-string-lite…
EgorBo Aug 18, 2022
ce3e19e
Merge branch 'main' of github.com:dotnet/runtime into poh-string-lite…
EgorBo Sep 1, 2022
b6fae80
Address feedback
EgorBo Sep 1, 2022
32cb338
Merge branch 'main' of github.com:dotnet/runtime into poh-string-lite…
EgorBo Sep 1, 2022
d20cc47
Address feedback
EgorBo Sep 2, 2022
31b5fdc
Fix potential issues
EgorBo Sep 3, 2022
1b1ab4c
Fix potential issues
EgorBo Sep 3, 2022
ed8ed7b
Merge branch 'main' of github.com:dotnet/runtime into poh-string-lite…
EgorBo Sep 4, 2022
88ad320
Address feedback
EgorBo Sep 4, 2022
943a235
Address feedback
EgorBo Sep 4, 2022
a3bfc8c
Check if this fixes all asserts
EgorBo Sep 5, 2022
1be12c5
Handle string.Empty (it used to be ignored)
EgorBo Sep 5, 2022
5c2a222
Apply suggestions from code review
EgorBo Sep 5, 2022
19d1734
Address feedback
EgorBo Sep 5, 2022
4762bcc
Address feedback
EgorBo Sep 5, 2022
2e03cc2
Update src/coreclr/vm/stringliteralmap.cpp
EgorBo Sep 5, 2022
2f00f71
Update src/coreclr/vm/stringliteralmap.cpp
EgorBo Sep 5, 2022
ddfbf73
fix compilation errors
EgorBo Sep 5, 2022
598b9c8
Merge branch 'main' of github.com:dotnet/runtime into poh-string-lite…
EgorBo Sep 7, 2022
802106e
Address Jan's feedback
EgorBo Sep 7, 2022
bfe96e7
Update src/coreclr/jit/gentree.cpp
EgorBo Sep 7, 2022
2e10c9c
Update gentree.cpp
EgorBo Sep 7, 2022
3415dc3
Merge branch 'main' of github.com:dotnet/runtime into poh-string-lite…
EgorBo Sep 12, 2022
bac0389
Address Maoni's feedback:
EgorBo Sep 13, 2022
01b5731
heap -> segment in comments
EgorBo Sep 13, 2022
8d139c9
Update src/coreclr/vm/frozenobjectheap.h
EgorBo Sep 13, 2022
b6deb3c
Address Jan's feedback around m_Crst creation
EgorBo Sep 13, 2022
1ba9d97
Fix order of initialization
EgorBo Sep 13, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/coreclr/gc/gc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7753,6 +7753,8 @@ inline
BOOL gc_heap::ephemeral_pointer_p (uint8_t* o)
{
#ifdef USE_REGIONS


int gen_num = object_gennum ((uint8_t*)o);
assert (gen_num >= 0);
return (gen_num < max_generation);
Expand Down Expand Up @@ -44690,6 +44692,13 @@ unsigned int GCHeap::GetGenerationWithRange (Object* object, uint8_t** ppStart,
bool GCHeap::IsEphemeral (Object* object)
{
uint8_t* o = (uint8_t*)object;
#if defined(FEATURE_BASICFREEZE) && defined(USE_REGIONS)
if (!is_in_heap_range(o))
{
// objects in frozen segments are not ephemeral
return FALSE;
}
#endif
gc_heap* hp = gc_heap::heap_of (o);
return !!hp->ephemeral_pointer_p (o);
}
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/inc/crsttypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,8 @@ enum CrstType
CrstUnwindInfoTableLock = 116,
CrstVSDIndirectionCellLock = 117,
CrstWrapperTemplate = 118,
kNumberOfCrstTypes = 119
CrstFrozenObjectHeap = 119,
kNumberOfCrstTypes = 120
};

#endif // __CRST_TYPES_INCLUDED
Expand Down
28 changes: 18 additions & 10 deletions src/coreclr/jit/assertionprop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1150,8 +1150,14 @@ void Compiler::optPrintAssertion(AssertionDsc* curAssertion, AssertionIndex asse

if (op1Type == TYP_REF)
{
assert(curAssertion->op2.u1.iconVal == 0);
printf("null");
if (curAssertion->op2.u1.iconVal == 0)
{
printf("null");
}
else
{
printf("[%08p]", dspPtr(curAssertion->op2.u1.iconVal));
}
}
else
{
Expand Down Expand Up @@ -3063,11 +3069,9 @@ GenTree* Compiler::optVNConstantPropOnTree(BasicBlock* block, GenTree* tree)

case TYP_REF:
{
assert(vnStore->ConstantValue<size_t>(vnCns) == 0);
// Support onle ref(ref(0)), do not support other forms (e.g byref(ref(0)).
if (tree->TypeGet() == TYP_REF)
{
conValTree = gtNewIconNode(0, TYP_REF);
conValTree = gtNewIconNode(vnStore->ConstantValue<size_t>(vnCns), TYP_REF);
}
}
break;
Expand Down Expand Up @@ -4024,8 +4028,14 @@ GenTree* Compiler::optAssertionPropGlobal_RelOp(ASSERT_VALARG_TP assertions, Gen
else if (op1->TypeGet() == TYP_REF)
{
// The only constant of TYP_REF that ValueNumbering supports is 'null'
assert(vnStore->ConstantValue<size_t>(vnCns) == 0);
printf("null\n");
if (vnStore->ConstantValue<size_t>(vnCns) == 0)
{
printf("null\n");
}
else
{
printf("%d (gcref)\n", static_cast<target_ssize_t>(vnStore->ConstantValue<size_t>(vnCns)));
}
}
else if (op1->TypeGet() == TYP_BYREF)
{
Expand Down Expand Up @@ -4078,9 +4088,7 @@ GenTree* Compiler::optAssertionPropGlobal_RelOp(ASSERT_VALARG_TP assertions, Gen
}
else if (op1->TypeGet() == TYP_REF)
{
op1->BashToConst(0, TYP_REF);
// The only constant of TYP_REF that ValueNumbering supports is 'null'
noway_assert(vnStore->ConstantValue<size_t>(vnCns) == 0);
op1->BashToConst(static_cast<target_ssize_t>(vnStore->ConstantValue<size_t>(vnCns)), TYP_REF);
}
else if (op1->TypeGet() == TYP_BYREF)
{
Expand Down
11 changes: 9 additions & 2 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3835,10 +3835,17 @@ bool Compiler::gtIsLikelyRegVar(GenTree* tree)
//
bool Compiler::gtCanSwapOrder(GenTree* firstNode, GenTree* secondNode)
{
// Relative of order of global / side effects can't be swapped.

bool canSwap = true;

// Don't swap "CONST_HDL op CNS"
if (firstNode->IsIntegralConst() && secondNode->IsIntegralConst() && varTypeIsGC(firstNode) &&
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
!varTypeIsGC(secondNode))
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
{
canSwap = false;
}
EgorBo marked this conversation as resolved.
Show resolved Hide resolved

// Relative of order of global / side effects can't be swapped.

if (optValnumCSE_phase)
{
canSwap = optCSE_canSwap(firstNode, secondNode);
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/vm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ set(VM_SOURCES_WKS
fcall.cpp
fieldmarshaler.cpp
finalizerthread.cpp
frozenobjectheap.cpp
gccover.cpp
gcenv.ee.static.cpp
gcenv.ee.common.cpp
Expand Down Expand Up @@ -431,6 +432,7 @@ set(VM_HEADERS_WKS
fcall.h
fieldmarshaler.h
finalizerthread.h
frozenobjectheap.h
gcenv.h
gcenv.ee.h
gcenv.os.h
Expand Down
20 changes: 20 additions & 0 deletions src/coreclr/vm/appdomain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "assemblynative.hpp"
#include "shimload.h"
#include "stringliteralmap.h"
#include "frozenobjectheap.h"
#include "codeman.h"
#include "comcallablewrapper.h"
#include "eventtrace.h"
Expand Down Expand Up @@ -101,6 +102,7 @@ int BaseDomain::m_iNumberOfProcessors = 0;

// System Domain Statics
GlobalStringLiteralMap* SystemDomain::m_pGlobalStringLiteralMap = NULL;
FrozenObjectHeap* SystemDomain::m_FrozenObjects = NULL;

DECLSPEC_ALIGN(16)
static BYTE g_pSystemDomainMemory[sizeof(SystemDomain)];
Expand Down Expand Up @@ -1196,6 +1198,24 @@ void SystemDomain::LazyInitGlobalStringLiteralMap()
}
}

void SystemDomain::LazyInitFrozenObjectsHeap()
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;

NewHolder<FrozenObjectHeap> pFoh(new FrozenObjectHeap());
if (InterlockedCompareExchangeT<FrozenObjectHeap*>(&m_FrozenObjects, pFoh, nullptr) == nullptr)
{
pFoh.SuppressRelease();
}
}

/*static*/ void SystemDomain::EnumAllStaticGCRefs(promote_func* fn, ScanContext* sc)
{
CONTRACT_VOID
Expand Down
15 changes: 15 additions & 0 deletions src/coreclr/vm/appdomain.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class SystemDomain;
class AppDomain;
class GlobalStringLiteralMap;
class StringLiteralMap;
class FrozenObjectHeap;
class MngStdInterfacesInfo;
class DomainAssembly;
class LoadLevelLimiter;
Expand Down Expand Up @@ -2393,6 +2394,7 @@ class SystemDomain : public BaseDomain
void Init();
void Stop();
static void LazyInitGlobalStringLiteralMap();
static void LazyInitFrozenObjectsHeap();

//****************************************************************************************
//
Expand Down Expand Up @@ -2465,6 +2467,18 @@ class SystemDomain : public BaseDomain
_ASSERTE(m_pGlobalStringLiteralMap);
return m_pGlobalStringLiteralMap;
}
static FrozenObjectHeap* GetSegmentWithFrozenObjects()
{
WRAPPER_NO_CONTRACT;

#ifdef FEATURE_BASICFREEZE
if (m_FrozenObjects == NULL)
{
SystemDomain::LazyInitFrozenObjectsHeap();
}
#endif
return m_FrozenObjects;
}
#endif // DACCESS_COMPILE

#if defined(FEATURE_COMINTEROP_APARTMENT_SUPPORT)
Expand Down Expand Up @@ -2634,6 +2648,7 @@ class SystemDomain : public BaseDomain
static CrstStatic m_SystemDomainCrst;

static GlobalStringLiteralMap *m_pGlobalStringLiteralMap;
static FrozenObjectHeap *m_FrozenObjects;
#endif // DACCESS_COMPILE

public:
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/vm/ceeload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2835,7 +2835,7 @@ void ModuleBase::InitializeStringData(DWORD token, EEStringData *pstrData, CQuic
}


OBJECTHANDLE ModuleBase::ResolveStringRef(DWORD token)
OBJECTHANDLE ModuleBase::ResolveStringRef(DWORD token, void** ppPinnedString)
{
CONTRACTL
{
Expand Down Expand Up @@ -2864,7 +2864,7 @@ OBJECTHANDLE ModuleBase::ResolveStringRef(DWORD token)

pLoaderAllocator = this->GetLoaderAllocator();

string = (OBJECTHANDLE)pLoaderAllocator->GetStringObjRefPtrFromUnicodeString(&strData);
string = (OBJECTHANDLE)pLoaderAllocator->GetStringObjRefPtrFromUnicodeString(&strData, ppPinnedString);

return string;
}
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/vm/ceeload.h
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,7 @@ class ModuleBase
}

// Resolving
OBJECTHANDLE ResolveStringRef(DWORD Token);
OBJECTHANDLE ResolveStringRef(DWORD Token, void** ppPinnedString = nullptr);
private:
// string helper
void InitializeStringData(DWORD token, EEStringData *pstrData, CQuickBytes *pqb);
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/vm/dynamicmethod.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1275,7 +1275,8 @@ STRINGREF* LCGMethodResolver::GetOrInternString(STRINGREF *pProtectedStringRef)
// lock the global string literal interning map
CrstHolder gch(pStringLiteralMap->GetHashTableCrstGlobal());

StringLiteralEntryHolder pEntry(pStringLiteralMap->GetInternedString(pProtectedStringRef, dwHash, /* bAddIfNotFound */ TRUE));
StringLiteralEntryHolder pEntry(pStringLiteralMap->GetInternedString(pProtectedStringRef, dwHash,
/* bAddIfNotFound */ TRUE, /* bPreferFrozenObjectHeap */ FALSE));

DynamicStringLiteral* pStringLiteral = (DynamicStringLiteral*)m_jitTempData.New(sizeof(DynamicStringLiteral));
pStringLiteral->m_pEntry = pEntry.Extract();
Expand Down
120 changes: 120 additions & 0 deletions src/coreclr/vm/frozenobjectheap.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#include "frozenobjectheap.h"
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
#include "memorypool.h"
EgorBo marked this conversation as resolved.
Show resolved Hide resolved

FrozenObjectHeap::FrozenObjectHeap():
m_pStart(nullptr),
m_pCurrent(nullptr),
m_pCommited(nullptr),
m_Size(0),
m_SegmentHandle(nullptr)
COMMA_INDEBUG(m_ObjectsCount(0))
{
m_PageSize = GetOsPageSize();
m_Crst.Init(CrstFrozenObjectHeap, CRST_UNSAFE_ANYMODE);
}

FrozenObjectHeap::~FrozenObjectHeap()
{
if (m_SegmentHandle != nullptr)
{
GCHeapUtilities::GetGCHeap()->UnregisterFrozenSegment(m_SegmentHandle);
}

if (m_pStart != nullptr)
{
ClrVirtualFree(m_pStart, 0, MEM_RELEASE);
}
}

bool FrozenObjectHeap::Initialize()
{
m_Size = m_PageSize * 1024; // e.g. 4Mb

_ASSERT(m_PageSize > MIN_OBJECT_SIZE);
_ASSERT(m_SegmentHandle == nullptr);
_ASSERT(m_pStart == nullptr);

// TODO: Implement COMMIT on demand.
void* alloc = ClrVirtualAllocAligned(nullptr, m_Size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE, m_PageSize);
ZeroMemory(alloc, m_Size); // Will remove, was just testing.

if (alloc != nullptr)
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
{
segment_info si;
si.pvMem = alloc;
si.ibFirstObject = sizeof(ObjHeader);
si.ibAllocated = m_Size;
si.ibCommit = m_Size;
si.ibReserved = m_Size;

m_SegmentHandle = GCHeapUtilities::GetGCHeap()->RegisterFrozenSegment(&si);
if (m_SegmentHandle != nullptr)
{
m_pStart = static_cast<uint8_t*>(alloc);
m_pCurrent = m_pStart;
m_pCommited = m_pStart;
INDEBUG(m_ObjectsCount = 0);
ASSERT((intptr_t)m_pCurrent % DATA_ALIGNMENT == 0);
return true;
}

// GC refused to register frozen segment (OOM?)
ClrVirtualFree(m_pStart, 0, MEM_RELEASE);
m_pStart = nullptr;
}
return false;
}

Object* FrozenObjectHeap::AllocateObject(size_t objectSize)
{
CrstHolder ch(&m_Crst);

if (objectSize > m_PageSize)
{
// Since FrozenObjectHeap is just an optimization, let's not fill it with large objects.
return nullptr;
}

if (m_pStart == nullptr)
{
// m_Size > 0 means we already tried to init and it failed.
// so bail out to avoid doing Alloc again.
if ((m_Size > 0) || !Initialize())
{
return nullptr;
}
}

_ASSERT(m_pStart != nullptr);
_ASSERT(m_SegmentHandle != nullptr);

_ASSERT(IS_ALIGNED(m_pCurrent, DATA_ALIGNMENT));
_ASSERT(IS_ALIGNED(objectSize, DATA_ALIGNMENT));

uint8_t* obj = m_pCurrent;
if ((size_t)(m_pStart + m_Size) < (size_t)(obj + objectSize))
{
// heap is full
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should allocate more segments when this segment is full?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left it as a TODO - I wasn't able to find an app that could fill all 4Mb I set as default size

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How much string literals are allocated by big apps like Bing?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try to obtain that data but also decided to implement multiple segments in my recent commits 🙂

return nullptr;
}

INDEBUG(m_ObjectsCount++);
m_pCurrent = obj + objectSize;

// Skip object header (NOTE: objectSize already includes it)
return reinterpret_cast<Object*>(obj + sizeof(ObjHeader));
}

bool FrozenObjectHeap::IsInHeap(Object* object)
{
const auto ptr = reinterpret_cast<uint8_t*>(object);
if (ptr >= m_pStart && ptr < m_pCurrent)
{
_ASSERT(GCHeapUtilities::GetGCHeap()->IsInFrozenSegment(object));
return true;
}
return false;
}
Loading