diff --git a/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h b/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h index 29baaa8a3927..aae6cd8e5806 100644 --- a/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h +++ b/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h @@ -428,6 +428,14 @@ BOOL canCast(CORINFO_CLASS_HANDLE child, // subtype (extends parent) // TRUE if cls1 and cls2 are considered equivalent types. BOOL areTypesEquivalent(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2); +// See if a cast from fromClass to toClass will succeed, fail, or needs +// to be resolved at runtime. +TypeCompareState compareTypesForCast(CORINFO_CLASS_HANDLE fromClass, CORINFO_CLASS_HANDLE toClass); + +// See if types represented by cls1 and cls2 compare equal, not +// equal, or the comparison needs to be resolved at runtime. +TypeCompareState compareTypesForEquality(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2); + // returns is the intersection of cls1 and cls2. CORINFO_CLASS_HANDLE mergeClasses(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2); diff --git a/src/ToolBox/superpmi/superpmi-shared/lwmlist.h b/src/ToolBox/superpmi/superpmi-shared/lwmlist.h index 21f7906510ac..1bca259a6f2f 100644 --- a/src/ToolBox/superpmi/superpmi-shared/lwmlist.h +++ b/src/ToolBox/superpmi/superpmi-shared/lwmlist.h @@ -31,6 +31,8 @@ LWM(CanInlineTypeCheckWithObjectVTable, DWORDLONG, DWORD) LWM(CanSkipMethodVerification, DLD, DWORD) LWM(CanTailCall, Agnostic_CanTailCall, DWORD) LWM(CheckMethodModifier, Agnostic_CheckMethodModifier, DWORD) +LWM(CompareTypesForCast, DLDL, DWORD) +LWM(CompareTypesForEquality, DLDL, DWORD) LWM(CompileMethod, DWORD, Agnostic_CompileMethod) LWM(ConstructStringLiteral, DLD, DLD) LWM(EmbedClassHandle, DWORDLONG, DLDL) diff --git a/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp b/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp index 0bf288d1969b..c3ae9d3a5290 100644 --- a/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp @@ -5288,6 +5288,72 @@ BOOL MethodContext::repAreTypesEquivalent(CORINFO_CLASS_HANDLE cls1, CORINFO_CLA return value; } +void MethodContext::recCompareTypesForCast(CORINFO_CLASS_HANDLE fromClass, CORINFO_CLASS_HANDLE toClass, TypeCompareState result) +{ + if (CompareTypesForCast == nullptr) + CompareTypesForCast = new LightWeightMap(); + + DLDL key; + ZeroMemory(&key, sizeof(DLDL)); // We use the input structs as a key and use memcmp to compare.. so we need to zero + // out padding too + + key.A = (DWORDLONG)fromClass; + key.B = (DWORDLONG)toClass; + + CompareTypesForCast->Add(key, (DWORD)result); +} +void MethodContext::dmpCompareTypesForCast(DLDL key, DWORD value) +{ + printf("CompareTypesForCast key fromClas=%016llX, toClass=%016llx, result=%d", key.A, key.B, value); +} +TypeCompareState MethodContext::repCompareTypesForCast(CORINFO_CLASS_HANDLE fromClass, CORINFO_CLASS_HANDLE toClass) +{ + DLDL key; + ZeroMemory(&key, sizeof(DLDL)); // We use the input structs as a key and use memcmp to compare.. so we need to zero + // out padding too + + key.A = (DWORDLONG)fromClass; + key.B = (DWORDLONG)toClass; + + AssertCodeMsg(CompareTypesForCast->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX %016llX", + (DWORDLONG)fromClass, (DWORDLONG)toClass); + TypeCompareState value = (TypeCompareState)CompareTypesForCast->Get(key); + return value; +} + +void MethodContext::recCompareTypesForEquality(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2, TypeCompareState result) +{ + if (CompareTypesForEquality == nullptr) + CompareTypesForEquality = new LightWeightMap(); + + DLDL key; + ZeroMemory(&key, sizeof(DLDL)); // We use the input structs as a key and use memcmp to compare.. so we need to zero + // out padding too + + key.A = (DWORDLONG)cls1; + key.B = (DWORDLONG)cls2; + + CompareTypesForEquality->Add(key, (DWORD)result); +} +void MethodContext::dmpCompareTypesForEquality(DLDL key, DWORD value) +{ + printf("CompareTypesForEquality key cls1=%016llX, cls2=%016llx, result=%d", key.A, key.B, value); +} +TypeCompareState MethodContext::repCompareTypesForEquality(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2) +{ + DLDL key; + ZeroMemory(&key, sizeof(DLDL)); // We use the input structs as a key and use memcmp to compare.. so we need to zero + // out padding too + + key.A = (DWORDLONG)cls1; + key.B = (DWORDLONG)cls2; + + AssertCodeMsg(CompareTypesForEquality->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX %016llX", + (DWORDLONG)cls1, (DWORDLONG)cls2); + TypeCompareState value = (TypeCompareState)CompareTypesForEquality->Get(key); + return value; +} + void MethodContext::recFindNameOfToken( CORINFO_MODULE_HANDLE module, mdToken metaTOK, char* szFQName, size_t FQNameCapacity, size_t result) { diff --git a/src/ToolBox/superpmi/superpmi-shared/methodcontext.h b/src/ToolBox/superpmi/superpmi-shared/methodcontext.h index 52515733d33c..9cc8f02ff085 100644 --- a/src/ToolBox/superpmi/superpmi-shared/methodcontext.h +++ b/src/ToolBox/superpmi/superpmi-shared/methodcontext.h @@ -1176,6 +1176,14 @@ class MethodContext void dmpAreTypesEquivalent(DLDL key, DWORD value); BOOL repAreTypesEquivalent(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2); + void recCompareTypesForCast(CORINFO_CLASS_HANDLE fromClass, CORINFO_CLASS_HANDLE toClass, TypeCompareState result); + void dmpCompareTypesForCast(DLDL key, DWORD value); + TypeCompareState repCompareTypesForCast(CORINFO_CLASS_HANDLE fromClass, CORINFO_CLASS_HANDLE toClass); + + void recCompareTypesForEquality(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2, TypeCompareState result); + void dmpCompareTypesForEquality(DLDL key, DWORD value); + TypeCompareState repCompareTypesForEquality(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2); + void recFindNameOfToken( CORINFO_MODULE_HANDLE module, mdToken metaTOK, char* szFQName, size_t FQNameCapacity, size_t result); void dmpFindNameOfToken(DLD key, DLD value); @@ -1257,7 +1265,7 @@ class MethodContext }; // ********************* Please keep this up-to-date to ease adding more *************** -// Highest packet number: 162 +// Highest packet number: 164 // ************************************************************************************* enum mcPackets { @@ -1279,6 +1287,8 @@ enum mcPackets Packet_CheckMethodModifier = 142, // retired as 13 on 2013/07/04 Retired3 = 14, Retired5 = 141, // retired as 14 on 2013/07/03 + Packet_CompareTypesForCast = 163, // Added 10/4/17 + Packet_CompareTypesForEquality = 164, // Added 10/4/17 Packet_CompileMethod = 143, // retired as 141 on 2013/07/09 Packet_ConstructStringLiteral = 15, Packet_EmbedClassHandle = 16, diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp index 6051de8840e3..af3e8c94de33 100644 --- a/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -889,6 +889,26 @@ BOOL interceptor_ICJI::areTypesEquivalent(CORINFO_CLASS_HANDLE cls1, CORINFO_CLA return temp; } +// See if a cast from fromClass to toClass will succeed, fail, or needs +// to be resolved at runtime. +TypeCompareState interceptor_ICJI::compareTypesForCast(CORINFO_CLASS_HANDLE fromClass, CORINFO_CLASS_HANDLE toClass) +{ + mc->cr->AddCall("compareTypesForCast"); + TypeCompareState temp = original_ICorJitInfo->compareTypesForCast(fromClass, toClass); + mc->recCompareTypesForCast(fromClass, toClass, temp); + return temp; +} + +// See if types represented by cls1 and cls2 compare equal, not +// equal, or the comparison needs to be resolved at runtime. +TypeCompareState interceptor_ICJI::compareTypesForEquality(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2) +{ + mc->cr->AddCall("compareTypesForEquality"); + TypeCompareState temp = original_ICorJitInfo->compareTypesForEquality(cls1, cls2); + mc->recCompareTypesForEquality(cls1, cls2, temp); + return temp; +} + // returns is the intersection of cls1 and cls2. CORINFO_CLASS_HANDLE interceptor_ICJI::mergeClasses(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2) { diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp b/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp index 17da100dac50..dd047e83a7de 100644 --- a/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp +++ b/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp @@ -703,6 +703,22 @@ BOOL interceptor_ICJI::areTypesEquivalent(CORINFO_CLASS_HANDLE cls1, CORINFO_CLA return original_ICorJitInfo->areTypesEquivalent(cls1, cls2); } +// See if a cast from fromClass to toClass will succeed, fail, or needs +// to be resolved at runtime. +TypeCompareState interceptor_ICJI::compareTypesForCast(CORINFO_CLASS_HANDLE fromClass, CORINFO_CLASS_HANDLE toClass) +{ + mcs->AddCall("compareTypesForCast"); + return original_ICorJitInfo->compareTypesForCast(fromClass, toClass); +} + +// See if types represented by cls1 and cls2 compare equal, not +// equal, or the comparison needs to be resolved at runtime. +TypeCompareState interceptor_ICJI::compareTypesForEquality(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2) +{ + mcs->AddCall("compareTypesForEquality"); + return original_ICorJitInfo->compareTypesForEquality(cls1, cls2); +} + // returns is the intersection of cls1 and cls2. CORINFO_CLASS_HANDLE interceptor_ICJI::mergeClasses(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2) { diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp b/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp index df4cfcfbcd1d..cc384f31a0e2 100644 --- a/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp +++ b/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp @@ -629,6 +629,20 @@ BOOL interceptor_ICJI::areTypesEquivalent(CORINFO_CLASS_HANDLE cls1, CORINFO_CLA return original_ICorJitInfo->areTypesEquivalent(cls1, cls2); } +// See if a cast from fromClass to toClass will succeed, fail, or needs +// to be resolved at runtime. +TypeCompareState interceptor_ICJI::compareTypesForCast(CORINFO_CLASS_HANDLE fromClass, CORINFO_CLASS_HANDLE toClass) +{ + return original_ICorJitInfo->compareTypesForCast(fromClass, toClass); +} + +// See if types represented by cls1 and cls2 compare equal, not +// equal, or the comparison needs to be resolved at runtime. +TypeCompareState interceptor_ICJI::compareTypesForEquality(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2) +{ + return original_ICorJitInfo->compareTypesForEquality(cls1, cls2); +} + // returns is the intersection of cls1 and cls2. CORINFO_CLASS_HANDLE interceptor_ICJI::mergeClasses(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2) { diff --git a/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp b/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp index 7c119b86dd37..e16903b9e147 100644 --- a/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp +++ b/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp @@ -760,6 +760,22 @@ BOOL MyICJI::areTypesEquivalent(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE return jitInstance->mc->repAreTypesEquivalent(cls1, cls2); } +// See if a cast from fromClass to toClass will succeed, fail, or needs +// to be resolved at runtime. +TypeCompareState MyICJI::compareTypesForCast(CORINFO_CLASS_HANDLE fromClass, CORINFO_CLASS_HANDLE toClass) +{ + jitInstance->mc->cr->AddCall("compareTypesForCast"); + return jitInstance->mc->repCompareTypesForCast(fromClass, toClass); +} + +// See if types represented by cls1 and cls2 compare equal, not +// equal, or the comparison needs to be resolved at runtime. +TypeCompareState MyICJI::compareTypesForEquality(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2) +{ + jitInstance->mc->cr->AddCall("compareTypesForEquality"); + return jitInstance->mc->repCompareTypesForEquality(cls1, cls2); +} + // returns is the intersection of cls1 and cls2. CORINFO_CLASS_HANDLE MyICJI::mergeClasses(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2) { diff --git a/src/inc/corinfo.h b/src/inc/corinfo.h index b03f0456ac39..dc3d22fa9f2b 100644 --- a/src/inc/corinfo.h +++ b/src/inc/corinfo.h @@ -213,12 +213,11 @@ TODO: Talk about initializing strutures before use #define SELECTANY extern __declspec(selectany) #endif -// {CFEC7B89-D5FF-4A67-823A-EF99FE0286F4} -SELECTANY const GUID JITEEVersionIdentifier = { - 0xcfec7b89, - 0xd5ff, - 0x4a67, - { 0x82, 0x3a, 0xef, 0x99, 0xfe, 0x2, 0x86, 0xf4 } +SELECTANY const GUID JITEEVersionIdentifier = { /* 8f51c68e-d515-425c-9e04-97e4a8148b07 */ + 0x8f51c68e, + 0xd515, + 0x425c, + {0x9e, 0x04, 0x97, 0xe4, 0xa8, 0x14, 0x8b, 0x07} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1955,6 +1954,14 @@ typedef SIZE_T GSCookie; const int MAX_EnC_HANDLER_NESTING_LEVEL = 6; +// Results from type comparison queries +enum class TypeCompareState +{ + MustNot = -1, // types are not equal + May = 0, // types may be equal (must test at runtime) + Must = 1, // type are equal +}; + // // This interface is logically split into sections for each class of information // (ICorMethodInfo, ICorModuleInfo, etc.). This split used to exist physically as well @@ -2492,6 +2499,20 @@ class ICorStaticInfo CORINFO_CLASS_HANDLE cls2 ) = 0; + // See if a cast from fromClass to toClass will succeed, fail, or needs + // to be resolved at runtime. + virtual TypeCompareState compareTypesForCast( + CORINFO_CLASS_HANDLE fromClass, + CORINFO_CLASS_HANDLE toClass + ) = 0; + + // See if types represented by cls1 and cls2 compare equal, not + // equal, or the comparison needs to be resolved at runtime. + virtual TypeCompareState compareTypesForEquality( + CORINFO_CLASS_HANDLE cls1, + CORINFO_CLASS_HANDLE cls2 + ) = 0; + // returns is the intersection of cls1 and cls2. virtual CORINFO_CLASS_HANDLE mergeClasses( CORINFO_CLASS_HANDLE cls1, diff --git a/src/jit/compiler.h b/src/jit/compiler.h index cd65c25b86d8..0e8b4c213598 100644 --- a/src/jit/compiler.h +++ b/src/jit/compiler.h @@ -2119,6 +2119,8 @@ class Compiler GenTreePtr gtNewAllocObjNode(unsigned int helper, CORINFO_CLASS_HANDLE clsHnd, var_types type, GenTreePtr op1); + GenTree* gtNewRuntimeLookup(CORINFO_GENERIC_HANDLE hnd, CorInfoGenericHandleType hndTyp, GenTree* lookupTree); + //------------------------------------------------------------------------ // Other GenTree functions @@ -9769,6 +9771,7 @@ class GenTreeVisitor case GT_RETURN: case GT_RETFILT: case GT_PHI: + case GT_RUNTIMELOOKUP: { GenTreeUnOp* const unOp = node->AsUnOp(); if (unOp->gtOp1 != nullptr) diff --git a/src/jit/compiler.hpp b/src/jit/compiler.hpp index 4a90760ce2f4..f357e6f5c809 100644 --- a/src/jit/compiler.hpp +++ b/src/jit/compiler.hpp @@ -1169,6 +1169,23 @@ inline GenTreePtr Compiler::gtNewAllocObjNode(unsigned int helper, return node; } +//------------------------------------------------------------------------ +// gtNewRuntimeLookup: Helper to create a runtime lookup node +// +// Arguments: +// hnd - generic handle being looked up +// hndTyp - type of the generic handle +// tree - tree for the lookup +// +// Return Value: +// New GenTreeRuntimeLookup node. +inline GenTree* Compiler::gtNewRuntimeLookup(CORINFO_GENERIC_HANDLE hnd, CorInfoGenericHandleType hndTyp, GenTree* tree) +{ + assert(tree != nullptr); + GenTree* node = new (this, GT_RUNTIMELOOKUP) GenTreeRuntimeLookup(hnd, hndTyp, tree); + return node; +} + /*****************************************************************************/ inline GenTreePtr Compiler::gtNewCodeRef(BasicBlock* block) diff --git a/src/jit/gentree.cpp b/src/jit/gentree.cpp index 540d1686fc4c..a4ea1036411e 100644 --- a/src/jit/gentree.cpp +++ b/src/jit/gentree.cpp @@ -1901,6 +1901,12 @@ unsigned Compiler::gtHashValue(GenTree* tree) reinterpret_cast(tree->gtAllocObj.gtAllocObjClsHnd))); hash = genTreeHashAdd(hash, tree->gtAllocObj.gtNewHelper); break; + case GT_RUNTIMELOOKUP: + hash = + genTreeHashAdd(hash, + static_cast(reinterpret_cast(tree->gtRuntimeLookup.gtHnd))); + break; + case GT_OBJ: hash = genTreeHashAdd(hash, static_cast(reinterpret_cast(tree->gtObj.gtClass))); @@ -5441,6 +5447,7 @@ bool GenTree::TryGetUse(GenTree* def, GenTree*** use) case GT_BLK: case GT_BOX: case GT_ALLOCOBJ: + case GT_RUNTIMELOOKUP: case GT_INIT_VAL: case GT_JTRUE: case GT_SWITCH: @@ -7523,6 +7530,15 @@ GenTreePtr Compiler::gtCloneExpr( } break; + case GT_RUNTIMELOOKUP: + { + GenTreeRuntimeLookup* asRuntimeLookup = tree->AsRuntimeLookup(); + + copy = new (this, GT_RUNTIMELOOKUP) + GenTreeRuntimeLookup(asRuntimeLookup->gtHnd, asRuntimeLookup->gtHndType, asRuntimeLookup->gtOp1); + } + break; + case GT_ARR_LENGTH: copy = gtNewArrLen(tree->TypeGet(), tree->gtOp.gtOp1, tree->gtArrLen.ArrLenOffset()); break; @@ -8842,6 +8858,7 @@ GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node) case GT_BLK: case GT_BOX: case GT_ALLOCOBJ: + case GT_RUNTIMELOOKUP: case GT_INIT_VAL: case GT_JTRUE: case GT_SWITCH: @@ -10078,6 +10095,31 @@ void Compiler::gtDispNode(GenTreePtr tree, IndentStack* indentStack, __in __in_z { printf(" => [clsHnd=%08X]", dspPtr(tree->gtArgPlace.gtArgPlaceClsHnd)); } + + if (tree->gtOper == GT_RUNTIMELOOKUP) + { +#ifdef _TARGET_64BIT_ + printf(" 0x%llx", dspPtr(tree->gtRuntimeLookup.gtHnd)); +#else + printf(" 0x%x", dspPtr(tree->gtRuntimeLookup.gtHnd)); +#endif + + switch (tree->gtRuntimeLookup.gtHndType) + { + case CORINFO_HANDLETYPE_CLASS: + printf(" class"); + break; + case CORINFO_HANDLETYPE_METHOD: + printf(" method"); + break; + case CORINFO_HANDLETYPE_FIELD: + printf(" field"); + break; + default: + printf(" unknown"); + break; + } + } } // for tracking down problems in reguse prediction or liveness tracking @@ -12365,6 +12407,55 @@ GenTree* Compiler::gtFoldTypeCompare(GenTree* tree) } } + // If the inputs to the type from handle operations are now + // either known class handles or runtime lookups, ask the VM + // if it knows the outcome of the equality comparison. + CORINFO_CLASS_HANDLE cls1Hnd = nullptr; + CORINFO_CLASS_HANDLE cls2Hnd = nullptr; + unsigned runtimeLookupCount = 0; + + if ((op1ClassFromHandle->OperGet() == GT_CNS_INT) && op1ClassFromHandle->IsIconHandle(GTF_ICON_CLASS_HDL)) + { + cls1Hnd = (CORINFO_CLASS_HANDLE)op1ClassFromHandle->gtIntCon.gtCompileTimeHandle; + } + else if (op1ClassFromHandle->OperGet() == GT_RUNTIMELOOKUP) + { + cls1Hnd = op1ClassFromHandle->AsRuntimeLookup()->GetClassHandle(); + runtimeLookupCount++; + } + + if ((op2ClassFromHandle->OperGet() == GT_CNS_INT) && op2ClassFromHandle->IsIconHandle(GTF_ICON_CLASS_HDL)) + { + cls2Hnd = (CORINFO_CLASS_HANDLE)op2ClassFromHandle->gtIntCon.gtCompileTimeHandle; + } + else if (op2ClassFromHandle->OperGet() == GT_RUNTIMELOOKUP) + { + cls2Hnd = op2ClassFromHandle->AsRuntimeLookup()->GetClassHandle(); + runtimeLookupCount++; + } + + if ((cls1Hnd != nullptr) && (cls2Hnd != nullptr)) + { + TypeCompareState s = info.compCompHnd->compareTypesForEquality(cls1Hnd, cls2Hnd); + + if (s != TypeCompareState::May) + { + // Type comparison result is known. + const bool typesAreEqual = (s == TypeCompareState::Must); + const bool operatorIsEQ = (oper == GT_EQ); + const int compareResult = operatorIsEQ ^ typesAreEqual ? 0 : 1; + JITDUMP("Runtime reports comparison is known at jit time: %u\n", compareResult); + GenTree* result = gtNewIconNode(compareResult); + + // The runtime lookups are now dead code, so we may not + // need the generic context kept alive either. + assert(lvaGenericsContextUseCount >= runtimeLookupCount); + lvaGenericsContextUseCount -= runtimeLookupCount; + return result; + } + } + + // We can't answer definitively at jit time, but can still simplfy the comparison. GenTree* compare = gtNewOperNode(oper, TYP_INT, op1ClassFromHandle, op2ClassFromHandle); // Drop any now-irrelvant flags @@ -13210,6 +13301,11 @@ GenTreePtr Compiler::gtFoldExprConst(GenTreePtr tree) return tree; } + if (tree->gtOper == GT_RUNTIMELOOKUP) + { + return tree; + } + if (kind & GTK_UNOP) { assert(op1->OperKind() & GTK_CONST); diff --git a/src/jit/gentree.h b/src/jit/gentree.h index e8803e0e1d48..60d9080dc660 100644 --- a/src/jit/gentree.h +++ b/src/jit/gentree.h @@ -5649,6 +5649,62 @@ struct GenTreeAllocObj final : public GenTreeUnOp #endif }; +// Represents GT_RUNTIMELOOKUP node + +struct GenTreeRuntimeLookup final : public GenTreeUnOp +{ + CORINFO_GENERIC_HANDLE gtHnd; + CorInfoGenericHandleType gtHndType; + + GenTreeRuntimeLookup(CORINFO_GENERIC_HANDLE hnd, CorInfoGenericHandleType hndTyp, GenTree* tree) + : GenTreeUnOp(GT_RUNTIMELOOKUP, tree->gtType, tree DEBUGARG(/*largeNode*/ FALSE)), gtHnd(hnd), gtHndType(hndTyp) + { + assert(hnd != nullptr); + } +#if DEBUGGABLE_GENTREE + GenTreeRuntimeLookup() : GenTreeUnOp() + { + } +#endif + + // Return reference to the actual tree that does the lookup + GenTree*& Lookup() + { + return gtOp1; + } + + bool IsClassHandle() const + { + return gtHndType == CORINFO_HANDLETYPE_CLASS; + } + bool IsMethodHandle() const + { + return gtHndType == CORINFO_HANDLETYPE_METHOD; + } + bool IsFieldHandle() const + { + return gtHndType == CORINFO_HANDLETYPE_FIELD; + } + + // Note these operations describe the handle that is input to the + // lookup, not the handle produced by the lookup. + CORINFO_CLASS_HANDLE GetClassHandle() const + { + assert(IsClassHandle()); + return (CORINFO_CLASS_HANDLE)gtHnd; + } + CORINFO_METHOD_HANDLE GetMethodHandle() const + { + assert(IsMethodHandle()); + return (CORINFO_METHOD_HANDLE)gtHnd; + } + CORINFO_FIELD_HANDLE GetFieldHandle() const + { + assert(IsMethodHandle()); + return (CORINFO_FIELD_HANDLE)gtHnd; + } +}; + // Represents a GT_JCC or GT_SETCC node. struct GenTreeCC final : public GenTree diff --git a/src/jit/gtlist.h b/src/jit/gtlist.h index 939f00c3340c..9ba21a36c8bd 100644 --- a/src/jit/gtlist.h +++ b/src/jit/gtlist.h @@ -99,6 +99,8 @@ GTNODE(ALLOCOBJ , GenTreeAllocObj ,0,GTK_UNOP|GTK_EXOP) // objec GTNODE(INIT_VAL , GenTreeOp ,0,GTK_UNOP) // Initialization value for an initBlk +GTNODE(RUNTIMELOOKUP , GenTreeRuntimeLookup, 0,GTK_UNOP|GTK_EXOP) // Runtime handle lookup + //----------------------------------------------------------------------------- // Binary operators (2 operands): //----------------------------------------------------------------------------- diff --git a/src/jit/gtstructs.h b/src/jit/gtstructs.h index 853ccebe9f8e..02143b49e2ca 100644 --- a/src/jit/gtstructs.h +++ b/src/jit/gtstructs.h @@ -105,6 +105,7 @@ GTSTRUCT_1(PhysReg , GT_PHYSREG) GTSTRUCT_1(SIMD , GT_SIMD) #endif // FEATURE_SIMD GTSTRUCT_1(AllocObj , GT_ALLOCOBJ) +GTSTRUCT_1(RuntimeLookup, GT_RUNTIMELOOKUP) GTSTRUCT_2(CC , GT_JCC, GT_SETCC) #if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_) #ifdef ARM_SOFTFP diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp index 73310076211a..332abbf1c7b0 100644 --- a/src/jit/importer.cpp +++ b/src/jit/importer.cpp @@ -1742,8 +1742,17 @@ GenTreePtr Compiler::impTokenToHandle(CORINFO_RESOLVED_TOKEN* pResolvedToken, } } - return impLookupToTree(pResolvedToken, &embedInfo.lookup, gtTokenToIconFlags(pResolvedToken->token), - embedInfo.compileTimeHandle); + // Generate the full lookup tree. May be null if we're abandoning an inline attempt. + GenTree* result = impLookupToTree(pResolvedToken, &embedInfo.lookup, gtTokenToIconFlags(pResolvedToken->token), + embedInfo.compileTimeHandle); + + // If we have a result and it requires runtime lookup, wrap it in a runtime lookup node. + if ((result != nullptr) && embedInfo.lookup.lookupKind.needsRuntimeLookup) + { + result = gtNewRuntimeLookup(embedInfo.compileTimeHandle, embedInfo.handleType, result); + } + + return result; } GenTreePtr Compiler::impLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken, @@ -14652,20 +14661,25 @@ void Compiler::impImportBlockCode(BasicBlock* block) // Look ahead for unbox.any if (codeAddr + (sz + 1 + sizeof(mdToken)) <= codeEndp && codeAddr[sz] == CEE_UNBOX_ANY) { - DWORD classAttribs = info.compCompHnd->getClassAttribs(resolvedToken.hClass); - if (!(classAttribs & CORINFO_FLG_SHAREDINST)) - { - CORINFO_RESOLVED_TOKEN unboxResolvedToken; + CORINFO_RESOLVED_TOKEN unboxResolvedToken; - impResolveToken(codeAddr + (sz + 1), &unboxResolvedToken, CORINFO_TOKENKIND_Class); + impResolveToken(codeAddr + (sz + 1), &unboxResolvedToken, CORINFO_TOKENKIND_Class); - if (unboxResolvedToken.hClass == resolvedToken.hClass) - { - JITDUMP("\n Importing BOX; UNBOX.ANY as NOP\n"); - // Skip the next unbox.any instruction - sz += sizeof(mdToken) + 1; - break; - } + // See if token types a equal. + const TypeCompareState compare = + info.compCompHnd->compareTypesForEquality(unboxResolvedToken.hClass, resolvedToken.hClass); + + // If so, box/unbox.any is a nop. + if (compare == TypeCompareState::Must) + { + JITDUMP("\n Importing BOX; UNBOX.ANY as NOP\n"); + // Skip the next unbox.any instruction + sz += sizeof(mdToken) + 1; + break; + } + else + { + assert(unboxResolvedToken.hClass != resolvedToken.hClass); } } diff --git a/src/jit/morph.cpp b/src/jit/morph.cpp index 1bdb2e571645..c4337b6e2300 100644 --- a/src/jit/morph.cpp +++ b/src/jit/morph.cpp @@ -11990,6 +11990,9 @@ GenTreePtr Compiler::fgMorphSmpOp(GenTreePtr tree, MorphAddrContext* mac) break; + case GT_RUNTIMELOOKUP: + return fgMorphTree(op1); + #ifdef _TARGET_ARM_ case GT_INTRINSIC: if (tree->gtIntrinsic.gtIntrinsicId == CORINFO_INTRINSIC_Round) diff --git a/src/vm/jitinterface.cpp b/src/vm/jitinterface.cpp index aa4106419845..5a5ff05cd664 100644 --- a/src/vm/jitinterface.cpp +++ b/src/vm/jitinterface.cpp @@ -4584,6 +4584,75 @@ BOOL CEEInfo::areTypesEquivalent( return result; } +/*********************************************************************/ +// See if a cast from fromClass to toClass will succeed, fail, or needs +// to be resolved at runtime. +TypeCompareState CEEInfo::compareTypesForCast( + CORINFO_CLASS_HANDLE fromClass, + CORINFO_CLASS_HANDLE toClass) +{ + // Stub for now + return TypeCompareState::May; +} + +/*********************************************************************/ +// See if types represented by cls1 and cls2 compare equal, not +// equal, or the comparison needs to be resolved at runtime. +TypeCompareState CEEInfo::compareTypesForEquality( + CORINFO_CLASS_HANDLE cls1, + CORINFO_CLASS_HANDLE cls2) +{ + CONTRACTL { + SO_TOLERANT; + THROWS; + GC_TRIGGERS; + MODE_PREEMPTIVE; + } CONTRACTL_END; + + TypeCompareState result = TypeCompareState::May; + + JIT_TO_EE_TRANSITION(); + + TypeHandle hnd1 = (TypeHandle) cls1; + TypeHandle hnd2 = (TypeHandle) cls2; + + // If neither type is a canonical subtype, type handle comparison suffices + if (!hnd1.IsCanonicalSubtype() && !hnd2.IsCanonicalSubtype()) + { + result = (hnd1 == hnd2 ? TypeCompareState::Must : TypeCompareState::MustNot); + } + // If either or both types are canonical subtypes, we can sometimes prove inequality. + else + { + // If either is a value type then the types cannot + // be equal unless the type defs are the same. + if (hnd1.IsValueType() || hnd2.IsValueType()) + { + if (!hnd1.GetMethodTable()->HasSameTypeDefAs(hnd2.GetMethodTable())) + { + result = TypeCompareState::MustNot; + } + } + // If we have two ref types that are not __Canon, then the + // types cannot be equal unless the type defs are the same. + else + { + TypeHandle canonHnd = TypeHandle(g_pCanonMethodTableClass); + if ((hnd1 != canonHnd) && (hnd2 != canonHnd)) + { + if (!hnd1.GetMethodTable()->HasSameTypeDefAs(hnd2.GetMethodTable())) + { + result = TypeCompareState::MustNot; + } + } + } + } + + EE_TO_JIT_TRANSITION(); + + return result; +} + /*********************************************************************/ // returns is the intersection of cls1 and cls2. CORINFO_CLASS_HANDLE CEEInfo::mergeClasses( diff --git a/src/vm/jitinterface.h b/src/vm/jitinterface.h index 56394e301604..aa140b00a01a 100644 --- a/src/vm/jitinterface.h +++ b/src/vm/jitinterface.h @@ -562,6 +562,20 @@ class CEEInfo : public ICorJitInfo CORINFO_CLASS_HANDLE cls2 ); + // See if a cast from fromClass to toClass will succeed, fail, or needs + // to be resolved at runtime. + TypeCompareState compareTypesForCast( + CORINFO_CLASS_HANDLE fromClass, + CORINFO_CLASS_HANDLE toClass + ); + + // See if types represented by cls1 and cls2 compare equal, not + // equal, or the comparison needs to be resolved at runtime. + TypeCompareState compareTypesForEquality( + CORINFO_CLASS_HANDLE cls1, + CORINFO_CLASS_HANDLE cls2 + ); + // returns is the intersection of cls1 and cls2. CORINFO_CLASS_HANDLE mergeClasses( CORINFO_CLASS_HANDLE cls1, diff --git a/src/zap/zapinfo.cpp b/src/zap/zapinfo.cpp index 01d3a3b7cc9e..4007f678ae3a 100644 --- a/src/zap/zapinfo.cpp +++ b/src/zap/zapinfo.cpp @@ -3107,6 +3107,16 @@ BOOL ZapInfo::areTypesEquivalent(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE return m_pEEJitInfo->areTypesEquivalent(cls1, cls2); } +TypeCompareState ZapInfo::compareTypesForCast(CORINFO_CLASS_HANDLE fromClass, CORINFO_CLASS_HANDLE toClass) +{ + return m_pEEJitInfo->compareTypesForCast(fromClass, toClass); +} + +TypeCompareState ZapInfo::compareTypesForEquality(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2) +{ + return m_pEEJitInfo->compareTypesForEquality(cls1, cls2); +} + CORINFO_CLASS_HANDLE ZapInfo::mergeClasses( CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2) diff --git a/src/zap/zapinfo.h b/src/zap/zapinfo.h index 5bb86dd88aae..f401cbaf9dc2 100644 --- a/src/zap/zapinfo.h +++ b/src/zap/zapinfo.h @@ -573,6 +573,9 @@ class ZapInfo CorInfoType getTypeForPrimitiveValueClass(CORINFO_CLASS_HANDLE cls); BOOL canCast(CORINFO_CLASS_HANDLE child, CORINFO_CLASS_HANDLE parent); BOOL areTypesEquivalent(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2); + TypeCompareState compareTypesForCast(CORINFO_CLASS_HANDLE fromClass, CORINFO_CLASS_HANDLE toClass); + TypeCompareState compareTypesForEquality(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2); + CORINFO_CLASS_HANDLE mergeClasses(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2); BOOL shouldEnforceCallvirtRestriction(CORINFO_MODULE_HANDLE scope);