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

JIT: recover types from helper calls and more #20447

Merged
merged 5 commits into from
Oct 24, 2018
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions src/System.Private.CoreLib/src/System/Internal.cs
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/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)]

Choose a reason for hiding this comment

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

It would be nice if in the future the JIT could discover this fact and have a way to persist it so the next time we try to ngen/jit it we consult the database of known methods for this kind of information.
Perhaphs could add an option to ngen/crossgen to create a simplist .IBC data file that contained this information and then check it in along with the real .IBC data.

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 like the idea but do not know how we'd actually do something like this.

I think it might be easier to improve the inlining heuristics in this area (a relatively small method body has multiple sequential calls that are not themselves inline candidates). A strong enough conviction here would automatically propagate noinline into the runtime metadata when prejitting.

Copy link
Member Author

Choose a reason for hiding this comment

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

Opened #20526 to track this idea.

private string GetCachedName(TypeNameKind kind)
{
return Cache.GetName(kind);
Expand Down
4 changes: 4 additions & 0 deletions src/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -2612,12 +2612,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/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16563,6 +16563,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;
AndyAyersMS marked this conversation as resolved.
Show resolved Hide resolved
}

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 @@ -16606,7 +16625,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 @@ -16644,6 +16696,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 @@ -16682,6 +16836,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/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