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

Detect class inited status more correctly in prestub/jitinterface #104253

Merged
merged 4 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
15 changes: 6 additions & 9 deletions src/coreclr/vm/jitinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1210,10 +1210,9 @@ CorInfoHelpFunc CEEInfo::getSharedStaticsHelper(FieldDesc * pField, MethodTable
{
STANDARD_VM_CONTRACT;

pFieldMT->AttemptToPreinit();
bool GCStatic = (pField->GetFieldType() == ELEMENT_TYPE_CLASS ||
pField->GetFieldType() == ELEMENT_TYPE_VALUETYPE);
bool noCtor = pFieldMT->IsClassInited();
bool noCtor = pFieldMT->IsClassInitedOrPreinited();
bool threadStatic = pField->IsThreadStatic();
bool isInexactMT = pFieldMT->IsSharedByGenericInstantiations();
bool isCollectible = pFieldMT->Collectible();
Expand Down Expand Up @@ -1451,7 +1450,7 @@ void CEEInfo::getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken,
}

// We are not going through a helper. The constructor has to be triggered explicitly.
if (!pFieldMT->IsClassInited())
if (!pFieldMT->IsClassInitedOrPreinited())
fieldFlags |= CORINFO_FLG_FIELD_INITCLASS;
}
else
Expand Down Expand Up @@ -1536,10 +1535,9 @@ void CEEInfo::getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken,
// Allocate space for the local class if necessary, but don't trigger
// class construction.
pFieldMT->EnsureStaticDataAllocated();
pFieldMT->AttemptToPreinit();

// We are not going through a helper. The constructor has to be triggered explicitly.
if (!pFieldMT->IsClassInited())
if (!pFieldMT->IsClassInitedOrPreinited())
fieldFlags |= CORINFO_FLG_FIELD_INITCLASS;

