diff options
author | Andy Ayers <andya@microsoft.com> | 2018-10-23 17:00:11 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-10-23 17:00:11 -0700 |
commit | 5af4d641711ba91d6bfa5bcdc510e35cec108754 (patch) | |
tree | 88e3885fb2fc8ca4f6617a5e2797eb6b62bdcb26 | |
parent | f53df5b2ac4c3417e0777cb8fe2fca9cf011ede1 (diff) | |
download | coreclr-5af4d641711ba91d6bfa5bcdc510e35cec108754.tar.gz coreclr-5af4d641711ba91d6bfa5bcdc510e35cec108754.tar.bz2 coreclr-5af4d641711ba91d6bfa5bcdc510e35cec108754.zip |
JIT: recover types from helper calls and more (#20447)
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.
-rw-r--r-- | src/System.Private.CoreLib/src/System/Internal.cs | 1 | ||||
-rw-r--r-- | src/System.Private.CoreLib/src/System/RtType.cs | 4 | ||||
-rw-r--r-- | src/jit/compiler.h | 4 | ||||
-rw-r--r-- | src/jit/gentree.cpp | 203 | ||||
-rw-r--r-- | src/jit/importer.cpp | 5 |
5 files changed, 216 insertions, 1 deletions
diff --git a/src/System.Private.CoreLib/src/System/Internal.cs b/src/System.Private.CoreLib/src/System/Internal.cs index 033ae9aa99..cc0b8283c0 100644 --- a/src/System.Private.CoreLib/src/System/Internal.cs +++ b/src/System.Private.CoreLib/src/System/Internal.cs @@ -202,6 +202,7 @@ namespace System // 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); diff --git a/src/System.Private.CoreLib/src/System/RtType.cs b/src/System.Private.CoreLib/src/System/RtType.cs index 0e5cf8104e..63cf950d9d 100644 --- a/src/System.Private.CoreLib/src/System/RtType.cs +++ b/src/System.Private.CoreLib/src/System/RtType.cs @@ -4532,6 +4532,10 @@ namespace System } } + // 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); diff --git a/src/jit/compiler.h b/src/jit/compiler.h index 721ef0ed42..cc8032f18b 100644 --- a/src/jit/compiler.h +++ b/src/jit/compiler.h @@ -2602,12 +2602,16 @@ public: 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 diff --git a/src/jit/gentree.cpp b/src/jit/gentree.cpp index c928494816..0f98c90323 100644 --- a/src/jit/gentree.cpp +++ b/src/jit/gentree.cpp @@ -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; } @@ -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; } @@ -16311,6 +16363,108 @@ CORINFO_CLASS_HANDLE Compiler::gtGetClassHandle(GenTree* tree, bool* isExact, bo } //------------------------------------------------------------------------ +// 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 // @@ -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) { diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp index 68f602b715..8fb41655bb 100644 --- a/src/jit/importer.cpp +++ b/src/jit/importer.cpp @@ -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 |