summaryrefslogtreecommitdiff
path: root/src/jit
diff options
context:
space:
mode:
authorAndy Ayers <andya@microsoft.com>2018-10-23 17:00:11 -0700
committerGitHub <noreply@github.com>2018-10-23 17:00:11 -0700
commit5af4d641711ba91d6bfa5bcdc510e35cec108754 (patch)
tree88e3885fb2fc8ca4f6617a5e2797eb6b62bdcb26 /src/jit
parentf53df5b2ac4c3417e0777cb8fe2fca9cf011ede1 (diff)
downloadcoreclr-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.
Diffstat (limited to 'src/jit')
-rw-r--r--src/jit/compiler.h4
-rw-r--r--src/jit/gentree.cpp203
-rw-r--r--src/jit/importer.cpp5
3 files changed, 211 insertions, 1 deletions
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