Skip to content

Commit

Permalink
Allow jit to examine type of initonly static ref typed fields
Browse files Browse the repository at this point in the history
The jit incorporates the value of integer and float typed initonly static
fields into its codegen, if the class initializer has already run.

The jit can't incorporate the values of ref typed initonly static fields,
but the types of those values can't change, and the jit can use this knowledge
to enable type based optimizations like devirtualization.

In particular for static fields initialized by complex class factory logic the
jit can now see the end result of that logic instead of having to try and deduce
the type of object that will initialize or did initialize the field.

Examples of this factory pattern in include `EqualityComparer<T>.Default` and
`Comparer<T>.Default`. The former is already optimized in some cases by via
special-purpose modelling in the framework, jit, and runtime (see dotnet#14125) but
the latter is not. With this change calls through `Comparer<T>.Default` may now
also devirtualize (though won't yet inline as the devirtualization happens
late).

Addresses #4108.
  • Loading branch information
AndyAyersMS committed Nov 12, 2018
1 parent cab9d87 commit 7e39a00
Show file tree
Hide file tree
Showing 16 changed files with 272 additions and 16 deletions.
3 changes: 3 additions & 0 deletions src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,9 @@ unsigned getClassDomainID(CORINFO_CLASS_HANDLE cls, void** ppIndirection = NULL)
// return the data's address (for static fields only)
void* getFieldAddress(CORINFO_FIELD_HANDLE field, void** ppIndirection = NULL);

// return the class handle for the current value of a static field
CORINFO_CLASS_HANDLE getStaticFieldCurrentClass(CORINFO_FIELD_HANDLE field, bool *isInitOnly);

// registers a vararg sig & returns a VM cookie for it (which can contain other stuff)
CORINFO_VARARGS_HANDLE getVarArgsHandle(CORINFO_SIG_INFO* pSig, void** ppIndirection = NULL);

Expand Down
1 change: 1 addition & 0 deletions src/ToolBox/superpmi/superpmi-shared/lwmlist.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ LWM(GetDelegateCtor, Agnostic_GetDelegateCtorIn, Agnostic_GetDelegateCtorOut)
LWM(GetEEInfo, DWORD, Agnostic_CORINFO_EE_INFO)
LWM(GetEHinfo, DLD, Agnostic_CORINFO_EH_CLAUSE)
LWM(GetFieldAddress, DWORDLONG, Agnostic_GetFieldAddress)
LWM(GetStaticFieldCurrentClass, DWORDLONG, Agnostic_GetStaticFieldCurrentClass)
LWM(GetFieldClass, DWORDLONG, DWORDLONG)
LWM(GetFieldInClass, DLD, DWORDLONG)
LWM(GetFieldInfo, Agnostic_GetFieldInfo, Agnostic_CORINFO_FIELD_INFO)
Expand Down
31 changes: 31 additions & 0 deletions src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3466,6 +3466,37 @@ void* MethodContext::repGetFieldAddress(CORINFO_FIELD_HANDLE field, void** ppInd
return temp;
}

void MethodContext::recGetStaticFieldCurrentClass(CORINFO_FIELD_HANDLE field, bool isInitOnly, CORINFO_CLASS_HANDLE result)
{
if (GetStaticFieldCurrentClass == nullptr)
GetStaticFieldCurrentClass = new LightWeightMap<DWORDLONG, Agnostic_GetStaticFieldCurrentClass>();

Agnostic_GetStaticFieldCurrentClass value;

value.classHandle = (DWORDLONG)result;
value.isInitOnly = isInitOnly;

GetStaticFieldCurrentClass->Add((DWORDLONG)field, value);
DEBUG_REC(dmpGetFieldAddress((DWORDLONG)field, value));
}
void MethodContext::dmpGetStaticFieldCurrentClass(DWORDLONG key, const Agnostic_GetStaticFieldCurrentClass& value)
{
printf("GetStaticFieldCurrentClass key fld-%016llX, value clsHnd-%016llX isInitOnly-%u", key, value.classHandle, value.isInitOnly);
}
CORINFO_CLASS_HANDLE MethodContext::repGetStaticFieldCurrentClass(CORINFO_FIELD_HANDLE field, bool* isInitOnly)
{
Agnostic_GetStaticFieldCurrentClass value = GetStaticFieldCurrentClass->Get((DWORDLONG) field);

if (isInitOnly != nullptr)
{
*isInitOnly = value.isInitOnly;
}

CORINFO_CLASS_HANDLE result = (CORINFO_CLASS_HANDLE) value.classHandle;
DEBUG_REP(dmpGetStaticFieldCurrentValue((DWORDLONG)field, value));
return result;
}

void MethodContext::recGetClassGClayout(CORINFO_CLASS_HANDLE cls, BYTE* gcPtrs, unsigned len, unsigned result)
{
if (GetClassGClayout == nullptr)
Expand Down
12 changes: 11 additions & 1 deletion src/ToolBox/superpmi/superpmi-shared/methodcontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,11 @@ class MethodContext
DWORDLONG fieldAddress;
DWORD fieldValue;
};
struct Agnostic_GetStaticFieldCurrentClass
{
DWORDLONG classHandle;
bool isInitOnly;
};
struct Agnostic_CORINFO_RESOLVED_TOKEN
{
Agnostic_CORINFO_RESOLVED_TOKENin inValue;
Expand Down Expand Up @@ -923,6 +928,10 @@ class MethodContext
void dmpGetFieldAddress(DWORDLONG key, const Agnostic_GetFieldAddress& value);
void* repGetFieldAddress(CORINFO_FIELD_HANDLE field, void** ppIndirection);

void recGetStaticFieldCurrentClass(CORINFO_FIELD_HANDLE field, bool isInitOnly, CORINFO_CLASS_HANDLE result);
void dmpGetStaticFieldCurrentClass(DWORDLONG key, const Agnostic_GetStaticFieldCurrentClass& value);
CORINFO_CLASS_HANDLE repGetStaticFieldCurrentClass(CORINFO_FIELD_HANDLE field, bool* isInitOnly);

void recGetClassGClayout(CORINFO_CLASS_HANDLE cls, BYTE* gcPtrs, unsigned len, unsigned result);
void dmpGetClassGClayout(DWORDLONG key, const Agnostic_GetClassGClayout& value);
unsigned repGetClassGClayout(CORINFO_CLASS_HANDLE cls, BYTE* gcPtrs);
Expand Down Expand Up @@ -1317,7 +1326,7 @@ class MethodContext
};

// ********************* Please keep this up-to-date to ease adding more ***************
// Highest packet number: 171
// Highest packet number: 172
// *************************************************************************************
enum mcPackets
{
Expand Down Expand Up @@ -1396,6 +1405,7 @@ enum mcPackets
Packet_GetEEInfo = 50,
Packet_GetEHinfo = 51,
Packet_GetFieldAddress = 52,
Packet_GetStaticFieldCurrentClass = 172, // Added 11/7/2018
Packet_GetFieldClass = 53,
Packet_GetFieldInClass = 54,
Packet_GetFieldInfo = 55,
Expand Down
14 changes: 14 additions & 0 deletions src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1816,6 +1816,20 @@ void* interceptor_ICJI::getFieldAddress(CORINFO_FIELD_HANDLE field, void** ppInd
return temp;
}

// return the class handle for the current value of a static field
CORINFO_CLASS_HANDLE interceptor_ICJI::getStaticFieldCurrentClass(CORINFO_FIELD_HANDLE field, bool *isInitOnly)
{
mc->cr->AddCall("getStaticFieldCurrentClass");
bool localIsInitOnly = false;
CORINFO_CLASS_HANDLE result = original_ICorJitInfo->getStaticFieldCurrentClass(field, &localIsInitOnly);
mc->recGetStaticFieldCurrentClass(field, localIsInitOnly, result);
if (isInitOnly != nullptr)
{
*isInitOnly = localIsInitOnly;
}
return result;
}

// registers a vararg sig & returns a VM cookie for it (which can contain other stuff)
CORINFO_VARARGS_HANDLE interceptor_ICJI::getVarArgsHandle(CORINFO_SIG_INFO* pSig, void** ppIndirection)
{
Expand Down
7 changes: 7 additions & 0 deletions src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1403,6 +1403,13 @@ void* interceptor_ICJI::getFieldAddress(CORINFO_FIELD_HANDLE field, void** ppInd
return original_ICorJitInfo->getFieldAddress(field, ppIndirection);
}

// return the class handle for the current value of a static field
CORINFO_CLASS_HANDLE interceptor_ICJI::getStaticFieldCurrentClass(CORINFO_FIELD_HANDLE field, bool *isInitOnly)
{
mcs->AddCall("getStaticFieldCurrentClass");
return original_ICorJitInfo->getStaticFieldCurrentClass(field, isInitOnly);
}

// registers a vararg sig & returns a VM cookie for it (which can contain other stuff)
CORINFO_VARARGS_HANDLE interceptor_ICJI::getVarArgsHandle(CORINFO_SIG_INFO* pSig, void** ppIndirection)
{
Expand Down
6 changes: 6 additions & 0 deletions src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1258,6 +1258,12 @@ void* interceptor_ICJI::getFieldAddress(CORINFO_FIELD_HANDLE field, void** ppInd
return original_ICorJitInfo->getFieldAddress(field, ppIndirection);
}

// return the class handle for the current value of a static field
CORINFO_CLASS_HANDLE interceptor_ICJI::getStaticFieldCurrentClass(CORINFO_FIELD_HANDLE field, bool *isInitOnly)
{
return original_ICorJitInfo->getStaticFieldCurrentClass(field, isInitOnly);
}

// registers a vararg sig & returns a VM cookie for it (which can contain other stuff)
CORINFO_VARARGS_HANDLE interceptor_ICJI::getVarArgsHandle(CORINFO_SIG_INFO* pSig, void** ppIndirection)
{
Expand Down
7 changes: 7 additions & 0 deletions src/ToolBox/superpmi/superpmi/icorjitinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1516,6 +1516,13 @@ void* MyICJI::getFieldAddress(CORINFO_FIELD_HANDLE field, void** ppIndirection)
return jitInstance->mc->repGetFieldAddress(field, ppIndirection);
}

// return the class handle for the current value of a static field
CORINFO_CLASS_HANDLE MyICJI::getStaticFieldCurrentClass(CORINFO_FIELD_HANDLE field, bool *isInitOnly)
{
jitInstance->mc->cr->AddCall("getStaticFieldCurrentClass");
return jitInstance->mc->repGetStaticFieldCurrentClass(field, isInitOnly);
}

// registers a vararg sig & returns a VM cookie for it (which can contain other stuff)
CORINFO_VARARGS_HANDLE MyICJI::getVarArgsHandle(CORINFO_SIG_INFO* pSig, void** ppIndirection)
{
Expand Down
17 changes: 12 additions & 5 deletions src/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,11 +213,11 @@ TODO: Talk about initializing strutures before use
#define SELECTANY extern __declspec(selectany)
#endif

SELECTANY const GUID JITEEVersionIdentifier = { /* 3be99428-36f8-4a6c-acde-b42778b0f8bf */
0x3be99428,
0x36f8,
0x4a6c,
{0xac, 0xde, 0xb4, 0x27, 0x78, 0xb0, 0xf8, 0xbf}
SELECTANY const GUID JITEEVersionIdentifier = { /* b2da2a6e-72fa-4730-b47c-4c9275e1c5ce */
0xb2da2a6e,
0x72fa,
0x4730,
{0xb4, 0x7c, 0x4c, 0x92, 0x75, 0xe1, 0xc5, 0xce}
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -3119,6 +3119,13 @@ class ICorDynamicInfo : public ICorStaticInfo
void **ppIndirection = NULL
) = 0;

// For ref-class typed static readonly fields, return the class handle for value of the field
// if there is a unique location for the static and the class is already initialized.
virtual CORINFO_CLASS_HANDLE getStaticFieldCurrentClass(
CORINFO_FIELD_HANDLE field,
bool *isInitOnly
) = 0;

// registers a vararg sig & returns a VM cookie for it (which can contain other stuff)
virtual CORINFO_VARARGS_HANDLE getVarArgsHandle(
CORINFO_SIG_INFO *pSig,
Expand Down
2 changes: 2 additions & 0 deletions src/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -2612,6 +2612,8 @@ class Compiler
CORINFO_CLASS_HANDLE gtGetHelperArgClassHandle(GenTree* array,
unsigned* runtimeLookupCount = nullptr,
GenTree** handleTree = nullptr);
// Get the class handle for a field
CORINFO_CLASS_HANDLE gtGetFieldClassHandle(CORINFO_FIELD_HANDLE fieldHnd, bool* isExact, bool* IsNonNull);
// Check if this tree is a gc static base helper call
bool gtIsStaticGCBaseHelperCall(GenTree* tree);

Expand Down
88 changes: 78 additions & 10 deletions src/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16173,12 +16173,7 @@ CORINFO_CLASS_HANDLE Compiler::gtGetClassHandle(GenTree* tree, bool* isExact, bo

if (fieldHnd != nullptr)
{
CORINFO_CLASS_HANDLE fieldClass = nullptr;
CorInfoType fieldCorType = info.compCompHnd->getFieldType(fieldHnd, &fieldClass);
if (fieldCorType == CORINFO_TYPE_CLASS)
{
objClass = fieldClass;
}
objClass = gtGetFieldClassHandle(fieldHnd, isExact, isNonNull);
}

break;
Expand Down Expand Up @@ -16354,13 +16349,16 @@ CORINFO_CLASS_HANDLE Compiler::gtGetClassHandle(GenTree* tree, bool* isExact, bo
fieldSeq = fieldSeq->m_next;
}

assert(!fieldSeq->IsPseudoField());

// No benefit to calling gtGetFieldClassHandle here, as
// the exact field being accessed can vary.
CORINFO_FIELD_HANDLE fieldHnd = fieldSeq->m_fieldHnd;
CORINFO_CLASS_HANDLE fieldClass = nullptr;
CorInfoType fieldCorType = info.compCompHnd->getFieldType(fieldHnd, &fieldClass);
if (fieldCorType == CORINFO_TYPE_CLASS)
{
objClass = fieldClass;
}

assert(fieldCorType == CORINFO_TYPE_CLASS);
objClass = fieldClass;
}
}
}
Expand Down Expand Up @@ -16543,6 +16541,76 @@ CORINFO_CLASS_HANDLE Compiler::gtGetArrayElementClassHandle(GenTree* array)
return nullptr;
}

//------------------------------------------------------------------------
// gtGetFieldClassHandle: find class handle for a field
//
// Arguments:
// fieldHnd - field handle for field in question
// isExact - [OUT] true if type is known exactly
// isNonNull - [OUT] true if field value is not null
//
// Return Value:
// nullptr if helper call result is not a ref class, or the class handle
// is unknown, otherwise the class handle.
//
// May examine runtime state of static field instances.

CORINFO_CLASS_HANDLE Compiler::gtGetFieldClassHandle(CORINFO_FIELD_HANDLE fieldHnd, bool* isExact, bool* isNonNull)
{
CORINFO_CLASS_HANDLE fieldClass = nullptr;
CorInfoType fieldCorType = info.compCompHnd->getFieldType(fieldHnd, &fieldClass);

if (fieldCorType == CORINFO_TYPE_CLASS)
{
// Optionally, look at the actual type of the field's value
bool queryForCurrentClass = true;
INDEBUG(queryForCurrentClass = (JitConfig.JitQueryCurrentStaticFieldClass() > 0););

if (queryForCurrentClass)
{

#if DEBUG
const char* fieldClassName = nullptr;
const char* fieldName = eeGetFieldName(fieldHnd, &fieldClassName);
JITDUMP("Querying runtime about current class of field %s.%s (declared as %s)\n", fieldClassName, fieldName,
eeGetClassName(fieldClass));
#endif // DEBUG

// Is this an initialized static init-only field?
bool isFieldInitOnly = false;
CORINFO_CLASS_HANDLE currentClass =
info.compCompHnd->getStaticFieldCurrentClass(fieldHnd, &isFieldInitOnly);

if (currentClass != NO_CLASS_HANDLE)
{
// We know the current type -- can we rely on it?
if (isFieldInitOnly)
{
// Yes! We know the class exactly and can rely on this to always be true.
fieldClass = currentClass;
*isExact = true;
*isNonNull = true;
JITDUMP("Runtime reports field is init-only and has type %s\n", eeGetClassName(fieldClass));
}
else
{
// No. We know the current type but it may change over time.
//
// We could use this type as an informed guess, someday,
// if it is a "better" type than the declared field type.
JITDUMP("Field is not init-only\n");
}
}
else
{
JITDUMP("Field's current class not available\n");
}
}
}

return fieldClass;
}

//------------------------------------------------------------------------
// gtIsGCStaticBaseHelperCall: true if tree is fetching the gc static base
// for a subsequent static field access
Expand Down
1 change: 1 addition & 0 deletions src/jit/jitconfigvalues.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ CONFIG_INTEGER(JitNoRegLoc, W("JitNoRegLoc"), 0)
CONFIG_INTEGER(JitNoStructPromotion, W("JitNoStructPromotion"), 0) // Disables struct promotion in Jit32
CONFIG_INTEGER(JitNoUnroll, W("JitNoUnroll"), 0)
CONFIG_INTEGER(JitOrder, W("JitOrder"), 0)
CONFIG_INTEGER(JitQueryCurrentStaticFieldClass, W("JitQueryCurrentStaticFieldClass"), 1)
CONFIG_INTEGER(JitReportFastTailCallDecisions, W("JitReportFastTailCallDecisions"), 0)
CONFIG_INTEGER(JitPInvokeCheckEnabled, W("JITPInvokeCheckEnabled"), 0)
CONFIG_INTEGER(JitPInvokeEnabled, W("JITPInvokeEnabled"), 1)
Expand Down
Loading

0 comments on commit 7e39a00

Please sign in to comment.