Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Commit

Permalink
Revert "Feature: dynamic expansion for generic dictionaries (#26262)"
Browse files Browse the repository at this point in the history
This reverts commit d840c75.
  • Loading branch information
stephentoub committed Nov 9, 2019
1 parent 8e709de commit ab8467a
Show file tree
Hide file tree
Showing 21 changed files with 245 additions and 1,158 deletions.
208 changes: 0 additions & 208 deletions Documentation/botr/shared-generics.md

This file was deleted.

6 changes: 3 additions & 3 deletions src/debug/daccess/nidump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6984,15 +6984,15 @@ NativeImageDumper::DumpMethodTable( PTR_MethodTable mt, const char * name,
if( currentDictionary != NULL )
{
PTR_DictionaryEntry entry(currentDictionary->EntryAddr(0));

PTR_DictionaryLayout layout( clazz->GetDictionaryLayout() );

DisplayStartStructure( "Dictionary",
DPtrToPreferredAddr(currentDictionary),
//if there is a layout, use it to compute
//the size, otherwise there is just the one
//entry.
DictionaryLayout::GetDictionarySizeFromLayout(mt->GetNumGenericArgs(), layout),
DictionaryLayout::GetFirstDictionaryBucketSize(mt->GetNumGenericArgs(), layout),
METHODTABLES );

DisplayStartArrayWithOffset( m_pEntries, NULL, Dictionary,
Expand Down Expand Up @@ -8018,7 +8018,7 @@ void NativeImageDumper::DumpMethodDesc( PTR_MethodDesc md, PTR_Module module )
{
PTR_DictionaryLayout layout(wrapped->IsSharedByGenericMethodInstantiations()
? dac_cast<TADDR>(wrapped->GetDictLayoutRaw()) : NULL );
dictSize = DictionaryLayout::GetDictionarySizeFromLayout(imd->GetNumGenericMethodArgs(),
dictSize = DictionaryLayout::GetFirstDictionaryBucketSize(imd->GetNumGenericMethodArgs(),
layout);
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/vm/appdomain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2068,10 +2068,6 @@ void SystemDomain::LoadBaseSystemClasses()
g_pDelegateClass = MscorlibBinder::GetClass(CLASS__DELEGATE);
g_pMulticastDelegateClass = MscorlibBinder::GetClass(CLASS__MULTICAST_DELEGATE);

#ifndef CROSSGEN_COMPILE
CrossLoaderAllocatorHashSetup::EnsureTypesLoaded();
#endif

// used by IsImplicitInterfaceOfSZArray
MscorlibBinder::GetClass(CLASS__IENUMERABLEGENERIC);
MscorlibBinder::GetClass(CLASS__ICOLLECTIONGENERIC);
Expand All @@ -2087,6 +2083,10 @@ void SystemDomain::LoadBaseSystemClasses()
g_pUtf8StringClass = MscorlibBinder::GetClass(CLASS__UTF8_STRING);
#endif // FEATURE_UTF8STRING

#ifndef CROSSGEN_COMPILE
CrossLoaderAllocatorHashSetup::EnsureTypesLoaded();
#endif

#ifndef CROSSGEN_COMPILE
ECall::PopulateManagedStringConstructors();
#endif // CROSSGEN_COMPILE
Expand Down
328 changes: 1 addition & 327 deletions src/vm/ceeload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,6 @@ void Module::Initialize(AllocMemTracker *pamTracker, LPCWSTR szName)
m_FixupCrst.Init(CrstModuleFixup, (CrstFlags)(CRST_HOST_BREAKABLE|CRST_REENTRANCY));
m_InstMethodHashTableCrst.Init(CrstInstMethodHashTable, CRST_REENTRANCY);
m_ISymUnmanagedReaderCrst.Init(CrstISymUnmanagedReader, CRST_DEBUGGER_THREAD);
m_DictionaryCrst.Init(CrstDomainLocalBlock);

if (!m_file->HasNativeImage())
{
Expand Down Expand Up @@ -688,12 +687,8 @@ void Module::Initialize(AllocMemTracker *pamTracker, LPCWSTR szName)
}
#endif // defined (PROFILING_SUPPORTED) &&!defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)

#ifndef CROSSGEN_COMPILE
m_dynamicSlotsHashForTypes.Init(GetLoaderAllocator());
m_dynamicSlotsHashForMethods.Init(GetLoaderAllocator());
#endif

LOG((LF_CLASSLOADER, LL_INFO10, "Loaded pModule: \"%ws\".\n", GetDebugName()));

}

#endif // DACCESS_COMPILE
Expand Down Expand Up @@ -13238,327 +13233,6 @@ void ReflectionModule::ReleaseILData()

Module::ReleaseILData();
}

TypeHandle* AllocateNewMethodDictionaryForExpansion(InstantiatedMethodDesc* pIMD, DWORD cbSize)
{
TypeHandle* pInstOrPerInstInfo = (TypeHandle*)(void*)pIMD->GetLoaderAllocator()->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(cbSize + sizeof(void*)));
ZeroMemory(pInstOrPerInstInfo, cbSize + sizeof(void*));

// Slot[-1] points at previous dictionary to help with diagnostics when investigating crashes
*(byte**)pInstOrPerInstInfo = (byte*)pIMD->m_pPerInstInfo.GetValue() + 1;
pInstOrPerInstInfo++;

// Copy old dictionary entry contents
memcpy(pInstOrPerInstInfo, (const void*)pIMD->m_pPerInstInfo.GetValue(), pIMD->GetDictionarySlotsSize());

ULONG_PTR* pSizeSlot = ((ULONG_PTR*)pInstOrPerInstInfo) + pIMD->GetNumGenericMethodArgs();
*pSizeSlot = cbSize;

return pInstOrPerInstInfo;
}

TypeHandle* AllocateNewTypeDictionaryForExpansion(MethodTable* pMT, DWORD cbSize)
{
TypeHandle* pInstOrPerInstInfo = (TypeHandle*)(void*)pMT->GetLoaderAllocator()->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(cbSize + sizeof(void*)));
ZeroMemory(pInstOrPerInstInfo, cbSize + sizeof(void*));

// Slot[-1] points at previous dictionary to help with diagnostics when investigating crashes
*(byte**)pInstOrPerInstInfo = (byte*)pMT->GetPerInstInfo()[pMT->GetNumDicts() - 1].GetValue() + 1;
pInstOrPerInstInfo++;

// Copy old dictionary entry contents
memcpy(pInstOrPerInstInfo, (const void*)pMT->GetPerInstInfo()[pMT->GetNumDicts() - 1].GetValue(), pMT->GetDictionarySlotsSize());

ULONG_PTR* pSizeSlot = ((ULONG_PTR*)pInstOrPerInstInfo) + pMT->GetNumGenericArgs();
*pSizeSlot = cbSize;

return pInstOrPerInstInfo;
}

#ifdef _DEBUG
void Module::EnsureTypeRecorded(MethodTable* pMT)
{
_ASSERTE(SystemDomain::SystemModule()->m_DictionaryCrst.OwnedByCurrentThread());

BOOL typeExistsInHashtable = FALSE;
auto lamda = [&typeExistsInHashtable, pMT](OBJECTREF obj, MethodTable* pMTKey, MethodTable* pMTValue)
{
typeExistsInHashtable = (pMT == pMTValue);
return pMT != pMTValue;
};

m_dynamicSlotsHashForTypes.VisitValuesOfKey(pMT->GetCanonicalMethodTable(), lamda);
_ASSERTE(typeExistsInHashtable);
}

void Module::EnsureMethodRecorded(MethodDesc* pMD)
{
_ASSERTE(SystemDomain::SystemModule()->m_DictionaryCrst.OwnedByCurrentThread());

BOOL methodExistsInHashtable = FALSE;
auto lamda = [&methodExistsInHashtable, pMD](OBJECTREF obj, MethodDesc* pMDKey, MethodDesc* pMDValue)
{
methodExistsInHashtable = (pMD== pMDValue);
return pMD != pMDValue;
};

m_dynamicSlotsHashForMethods.VisitValuesOfKey(pMD->GetExistingWrappedMethodDesc(), lamda);
_ASSERTE(methodExistsInHashtable);
}
#endif

void Module::RecordTypeForDictionaryExpansion_Locked(MethodTable* pGenericParentMT, MethodTable* pDependencyMT)
{
CONTRACTL
{
GC_TRIGGERS;
PRECONDITION(CheckPointer(pGenericParentMT) && CheckPointer(pDependencyMT));
PRECONDITION(pGenericParentMT->HasInstantiation() && pGenericParentMT != pGenericParentMT->GetCanonicalMethodTable());
PRECONDITION(SystemDomain::SystemModule()->m_DictionaryCrst.OwnedByCurrentThread());
}
CONTRACTL_END

DictionaryLayout* pDictLayout = pDependencyMT->GetClass()->GetDictionaryLayout();
if (pDictLayout != NULL && pDictLayout->GetMaxSlots() > 0)
{
DWORD sizeFromDictLayout = DictionaryLayout::GetDictionarySizeFromLayout(pDependencyMT->GetNumGenericArgs(), pDictLayout);
if (pDependencyMT->GetDictionarySlotsSize() != sizeFromDictLayout)
{
_ASSERT(pDependencyMT->GetDictionarySlotsSize() < sizeFromDictLayout);

//
// Another thread got a chance to expand the dictionary layout and expand the dictionary slots of
// other types, but not for this one yet because we're still in the process of recording it for
// expansions.
// Expand the dictionary slots here before finally adding the type to the hashtable.
//

TypeHandle* pInstOrPerInstInfo = AllocateNewTypeDictionaryForExpansion(pDependencyMT, sizeFromDictLayout);

// Publish the new dictionary slots to the type.
TypeHandle** pPerInstInfo = (TypeHandle**)pDependencyMT->GetPerInstInfo()->GetValuePtr();
FastInterlockExchangePointer(pPerInstInfo + (pDependencyMT->GetNumDicts() - 1), pInstOrPerInstInfo);

FlushProcessWriteBuffers();
}
}

GCX_COOP();
m_dynamicSlotsHashForTypes.Add(pGenericParentMT->GetCanonicalMethodTable(), pDependencyMT, GetLoaderAllocator());
}

void Module::RecordMethodForDictionaryExpansion_Locked(MethodDesc* pDependencyMD)
{
CONTRACTL
{
GC_TRIGGERS;
PRECONDITION(CheckPointer(pDependencyMD) && pDependencyMD->HasMethodInstantiation() && pDependencyMD->IsInstantiatingStub());
PRECONDITION(pDependencyMD->GetDictionaryLayout() != NULL && pDependencyMD->GetDictionaryLayout()->GetMaxSlots() > 0);
PRECONDITION(SystemDomain::SystemModule()->m_DictionaryCrst.OwnedByCurrentThread());
}
CONTRACTL_END

DictionaryLayout* pDictLayout = pDependencyMD->GetDictionaryLayout();
InstantiatedMethodDesc* pIMDDependency = pDependencyMD->AsInstantiatedMethodDesc();

DWORD sizeFromDictLayout = DictionaryLayout::GetDictionarySizeFromLayout(pDependencyMD->GetNumGenericMethodArgs(), pDictLayout);
if (pIMDDependency->GetDictionarySlotsSize() != sizeFromDictLayout)
{
_ASSERT(pIMDDependency->GetDictionarySlotsSize() < sizeFromDictLayout);

//
// Another thread got a chance to expand the dictionary layout and expand the dictionary slots of
// other methods, but not for this one yet because we're still in the process of recording it for
// expansions.
// Expand the dictionary slots here before finally adding the method to the hashtable.
//

TypeHandle* pInstOrPerInstInfo = AllocateNewMethodDictionaryForExpansion(pIMDDependency, sizeFromDictLayout);

FastInterlockExchangePointer((TypeHandle**)pIMDDependency->m_pPerInstInfo.GetValuePtr(), pInstOrPerInstInfo);

FlushProcessWriteBuffers();
}

GCX_COOP();

_ASSERTE(pDependencyMD->GetExistingWrappedMethodDesc() != NULL);
m_dynamicSlotsHashForMethods.Add(pDependencyMD->GetExistingWrappedMethodDesc(), pDependencyMD, GetLoaderAllocator());
}

void Module::ExpandTypeDictionaries_Locked(MethodTable* pMT, DictionaryLayout* pOldLayout, DictionaryLayout* pNewLayout)
{
CONTRACTL
{
STANDARD_VM_CHECK;
INJECT_FAULT(ThrowOutOfMemory(););
PRECONDITION(CheckPointer(pOldLayout) && CheckPointer(pNewLayout));
PRECONDITION(CheckPointer(pMT) && pMT->HasInstantiation() && pMT->GetClass()->GetDictionaryLayout() == pOldLayout);
PRECONDITION(SystemDomain::SystemModule()->m_DictionaryCrst.OwnedByCurrentThread());
}
CONTRACTL_END

GCX_COOP();

MethodTable* pCanonMT = pMT->GetCanonicalMethodTable();
DWORD oldInfoSize = DictionaryLayout::GetDictionarySizeFromLayout(pMT->GetNumGenericArgs(), pOldLayout);
DWORD newInfoSize = DictionaryLayout::GetDictionarySizeFromLayout(pMT->GetNumGenericArgs(), pNewLayout);

//
// Dictionary expansion for types needs to be done in multiple steps, given how derived types do not directly embed dictionaries
// from parent types, but instead reference them from directly from the parent types. Also, this is necessary to ensure correctness
// for lock-free read operations for dictionary slots:
// 1) Allocate new dictionaries for all instantiated types of the same typedef as the one being expanded.
// 2) After all allocations and initializations are completed, publish the dictionaries to the types in #1 after
// flushing write buffers
// 3) For all types that derive from #1, update the embedded dictinoary pointer to the newly allocated one.
//

struct NewDictionary
{
MethodTable* pMT;
TypeHandle* pDictSlots;
};
StackSArray<NewDictionary> dictionaryUpdates;

#ifdef _DEBUG
auto expandPerInstInfos = [oldInfoSize, newInfoSize, &dictionaryUpdates](OBJECTREF obj, MethodTable* pMTKey, MethodTable* pMTToUpdate)
#else
auto expandPerInstInfos = [newInfoSize, &dictionaryUpdates](OBJECTREF obj, MethodTable* pMTKey, MethodTable* pMTToUpdate)
#endif
{
if (!pMTToUpdate->HasSameTypeDefAs(pMTKey))
return true;

_ASSERTE(pMTToUpdate != pMTToUpdate->GetCanonicalMethodTable() && pMTToUpdate->GetCanonicalMethodTable() == pMTKey);
_ASSERTE(pMTToUpdate->GetDictionarySlotsSize() == oldInfoSize);

TypeHandle* pInstOrPerInstInfo = AllocateNewTypeDictionaryForExpansion(pMTToUpdate, newInfoSize);

NewDictionary entry;
entry.pMT = pMTToUpdate;
entry.pDictSlots = pInstOrPerInstInfo;
dictionaryUpdates.Append(entry);

return true; // Keep walking
};

m_dynamicSlotsHashForTypes.VisitValuesOfKey(pCanonMT, expandPerInstInfos);

// Flush write buffers to ensure new dictionaries are fully writted and initalized before publishing them
FlushProcessWriteBuffers();

for (SArray<NewDictionary>::Iterator i = dictionaryUpdates.Begin(); i != dictionaryUpdates.End(); i++)
{
MethodTable* pMT = i->pMT;
TypeHandle** pPerInstInfo = (TypeHandle**)pMT->GetPerInstInfo()->GetValuePtr();
FastInterlockExchangePointer(pPerInstInfo + (pMT->GetNumDicts() - 1), i->pDictSlots);
_ASSERTE(pMT->GetDictionarySlotsSize() == newInfoSize);
_ASSERTE((TypeHandle*)pMT->GetPerInstInfo()[pMT->GetNumDicts() - 1].GetValue() == i->pDictSlots);
}

auto updateDependentDicts = [](OBJECTREF obj, MethodTable* pMTKey, MethodTable* pMTToUpdate)
{
if (pMTToUpdate->HasSameTypeDefAs(pMTKey))
return true;

MethodTable* pCurrentMT = pMTToUpdate->GetParentMethodTable();
while (pCurrentMT)
{
if (pCurrentMT->HasSameTypeDefAs(pMTKey))
{
DWORD dictToUpdate = pCurrentMT->GetNumDicts() - 1;
Dictionary* pUpdatedParentDict = pCurrentMT->GetPerInstInfo()[dictToUpdate].GetValue();
TypeHandle** pPerInstInfo = (TypeHandle**)pMTToUpdate->GetPerInstInfo()->GetValuePtr();
FastInterlockExchangePointer(pPerInstInfo + dictToUpdate, (TypeHandle*)pUpdatedParentDict);
_ASSERTE(pMTToUpdate->GetPerInstInfo()[dictToUpdate].GetValue() == pUpdatedParentDict);

return true; // Keep walking
}
pCurrentMT = pCurrentMT->GetParentMethodTable();
}

UNREACHABLE();
};

m_dynamicSlotsHashForTypes.VisitValuesOfKey(pCanonMT, updateDependentDicts);

// Ensure no other thread uses old dictionary pointers
FlushProcessWriteBuffers();
}

void Module::ExpandMethodDictionaries_Locked(MethodDesc* pMD, DictionaryLayout* pOldLayout, DictionaryLayout* pNewLayout)
{
CONTRACTL
{
STANDARD_VM_CHECK;
INJECT_FAULT(ThrowOutOfMemory(););
PRECONDITION(CheckPointer(pOldLayout) && CheckPointer(pNewLayout));
PRECONDITION(CheckPointer(pMD));
PRECONDITION(pMD->HasMethodInstantiation() && pMD->GetDictionaryLayout() == pOldLayout);
PRECONDITION(SystemDomain::SystemModule()->m_DictionaryCrst.OwnedByCurrentThread());
}
CONTRACTL_END

GCX_COOP();

//
// Dictionary expansion for methods needs to be done in two steps to ensure correctness for lock-free read operations
// for dictionary slots:
// 1) Allocate new dictionaries for all instantiated methods sharing the same canonical form as the input method
// 2) After all allocations and initializations are completed, publish the dictionaries to the methods after
// flushing write buffers
//

MethodDesc* pCanonMD = pMD->IsInstantiatingStub() ? pMD->GetExistingWrappedMethodDesc() : pMD;
_ASSERTE(pCanonMD != NULL);
DWORD oldInfoSize = DictionaryLayout::GetDictionarySizeFromLayout(pMD->GetNumGenericMethodArgs(), pOldLayout);
DWORD newInfoSize = DictionaryLayout::GetDictionarySizeFromLayout(pMD->GetNumGenericMethodArgs(), pNewLayout);

struct NewDictionary
{
InstantiatedMethodDesc* pIMD;
TypeHandle* pDictSlots;
};
StackSArray<NewDictionary> dictionaryUpdates;

#ifdef _DEBUG
auto lambda = [oldInfoSize, newInfoSize, &dictionaryUpdates](OBJECTREF obj, MethodDesc* pMDKey, MethodDesc* pMDToUpdate)
#else
auto lambda = [newInfoSize, &dictionaryUpdates](OBJECTREF obj, MethodDesc* pMDKey, MethodDesc* pMDToUpdate)
#endif
{
// Update m_pPerInstInfo for the pMethodDesc being visited here
_ASSERTE(pMDToUpdate->IsInstantiatingStub() && pMDToUpdate->GetExistingWrappedMethodDesc() == pMDKey);

InstantiatedMethodDesc* pInstantiatedMD = pMDToUpdate->AsInstantiatedMethodDesc();
_ASSERTE(pInstantiatedMD->GetDictionarySlotsSize() == oldInfoSize);

TypeHandle* pInstOrPerInstInfo = AllocateNewMethodDictionaryForExpansion(pInstantiatedMD, newInfoSize);

NewDictionary entry;
entry.pIMD = pInstantiatedMD;
entry.pDictSlots = pInstOrPerInstInfo;
dictionaryUpdates.Append(entry);

return true; // Keep walking
};

m_dynamicSlotsHashForMethods.VisitValuesOfKey(pCanonMD, lambda);

// Flush write buffers to ensure new dictionaries are fully writted and initalized before publishing them
FlushProcessWriteBuffers();

for (SArray<NewDictionary>::Iterator i = dictionaryUpdates.Begin(); i != dictionaryUpdates.End(); i++)
{
FastInterlockExchangePointer((TypeHandle**)i->pIMD->m_pPerInstInfo.GetValuePtr(), i->pDictSlots);
_ASSERTE((TypeHandle*)i->pIMD->m_pPerInstInfo.GetValue() == i->pDictSlots);
_ASSERTE(i->pIMD->GetDictionarySlotsSize() == newInfoSize);
}

// Ensure no other thread uses old dictionary pointers
FlushProcessWriteBuffers();
}
#endif // !CROSSGEN_COMPILE

#endif // !DACCESS_COMPILE
Expand Down
Loading

0 comments on commit ab8467a

Please sign in to comment.