diff options
author | Michal Strehovský <MichalStrehovsky@users.noreply.github.com> | 2018-11-19 09:23:22 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-11-19 09:23:22 +0100 |
commit | 497419bf8f19c649d821295da7e225e55581cce9 (patch) | |
tree | 5f2813fefef41417a102627277db2fb01b76d7d3 | |
parent | d76d97ffc9ba23e6eec30536c6f07756e08151f2 (diff) | |
download | coreclr-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.h | 5 | ||||
-rw-r--r-- | src/ToolBox/superpmi/superpmi-shared/lwmlist.h | 1 | ||||
-rw-r--r-- | src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp | 33 | ||||
-rw-r--r-- | src/ToolBox/superpmi/superpmi-shared/methodcontext.h | 6 | ||||
-rw-r--r-- | src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp | 11 | ||||
-rw-r--r-- | src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp | 9 | ||||
-rw-r--r-- | src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp | 8 | ||||
-rw-r--r-- | src/ToolBox/superpmi/superpmi/icorjitinfo.cpp | 9 | ||||
-rw-r--r-- | src/inc/corinfo.h | 36 | ||||
-rw-r--r-- | src/inc/jithelpers.h | 7 | ||||
-rw-r--r-- | src/jit/ICorJitInfo_API_names.h | 1 | ||||
-rw-r--r-- | src/jit/ICorJitInfo_API_wrapper.hpp | 8 | ||||
-rw-r--r-- | src/jit/compiler.h | 5 | ||||
-rw-r--r-- | src/jit/gentree.cpp | 100 | ||||
-rw-r--r-- | src/jit/importer.cpp | 26 | ||||
-rw-r--r-- | src/jit/utils.cpp | 7 | ||||
-rw-r--r-- | src/jit/valuenum.cpp | 8 | ||||
-rw-r--r-- | src/jit/valuenumfuncs.h | 3 | ||||
-rw-r--r-- | src/vm/jitinterface.cpp | 38 | ||||
-rw-r--r-- | src/vm/jitinterface.h | 1 | ||||
-rw-r--r-- | src/zap/zapinfo.cpp | 5 | ||||
-rw-r--r-- | src/zap/zapinfo.h | 1 |
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); |