Skip to content

Commit

Permalink
JIT: recover types from helper calls and more (dotnet/coreclr#20447)
Browse files Browse the repository at this point in the history
The jit needs to recover class handles in order to devirtualize and
do other type-based optimizations. This change allows the jit to find
the type for more trees: in particular, helper calls, intrinsics, and
expanded static field accesses.

Also, annotate a few methods to control jit optimization

We don't want to optimize special methods that are used to inform crossgen
about desirable generic instantiations. `CommonlyUsedGenericInstantiations`
was already annotated but `CommonlyUsedWinRTRedirectedInterfaceStubs` wasn't.

And because `RuntimeType` is sealed calls through types are now often
devirtualized. Name lookups on types are frequent, especially on error paths.
The method `GetCachedName` looks like an attractive inline but simply expands
into a larger sequence of two other calls. So block it from being inlined.


Commit migrated from dotnet/coreclr@5af4d64
  • Loading branch information
AndyAyersMS committed Oct 24, 2018
1 parent cbca8f9 commit 51f6e74
Show file tree
Hide file tree
Showing 5 changed files with 216 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ private static async Task AsyncHelper3()
// typed as matching instantiations of mscorlib copies of WinRT interfaces (IIterable<T>, IVector<T>,
// IMap<K, V>, ...) which is necessary to generate all required IL stubs.

[MethodImplAttribute(MethodImplOptions.NoOptimization)]
private static void CommonlyUsedWinRTRedirectedInterfaceStubs()
{
WinRT_IEnumerable<byte>(null, null, null);
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/src/System.Private.CoreLib/src/System/RtType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4532,6 +4532,10 @@ internal override string FormatTypeName(bool serialization)
}
}

// This method looks like an attractive inline but expands to two calls,
// neither of which can be inlined or optimized further. So block it
// from inlining.
[MethodImpl(MethodImplOptions.NoInlining)]
private string GetCachedName(TypeNameKind kind)
{
return Cache.GetName(kind);
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/src/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -2602,12 +2602,16 @@ class Compiler
CORINFO_CLASS_HANDLE gtGetStructHandle(GenTree* tree);
// Get the handle for a ref type.
CORINFO_CLASS_HANDLE gtGetClassHandle(GenTree* tree, bool* isExact, bool* isNonNull);
// Get the class handle for an helper call
CORINFO_CLASS_HANDLE gtGetHelperCallClassHandle(GenTreeCall* call, bool* isExact, bool* isNonNull);
// Get the element handle for an array of ref type.
CORINFO_CLASS_HANDLE gtGetArrayElementClassHandle(GenTree* array);
// Get a class handle from a helper call argument
CORINFO_CLASS_HANDLE gtGetHelperArgClassHandle(GenTree* array,
unsigned* runtimeLookupCount = nullptr,
GenTree** handleTree = nullptr);
// Check if this tree is a gc static base helper call
bool gtIsStaticGCBaseHelperCall(GenTree* tree);

//-------------------------------------------------------------------------
// Functions to display the trees
Expand Down
203 changes: 203 additions & 0 deletions src/coreclr/src/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16229,6 +16229,25 @@ CORINFO_CLASS_HANDLE Compiler::gtGetClassHandle(GenTree* tree, bool* isExact, bo
objClass = sig.retTypeClass;
}
}
else if (call->gtCallType == CT_HELPER)
{
objClass = gtGetHelperCallClassHandle(call, isExact, isNonNull);
}

break;
}

case GT_INTRINSIC:
{
GenTreeIntrinsic* intrinsic = obj->AsIntrinsic();

if (intrinsic->gtIntrinsicId == CORINFO_INTRINSIC_Object_GetType)
{
CORINFO_CLASS_HANDLE runtimeType = info.compCompHnd->getBuiltinClass(CLASSID_RUNTIME_TYPE);
objClass = runtimeType;
*isExact = false;
*isNonNull = true;
}

break;
}
Expand Down Expand Up @@ -16272,7 +16291,40 @@ CORINFO_CLASS_HANDLE Compiler::gtGetClassHandle(GenTree* tree, bool* isExact, bo
*isExact = false;
*isNonNull = false;
}
else if (base->OperGet() == GT_ADD)
{
// This could be a static field access.
//
// See if op1 is a static field base helper call
// and if so, op2 will have the field info.
GenTree* op1 = base->gtOp.gtOp1;
GenTree* op2 = base->gtOp.gtOp2;

const bool op1IsStaticFieldBase = gtIsStaticGCBaseHelperCall(op1);

if (op1IsStaticFieldBase && (op2->OperGet() == GT_CNS_INT))
{
FieldSeqNode* fieldSeq = op2->AsIntCon()->gtFieldSeq;

if (fieldSeq != nullptr)
{
while (fieldSeq->m_next != nullptr)
{
fieldSeq = fieldSeq->m_next;
}

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;
}
}
}
}
}

break;
}

Expand Down Expand Up @@ -16310,6 +16362,108 @@ CORINFO_CLASS_HANDLE Compiler::gtGetClassHandle(GenTree* tree, bool* isExact, bo
return objClass;
}

//------------------------------------------------------------------------
// gtGetHelperCallClassHandle: find class handle for return value of a
// helper call
//
// Arguments:
// call - helper call to examine
// isExact - [OUT] true if type is known exactly
// isNonNull - [OUT] true if return 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.

