summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichal Strehovský <MichalStrehovsky@users.noreply.github.com>2018-11-19 09:23:22 +0100
committerGitHub <noreply@github.com>2018-11-19 09:23:22 +0100
commit497419bf8f19c649d821295da7e225e55581cce9 (patch)
tree5f2813fefef41417a102627277db2fb01b76d7d3
parentd76d97ffc9ba23e6eec30536c6f07756e08151f2 (diff)
downloadcoreclr-497419bf8f19c649d821295da7e225e55581cce9.tar.gz
coreclr-497419bf8f19c649d821295da7e225e55581cce9.tar.bz2
coreclr-497419bf8f19c649d821295da7e225e55581cce9.zip
Make type comparisons more general purpose (#20940)
This has two parts: ## Part 1 CoreRT represents native type handles differently from CoreCLR - on CoreCLR, `RuntimeTypeHandle` is a wrapper over `RuntimeType` and RyuJIT is aware of that. On CoreRT, `RuntimeTypeHandle` wraps the native type handle, not a `RuntimeType`. The knowledge is hardcoded in importer when importing the sequence "ldtoken foo / call Type.GetTypeFromHandle" - importer just removes the call and bashes the result of ldtoken to be a reference type. CoreRT had to avoid reporting `Type.GetTypeFromHandle` as an intrinsic because of that. I'm adding another helper that lets RyuJIT avoid hardcoding that knowledge. Instead of just bashing the return type, we swap the helper call. ## Part 2 Native type handle equality checks need to go through a helper, unless the EE side says it's okay to compare native type handles directly.
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h5
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/lwmlist.h1
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp33
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/methodcontext.h6
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp11
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp9
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp8
-rw-r--r--src/ToolBox/superpmi/superpmi/icorjitinfo.cpp9
-rw-r--r--src/inc/corinfo.h36
-rw-r--r--src/inc/jithelpers.h7
-rw-r--r--src/jit/ICorJitInfo_API_names.h1
-rw-r--r--src/jit/ICorJitInfo_API_wrapper.hpp8
-rw-r--r--src/jit/compiler.h5
-rw-r--r--src/jit/gentree.cpp100
-rw-r--r--src/jit/importer.cpp26
-rw-r--r--src/jit/utils.cpp7
-rw-r--r--src/jit/valuenum.cpp8
-rw-r--r--src/jit/valuenumfuncs.h3
-rw-r--r--src/vm/jitinterface.cpp38
-rw-r--r--src/vm/jitinterface.h1
-rw-r--r--src/zap/zapinfo.cpp5
-rw-r--r--src/zap/zapinfo.h1
22 files changed, 302 insertions, 26 deletions
diff --git a/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h b/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h
index 295bcb229f..73a8d28fc9 100644
--- a/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h
+++ b/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h
@@ -291,6 +291,11 @@ int appendClassName(__deref_inout_ecount(*pnBufLen) WCHAR** ppBuf,
// CORINFO_FLG_VALUECLASS, except faster.
BOOL isValueClass(CORINFO_CLASS_HANDLE cls);
+// Decides how the JIT should do the optimization to inline the check for
+// GetTypeFromHandle(handle) == obj.GetType() (for CORINFO_INLINE_TYPECHECK_SOURCE_VTABLE)
+// GetTypeFromHandle(X) == GetTypeFromHandle(Y) (for CORINFO_INLINE_TYPECHECK_SOURCE_TOKEN)
+CorInfoInlineTypeCheck canInlineTypeCheck(CORINFO_CLASS_HANDLE cls, CorInfoInlineTypeCheckSource source);
+
// If this method returns true, JIT will do optimization to inline the check for
// GetTypeFromHandle(handle) == obj.GetType()
BOOL canInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls);
diff --git a/src/ToolBox/superpmi/superpmi-shared/lwmlist.h b/src/ToolBox/superpmi/superpmi-shared/lwmlist.h
index 0a7daabe87..a50e9b6b34 100644
--- a/src/ToolBox/superpmi/superpmi-shared/lwmlist.h
+++ b/src/ToolBox/superpmi/superpmi-shared/lwmlist.h
@@ -27,6 +27,7 @@ LWM(CanCast, DLDL, DWORD)
LWM(CanGetCookieForPInvokeCalliSig, CanGetCookieForPInvokeCalliSigValue, DWORD)
LWM(CanGetVarArgsHandle, CanGetVarArgsHandleValue, DWORD)
LWM(CanInline, DLDL, Agnostic_CanInline)
+LWM(CanInlineTypeCheck, DLD, DWORD)
LWM(CanInlineTypeCheckWithObjectVTable, DWORDLONG, DWORD)
LWM(CanSkipMethodVerification, DLD, DWORD)
LWM(CanTailCall, Agnostic_CanTailCall, DWORD)
diff --git a/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp b/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp
index 6e839b504e..6729367701 100644
--- a/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp
+++ b/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp
@@ -4402,6 +4402,39 @@ const char* MethodContext::repGetFieldName(CORINFO_FIELD_HANDLE ftn, const char*
return (const char*)GetFieldName->GetBuffer(value.A);
}
+void MethodContext::recCanInlineTypeCheck(CORINFO_CLASS_HANDLE cls, CorInfoInlineTypeCheckSource source, CorInfoInlineTypeCheck result)
+{
+ if (CanInlineTypeCheck == nullptr)
+ CanInlineTypeCheck = new LightWeightMap<DLD, DWORD>();
+
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); // We use the input structs as a key and use memcmp to compare.. so we need to zero
+ // out padding too
+
+ key.A = (DWORDLONG)cls;
+ key.B = (DWORD)source;
+
+ CanInlineTypeCheck->Add(key, (DWORD)result);
+}
+void MethodContext::dmpCanInlineTypeCheck(DLD key, DWORD value)
+{
+ printf("CanInlineTypeCheck key cls-%016llX src-%08X, value res-%u", key.A, key.B, value);
+}
+CorInfoInlineTypeCheck MethodContext::repCanInlineTypeCheck(CORINFO_CLASS_HANDLE cls, CorInfoInlineTypeCheckSource source)
+{
+ AssertCodeMsg(CanInlineTypeCheck != nullptr, EXCEPTIONCODE_MC,
+ "No map for CanInlineTypeCheck");
+
+ DLD key;
+ ZeroMemory(&key, sizeof(DLD)); // We use the input structs as a key and use memcmp to compare.. so we need to zero
+ // out padding too
+
+ key.A = (DWORDLONG)cls;
+ key.B = (DWORD)source;
+
+ return (CorInfoInlineTypeCheck)CanInlineTypeCheck->Get(key);
+}
+
void MethodContext::recCanInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls, BOOL result)
{
if (CanInlineTypeCheckWithObjectVTable == nullptr)
diff --git a/src/ToolBox/superpmi/superpmi-shared/methodcontext.h b/src/ToolBox/superpmi/superpmi-shared/methodcontext.h
index a10ab65ae6..33903ee312 100644
--- a/src/ToolBox/superpmi/superpmi-shared/methodcontext.h
+++ b/src/ToolBox/superpmi/superpmi-shared/methodcontext.h
@@ -1044,6 +1044,9 @@ public:
void dmpGetFieldName(DWORDLONG key, DD value);
const char* repGetFieldName(CORINFO_FIELD_HANDLE ftn, const char** moduleName);
+ void recCanInlineTypeCheck(CORINFO_CLASS_HANDLE cls, CorInfoInlineTypeCheckSource source, CorInfoInlineTypeCheck result);
+ void dmpCanInlineTypeCheck(DLD key, DWORD value);
+ CorInfoInlineTypeCheck repCanInlineTypeCheck(CORINFO_CLASS_HANDLE cls, CorInfoInlineTypeCheckSource source);
void recCanInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls, BOOL result);
void dmpCanInlineTypeCheckWithObjectVTable(DWORDLONG key, DWORD value);
BOOL repCanInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls);
@@ -1326,7 +1329,7 @@ private:
};
// ********************* Please keep this up-to-date to ease adding more ***************
-// Highest packet number: 172
+// Highest packet number: 173
// *************************************************************************************
enum mcPackets
{
@@ -1341,6 +1344,7 @@ enum mcPackets
Packet_CanGetCookieForPInvokeCalliSig = 7,
Packet_CanGetVarArgsHandle = 8,
Packet_CanInline = 9,
+ Packet_CanInlineTypeCheck = 173, // Added 11/15/2018 as a replacement for CanInlineTypeCheckWithObjectVTable
Packet_CanInlineTypeCheckWithObjectVTable = 10,
Packet_CanSkipMethodVerification = 11,
Packet_CanTailCall = 12,
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp
index c628f16d52..c1e42f9b81 100644
--- a/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp
@@ -595,6 +595,17 @@ BOOL interceptor_ICJI::isValueClass(CORINFO_CLASS_HANDLE cls)
return temp;
}
+// Decides how the JIT should do the optimization to inline the check for
+// GetTypeFromHandle(handle) == obj.GetType() (for CORINFO_INLINE_TYPECHECK_SOURCE_VTABLE)
+// GetTypeFromHandle(X) == GetTypeFromHandle(Y) (for CORINFO_INLINE_TYPECHECK_SOURCE_TOKEN)
+CorInfoInlineTypeCheck interceptor_ICJI::canInlineTypeCheck(CORINFO_CLASS_HANDLE cls, CorInfoInlineTypeCheckSource source)
+{
+ mc->cr->AddCall("canInlineTypeCheck");
+ CorInfoInlineTypeCheck temp = original_ICorJitInfo->canInlineTypeCheck(cls, source);
+ mc->recCanInlineTypeCheck(cls, source, temp);
+ return temp;
+}
+
// If this method returns true, JIT will do optimization to inline the check for
// GetTypeFromHandle(handle) == obj.GetType()
BOOL interceptor_ICJI::canInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls)
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp b/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp
index d6dbecdf08..02e94e9ff7 100644
--- a/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp
@@ -441,6 +441,15 @@ BOOL interceptor_ICJI::isValueClass(CORINFO_CLASS_HANDLE cls)
return original_ICorJitInfo->isValueClass(cls);
}
+// Decides how the JIT should do the optimization to inline the check for
+// GetTypeFromHandle(handle) == obj.GetType() (for CORINFO_INLINE_TYPECHECK_SOURCE_VTABLE)
+// GetTypeFromHandle(X) == GetTypeFromHandle(Y) (for CORINFO_INLINE_TYPECHECK_SOURCE_TOKEN)
+CorInfoInlineTypeCheck interceptor_ICJI::canInlineTypeCheck(CORINFO_CLASS_HANDLE cls, CorInfoInlineTypeCheckSource source)
+{
+ mcs->AddCall("canInlineTypeCheck");
+ return original_ICorJitInfo->canInlineTypeCheck(cls, source);
+}
+
// If this method returns true, JIT will do optimization to inline the check for
// GetTypeFromHandle(handle) == obj.GetType()
BOOL interceptor_ICJI::canInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls)
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp b/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp
index 04c0ba0950..0172abd76a 100644
--- a/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp
@@ -396,6 +396,14 @@ BOOL interceptor_ICJI::isValueClass(CORINFO_CLASS_HANDLE cls)
return original_ICorJitInfo->isValueClass(cls);
}
+// Decides how the JIT should do the optimization to inline the check for
+// GetTypeFromHandle(handle) == obj.GetType() (for CORINFO_INLINE_TYPECHECK_SOURCE_VTABLE)
+// GetTypeFromHandle(X) == GetTypeFromHandle(Y) (for CORINFO_INLINE_TYPECHECK_SOURCE_TOKEN)
+CorInfoInlineTypeCheck interceptor_ICJI::canInlineTypeCheck(CORINFO_CLASS_HANDLE cls, CorInfoInlineTypeCheckSource source)
+{
+ return original_ICorJitInfo->canInlineTypeCheck(cls, source);
+}
+
// If this method returns true, JIT will do optimization to inline the check for
// GetTypeFromHandle(handle) == obj.GetType()
BOOL interceptor_ICJI::canInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls)
diff --git a/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp b/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp
index 60cf36c1c8..5346342e51 100644
--- a/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp
+++ b/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp
@@ -486,6 +486,15 @@ BOOL MyICJI::isValueClass(CORINFO_CLASS_HANDLE cls)
return jitInstance->mc->repIsValueClass(cls);
}
+// Decides how the JIT should do the optimization to inline the check for
+// GetTypeFromHandle(handle) == obj.GetType() (for CORINFO_INLINE_TYPECHECK_SOURCE_VTABLE)
+// GetTypeFromHandle(X) == GetTypeFromHandle(Y) (for CORINFO_INLINE_TYPECHECK_SOURCE_TOKEN)
+CorInfoInlineTypeCheck MyICJI::canInlineTypeCheck(CORINFO_CLASS_HANDLE cls, CorInfoInlineTypeCheckSource source)
+{
+ jitInstance->mc->cr->AddCall("canInlineTypeCheck");
+ return jitInstance->mc->repCanInlineTypeCheck(cls, source);
+}
+
// If this method returns true, JIT will do optimization to inline the check for
// GetTypeFromHandle(handle) == obj.GetType()
BOOL MyICJI::canInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls)
diff --git a/src/inc/corinfo.h b/src/inc/corinfo.h
index 059dbbe0a7..68eec6b57e 100644
--- a/src/inc/corinfo.h
+++ b/src/inc/corinfo.h
@@ -213,11 +213,11 @@ TODO: Talk about initializing strutures before use
#define SELECTANY extern __declspec(selectany)
#endif
-SELECTANY const GUID JITEEVersionIdentifier = { /* b2da2a6e-72fa-4730-b47c-4c9275e1c5ce */
- 0xb2da2a6e,
- 0x72fa,
- 0x4730,
- {0xb4, 0x7c, 0x4c, 0x92, 0x75, 0xe1, 0xc5, 0xce}
+SELECTANY const GUID JITEEVersionIdentifier = { /* 09F7AAE2-07DF-4433-B8C5-BA864CCABDA3 */
+ 0x9f7aae2,
+ 0x7df,
+ 0x4433,
+ {0xb8, 0xc5, 0xba, 0x86, 0x4c, 0xca, 0xbd, 0xa3}
};
//////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -581,15 +581,14 @@ enum CorInfoHelpFunc
CORINFO_HELP_RUNTIMEHANDLE_CLASS, // determine a type/field/method handle at run-time
CORINFO_HELP_RUNTIMEHANDLE_CLASS_LOG, // determine a type/field/method handle at run-time, with IBC logging
- // These helpers are required for MDIL backward compatibility only. They are not used by current JITed code.
- CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE_OBSOLETE, // Convert from a TypeHandle (native structure pointer) to RuntimeTypeHandle at run-time
- CORINFO_HELP_METHODDESC_TO_RUNTIMEMETHODHANDLE_OBSOLETE, // Convert from a MethodDesc (native structure pointer) to RuntimeMethodHandle at run-time
- CORINFO_HELP_FIELDDESC_TO_RUNTIMEFIELDHANDLE_OBSOLETE, // Convert from a FieldDesc (native structure pointer) to RuntimeFieldHandle at run-time
-
CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE, // Convert from a TypeHandle (native structure pointer) to RuntimeType at run-time
CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE_MAYBENULL, // Convert from a TypeHandle (native structure pointer) to RuntimeType at run-time, the type may be null
CORINFO_HELP_METHODDESC_TO_STUBRUNTIMEMETHOD, // Convert from a MethodDesc (native structure pointer) to RuntimeMethodHandle at run-time
CORINFO_HELP_FIELDDESC_TO_STUBRUNTIMEFIELD, // Convert from a FieldDesc (native structure pointer) to RuntimeFieldHandle at run-time
+ CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE, // Convert from a TypeHandle (native structure pointer) to RuntimeTypeHandle at run-time
+ CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE_MAYBENULL, // Convert from a TypeHandle (native structure pointer) to RuntimeTypeHandle at run-time, handle might point to a null type
+
+ CORINFO_HELP_ARE_TYPES_EQUIVALENT, // Check whether two TypeHandles (native structure pointers) are equivalent
CORINFO_HELP_VIRTUAL_FUNC_PTR, // look up a virtual method at run-time
//CORINFO_HELP_VIRTUAL_FUNC_PTR_LOG, // look up a virtual method at run-time, with IBC logging
@@ -1022,6 +1021,18 @@ enum CorInfoInlineRestrictions
INLINE_SAME_THIS = 0x00000004, // You can inline only if the callee is on the same this reference as caller
};
+enum CorInfoInlineTypeCheck
+{
+ CORINFO_INLINE_TYPECHECK_NONE = 0x00000000, // It's not okay to compare type's vtable with a native type handle
+ CORINFO_INLINE_TYPECHECK_PASS = 0x00000001, // It's okay to compare type's vtable with a native type handle
+ CORINFO_INLINE_TYPECHECK_USE_HELPER = 0x00000002, // Use a specialized helper to compare type's vtable with native type handle
+};
+
+enum CorInfoInlineTypeCheckSource
+{
+ CORINFO_INLINE_TYPECHECK_SOURCE_VTABLE = 0x00000000, // Type handle comes from the vtable
+ CORINFO_INLINE_TYPECHECK_SOURCE_TOKEN = 0x00000001, // Type handle comes from an ldtoken
+};
// If you add more values here, keep it in sync with TailCallTypeMap in ..\vm\ClrEtwAll.man
// and the string enum in CEEInfo::reportTailCallDecision in ..\vm\JITInterface.cpp
@@ -2335,6 +2346,11 @@ public:
// Quick check whether the type is a value class. Returns the same value as getClassAttribs(cls) & CORINFO_FLG_VALUECLASS, except faster.
virtual BOOL isValueClass(CORINFO_CLASS_HANDLE cls) = 0;
+ // Decides how the JIT should do the optimization to inline the check for
+ // GetTypeFromHandle(handle) == obj.GetType() (for CORINFO_INLINE_TYPECHECK_SOURCE_VTABLE)
+ // GetTypeFromHandle(X) == GetTypeFromHandle(Y) (for CORINFO_INLINE_TYPECHECK_SOURCE_TOKEN)
+ virtual CorInfoInlineTypeCheck canInlineTypeCheck(CORINFO_CLASS_HANDLE cls, CorInfoInlineTypeCheckSource source) = 0;
+
// If this method returns true, JIT will do optimization to inline the check for
// GetTypeFromHandle(handle) == obj.GetType()
virtual BOOL canInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls) = 0;
diff --git a/src/inc/jithelpers.h b/src/inc/jithelpers.h
index 7531e694aa..1da962aaff 100644
--- a/src/inc/jithelpers.h
+++ b/src/inc/jithelpers.h
@@ -263,13 +263,14 @@
JITHELPER(CORINFO_HELP_RUNTIMEHANDLE_METHOD_LOG,JIT_GenericHandleMethodLogging, CORINFO_HELP_SIG_REG_ONLY)
JITHELPER(CORINFO_HELP_RUNTIMEHANDLE_CLASS, JIT_GenericHandleClass, CORINFO_HELP_SIG_REG_ONLY)
JITHELPER(CORINFO_HELP_RUNTIMEHANDLE_CLASS_LOG, JIT_GenericHandleClassLogging, CORINFO_HELP_SIG_REG_ONLY)
- JITHELPER(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE_OBSOLETE, NULL, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB)
- JITHELPER(CORINFO_HELP_METHODDESC_TO_RUNTIMEMETHODHANDLE_OBSOLETE, NULL, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB)
- JITHELPER(CORINFO_HELP_FIELDDESC_TO_RUNTIMEFIELDHANDLE_OBSOLETE, NULL, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB)
JITHELPER(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE, JIT_GetRuntimeType, CORINFO_HELP_SIG_REG_ONLY)
JITHELPER(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE_MAYBENULL, JIT_GetRuntimeType_MaybeNull, CORINFO_HELP_SIG_REG_ONLY)
JITHELPER(CORINFO_HELP_METHODDESC_TO_STUBRUNTIMEMETHOD, JIT_GetRuntimeMethodStub,CORINFO_HELP_SIG_REG_ONLY)
JITHELPER(CORINFO_HELP_FIELDDESC_TO_STUBRUNTIMEFIELD, JIT_GetRuntimeFieldStub, CORINFO_HELP_SIG_REG_ONLY)
+ JITHELPER(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE, JIT_GetRuntimeType, CORINFO_HELP_SIG_REG_ONLY)
+ JITHELPER(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE_MAYBENULL, JIT_GetRuntimeType_MaybeNull, CORINFO_HELP_SIG_REG_ONLY)
+
+ JITHELPER(CORINFO_HELP_ARE_TYPES_EQUIVALENT, NULL, CORINFO_HELP_SIG_REG_ONLY)
JITHELPER(CORINFO_HELP_VIRTUAL_FUNC_PTR, JIT_VirtualFunctionPointer, CORINFO_HELP_SIG_4_STACK)
//JITHELPER(CORINFO_HELP_VIRTUAL_FUNC_PTR_LOG,JIT_VirtualFunctionPointerLogging)
diff --git a/src/jit/ICorJitInfo_API_names.h b/src/jit/ICorJitInfo_API_names.h
index 45a1a080ac..fe7253146d 100644
--- a/src/jit/ICorJitInfo_API_names.h
+++ b/src/jit/ICorJitInfo_API_names.h
@@ -41,6 +41,7 @@ DEF_CLR_API(getClassNameFromMetadata)
DEF_CLR_API(getTypeInstantiationArgument)
DEF_CLR_API(appendClassName)
DEF_CLR_API(isValueClass)
+DEF_CLR_API(canInlineTypeCheck)
DEF_CLR_API(canInlineTypeCheckWithObjectVTable)
DEF_CLR_API(getClassAttribs)
DEF_CLR_API(isStructRequiringStackAllocRetBuf)
diff --git a/src/jit/ICorJitInfo_API_wrapper.hpp b/src/jit/ICorJitInfo_API_wrapper.hpp
index 970a9b8a30..6d93f5af30 100644
--- a/src/jit/ICorJitInfo_API_wrapper.hpp
+++ b/src/jit/ICorJitInfo_API_wrapper.hpp
@@ -395,6 +395,14 @@ BOOL WrapICorJitInfo::isValueClass(CORINFO_CLASS_HANDLE cls)
return temp;
}
+CorInfoInlineTypeCheck canInlineTypeCheck(CORINFO_CLASS_HANDLE cls, CorInfoInlineTypeCheckSource source)
+{
+ API_ENTER(canInlineTypeCheck);
+ CorInfoInlineTypeCheck temp = wrapHnd->canInlineTypeCheck(cls, source);
+ API_LEAVE(canInlineTypeCheck);
+ return temp;
+}
+
BOOL WrapICorJitInfo::canInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls)
{
API_ENTER(canInlineTypeCheckWithObjectVTable);
diff --git a/src/jit/compiler.h b/src/jit/compiler.h
index a60b22c454..1ffd5e05be 100644
--- a/src/jit/compiler.h
+++ b/src/jit/compiler.h
@@ -2583,6 +2583,10 @@ public:
gtFoldExprConst(GenTree* tree);
GenTree* gtFoldExprSpecial(GenTree* tree);
GenTree* gtFoldExprCompare(GenTree* tree);
+ GenTree* gtCreateHandleCompare(genTreeOps oper,
+ GenTree* op1,
+ GenTree* op2,
+ CorInfoInlineTypeCheck typeCheckInliningResult);
GenTree* gtFoldExprCall(GenTreeCall* call);
GenTree* gtFoldTypeCompare(GenTree* tree);
GenTree* gtFoldTypeEqualityCall(CorInfoIntrinsics methodID, GenTree* op1, GenTree* op2);
@@ -5392,6 +5396,7 @@ private:
TypeProducerKind gtGetTypeProducerKind(GenTree* tree);
bool gtIsTypeHandleToRuntimeTypeHelper(GenTreeCall* call);
+ bool gtIsTypeHandleToRuntimeTypeHandleHelper(GenTreeCall* call, CorInfoHelpFunc* pHelper = nullptr);
bool gtIsActiveCSE_Candidate(GenTree* tree);
#ifdef DEBUG
diff --git a/src/jit/gentree.cpp b/src/jit/gentree.cpp
index 6c13285a77..cf1de878ca 100644
--- a/src/jit/gentree.cpp
+++ b/src/jit/gentree.cpp
@@ -1301,6 +1301,7 @@ AGAIN:
// For the ones below no extra argument matters for comparison.
case GT_BOX:
+ case GT_RUNTIMELOOKUP:
break;
default:
@@ -11813,6 +11814,48 @@ GenTree* Compiler::gtFoldExprCompare(GenTree* tree)
}
//------------------------------------------------------------------------
+// gtCreateHandleCompare: generate a type handle comparison
+//
+// Arguments:
+// oper -- comparison operation (equal/not equal)
+// op1 -- first operand
+// op2 -- second operand
+// typeCheckInliningResult -- indicates how the comparison should happen
+//
+// Returns:
+// Type comparison tree
+//
+
+GenTree* Compiler::gtCreateHandleCompare(genTreeOps oper,
+ GenTree* op1,
+ GenTree* op2,
+ CorInfoInlineTypeCheck typeCheckInliningResult)
+{
+ // If we can compare pointers directly, just emit the binary operation
+ if (typeCheckInliningResult == CORINFO_INLINE_TYPECHECK_PASS)
+ {
+ return gtNewOperNode(oper, TYP_INT, op1, op2);
+ }
+
+ assert(typeCheckInliningResult == CORINFO_INLINE_TYPECHECK_USE_HELPER);
+
+ // Emit a call to a runtime helper
+ GenTreeArgList* helperArgs = gtNewArgList(op1, op2);
+ GenTree* ret = gtNewHelperCallNode(CORINFO_HELP_ARE_TYPES_EQUIVALENT, TYP_INT, helperArgs);
+ if (oper == GT_EQ)
+ {
+ ret = gtNewOperNode(GT_NE, TYP_INT, ret, gtNewIconNode(0, TYP_INT));
+ }
+ else
+ {
+ assert(oper == GT_NE);
+ ret = gtNewOperNode(GT_EQ, TYP_INT, ret, gtNewIconNode(0, TYP_INT));
+ }
+
+ return ret;
+}
+
+//------------------------------------------------------------------------
// gtFoldTypeCompare: see if a type comparison can be further simplified
//
// Arguments:
@@ -11926,16 +11969,29 @@ GenTree* Compiler::gtFoldTypeCompare(GenTree* tree)
// We can't answer the equality comparison definitively at jit
// time, but can still simplfy the comparison.
//
+ // Find out how we can compare the two handles.
+ // NOTE: We're potentially passing NO_CLASS_HANDLE, but the runtime knows what to do with it here.
+ CorInfoInlineTypeCheck inliningKind =
+ info.compCompHnd->canInlineTypeCheck(cls1Hnd, CORINFO_INLINE_TYPECHECK_SOURCE_TOKEN);
+
+ // If the first type needs helper, check the other type: it might be okay with a simple compare.
+ if (inliningKind == CORINFO_INLINE_TYPECHECK_USE_HELPER)
+ {
+ inliningKind = info.compCompHnd->canInlineTypeCheck(cls2Hnd, CORINFO_INLINE_TYPECHECK_SOURCE_TOKEN);
+ }
+
+ assert(inliningKind == CORINFO_INLINE_TYPECHECK_PASS || inliningKind == CORINFO_INLINE_TYPECHECK_USE_HELPER);
+
// If we successfully tunneled through both operands, compare
// the tunneled values, otherwise compare the original values.
- GenTree* compare = nullptr;
+ GenTree* compare;
if ((op1TunneledHandle != nullptr) && (op2TunneledHandle != nullptr))
{
- compare = gtNewOperNode(oper, TYP_INT, op1TunneledHandle, op2TunneledHandle);
+ compare = gtCreateHandleCompare(oper, op1TunneledHandle, op2TunneledHandle, inliningKind);
}
else
{
- compare = gtNewOperNode(oper, TYP_INT, op1ClassFromHandle, op2ClassFromHandle);
+ compare = gtCreateHandleCompare(oper, op1ClassFromHandle, op2ClassFromHandle, inliningKind);
}
// Drop any now-irrelvant flags
@@ -11971,7 +12027,9 @@ GenTree* Compiler::gtFoldTypeCompare(GenTree* tree)
// Ask the VM if this type can be equality tested by a simple method
// table comparison.
- if (!info.compCompHnd->canInlineTypeCheckWithObjectVTable(clsHnd))
+ CorInfoInlineTypeCheck typeCheckInliningResult =
+ info.compCompHnd->canInlineTypeCheck(clsHnd, CORINFO_INLINE_TYPECHECK_SOURCE_VTABLE);
+ if (typeCheckInliningResult == CORINFO_INLINE_TYPECHECK_NONE)
{
return tree;
}
@@ -12005,7 +12063,7 @@ GenTree* Compiler::gtFoldTypeCompare(GenTree* tree)
optMethodFlags |= OMF_HAS_VTABLEREF;
// Compare the two method tables
- GenTree* const compare = gtNewOperNode(oper, TYP_INT, objMT, knownMT);
+ GenTree* const compare = gtCreateHandleCompare(oper, objMT, knownMT, typeCheckInliningResult);
// Drop any any now irrelevant flags
compare->gtFlags |= tree->gtFlags & (GTF_RELOP_JMP_USED | GTF_RELOP_QMARK | GTF_DONT_CSE);
@@ -15128,6 +15186,38 @@ bool Compiler::gtIsTypeHandleToRuntimeTypeHelper(GenTreeCall* call)
call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE_MAYBENULL);
}
+//------------------------------------------------------------------------
+// gtIsTypeHandleToRuntimeTypeHandleHelperCall -- see if tree is constructing
+// a RuntimeTypeHandle from a handle
+//
+// Arguments:
+// tree - tree to examine
+// pHelper - optional pointer to a variable that receives the type of the helper
+//
+// Return Value:
+// True if so
+
+bool Compiler::gtIsTypeHandleToRuntimeTypeHandleHelper(GenTreeCall* call, CorInfoHelpFunc* pHelper)
+{
+ CorInfoHelpFunc helper = CORINFO_HELP_UNDEF;
+
+ if (call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE))
+ {
+ helper = CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE;
+ }
+ else if (call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE_MAYBENULL))
+ {
+ helper = CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE_MAYBENULL;
+ }
+
+ if (pHelper != nullptr)
+ {
+ *pHelper = helper;
+ }
+
+ return helper != CORINFO_HELP_UNDEF;
+}
+
bool Compiler::gtIsActiveCSE_Candidate(GenTree* tree)
{
return (optValnumCSE_phase && IS_CSE_INDEX(tree->gtCSEnum));
diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp
index 26aafb72b2..6aba97417c 100644
--- a/src/jit/importer.cpp
+++ b/src/jit/importer.cpp
@@ -3621,11 +3621,23 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis,
case CORINFO_INTRINSIC_GetTypeFromHandle:
op1 = impStackTop(0).val;
+ CorInfoHelpFunc typeHandleHelper;
if (op1->gtOper == GT_CALL && (op1->gtCall.gtCallType == CT_HELPER) &&
- gtIsTypeHandleToRuntimeTypeHelper(op1->AsCall()))
+ gtIsTypeHandleToRuntimeTypeHandleHelper(op1->AsCall(), &typeHandleHelper))
{
op1 = impPopStack().val;
- // Change call to return RuntimeType directly.
+ // Replace helper with a more specialized helper that returns RuntimeType
+ if (typeHandleHelper == CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE)
+ {
+ typeHandleHelper = CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE;
+ }
+ else
+ {
+ assert(typeHandleHelper == CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE_MAYBENULL);
+ typeHandleHelper = CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE_MAYBENULL;
+ }
+ assert(op1->gtCall.gtCallArgs->gtOp.gtOp2 == nullptr);
+ op1 = gtNewHelperCallNode(typeHandleHelper, TYP_REF, op1->gtCall.gtCallArgs);
op1->gtType = TYP_REF;
retNode = op1;
}
@@ -3635,7 +3647,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis,
case CORINFO_INTRINSIC_RTH_GetValueInternal:
op1 = impStackTop(0).val;
if (op1->gtOper == GT_CALL && (op1->gtCall.gtCallType == CT_HELPER) &&
- gtIsTypeHandleToRuntimeTypeHelper(op1->AsCall()))
+ gtIsTypeHandleToRuntimeTypeHandleHelper(op1->AsCall()))
{
// Old tree
// Helper-RuntimeTypeHandle -> TreeToGetNativeTypeHandle
@@ -8388,7 +8400,8 @@ DONE_CALL:
if (call->IsCall())
{
GenTreeCall* callNode = call->AsCall();
- if ((callNode->gtCallType == CT_HELPER) && gtIsTypeHandleToRuntimeTypeHelper(callNode))
+ if ((callNode->gtCallType == CT_HELPER) && (gtIsTypeHandleToRuntimeTypeHelper(callNode) ||
+ gtIsTypeHandleToRuntimeTypeHandleHelper(callNode)))
{
spillStack = false;
}
@@ -14707,7 +14720,8 @@ void Compiler::impImportBlockCode(BasicBlock* block)
{
GenTreeArgList* helperArgs = gtNewArgList(op1);
- op1 = gtNewHelperCallNode(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE_MAYBENULL, TYP_STRUCT, helperArgs);
+ op1 = gtNewHelperCallNode(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE_MAYBENULL, TYP_STRUCT,
+ helperArgs);
// The handle struct is returned in register
op1->gtCall.gtReturnType = GetRuntimeHandleUnderlyingType();
@@ -14733,7 +14747,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
return;
}
- helper = CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE;
+ helper = CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE;
assert(resolvedToken.hClass != nullptr);
if (resolvedToken.hMethod != nullptr)
diff --git a/src/jit/utils.cpp b/src/jit/utils.cpp
index 13a02dd2a3..8ccae2ccc8 100644
--- a/src/jit/utils.cpp
+++ b/src/jit/utils.cpp
@@ -1308,11 +1308,18 @@ void HelperCallProperties::init()
case CORINFO_HELP_ISINSTANCEOFANY:
case CORINFO_HELP_READYTORUN_ISINSTANCEOF:
case CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE:
+ case CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE:
isPure = true;
noThrow = true; // These return null for a failing cast
break;
+ case CORINFO_HELP_ARE_TYPES_EQUIVALENT:
+
+ isPure = true;
+ noThrow = true;
+ break;
+
// type casting helpers that throw
case CORINFO_HELP_CHKCASTINTERFACE:
case CORINFO_HELP_CHKCASTARRAY:
diff --git a/src/jit/valuenum.cpp b/src/jit/valuenum.cpp
index f35f90186e..b8948bd0a4 100644
--- a/src/jit/valuenum.cpp
+++ b/src/jit/valuenum.cpp
@@ -8913,6 +8913,14 @@ VNFunc Compiler::fgValueNumberJitHelperMethodVNFunc(CorInfoHelpFunc helpFunc)
vnf = VNF_TypeHandleToRuntimeType;
break;
+ case CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE:
+ vnf = VNF_TypeHandleToRuntimeTypeHandle;
+ break;
+
+ case CORINFO_HELP_ARE_TYPES_EQUIVALENT:
+ vnf = VNF_AreTypesEquivalent;
+ break;
+
case CORINFO_HELP_READYTORUN_ISINSTANCEOF:
vnf = VNF_ReadyToRunIsInstanceOf;
break;
diff --git a/src/jit/valuenumfuncs.h b/src/jit/valuenumfuncs.h
index e29721b480..b5952dd9ad 100644
--- a/src/jit/valuenumfuncs.h
+++ b/src/jit/valuenumfuncs.h
@@ -37,6 +37,9 @@ ValueNumFuncDef(IsInstanceOf, 2, false, false, false) // Args: 0: Handle o
ValueNumFuncDef(ReadyToRunCastClass, 2, false, false, false) // Args: 0: Helper stub address, 1: object being cast.
ValueNumFuncDef(ReadyToRunIsInstanceOf, 2, false, false, false) // Args: 0: Helper stub address, 1: object being queried.
ValueNumFuncDef(TypeHandleToRuntimeType, 1, false, false, false) // Args: 0: TypeHandle to translate
+ValueNumFuncDef(TypeHandleToRuntimeTypeHandle, 1, false, false, false) // Args: 0: TypeHandle to translate
+
+ValueNumFuncDef(AreTypesEquivalent, 2, false, false, false) // Args: 0: first TypeHandle, 1: second TypeHandle
ValueNumFuncDef(LdElemA, 3, false, false, false) // Args: 0: array value; 1: index value; 2: type handle of element.
diff --git a/src/vm/jitinterface.cpp b/src/vm/jitinterface.cpp
index 51ab2cecab..266f0c8f18 100644
--- a/src/vm/jitinterface.cpp
+++ b/src/vm/jitinterface.cpp
@@ -3829,8 +3829,44 @@ BOOL CEEInfo::isValueClass(CORINFO_CLASS_HANDLE clsHnd)
}
/*********************************************************************/
+// Decides how the JIT should do the optimization to inline the check for
+// GetTypeFromHandle(handle) == obj.GetType() (for CORINFO_INLINE_TYPECHECK_SOURCE_VTABLE)
+// GetTypeFromHandle(X) == GetTypeFromHandle(Y) (for CORINFO_INLINE_TYPECHECK_SOURCE_TOKEN)
+//
+// This will enable to use directly the typehandle instead of going through getClassByHandle
+CorInfoInlineTypeCheck CEEInfo::canInlineTypeCheck(CORINFO_CLASS_HANDLE clsHnd, CorInfoInlineTypeCheckSource source)
+{
+ CONTRACTL {
+ SO_TOLERANT;
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_PREEMPTIVE;
+ } CONTRACTL_END;
+
+ CorInfoInlineTypeCheck ret;
+
+ JIT_TO_EE_TRANSITION_LEAF();
+
+ if (source == CORINFO_INLINE_TYPECHECK_SOURCE_TOKEN)
+ {
+ // It's always okay to compare type handles coming from IL tokens
+ ret = CORINFO_INLINE_TYPECHECK_PASS;
+ }
+ else
+ {
+ _ASSERTE(source == CORINFO_INLINE_TYPECHECK_SOURCE_VTABLE);
+ ret = canInlineTypeCheckWithObjectVTable(clsHnd) ?
+ CORINFO_INLINE_TYPECHECK_PASS : CORINFO_INLINE_TYPECHECK_NONE;
+ }
+
+ EE_TO_JIT_TRANSITION_LEAF();
+
+ return(ret);
+}
+
+/*********************************************************************/
// If this method returns true, JIT will do optimization to inline the check for
-// GetClassFromHandle(handle) == obj.GetType()
+// GetTypeFromHandle(handle) == obj.GetType()
//
// This will enable to use directly the typehandle instead of going through getClassByHandle
BOOL CEEInfo::canInlineTypeCheckWithObjectVTable (CORINFO_CLASS_HANDLE clsHnd)
diff --git a/src/vm/jitinterface.h b/src/vm/jitinterface.h
index 4ec732fdb0..dae6352000 100644
--- a/src/vm/jitinterface.h
+++ b/src/vm/jitinterface.h
@@ -481,6 +481,7 @@ public:
BOOL fFullInst,
BOOL fAssembly);
BOOL isValueClass (CORINFO_CLASS_HANDLE cls);
+ CorInfoInlineTypeCheck canInlineTypeCheck (CORINFO_CLASS_HANDLE cls, CorInfoInlineTypeCheckSource source);
BOOL canInlineTypeCheckWithObjectVTable (CORINFO_CLASS_HANDLE cls);
DWORD getClassAttribs (CORINFO_CLASS_HANDLE cls);
diff --git a/src/zap/zapinfo.cpp b/src/zap/zapinfo.cpp
index e0acd819c9..b94809f45e 100644
--- a/src/zap/zapinfo.cpp
+++ b/src/zap/zapinfo.cpp
@@ -3073,6 +3073,11 @@ BOOL ZapInfo::isValueClass(CORINFO_CLASS_HANDLE cls)
return m_pEEJitInfo->isValueClass(cls);
}
+CorInfoInlineTypeCheck ZapInfo::canInlineTypeCheck (CORINFO_CLASS_HANDLE cls, CorInfoInlineTypeCheckSource source)
+{
+ return m_pEEJitInfo->canInlineTypeCheck(cls, source);
+}
+
BOOL ZapInfo::canInlineTypeCheckWithObjectVTable (CORINFO_CLASS_HANDLE cls)
{
return m_pEEJitInfo->canInlineTypeCheckWithObjectVTable(cls);
diff --git a/src/zap/zapinfo.h b/src/zap/zapinfo.h
index ded4d53923..0e4a84eb4c 100644
--- a/src/zap/zapinfo.h
+++ b/src/zap/zapinfo.h
@@ -521,6 +521,7 @@ public:
BOOL fFullInst,
BOOL fAssembly);
BOOL isValueClass(CORINFO_CLASS_HANDLE clsHnd);
+ CorInfoInlineTypeCheck canInlineTypeCheck(CORINFO_CLASS_HANDLE cls, CorInfoInlineTypeCheckSource source);
BOOL canInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE clsHnd);
DWORD getClassAttribs(CORINFO_CLASS_HANDLE cls);
BOOL isStructRequiringStackAllocRetBuf(CORINFO_CLASS_HANDLE cls);