GCX_COOP();
Expand Down Expand Up @@ -3884,8 +3882,7 @@ CorInfoInitClassResult CEEInfo::initClass(

MethodTable *pTypeToInitMT = typeToInitTH.AsMethodTable();

pTypeToInitMT->AttemptToPreinit();
if (pTypeToInitMT->IsClassInited())
if (pTypeToInitMT->IsClassInitedOrPreinited())
{
// If the type is initialized there really is nothing to do.
result = CORINFO_INITCLASS_INITIALIZED;
Expand Down Expand Up @@ -11725,7 +11722,7 @@ bool CEEInfo::getStaticFieldContent(CORINFO_FIELD_HANDLE fieldHnd, uint8_t* buff
// class construction.
pEnclosingMT->EnsureStaticDataAllocated();

if (!field->IsThreadStatic() && pEnclosingMT->IsClassInited() && IsFdInitOnly(field->GetAttributes()))
if (!field->IsThreadStatic() && pEnclosingMT->IsClassInitedOrPreinited() && IsFdInitOnly(field->GetAttributes()))
{
if (field->IsObjRef())
{
Expand Down Expand Up @@ -11913,7 +11910,7 @@ CORINFO_CLASS_HANDLE CEEJitInfo::getStaticFieldCurrentClass(CORINFO_FIELD_HANDLE
VALIDATEOBJECTREF(fieldObj);

// Check for initialization before looking at the value
isClassInitialized = !!pEnclosingMT->IsClassInited();
isClassInitialized = !!pEnclosingMT->IsClassInitedOrPreinited();

if (fieldObj != NULL)
{
Expand Down
43 changes: 30 additions & 13 deletions src/coreclr/vm/methodtable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3782,7 +3782,7 @@ void MethodTable::EnsureStaticDataAllocated()
CONTRACTL_END;

PTR_MethodTableAuxiliaryData pAuxiliaryData = GetAuxiliaryDataForWrite();
if (!pAuxiliaryData->IsStaticDataAllocated() && IsDynamicStatics())
if (!pAuxiliaryData->IsStaticDataAllocated() && IsDynamicStatics() && !IsSharedByGenericInstantiations())
{
DynamicStaticsInfo *pDynamicStaticsInfo = GetDynamicStaticsInfo();
// Allocate space for normal statics if we might have them
Expand All @@ -3792,10 +3792,28 @@ void MethodTable::EnsureStaticDataAllocated()
if (pDynamicStaticsInfo->GetGCStaticsPointer() == NULL)
GetLoaderAllocator()->AllocateGCHandlesBytesForStaticVariables(pDynamicStaticsInfo, GetClass()->GetNumHandleRegularStatics(), this->HasBoxedRegularStatics() ? this : NULL);
}
pAuxiliaryData->SetIsStaticDataAllocated();
pAuxiliaryData->SetIsStaticDataAllocated(IsInitedIfStaticDataAllocated());
}

void MethodTable::AttemptToPreinit()
bool MethodTable::IsClassInitedOrPreinited()
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM());
}
CONTRACTL_END;

bool initResult;
if (GetAuxiliaryData()->IsClassInitedOrPreinitedDecided(&initResult))
return initResult;

EnsureStaticDataAllocated();
return IsClassInited();
}

bool MethodTable::IsInitedIfStaticDataAllocated()
{
CONTRACTL
{
Expand All @@ -3806,33 +3824,32 @@ void MethodTable::AttemptToPreinit()
CONTRACTL_END;

if (IsClassInited())
return;
{
return true;
}

if (HasClassConstructor())
{
// If there is a class constructor, then the class cannot be preinitted.
return;
return false;
}

if (GetClass()->GetNonGCRegularStaticFieldBytes() == 0 && GetClass()->GetNumHandleRegularStatics() == 0)
{
// If there are static fields that are not thread statics, then the class is preinitted.
SetClassInited();
return;
// If there aren't static fields that are not thread statics, then the class is preinitted.
return true;
}

// At this point, we are looking at a class that has no class constructor, but does have static fields

if (IsSharedByGenericInstantiations())
{
// If we don't know the exact type, we can't pre-allocate the fields
return;
// If we don't know the exact type, we can't pre-init the the fields
return false;
}

// All this class needs to be initialized is to allocate the memory for the static fields. Do so, and mark the type as initialized
EnsureStaticDataAllocated();
SetClassInited();
return;
return true;
}

void MethodTable::EnsureTlsIndexAllocated()
Expand Down
26 changes: 22 additions & 4 deletions src/coreclr/vm/methodtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ struct MethodTableAuxiliaryData
enum_flag_DependenciesLoaded = 0x0080, // class and all dependencies loaded up to CLASS_LOADED_BUT_NOT_VERIFIED

enum_flag_IsInitError = 0x0100,
enum_flag_IsStaticDataAllocated = 0x0200,
enum_flag_IsStaticDataAllocated = 0x0200, // When this is set, if the class can be marked as initialized without any further code execution it will be.
// unum_unused = 0x0400,
enum_flag_IsTlsIndexAllocated = 0x0800,
enum_flag_MayHaveOpenInterfaceInInterfaceMap = 0x1000,
Expand Down Expand Up @@ -447,9 +447,19 @@ struct MethodTableAuxiliaryData

inline BOOL IsClassInited() const
{
LIMITED_METHOD_DAC_CONTRACT;
return VolatileLoad(&m_dwFlags) & enum_flag_Initialized;
}

inline bool IsClassInitedOrPreinitedDecided(bool *initResult) const
{
LIMITED_METHOD_DAC_CONTRACT;

DWORD dwFlags = VolatileLoad(&m_dwFlags);
*initResult = m_dwFlags & enum_flag_Initialized;
return (dwFlags & (enum_flag_IsStaticDataAllocated|enum_flag_Initialized)) != 0;
}

#ifndef DACCESS_COMPILE
inline void SetClassInited()
{
Expand All @@ -465,10 +475,10 @@ struct MethodTableAuxiliaryData
}

#ifndef DACCESS_COMPILE
inline void SetIsStaticDataAllocated()
inline void SetIsStaticDataAllocated(bool markAsInitedToo)
{
LIMITED_METHOD_CONTRACT;
InterlockedOr((LONG*)&m_dwFlags, (LONG)enum_flag_IsStaticDataAllocated);
InterlockedOr((LONG*)&m_dwFlags, markAsInitedToo ? (LONG)(enum_flag_IsStaticDataAllocated|enum_flag_Initialized) : (LONG)enum_flag_IsStaticDataAllocated);
}
#endif

Expand Down Expand Up @@ -1049,11 +1059,19 @@ class MethodTable
}
}

void AttemptToPreinit();
private:
bool IsInitedIfStaticDataAllocated();
public:
// Is the MethodTable current initialized, and/or can the runtime initialize the MethodTable
// without running any user code. (This function may allocate memory, and may throw OutOfMemory)
bool IsClassInitedOrPreinited();
#endif

// Is the MethodTable current known to be initialized
// If you want to know if it is initialized and allocation/throwing is permitted, call IsClassInitedOrPreinited instead
BOOL IsClassInited()
{
LIMITED_METHOD_DAC_CONTRACT;
return GetAuxiliaryDataForWrite()->IsClassInited();
}

Expand Down
5 changes: 2 additions & 3 deletions src/coreclr/vm/prestub.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3545,9 +3545,8 @@ static PCODE getHelperForStaticBase(Module * pModule, CORCOMPILE_FIXUP_BLOB_KIND
{
STANDARD_VM_CONTRACT;

pMT->AttemptToPreinit();
bool GCStatic = (kind == ENCODE_STATIC_BASE_GC_HELPER || kind == ENCODE_THREAD_STATIC_BASE_GC_HELPER);
bool noCtor = pMT->IsClassInited();
bool noCtor = pMT->IsClassInitedOrPreinited();
bool threadStatic = (kind == ENCODE_THREAD_STATIC_BASE_NONGC_HELPER || kind == ENCODE_THREAD_STATIC_BASE_GC_HELPER);

CorInfoHelpFunc helper;
Expand Down Expand Up @@ -3916,7 +3915,7 @@ PCODE DynamicHelperFixup(TransitionBlock * pTransitionBlock, TADDR * pCell, DWOR
else
{
// Delay the creation of the helper until the type is initialized
if (pMT->IsClassInited())
if (pMT->IsClassInitedOrPreinited())
pHelper = getHelperForInitializedStatic(pModule, (CORCOMPILE_FIXUP_BLOB_KIND)kind, pMT, pFD);
}
}
Expand Down
Loading