CORINFO_CLASS_HANDLE Compiler::gtGetHelperCallClassHandle(GenTreeCall* call, bool* isExact, bool* isNonNull)
{
assert(call->gtCallType == CT_HELPER);

*isNonNull = false;
*isExact = false;
CORINFO_CLASS_HANDLE objClass = nullptr;
const CorInfoHelpFunc helper = eeGetHelperNum(call->gtCallMethHnd);

switch (helper)
{
case CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE:
case CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE_MAYBENULL:
{
// Note for some runtimes these helpers return exact types.
//
// But in those cases the types are also sealed, so there's no
// need to claim exactness here.
const bool helperResultNonNull = (helper == CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE);
CORINFO_CLASS_HANDLE runtimeType = info.compCompHnd->getBuiltinClass(CLASSID_RUNTIME_TYPE);

objClass = runtimeType;
*isNonNull = helperResultNonNull;
break;
}

case CORINFO_HELP_CHKCASTCLASS:
case CORINFO_HELP_CHKCASTANY:
case CORINFO_HELP_CHKCASTARRAY:
case CORINFO_HELP_CHKCASTINTERFACE:
case CORINFO_HELP_CHKCASTCLASS_SPECIAL:
case CORINFO_HELP_ISINSTANCEOFINTERFACE:
case CORINFO_HELP_ISINSTANCEOFARRAY:
case CORINFO_HELP_ISINSTANCEOFCLASS:
case CORINFO_HELP_ISINSTANCEOFANY:
{
// Fetch the class handle from the helper call arglist
GenTreeArgList* args = call->gtCallArgs;
GenTree* typeArg = args->Current();
CORINFO_CLASS_HANDLE castHnd = gtGetHelperArgClassHandle(typeArg);

// We generally assume the type being cast to is the best type
// for the result, unless it is an interface type.
//
// TODO-CQ: when we have default interface methods then
// this might not be the best assumption. We could also
// explore calling something like mergeClasses to identify
// the more specific class. A similar issue arises when
// typing the temp in impCastClassOrIsInstToTree, when we
// expand the cast inline.
if (castHnd != nullptr)
{
DWORD attrs = info.compCompHnd->getClassAttribs(castHnd);

if ((attrs & CORINFO_FLG_INTERFACE) != 0)
{
castHnd = nullptr;
}
}

// If we don't have a good estimate for the type we can use the
// type from the value being cast instead.
if (castHnd == nullptr)
{
GenTree* valueArg = args->Rest()->Current();
castHnd = gtGetClassHandle(valueArg, isExact, isNonNull);
}

// We don't know at jit time if the cast will succeed or fail, but if it
// fails at runtime then an exception is thrown for cast helpers, or the
// result is set null for instance helpers.
//
// So it safe to claim the result has the cast type.
// Note we don't know for sure that it is exactly this type.
if (castHnd != nullptr)
{
objClass = castHnd;
}

break;
}

default:
break;
}

return objClass;
}

//------------------------------------------------------------------------
// gtGetArrayElementClassHandle: find class handle for elements of an array
// of ref types
Expand Down Expand Up @@ -16348,6 +16502,55 @@ CORINFO_CLASS_HANDLE Compiler::gtGetArrayElementClassHandle(GenTree* array)
return nullptr;
}

//------------------------------------------------------------------------
// gtIsGCStaticBaseHelperCall: true if tree is fetching the gc static base
// for a subsequent static field access
//
// Arguments:
// tree - tree to consider
//
// Return Value:
// true if the tree is a suitable helper call
//
// Notes:
// Excludes R2R helpers as they specify the target field in a way
// that is opaque to the jit.

bool Compiler::gtIsStaticGCBaseHelperCall(GenTree* tree)
{
if (tree->OperGet() != GT_CALL)
{
return false;
}

GenTreeCall* call = tree->AsCall();

if (call->gtCallType != CT_HELPER)
{
return false;
}

const CorInfoHelpFunc helper = eeGetHelperNum(call->gtCallMethHnd);

switch (helper)
{
// We are looking for a REF type so only need to check for the GC base helpers
case CORINFO_HELP_GETGENERICS_GCSTATIC_BASE:
case CORINFO_HELP_GETSHARED_GCSTATIC_BASE:
case CORINFO_HELP_GETSHARED_GCSTATIC_BASE_NOCTOR:
case CORINFO_HELP_GETSHARED_GCSTATIC_BASE_DYNAMICCLASS:
case CORINFO_HELP_GETGENERICS_GCTHREADSTATIC_BASE:
case CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE:
case CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR:
case CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_DYNAMICCLASS:
return true;
default:
break;
}

return false;
}

void GenTree::ParseArrayAddress(
Compiler* comp, ArrayInfo* arrayInfo, GenTree** pArr, ValueNum* pInxVN, FieldSeqNode** pFldSeq)
{
Expand Down
5 changes: 4 additions & 1 deletion src/coreclr/src/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10098,7 +10098,10 @@ GenTree* Compiler::impCastClassOrIsInstToTree(GenTree* op1,
unsigned tmp = lvaGrabTemp(true DEBUGARG("spilling QMark2"));
impAssignTempGen(tmp, qmarkNull, (unsigned)CHECK_SPILL_NONE);

// TODO: Is it possible op1 has a better type?
// TODO-CQ: Is it possible op1 has a better type?
//
// See also gtGetHelperCallClassHandle where we make the same
// determination for the helper call variants.
lvaSetClass(tmp, pResolvedToken->hClass);
return gtNewLclvNode(tmp, TYP_REF);
#endif
Expand Down

0 comments on commit 51f6e74

Please sign in to comment.