From 17c3b8ca3ab9cdf5c3db8a516ba0868b5bdd897d Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 29 Jan 2022 23:40:07 +0300 Subject: [PATCH 01/30] Initial implementation --- src/coreclr/inc/corinfo.h | 9 + src/coreclr/inc/icorjitinfoimpl_generated.h | 5 + src/coreclr/inc/jiteeversionguid.h | 10 +- src/coreclr/jit/ICorJitInfo_API_names.h | 1 + src/coreclr/jit/ICorJitInfo_API_wrapper.hpp | 11 ++ src/coreclr/jit/gentree.h | 6 + src/coreclr/jit/importer.cpp | 62 ++++--- src/coreclr/jit/indirectcalltransformer.cpp | 12 +- .../tools/Common/JitInterface/CorInfoBase.cs | 160 ++++++++++-------- .../tools/Common/JitInterface/CorInfoImpl.cs | 6 + .../ThunkGenerator/ThunkInput.txt | 1 + .../tools/aot/jitinterface/jitinterface.h | 12 ++ .../tools/superpmi/superpmi-shared/lwmlist.h | 1 + .../superpmi-shared/methodcontext.cpp | 31 ++++ .../superpmi/superpmi-shared/methodcontext.h | 12 +- .../superpmi-shim-collector/icorjitinfo.cpp | 10 ++ .../superpmi-shim-counter/icorjitinfo.cpp | 9 + .../superpmi-shim-simple/icorjitinfo.cpp | 8 + .../tools/superpmi/superpmi/icorjitinfo.cpp | 9 + src/coreclr/vm/jitinterface.cpp | 29 ++++ 20 files changed, 303 insertions(+), 101 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 3bad0df909e9a..2be098b787b15 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -2719,6 +2719,15 @@ class ICorStaticInfo CORINFO_CLASS_HANDLE *vcTypeRet /* OUT */ ) = 0; + // Obtains a list of exact classes for a given base type. Returns 0 if the number of + // the exact classes is greater than maxExactClasses or if more types might be loaded + // in future. + virtual int getExactClasses( + CORINFO_CLASS_HANDLE baseType, /* IN */ + int maxExactClasses, /* IN */ + CORINFO_CLASS_HANDLE* exactClsRet /* OUT */ + ) = 0; + // If the Arg is a CORINFO_TYPE_CLASS fetch the class handle associated with it virtual CORINFO_CLASS_HANDLE getArgClass ( CORINFO_SIG_INFO* sig, /* IN */ diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index 21236cc92cb17..258c50c139c4c 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -419,6 +419,11 @@ CorInfoTypeWithMod getArgType( CORINFO_ARG_LIST_HANDLE args, CORINFO_CLASS_HANDLE* vcTypeRet) override; +int getExactClasses( + CORINFO_CLASS_HANDLE baseType, + int maxExactClasses, + CORINFO_CLASS_HANDLE* exactClsRet) override; + CORINFO_CLASS_HANDLE getArgClass( CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE args) override; diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index e7b8884419e5a..f8d14ba94c4bc 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID; #define GUID_DEFINED #endif // !GUID_DEFINED -constexpr GUID JITEEVersionIdentifier = { /* 323185e9-208a-4b35-a413-23f9aac2d5f7 */ - 0x323185e9, - 0x208a, - 0x4b35, - {0xa4, 0x13, 0x23, 0xf9, 0xaa, 0xc2, 0xd5, 0xf7} +constexpr GUID JITEEVersionIdentifier = { /* 4f13a9a8-63a8-4a2d-9538-0c184fad6bbf */ + 0x4f13a9a8, + 0x63a8, + 0x4a2d, + {0x95, 0x38, 0xc, 0x18, 0x4f, 0xad, 0x6b, 0xbf} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/jit/ICorJitInfo_API_names.h b/src/coreclr/jit/ICorJitInfo_API_names.h index 36a7f89f4718c..43434198b4a30 100644 --- a/src/coreclr/jit/ICorJitInfo_API_names.h +++ b/src/coreclr/jit/ICorJitInfo_API_names.h @@ -105,6 +105,7 @@ DEF_CLR_API(allocateArray) DEF_CLR_API(freeArray) DEF_CLR_API(getArgNext) DEF_CLR_API(getArgType) +DEF_CLR_API(getExactClasses) DEF_CLR_API(getArgClass) DEF_CLR_API(getHFAType) DEF_CLR_API(GetErrorHRESULT) diff --git a/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp b/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp index 6de2cf03a26b1..63d420ed52949 100644 --- a/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp +++ b/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp @@ -994,6 +994,17 @@ CorInfoTypeWithMod WrapICorJitInfo::getArgType( return temp; } +int WrapICorJitInfo::getExactClasses( + CORINFO_CLASS_HANDLE baseType, + int maxExactClasses, + CORINFO_CLASS_HANDLE* exactClsRet) +{ + API_ENTER(getExactClasses); + int temp = wrapHnd->getExactClasses(baseType, maxExactClasses, exactClsRet); + API_LEAVE(getExactClasses); + return temp; +} + CORINFO_CLASS_HANDLE WrapICorJitInfo::getArgClass( CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE args) diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 085bc17a1e510..6e1d37c747fb3 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -3708,6 +3708,7 @@ enum GenTreeCallFlags : unsigned int GTF_CALL_M_UNBOXED = 0x00080000, // this call was optimized to use the unboxed entry point GTF_CALL_M_GUARDED_DEVIRT = 0x00100000, // this call is a candidate for guarded devirtualization GTF_CALL_M_GUARDED_DEVIRT_CHAIN = 0x00200000, // this call is a candidate for chained guarded devirtualization + GTF_CALL_M_GUARDED_DEVIRT_EXACT = 0x20000000, // this call was transformed by guarded devirtualization for exact classes (no fallback) GTF_CALL_M_GUARDED = 0x00400000, // this call was transformed by guarded devirtualization GTF_CALL_M_ALLOC_SIDE_EFFECTS = 0x00800000, // this is a call to an allocator with side effects GTF_CALL_M_SUPPRESS_GC_TRANSITION = 0x01000000, // suppress the GC transition (i.e. during a pinvoke) but a separate GC safe point is required. @@ -4568,6 +4569,11 @@ struct GenTreeCall final : public GenTree return (gtCallMoreFlags & GTF_CALL_M_GUARDED_DEVIRT) != 0; } + bool IsGuardedDevirtualizationCandidateForExactClasses() const + { + return (gtCallMoreFlags & GTF_CALL_M_GUARDED_DEVIRT_EXACT) != 0; + } + bool IsPure(Compiler* compiler) const; bool HasSideEffects(Compiler* compiler, bool ignoreExceptions = false, bool ignoreCctors = false) const; diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index b9cc2ff883d64..cbdbe0b64362d 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -21577,12 +21577,16 @@ void Compiler::considerGuardedDevirtualization( JITDUMP("Considering guarded devirtualization at IL offset %u (0x%x)\n", ilOffset, ilOffset); + const int maxExactClasses = 1; + CORINFO_CLASS_HANDLE exactClasses[maxExactClasses] = {0}; + int exactClassesCount = info.compCompHnd->getExactClasses(baseClass, maxExactClasses, exactClasses); + // We currently only get likely class guesses when there is PGO data // with class profiles. // - if (fgPgoClassProfiles == 0) + if (fgPgoClassProfiles == 0 && exactClassesCount == 0) { - JITDUMP("Not guessing for class: no class profile pgo data, or pgo disabled\n"); + JITDUMP("Not guessing for class: no class profile pgo data, or pgo disabled, or no exact classes\n"); return; } @@ -21599,37 +21603,46 @@ void Compiler::considerGuardedDevirtualization( const int maxLikelyClasses = 32; LikelyClassRecord likelyClasses[maxLikelyClasses]; -#ifdef DEBUG - // Optional stress mode to pick a random known class, rather than - // the most likely known class. - // - doRandomDevirt = JitConfig.JitRandomGuardedDevirtualization() != 0; - - if (doRandomDevirt) + if (fgPgoClassProfiles > 0) { - // Reuse the random inliner's random state. +#ifdef DEBUG + // Optional stress mode to pick a random known class, rather than + // the most likely known class. // - CLRRandom* const random = - impInlineRoot()->m_inlineStrategy->GetRandom(JitConfig.JitRandomGuardedDevirtualization()); - likelyClasses[0].clsHandle = getRandomClass(fgPgoSchema, fgPgoSchemaCount, fgPgoData, ilOffset, random); - likelyClasses[0].likelihood = 100; - if (likelyClasses[0].clsHandle != NO_CLASS_HANDLE) + doRandomDevirt = JitConfig.JitRandomGuardedDevirtualization() != 0; + + if (doRandomDevirt) { - numberOfClasses = 1; + // Reuse the random inliner's random state. + // + CLRRandom* const random = + impInlineRoot()->m_inlineStrategy->GetRandom(JitConfig.JitRandomGuardedDevirtualization()); + likelyClasses[0].clsHandle = getRandomClass(fgPgoSchema, fgPgoSchemaCount, fgPgoData, ilOffset, random); + likelyClasses[0].likelihood = 100; + if (likelyClasses[0].clsHandle != NO_CLASS_HANDLE) + { + numberOfClasses = 1; + } } - } - else + else #endif - { - numberOfClasses = - getLikelyClasses(likelyClasses, maxLikelyClasses, fgPgoSchema, fgPgoSchemaCount, fgPgoData, ilOffset); + { + numberOfClasses = + getLikelyClasses(likelyClasses, maxLikelyClasses, fgPgoSchema, fgPgoSchemaCount, fgPgoData, ilOffset); + } } // For now we only use the most popular type - likelihood = likelyClasses[0].likelihood; likelyClass = likelyClasses[0].clsHandle; + if (exactClassesCount == 1) + { + likelyClass = exactClasses[0]; + likelihood = 100; + numberOfClasses = 1; + } + if (numberOfClasses < 1) { JITDUMP("No likely class, sorry\n"); @@ -21689,6 +21702,11 @@ void Compiler::considerGuardedDevirtualization( CORINFO_METHOD_HANDLE likelyMethod = dvInfo.devirtualizedMethod; JITDUMP("%s call would invoke method %s\n", callKind, eeGetMethodName(likelyMethod, nullptr)); + if (exactClassesCount > 0) + { + call->gtCallMoreFlags |= GTF_CALL_M_GUARDED_DEVIRT_EXACT; + } + // Add this as a potential candidate. // uint32_t const likelyMethodAttribs = info.compCompHnd->getMethodAttribs(likelyMethod); diff --git a/src/coreclr/jit/indirectcalltransformer.cpp b/src/coreclr/jit/indirectcalltransformer.cpp index c0bc70a2850ad..88176022eaa67 100644 --- a/src/coreclr/jit/indirectcalltransformer.cpp +++ b/src/coreclr/jit/indirectcalltransformer.cpp @@ -583,7 +583,16 @@ class IndirectCallTransformer { // There's no need for a new block here. We can just append to currBlock. // - checkBlock = currBlock; + checkBlock = currBlock; + + // Create empty check for "Exact Classes" case. In future, it's going to support + // multiple exact classes with help of https://github.com/dotnet/runtime/pull/59604 + if (origCall->IsGuardedDevirtualizationCandidateForExactClasses()) + { + checkBlock->bbJumpKind = BBJ_NONE; + return; + } + checkBlock->bbJumpKind = BBJ_COND; // Fetch method table from object arg to call. @@ -1012,6 +1021,7 @@ class IndirectCallTransformer GenTreeCall* const call = root->AsCall(); if (call->IsGuardedDevirtualizationCandidate() && + !call->IsGuardedDevirtualizationCandidateForExactClasses() && // no need for chaining for now (call->gtGuardedDevirtualizationCandidateInfo->likelihood >= gdvChainLikelihood)) { JITDUMP("GDV call at [%06u] has likelihood %u >= %u; chaining (%u stmts, %u nodes to dup).\n", diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs b/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs index 006409bdbb533..3c3a7951c820f 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs @@ -1505,6 +1505,21 @@ static CorInfoTypeWithMod _getArgType(IntPtr thisHandle, IntPtr* ppException, CO } } + [UnmanagedCallersOnly] + static int _getExactClasses(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* baseType, int maxExactClasses, CORINFO_CLASS_STRUCT_** exactClsRet) + { + var _this = GetThis(thisHandle); + try + { + return _this.getExactClasses(baseType, maxExactClasses, exactClsRet); + } + catch (Exception ex) + { + *ppException = _this.AllocException(ex); + return default; + } + } + [UnmanagedCallersOnly] static CORINFO_CLASS_STRUCT_* _getArgClass(IntPtr thisHandle, IntPtr* ppException, CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_STRUCT_* args) { @@ -2552,7 +2567,7 @@ static byte _doesFieldBelongToClass(IntPtr thisHandle, IntPtr* ppException, CORI static IntPtr GetUnmanagedCallbacks() { - void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 172); + void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 173); callbacks[0] = (delegate* unmanaged)&_isIntrinsic; callbacks[1] = (delegate* unmanaged)&_getMethodAttribs; @@ -2655,77 +2670,78 @@ static IntPtr GetUnmanagedCallbacks() callbacks[98] = (delegate* unmanaged)&_freeArray; callbacks[99] = (delegate* unmanaged)&_getArgNext; callbacks[100] = (delegate* unmanaged)&_getArgType; - callbacks[101] = (delegate* unmanaged)&_getArgClass; - callbacks[102] = (delegate* unmanaged)&_getHFAType; - callbacks[103] = (delegate* unmanaged)&_GetErrorHRESULT; - callbacks[104] = (delegate* unmanaged)&_GetErrorMessage; - callbacks[105] = (delegate* unmanaged)&_FilterException; - callbacks[106] = (delegate* unmanaged)&_ThrowExceptionForJitResult; - callbacks[107] = (delegate* unmanaged)&_ThrowExceptionForHelper; - callbacks[108] = (delegate* unmanaged)&_runWithErrorTrap; - callbacks[109] = (delegate* unmanaged)&_runWithSPMIErrorTrap; - callbacks[110] = (delegate* unmanaged)&_getEEInfo; - callbacks[111] = (delegate* unmanaged)&_getJitTimeLogFilename; - callbacks[112] = (delegate* unmanaged)&_getMethodDefFromMethod; - callbacks[113] = (delegate* unmanaged)&_getMethodName; - callbacks[114] = (delegate* unmanaged)&_getMethodNameFromMetadata; - callbacks[115] = (delegate* unmanaged)&_getMethodHash; - callbacks[116] = (delegate* unmanaged)&_findNameOfToken; - callbacks[117] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; - callbacks[118] = (delegate* unmanaged)&_getThreadTLSIndex; - callbacks[119] = (delegate* unmanaged)&_getInlinedCallFrameVptr; - callbacks[120] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; - callbacks[121] = (delegate* unmanaged)&_getHelperFtn; - callbacks[122] = (delegate* unmanaged)&_getFunctionEntryPoint; - callbacks[123] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; - callbacks[124] = (delegate* unmanaged)&_getMethodSync; - callbacks[125] = (delegate* unmanaged)&_getLazyStringLiteralHelper; - callbacks[126] = (delegate* unmanaged)&_embedModuleHandle; - callbacks[127] = (delegate* unmanaged)&_embedClassHandle; - callbacks[128] = (delegate* unmanaged)&_embedMethodHandle; - callbacks[129] = (delegate* unmanaged)&_embedFieldHandle; - callbacks[130] = (delegate* unmanaged)&_embedGenericHandle; - callbacks[131] = (delegate* unmanaged)&_getLocationOfThisType; - callbacks[132] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; - callbacks[133] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; - callbacks[134] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; - callbacks[135] = (delegate* unmanaged)&_getJustMyCodeHandle; - callbacks[136] = (delegate* unmanaged)&_GetProfilingHandle; - callbacks[137] = (delegate* unmanaged)&_getCallInfo; - callbacks[138] = (delegate* unmanaged)&_canAccessFamily; - callbacks[139] = (delegate* unmanaged)&_isRIDClassDomainID; - callbacks[140] = (delegate* unmanaged)&_getClassDomainID; - callbacks[141] = (delegate* unmanaged)&_getFieldAddress; - callbacks[142] = (delegate* unmanaged)&_getStaticFieldCurrentClass; - callbacks[143] = (delegate* unmanaged)&_getVarArgsHandle; - callbacks[144] = (delegate* unmanaged)&_canGetVarArgsHandle; - callbacks[145] = (delegate* unmanaged)&_constructStringLiteral; - callbacks[146] = (delegate* unmanaged)&_emptyStringLiteral; - callbacks[147] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; - callbacks[148] = (delegate* unmanaged)&_addActiveDependency; - callbacks[149] = (delegate* unmanaged)&_GetDelegateCtor; - callbacks[150] = (delegate* unmanaged)&_MethodCompileComplete; - callbacks[151] = (delegate* unmanaged)&_getTailCallHelpers; - callbacks[152] = (delegate* unmanaged)&_convertPInvokeCalliToCall; - callbacks[153] = (delegate* unmanaged)&_notifyInstructionSetUsage; - callbacks[154] = (delegate* unmanaged)&_updateEntryPointForTailCall; - callbacks[155] = (delegate* unmanaged)&_allocMem; - callbacks[156] = (delegate* unmanaged)&_reserveUnwindInfo; - callbacks[157] = (delegate* unmanaged)&_allocUnwindInfo; - callbacks[158] = (delegate* unmanaged)&_allocGCInfo; - callbacks[159] = (delegate* unmanaged)&_setEHcount; - callbacks[160] = (delegate* unmanaged)&_setEHinfo; - callbacks[161] = (delegate* unmanaged)&_logMsg; - callbacks[162] = (delegate* unmanaged)&_doAssert; - callbacks[163] = (delegate* unmanaged)&_reportFatalError; - callbacks[164] = (delegate* unmanaged)&_getPgoInstrumentationResults; - callbacks[165] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; - callbacks[166] = (delegate* unmanaged)&_recordCallSite; - callbacks[167] = (delegate* unmanaged)&_recordRelocation; - callbacks[168] = (delegate* unmanaged)&_getRelocTypeHint; - callbacks[169] = (delegate* unmanaged)&_getExpectedTargetArchitecture; - callbacks[170] = (delegate* unmanaged)&_getJitFlags; - callbacks[171] = (delegate* unmanaged)&_doesFieldBelongToClass; + callbacks[101] = (delegate* unmanaged)&_getExactClasses; + callbacks[102] = (delegate* unmanaged)&_getArgClass; + callbacks[103] = (delegate* unmanaged)&_getHFAType; + callbacks[104] = (delegate* unmanaged)&_GetErrorHRESULT; + callbacks[105] = (delegate* unmanaged)&_GetErrorMessage; + callbacks[106] = (delegate* unmanaged)&_FilterException; + callbacks[107] = (delegate* unmanaged)&_ThrowExceptionForJitResult; + callbacks[108] = (delegate* unmanaged)&_ThrowExceptionForHelper; + callbacks[109] = (delegate* unmanaged)&_runWithErrorTrap; + callbacks[110] = (delegate* unmanaged)&_runWithSPMIErrorTrap; + callbacks[111] = (delegate* unmanaged)&_getEEInfo; + callbacks[112] = (delegate* unmanaged)&_getJitTimeLogFilename; + callbacks[113] = (delegate* unmanaged)&_getMethodDefFromMethod; + callbacks[114] = (delegate* unmanaged)&_getMethodName; + callbacks[115] = (delegate* unmanaged)&_getMethodNameFromMetadata; + callbacks[116] = (delegate* unmanaged)&_getMethodHash; + callbacks[117] = (delegate* unmanaged)&_findNameOfToken; + callbacks[118] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; + callbacks[119] = (delegate* unmanaged)&_getThreadTLSIndex; + callbacks[120] = (delegate* unmanaged)&_getInlinedCallFrameVptr; + callbacks[121] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; + callbacks[122] = (delegate* unmanaged)&_getHelperFtn; + callbacks[123] = (delegate* unmanaged)&_getFunctionEntryPoint; + callbacks[124] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; + callbacks[125] = (delegate* unmanaged)&_getMethodSync; + callbacks[126] = (delegate* unmanaged)&_getLazyStringLiteralHelper; + callbacks[127] = (delegate* unmanaged)&_embedModuleHandle; + callbacks[128] = (delegate* unmanaged)&_embedClassHandle; + callbacks[129] = (delegate* unmanaged)&_embedMethodHandle; + callbacks[130] = (delegate* unmanaged)&_embedFieldHandle; + callbacks[131] = (delegate* unmanaged)&_embedGenericHandle; + callbacks[132] = (delegate* unmanaged)&_getLocationOfThisType; + callbacks[133] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; + callbacks[134] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; + callbacks[135] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; + callbacks[136] = (delegate* unmanaged)&_getJustMyCodeHandle; + callbacks[137] = (delegate* unmanaged)&_GetProfilingHandle; + callbacks[138] = (delegate* unmanaged)&_getCallInfo; + callbacks[139] = (delegate* unmanaged)&_canAccessFamily; + callbacks[140] = (delegate* unmanaged)&_isRIDClassDomainID; + callbacks[141] = (delegate* unmanaged)&_getClassDomainID; + callbacks[142] = (delegate* unmanaged)&_getFieldAddress; + callbacks[143] = (delegate* unmanaged)&_getStaticFieldCurrentClass; + callbacks[144] = (delegate* unmanaged)&_getVarArgsHandle; + callbacks[145] = (delegate* unmanaged)&_canGetVarArgsHandle; + callbacks[146] = (delegate* unmanaged)&_constructStringLiteral; + callbacks[147] = (delegate* unmanaged)&_emptyStringLiteral; + callbacks[148] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; + callbacks[149] = (delegate* unmanaged)&_addActiveDependency; + callbacks[150] = (delegate* unmanaged)&_GetDelegateCtor; + callbacks[151] = (delegate* unmanaged)&_MethodCompileComplete; + callbacks[152] = (delegate* unmanaged)&_getTailCallHelpers; + callbacks[153] = (delegate* unmanaged)&_convertPInvokeCalliToCall; + callbacks[154] = (delegate* unmanaged)&_notifyInstructionSetUsage; + callbacks[155] = (delegate* unmanaged)&_updateEntryPointForTailCall; + callbacks[156] = (delegate* unmanaged)&_allocMem; + callbacks[157] = (delegate* unmanaged)&_reserveUnwindInfo; + callbacks[158] = (delegate* unmanaged)&_allocUnwindInfo; + callbacks[159] = (delegate* unmanaged)&_allocGCInfo; + callbacks[160] = (delegate* unmanaged)&_setEHcount; + callbacks[161] = (delegate* unmanaged)&_setEHinfo; + callbacks[162] = (delegate* unmanaged)&_logMsg; + callbacks[163] = (delegate* unmanaged)&_doAssert; + callbacks[164] = (delegate* unmanaged)&_reportFatalError; + callbacks[165] = (delegate* unmanaged)&_getPgoInstrumentationResults; + callbacks[166] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; + callbacks[167] = (delegate* unmanaged)&_recordCallSite; + callbacks[168] = (delegate* unmanaged)&_recordRelocation; + callbacks[169] = (delegate* unmanaged)&_getRelocTypeHint; + callbacks[170] = (delegate* unmanaged)&_getExpectedTargetArchitecture; + callbacks[171] = (delegate* unmanaged)&_getJitFlags; + callbacks[172] = (delegate* unmanaged)&_doesFieldBelongToClass; return (IntPtr)callbacks; } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index cd464db795534..3a0d4e6804cf2 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -2927,6 +2927,12 @@ private CorInfoTypeWithMod getArgType(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_ST } } + private int getExactClasses(CORINFO_CLASS_STRUCT_* baseType, int maxExactClasses, CORINFO_CLASS_STRUCT_** exactClsRet) + { + // TODO: implement + return 0; + } + private CORINFO_CLASS_STRUCT_* getArgClass(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_STRUCT_* args) { int index = (int)args; diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index 92bc30aaf2570..28a5034ceea46 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -254,6 +254,7 @@ FUNCTIONS void freeArray(void*array); CORINFO_ARG_LIST_HANDLE getArgNext(CORINFO_ARG_LIST_HANDLE args); CorInfoTypeWithMod getArgType(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE args, CORINFO_CLASS_HANDLE* vcTypeRet); + int getExactClasses(CORINFO_CLASS_HANDLE baseType, int maxExactClasses, CORINFO_CLASS_HANDLE* exactClsRet); CORINFO_CLASS_HANDLE getArgClass(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE args); CorInfoHFAElemType getHFAType(CORINFO_CLASS_HANDLE hClass); JITINTERFACE_HRESULT GetErrorHRESULT(struct _EXCEPTION_POINTERS *pExceptionPointers); diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface.h b/src/coreclr/tools/aot/jitinterface/jitinterface.h index 14286588063b2..e1a80c08874bb 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface.h @@ -112,6 +112,7 @@ struct JitInterfaceCallbacks void (* freeArray)(void * thisHandle, CorInfoExceptionClass** ppException, void* array); CORINFO_ARG_LIST_HANDLE (* getArgNext)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_ARG_LIST_HANDLE args); CorInfoTypeWithMod (* getArgType)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE args, CORINFO_CLASS_HANDLE* vcTypeRet); + int (* getExactClasses)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE baseType, int maxExactClasses, CORINFO_CLASS_HANDLE* exactClsRet); CORINFO_CLASS_HANDLE (* getArgClass)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE args); CorInfoHFAElemType (* getHFAType)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE hClass); JITINTERFACE_HRESULT (* GetErrorHRESULT)(void * thisHandle, CorInfoExceptionClass** ppException, struct _EXCEPTION_POINTERS* pExceptionPointers); @@ -1180,6 +1181,17 @@ class JitInterfaceWrapper : public ICorJitInfo return temp; } + virtual int getExactClasses( + CORINFO_CLASS_HANDLE baseType, + int maxExactClasses, + CORINFO_CLASS_HANDLE* exactClsRet) +{ + CorInfoExceptionClass* pException = nullptr; + int temp = _callbacks->getExactClasses(_thisHandle, &pException, baseType, maxExactClasses, exactClsRet); + if (pException != nullptr) throw pException; + return temp; +} + virtual CORINFO_CLASS_HANDLE getArgClass( CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE args) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h index aae223f728562..6356f4a6528dd 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h @@ -51,6 +51,7 @@ LWM(GetAddrOfCaptureThreadGlobal, DWORD, DLDL) LWM(GetArgClass, Agnostic_GetArgClass_Key, Agnostic_GetArgClass_Value) LWM(GetArgNext, DWORDLONG, DWORDLONG) LWM(GetArgType, Agnostic_GetArgType_Key, Agnostic_GetArgType_Value) +LWM(GetExactClasses, DLD, DLD) LWM(GetArrayInitializationData, DLD, DWORDLONG) LWM(GetArrayRank, DWORDLONG, DWORD) LWM(GetArrayIntrinsicID, DWORDLONG, DWORD) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index ab8717dae42e8..878693468a7df 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -2709,6 +2709,37 @@ CorInfoTypeWithMod MethodContext::repGetArgType(CORINFO_SIG_INFO* sig, return temp; } +void MethodContext::recGetExactClasses(CORINFO_CLASS_HANDLE baseType, int maxExactClasses, CORINFO_CLASS_HANDLE* exactClsRet, int result) +{ + DLD key; + ZeroMemory(&key, sizeof(key)); + key.A = CastHandle(baseType); + key.B = maxExactClasses; + + DLD value; + ZeroMemory(&value, sizeof(value)); + value.A = CastHandle(*exactClsRet); + value.B = result; + + GetExactClasses->Add(key, value); +} +void MethodContext::dmpGetExactClasses(DLD key, DLD value) +{ + printf("GetExactClasses key baseType-%016llX, key maxExactCls %u, value exactCls %016llX, value exactClsCount %u", + key.A, key.B, value.A, value.B); +} +int MethodContext::repGetExactClasses(CORINFO_CLASS_HANDLE baseType, int maxExactClasses, CORINFO_CLASS_HANDLE* exactClsRet) +{ + DLD key; + ZeroMemory(&key, sizeof(key)); + key.A = CastHandle(baseType); + key.B = maxExactClasses; + + DLD value = GetExactClasses->Get(key); + *exactClsRet = (CORINFO_CLASS_HANDLE)value.A; + return value.B; +} + void MethodContext::recGetArgNext(CORINFO_ARG_LIST_HANDLE args, CORINFO_ARG_LIST_HANDLE result) { if (GetArgNext == nullptr) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h index d18c531dd8648..701fbc302729d 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h @@ -369,6 +369,15 @@ class MethodContext CORINFO_CLASS_HANDLE* vcTypeRet, DWORD* exception); + void recGetExactClasses(CORINFO_CLASS_HANDLE baseType, + int maxExactClasses, + CORINFO_CLASS_HANDLE* exactClsRet, + int result); + void dmpGetExactClasses(DLD key, DLD value); + int repGetExactClasses(CORINFO_CLASS_HANDLE baseType, + int maxExactClasses, + CORINFO_CLASS_HANDLE* exactClsRet); + void recGetArgNext(CORINFO_ARG_LIST_HANDLE args, CORINFO_ARG_LIST_HANDLE result); void dmpGetArgNext(DWORDLONG key, DWORDLONG value); CORINFO_ARG_LIST_HANDLE repGetArgNext(CORINFO_ARG_LIST_HANDLE args); @@ -1043,6 +1052,7 @@ enum mcPackets Packet_IsFieldStatic = 137, PacketCR_AssertLog = 138, Packet_GetArgClass = 139, + Packet_GetExactClasses = 194, Packet_GetArgType = 140, //Retired5 = 141, Packet_CheckMethodModifier = 142, @@ -1096,7 +1106,7 @@ enum mcPackets Packet_GetModuleAssembly = 190, Packet_GetAssemblyName = 191, Packet_IsIntrinsic = 192, - Packet_UpdateEntryPointForTailCall = 193, + Packet_UpdateEntryPointForTailCall = 193 }; void SetDebugDumpVariables(); diff --git a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp index 061d3d6a77b92..135cd9673a823 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -1209,6 +1209,16 @@ CorInfoTypeWithMod interceptor_ICJI::getArgType(CORINFO_SIG_INFO* sig, return temp; } +int interceptor_ICJI::getExactClasses(CORINFO_CLASS_HANDLE baseType, /* IN */ + int maxExactClasses, /* IN */ + CORINFO_CLASS_HANDLE* exactClsRet) /* OUT */ +{ + mc->cr->AddCall("getExactClasses"); + int result = original_ICorJitInfo->getExactClasses(baseType, maxExactClasses, exactClsRet); + this->mc->recGetExactClasses(baseType, maxExactClasses, exactClsRet, result); + return result; +} + // If the Arg is a CORINFO_TYPE_CLASS fetch the class handle associated with it CORINFO_CLASS_HANDLE interceptor_ICJI::getArgClass(CORINFO_SIG_INFO* sig, /* IN */ CORINFO_ARG_LIST_HANDLE args /* IN */ diff --git a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo.cpp index 657575cce71fe..745b86707e828 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo.cpp @@ -814,6 +814,15 @@ CorInfoTypeWithMod interceptor_ICJI::getArgType( return original_ICorJitInfo->getArgType(sig, args, vcTypeRet); } +int interceptor_ICJI::getExactClasses( + CORINFO_CLASS_HANDLE baseType, + int maxExactClasses, + CORINFO_CLASS_HANDLE* exactClsRet) +{ + mcs->AddCall("getExactClasses"); + return original_ICorJitInfo->getExactClasses(baseType, maxExactClasses, exactClsRet); +} + CORINFO_CLASS_HANDLE interceptor_ICJI::getArgClass( CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE args) diff --git a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo.cpp index 294750ccde5c1..9794f54e57120 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo.cpp @@ -713,6 +713,14 @@ CorInfoTypeWithMod interceptor_ICJI::getArgType( return original_ICorJitInfo->getArgType(sig, args, vcTypeRet); } +int interceptor_ICJI::getExactClasses( + CORINFO_CLASS_HANDLE baseType, + int maxExactClasses, + CORINFO_CLASS_HANDLE* exactClsRet) +{ + return original_ICorJitInfo->getExactClasses(baseType, maxExactClasses, exactClsRet); +} + CORINFO_CLASS_HANDLE interceptor_ICJI::getArgClass( CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE args) diff --git a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp index 16c66a6a39847..9bd9883ba2ed9 100644 --- a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp @@ -1060,6 +1060,15 @@ CorInfoTypeWithMod MyICJI::getArgType(CORINFO_SIG_INFO* sig, /* IN */ return value; } +int MyICJI::getExactClasses(CORINFO_CLASS_HANDLE baseType, /* IN */ + int maxExactClasses, /* IN */ + CORINFO_CLASS_HANDLE* exactClsRet /* OUT */ + ) +{ + jitInstance->mc->cr->AddCall("getExactClasses"); + return jitInstance->mc->repGetExactClasses(baseType, maxExactClasses, exactClsRet); +} + // If the Arg is a CORINFO_TYPE_CLASS fetch the class handle associated with it CORINFO_CLASS_HANDLE MyICJI::getArgClass(CORINFO_SIG_INFO* sig, /* IN */ CORINFO_ARG_LIST_HANDLE args /* IN */ diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 3a9f5c0207a6b..92359824faa1d 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -9320,6 +9320,35 @@ CorInfoTypeWithMod CEEInfo::getArgType ( return result; } + +/*********************************************************************/ + +int CEEInfo::getExactClasses ( + CORINFO_CLASS_HANDLE baseType, + int maxExactClasses, + CORINFO_CLASS_HANDLE* exactClsRet + ) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } CONTRACTL_END; + + int exactClassesCount; + + JIT_TO_EE_TRANSITION(); + + // This function is currently implemented only on NativeAOT + // but can be implemented for CoreCLR as well (e.g. for internal types) + + exactClassesCount = 0; + + EE_TO_JIT_TRANSITION(); + + return exactClassesCount; +} + /*********************************************************************/ CORINFO_CLASS_HANDLE CEEInfo::getArgClass ( From e0a25e0e4f7ca33c9d1a5b556508af9f6decf748 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 29 Jan 2022 23:52:21 +0300 Subject: [PATCH 02/30] basic getExactClasses impl --- .../tools/Common/JitInterface/CorInfoImpl.cs | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 3a0d4e6804cf2..cc98195a146d0 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -2929,7 +2929,26 @@ private CorInfoTypeWithMod getArgType(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_ST private int getExactClasses(CORINFO_CLASS_STRUCT_* baseType, int maxExactClasses, CORINFO_CLASS_STRUCT_** exactClsRet) { - // TODO: implement +#if !READYTORUN + MetadataType type = HandleToObject(baseType) as MetadataType; + + if (type == null) + return 0; + + // Give up on shared types + if (type.IsCanonicalSubtype(CanonicalFormKind.Any) || type.HasVariance || type.IsArray) + return 0; + + // type is already sealed, return it + if (_compilation.IsEffectivelySealed(type)) + { + *exactClsRet = baseType; + return 1; + } + + // TODO: find all implementations/subclasses + // the number of them must be <= maxExactClasses +#endif return 0; } From 9643359fe496c25ec866ce9eda4e87fe93289da4 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 25 Feb 2022 13:19:47 +0300 Subject: [PATCH 03/30] fix merge conflicts --- .../tools/Common/JitInterface/CorInfoBase.cs | 162 ++++++++++-------- 1 file changed, 89 insertions(+), 73 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs b/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs index ae02a31a463b7..e4250e105b564 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs @@ -1505,6 +1505,21 @@ static CorInfoTypeWithMod _getArgType(IntPtr thisHandle, IntPtr* ppException, CO } } + [UnmanagedCallersOnly] + static int _getExactClasses(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* baseType, int maxExactClasses, CORINFO_CLASS_STRUCT_** exactClsRet) + { + var _this = GetThis(thisHandle); + try + { + return _this.getExactClasses(baseType, maxExactClasses, exactClsRet); + } + catch (Exception ex) + { + *ppException = _this.AllocException(ex); + return default; + } + } + [UnmanagedCallersOnly] static CORINFO_CLASS_STRUCT_* _getArgClass(IntPtr thisHandle, IntPtr* ppException, CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_STRUCT_* args) { @@ -2567,7 +2582,7 @@ static byte _doesFieldBelongToClass(IntPtr thisHandle, IntPtr* ppException, CORI static IntPtr GetUnmanagedCallbacks() { - void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 173); + void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 174); callbacks[0] = (delegate* unmanaged)&_isIntrinsic; callbacks[1] = (delegate* unmanaged)&_getMethodAttribs; @@ -2670,78 +2685,79 @@ static IntPtr GetUnmanagedCallbacks() callbacks[98] = (delegate* unmanaged)&_freeArray; callbacks[99] = (delegate* unmanaged)&_getArgNext; callbacks[100] = (delegate* unmanaged)&_getArgType; - callbacks[101] = (delegate* unmanaged)&_getArgClass; - callbacks[102] = (delegate* unmanaged)&_getHFAType; - callbacks[103] = (delegate* unmanaged)&_GetErrorHRESULT; - callbacks[104] = (delegate* unmanaged)&_GetErrorMessage; - callbacks[105] = (delegate* unmanaged)&_FilterException; - callbacks[106] = (delegate* unmanaged)&_ThrowExceptionForJitResult; - callbacks[107] = (delegate* unmanaged)&_ThrowExceptionForHelper; - callbacks[108] = (delegate* unmanaged)&_runWithErrorTrap; - callbacks[109] = (delegate* unmanaged)&_runWithSPMIErrorTrap; - callbacks[110] = (delegate* unmanaged)&_getEEInfo; - callbacks[111] = (delegate* unmanaged)&_getJitTimeLogFilename; - callbacks[112] = (delegate* unmanaged)&_getMethodDefFromMethod; - callbacks[113] = (delegate* unmanaged)&_getMethodName; - callbacks[114] = (delegate* unmanaged)&_getMethodNameFromMetadata; - callbacks[115] = (delegate* unmanaged)&_getMethodHash; - callbacks[116] = (delegate* unmanaged)&_findNameOfToken; - callbacks[117] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; - callbacks[118] = (delegate* unmanaged)&_getLoongArch64PassStructInRegisterFlags; - callbacks[119] = (delegate* unmanaged)&_getThreadTLSIndex; - callbacks[120] = (delegate* unmanaged)&_getInlinedCallFrameVptr; - callbacks[121] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; - callbacks[122] = (delegate* unmanaged)&_getHelperFtn; - callbacks[123] = (delegate* unmanaged)&_getFunctionEntryPoint; - callbacks[124] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; - callbacks[125] = (delegate* unmanaged)&_getMethodSync; - callbacks[126] = (delegate* unmanaged)&_getLazyStringLiteralHelper; - callbacks[127] = (delegate* unmanaged)&_embedModuleHandle; - callbacks[128] = (delegate* unmanaged)&_embedClassHandle; - callbacks[129] = (delegate* unmanaged)&_embedMethodHandle; - callbacks[130] = (delegate* unmanaged)&_embedFieldHandle; - callbacks[131] = (delegate* unmanaged)&_embedGenericHandle; - callbacks[132] = (delegate* unmanaged)&_getLocationOfThisType; - callbacks[133] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; - callbacks[134] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; - callbacks[135] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; - callbacks[136] = (delegate* unmanaged)&_getJustMyCodeHandle; - callbacks[137] = (delegate* unmanaged)&_GetProfilingHandle; - callbacks[138] = (delegate* unmanaged)&_getCallInfo; - callbacks[139] = (delegate* unmanaged)&_canAccessFamily; - callbacks[140] = (delegate* unmanaged)&_isRIDClassDomainID; - callbacks[141] = (delegate* unmanaged)&_getClassDomainID; - callbacks[142] = (delegate* unmanaged)&_getFieldAddress; - callbacks[143] = (delegate* unmanaged)&_getStaticFieldCurrentClass; - callbacks[144] = (delegate* unmanaged)&_getVarArgsHandle; - callbacks[145] = (delegate* unmanaged)&_canGetVarArgsHandle; - callbacks[146] = (delegate* unmanaged)&_constructStringLiteral; - callbacks[147] = (delegate* unmanaged)&_emptyStringLiteral; - callbacks[148] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; - callbacks[149] = (delegate* unmanaged)&_addActiveDependency; - callbacks[150] = (delegate* unmanaged)&_GetDelegateCtor; - callbacks[151] = (delegate* unmanaged)&_MethodCompileComplete; - callbacks[152] = (delegate* unmanaged)&_getTailCallHelpers; - callbacks[153] = (delegate* unmanaged)&_convertPInvokeCalliToCall; - callbacks[154] = (delegate* unmanaged)&_notifyInstructionSetUsage; - callbacks[155] = (delegate* unmanaged)&_updateEntryPointForTailCall; - callbacks[156] = (delegate* unmanaged)&_allocMem; - callbacks[157] = (delegate* unmanaged)&_reserveUnwindInfo; - callbacks[158] = (delegate* unmanaged)&_allocUnwindInfo; - callbacks[159] = (delegate* unmanaged)&_allocGCInfo; - callbacks[160] = (delegate* unmanaged)&_setEHcount; - callbacks[161] = (delegate* unmanaged)&_setEHinfo; - callbacks[162] = (delegate* unmanaged)&_logMsg; - callbacks[163] = (delegate* unmanaged)&_doAssert; - callbacks[164] = (delegate* unmanaged)&_reportFatalError; - callbacks[165] = (delegate* unmanaged)&_getPgoInstrumentationResults; - callbacks[166] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; - callbacks[167] = (delegate* unmanaged)&_recordCallSite; - callbacks[168] = (delegate* unmanaged)&_recordRelocation; - callbacks[169] = (delegate* unmanaged)&_getRelocTypeHint; - callbacks[170] = (delegate* unmanaged)&_getExpectedTargetArchitecture; - callbacks[171] = (delegate* unmanaged)&_getJitFlags; - callbacks[172] = (delegate* unmanaged)&_doesFieldBelongToClass; + callbacks[101] = (delegate* unmanaged)&_getExactClasses; + callbacks[102] = (delegate* unmanaged)&_getArgClass; + callbacks[103] = (delegate* unmanaged)&_getHFAType; + callbacks[104] = (delegate* unmanaged)&_GetErrorHRESULT; + callbacks[105] = (delegate* unmanaged)&_GetErrorMessage; + callbacks[106] = (delegate* unmanaged)&_FilterException; + callbacks[107] = (delegate* unmanaged)&_ThrowExceptionForJitResult; + callbacks[108] = (delegate* unmanaged)&_ThrowExceptionForHelper; + callbacks[109] = (delegate* unmanaged)&_runWithErrorTrap; + callbacks[110] = (delegate* unmanaged)&_runWithSPMIErrorTrap; + callbacks[111] = (delegate* unmanaged)&_getEEInfo; + callbacks[112] = (delegate* unmanaged)&_getJitTimeLogFilename; + callbacks[113] = (delegate* unmanaged)&_getMethodDefFromMethod; + callbacks[114] = (delegate* unmanaged)&_getMethodName; + callbacks[115] = (delegate* unmanaged)&_getMethodNameFromMetadata; + callbacks[116] = (delegate* unmanaged)&_getMethodHash; + callbacks[117] = (delegate* unmanaged)&_findNameOfToken; + callbacks[118] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; + callbacks[119] = (delegate* unmanaged)&_getLoongArch64PassStructInRegisterFlags; + callbacks[120] = (delegate* unmanaged)&_getThreadTLSIndex; + callbacks[121] = (delegate* unmanaged)&_getInlinedCallFrameVptr; + callbacks[122] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; + callbacks[123] = (delegate* unmanaged)&_getHelperFtn; + callbacks[124] = (delegate* unmanaged)&_getFunctionEntryPoint; + callbacks[125] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; + callbacks[126] = (delegate* unmanaged)&_getMethodSync; + callbacks[127] = (delegate* unmanaged)&_getLazyStringLiteralHelper; + callbacks[128] = (delegate* unmanaged)&_embedModuleHandle; + callbacks[129] = (delegate* unmanaged)&_embedClassHandle; + callbacks[130] = (delegate* unmanaged)&_embedMethodHandle; + callbacks[131] = (delegate* unmanaged)&_embedFieldHandle; + callbacks[132] = (delegate* unmanaged)&_embedGenericHandle; + callbacks[133] = (delegate* unmanaged)&_getLocationOfThisType; + callbacks[134] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; + callbacks[135] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; + callbacks[136] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; + callbacks[137] = (delegate* unmanaged)&_getJustMyCodeHandle; + callbacks[138] = (delegate* unmanaged)&_GetProfilingHandle; + callbacks[139] = (delegate* unmanaged)&_getCallInfo; + callbacks[140] = (delegate* unmanaged)&_canAccessFamily; + callbacks[141] = (delegate* unmanaged)&_isRIDClassDomainID; + callbacks[142] = (delegate* unmanaged)&_getClassDomainID; + callbacks[143] = (delegate* unmanaged)&_getFieldAddress; + callbacks[144] = (delegate* unmanaged)&_getStaticFieldCurrentClass; + callbacks[145] = (delegate* unmanaged)&_getVarArgsHandle; + callbacks[146] = (delegate* unmanaged)&_canGetVarArgsHandle; + callbacks[147] = (delegate* unmanaged)&_constructStringLiteral; + callbacks[148] = (delegate* unmanaged)&_emptyStringLiteral; + callbacks[149] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; + callbacks[150] = (delegate* unmanaged)&_addActiveDependency; + callbacks[151] = (delegate* unmanaged)&_GetDelegateCtor; + callbacks[152] = (delegate* unmanaged)&_MethodCompileComplete; + callbacks[153] = (delegate* unmanaged)&_getTailCallHelpers; + callbacks[154] = (delegate* unmanaged)&_convertPInvokeCalliToCall; + callbacks[155] = (delegate* unmanaged)&_notifyInstructionSetUsage; + callbacks[156] = (delegate* unmanaged)&_updateEntryPointForTailCall; + callbacks[157] = (delegate* unmanaged)&_allocMem; + callbacks[158] = (delegate* unmanaged)&_reserveUnwindInfo; + callbacks[159] = (delegate* unmanaged)&_allocUnwindInfo; + callbacks[160] = (delegate* unmanaged)&_allocGCInfo; + callbacks[161] = (delegate* unmanaged)&_setEHcount; + callbacks[162] = (delegate* unmanaged)&_setEHinfo; + callbacks[163] = (delegate* unmanaged)&_logMsg; + callbacks[164] = (delegate* unmanaged)&_doAssert; + callbacks[165] = (delegate* unmanaged)&_reportFatalError; + callbacks[166] = (delegate* unmanaged)&_getPgoInstrumentationResults; + callbacks[167] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; + callbacks[168] = (delegate* unmanaged)&_recordCallSite; + callbacks[169] = (delegate* unmanaged)&_recordRelocation; + callbacks[170] = (delegate* unmanaged)&_getRelocTypeHint; + callbacks[171] = (delegate* unmanaged)&_getExpectedTargetArchitecture; + callbacks[172] = (delegate* unmanaged)&_getJitFlags; + callbacks[173] = (delegate* unmanaged)&_doesFieldBelongToClass; return (IntPtr)callbacks; } From 10ed0692a63c1513b50b55db8759ed98fbbe7d35 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 25 Feb 2022 13:27:51 +0300 Subject: [PATCH 04/30] fix merge conflicts --- src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h index ec114a25904a1..99ebd96f86b17 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h @@ -1108,8 +1108,8 @@ enum mcPackets Packet_GetModuleAssembly = 190, Packet_GetAssemblyName = 191, Packet_IsIntrinsic = 192, - Packet_UpdateEntryPointForTailCall = 193 - Packet_GetLoongArch64PassStructInRegisterFlags = 194, + Packet_UpdateEntryPointForTailCall = 193, + Packet_GetLoongArch64PassStructInRegisterFlags = 194 }; void SetDebugDumpVariables(); From 84b0b50a988f2759dd6da1dff835daf3a5f6b4b1 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 25 Feb 2022 13:37:00 +0300 Subject: [PATCH 05/30] fix merge conflicts --- src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h index 99ebd96f86b17..653d5095a06f2 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h @@ -1054,7 +1054,6 @@ enum mcPackets Packet_IsFieldStatic = 137, PacketCR_AssertLog = 138, Packet_GetArgClass = 139, - Packet_GetExactClasses = 194, Packet_GetArgType = 140, //Retired5 = 141, Packet_CheckMethodModifier = 142, @@ -1109,7 +1108,8 @@ enum mcPackets Packet_GetAssemblyName = 191, Packet_IsIntrinsic = 192, Packet_UpdateEntryPointForTailCall = 193, - Packet_GetLoongArch64PassStructInRegisterFlags = 194 + Packet_GetLoongArch64PassStructInRegisterFlags = 194, + Packet_GetExactClasses = 195, }; void SetDebugDumpVariables(); From ee679131e323f6fb3a9c430dc0d13f849d90c6de Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 25 Feb 2022 13:43:53 +0300 Subject: [PATCH 06/30] fix merge conflicts --- src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index bd03ed79177cc..66bba14bdaa97 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -2941,8 +2941,6 @@ private uint getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_STRUCT_* cls) throw new NotImplementedException("For LoongArch64, would be implemented later"); } - private CORINFO_CLASS_STRUCT_* getArgClass(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_STRUCT_* args) - private int getExactClasses(CORINFO_CLASS_STRUCT_* baseType, int maxExactClasses, CORINFO_CLASS_STRUCT_** exactClsRet) { #if !READYTORUN From 07f053e1cc27243c1d038fab2d95bac5e5147786 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 25 Feb 2022 19:16:41 +0300 Subject: [PATCH 07/30] Implement GetImplementingClasses --- src/coreclr/jit/importer.cpp | 4 +- .../Compiler/DevirtualizationManager.cs | 2 + .../tools/Common/JitInterface/CorInfoImpl.cs | 36 ++++++++++---- .../Compiler/Compilation.cs | 1 + .../ILCompiler.Compiler/Compiler/ILScanner.cs | 47 +++++++++++++++++-- 5 files changed, 76 insertions(+), 14 deletions(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 93875b6826dd1..c54fc978788c8 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -21635,7 +21635,7 @@ void Compiler::considerGuardedDevirtualization( JITDUMP("Considering guarded devirtualization at IL offset %u (0x%x)\n", ilOffset, ilOffset); - const int maxExactClasses = 1; + const int maxExactClasses = 1; CORINFO_CLASS_HANDLE exactClasses[maxExactClasses] = {0}; int exactClassesCount = info.compCompHnd->getExactClasses(baseClass, maxExactClasses, exactClasses); @@ -21675,7 +21675,7 @@ void Compiler::considerGuardedDevirtualization( // CLRRandom* const random = impInlineRoot()->m_inlineStrategy->GetRandom(JitConfig.JitRandomGuardedDevirtualization()); - likelyClasses[0].clsHandle = getRandomClass(fgPgoSchema, fgPgoSchemaCount, fgPgoData, ilOffset, random); + likelyClasses[0].clsHandle = getRandomClass(fgPgoSchema, fgPgoSchemaCount, fgPgoData, ilOffset, random); likelyClasses[0].likelihood = 100; if (likelyClasses[0].clsHandle != NO_CLASS_HANDLE) { diff --git a/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs b/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs index f7bd609553fc8..7fb2b1e2307a5 100644 --- a/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs +++ b/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs @@ -212,5 +212,7 @@ protected virtual MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefType /// public virtual bool CanConstructType(TypeDesc type) => true; #endif + + public virtual TypeDesc[] GetImplementingClasses(TypeDesc type) => null; } } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 66bba14bdaa97..7d365b990ee89 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -2945,25 +2945,43 @@ private int getExactClasses(CORINFO_CLASS_STRUCT_* baseType, int maxExactClasses { #if !READYTORUN MetadataType type = HandleToObject(baseType) as MetadataType; - if (type == null) + { return 0; - - // Give up on shared types - if (type.IsCanonicalSubtype(CanonicalFormKind.Any) || type.HasVariance || type.IsArray) - return 0; + } // type is already sealed, return it - if (_compilation.IsEffectivelySealed(type)) + if (!type.IsInterface && _compilation.IsEffectivelySealed(type)) { *exactClsRet = baseType; return 1; } - // TODO: find all implementations/subclasses - // the number of them must be <= maxExactClasses -#endif + TypeDesc[] implClasses = _compilation.DevirtualizationManager?.GetImplementingClasses(type); + if (implClasses == null || implClasses.Length > maxExactClasses) + { + return 0; + } + + int index = 0; + foreach (TypeDesc implClass in implClasses) + { + Debug.Assert(!implClass.IsInterface); + + if (implClass.IsCanonicalSubtype(CanonicalFormKind.Universal) || implClass.HasVariance || + implClass.IsArray) + { + // Give up if we see shared types among implementations + return 0; + } + exactClsRet[index++] = ObjectToHandle(implClass); + } + + Debug.Assert(index <= maxExactClasses); + return index; +#else return 0; +#endif } private CORINFO_CLASS_STRUCT_* getArgClass(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_STRUCT_* args) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs index d19b60b104c24..1c7bd61b6b260 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs @@ -34,6 +34,7 @@ public abstract class Compilation : ICompilation public CompilerTypeSystemContext TypeSystemContext => NodeFactory.TypeSystemContext; public Logger Logger => _logger; public PInvokeILProvider PInvokeILProvider { get; } + public DevirtualizationManager DevirtualizationManager => _devirtualizationManager; private readonly TypeGetTypeMethodThunkCache _typeGetTypeMethodThunks; private readonly AssemblyGetExecutingAssemblyMethodThunkCache _assemblyGetExecutingAssemblyMethodThunks; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs index 23a2af0219da7..a8aeeafc40ff5 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs @@ -364,9 +364,10 @@ public override DictionaryLayoutNode GetLayout(TypeSystemEntity methodOrType) private class ScannedDevirtualizationManager : DevirtualizationManager { - private HashSet _constructedTypes = new HashSet(); - private HashSet _unsealedTypes = new HashSet(); - private HashSet _abstractButNonabstractlyOverridenTypes = new HashSet(); + private HashSet _constructedTypes = new(); + private HashSet _unsealedTypes = new(); + private HashSet _abstractButNonabstractlyOverridenTypes = new(); + private Dictionary> _interfaceImplementators = new(); public ScannedDevirtualizationManager(ImmutableArray> markedNodes) { @@ -395,12 +396,24 @@ public ScannedDevirtualizationManager(ImmutableArray implList; + if (!_interfaceImplementators.TryGetValue(type, out implList)) + { + implList = new(); + _interfaceImplementators[type] = implList; + } + implList.Add(implType); + } + public override bool IsEffectivelySealed(TypeDesc type) { // If we know we scanned a type that derives from this one, this for sure can't be reported as sealed. @@ -463,6 +489,21 @@ protected override MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefTyp } public override bool CanConstructType(TypeDesc type) => _constructedTypes.Contains(type); + + public override TypeDesc[] GetImplementingClasses(TypeDesc type) + { + if (type.IsInterface && _interfaceImplementators.TryGetValue(type, out HashSet implementations)) + { + var types = new TypeDesc[implementations.Count]; + int index = 0; + foreach (TypeDesc implementation in implementations) + { + types[index++] = implementation; + } + return types; + } + return null; + } } private class ScannedInliningPolicy : IInliningPolicy From ec97ddbf9fc9243add4f46cc4ff31ba86fb2a7e3 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 25 Feb 2022 23:32:17 +0300 Subject: [PATCH 08/30] Make checks more conservative --- src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 7d365b990ee89..556d56eeaa9db 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -2945,7 +2945,9 @@ private int getExactClasses(CORINFO_CLASS_STRUCT_* baseType, int maxExactClasses { #if !READYTORUN MetadataType type = HandleToObject(baseType) as MetadataType; - if (type == null) + + if (type == null || type.IsCanonicalSubtype(CanonicalFormKind.Any) || + type.HasVariance || type.IsArray || type.IsNullable) { return 0; } @@ -2968,8 +2970,8 @@ private int getExactClasses(CORINFO_CLASS_STRUCT_* baseType, int maxExactClasses { Debug.Assert(!implClass.IsInterface); - if (implClass.IsCanonicalSubtype(CanonicalFormKind.Universal) || implClass.HasVariance || - implClass.IsArray) + if (implClass.IsCanonicalSubtype(CanonicalFormKind.Any) || + implClass.HasVariance || implClass.IsArray || implClass.IsNullable) { // Give up if we see shared types among implementations return 0; From 0685c0751ef9b65f054e4b5305832b5d2f087d7a Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 26 Feb 2022 02:25:59 +0300 Subject: [PATCH 09/30] Another attempt --- .../tools/Common/JitInterface/CorInfoImpl.cs | 20 ++++++++++++------- .../Compiler/Compilation.cs | 6 +++++- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 556d56eeaa9db..36a6ee27a88f1 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -2945,21 +2945,25 @@ private int getExactClasses(CORINFO_CLASS_STRUCT_* baseType, int maxExactClasses { #if !READYTORUN MetadataType type = HandleToObject(baseType) as MetadataType; - - if (type == null || type.IsCanonicalSubtype(CanonicalFormKind.Any) || - type.HasVariance || type.IsArray || type.IsNullable) + if (type == null) { return 0; } // type is already sealed, return it - if (!type.IsInterface && _compilation.IsEffectivelySealed(type)) + if (_compilation.IsEffectivelySealed(type)) { *exactClsRet = baseType; return 1; } - TypeDesc[] implClasses = _compilation.DevirtualizationManager?.GetImplementingClasses(type); + if (!type.IsInterface) + { + // TODO: handle classes + return 0; + } + + TypeDesc[] implClasses = _compilation.GetImplementingClasses(type); if (implClasses == null || implClasses.Length > maxExactClasses) { return 0; @@ -2970,8 +2974,10 @@ private int getExactClasses(CORINFO_CLASS_STRUCT_* baseType, int maxExactClasses { Debug.Assert(!implClass.IsInterface); - if (implClass.IsCanonicalSubtype(CanonicalFormKind.Any) || - implClass.HasVariance || implClass.IsArray || implClass.IsNullable) + if (implClass.IsCanonicalDefinitionType(CanonicalFormKind.Any) || + implClass.IsCanonicalSubtype(CanonicalFormKind.Any) || + implClass.HasVariance || implClass.IsArray || implClass.IsNullable || implClass.IsDelegate || + implClass.IsIDynamicInterfaceCastable || implClass.HasInstantiation) { // Give up if we see shared types among implementations return 0; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs index 1c7bd61b6b260..326206d5c0244 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs @@ -34,7 +34,6 @@ public abstract class Compilation : ICompilation public CompilerTypeSystemContext TypeSystemContext => NodeFactory.TypeSystemContext; public Logger Logger => _logger; public PInvokeILProvider PInvokeILProvider { get; } - public DevirtualizationManager DevirtualizationManager => _devirtualizationManager; private readonly TypeGetTypeMethodThunkCache _typeGetTypeMethodThunks; private readonly AssemblyGetExecutingAssemblyMethodThunkCache _assemblyGetExecutingAssemblyMethodThunks; @@ -223,6 +222,11 @@ public bool IsEffectivelySealed(TypeDesc type) return _devirtualizationManager.IsEffectivelySealed(type); } + public TypeDesc[] GetImplementingClasses(TypeDesc type) + { + return _devirtualizationManager.GetImplementingClasses(type); + } + public bool IsEffectivelySealed(MethodDesc method) { return _devirtualizationManager.IsEffectivelySealed(method); From cda167d5de3e6276fca6801549fef6ecffb795b8 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Mon, 18 Jul 2022 01:22:01 +0200 Subject: [PATCH 10/30] update guid --- src/coreclr/inc/jiteeversionguid.h | 10 +- .../tools/Common/JitInterface/CorInfoBase.cs | 160 ++++++++++-------- src/coreclr/vm/jitinterface.cpp | 4 +- 3 files changed, 94 insertions(+), 80 deletions(-) diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 2a9fc7d7b044b..a89deaa2a481f 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID; #define GUID_DEFINED #endif // !GUID_DEFINED -constexpr GUID JITEEVersionIdentifier = { /* 0d853657-7a01-421f-b1b0-d22a8e691441 */ - 0x0d853657, - 0x7a01, - 0x421f, - {0xb1, 0xb0, 0xd2, 0x2a, 0x8e, 0x69, 0x14, 0x41} +constexpr GUID JITEEVersionIdentifier = { /* 6d268f60-6562-4bb1-9ebc-79b6ae8f88b8 */ + 0x6d268f60, + 0x6562, + 0x4bb1, + {0x9e, 0xbc, 0x79, 0xb6, 0xae, 0x8f, 0x88, 0xb8} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs b/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs index 036a2497e9953..541c0dc424c55 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs @@ -1533,6 +1533,21 @@ static CorInfoTypeWithMod _getArgType(IntPtr thisHandle, IntPtr* ppException, CO } } + [UnmanagedCallersOnly] + static int _getExactClasses(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* baseType, int maxExactClasses, CORINFO_CLASS_STRUCT_** exactClsRet) + { + var _this = GetThis(thisHandle); + try + { + return _this.getExactClasses(baseType, maxExactClasses, exactClsRet); + } + catch (Exception ex) + { + *ppException = _this.AllocException(ex); + return default; + } + } + [UnmanagedCallersOnly] static CORINFO_CLASS_STRUCT_* _getArgClass(IntPtr thisHandle, IntPtr* ppException, CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_STRUCT_* args) { @@ -2580,7 +2595,7 @@ static uint _getJitFlags(IntPtr thisHandle, IntPtr* ppException, CORJIT_FLAGS* f static IntPtr GetUnmanagedCallbacks() { - void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 174); + void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 175); callbacks[0] = (delegate* unmanaged)&_isIntrinsic; callbacks[1] = (delegate* unmanaged)&_getMethodAttribs; @@ -2685,77 +2700,78 @@ static IntPtr GetUnmanagedCallbacks() callbacks[100] = (delegate* unmanaged)&_freeArray; callbacks[101] = (delegate* unmanaged)&_getArgNext; callbacks[102] = (delegate* unmanaged)&_getArgType; - callbacks[103] = (delegate* unmanaged)&_getArgClass; - callbacks[104] = (delegate* unmanaged)&_getHFAType; - callbacks[105] = (delegate* unmanaged)&_GetErrorHRESULT; - callbacks[106] = (delegate* unmanaged)&_GetErrorMessage; - callbacks[107] = (delegate* unmanaged)&_FilterException; - callbacks[108] = (delegate* unmanaged)&_ThrowExceptionForJitResult; - callbacks[109] = (delegate* unmanaged)&_ThrowExceptionForHelper; - callbacks[110] = (delegate* unmanaged)&_runWithErrorTrap; - callbacks[111] = (delegate* unmanaged)&_runWithSPMIErrorTrap; - callbacks[112] = (delegate* unmanaged)&_getEEInfo; - callbacks[113] = (delegate* unmanaged)&_getJitTimeLogFilename; - callbacks[114] = (delegate* unmanaged)&_getMethodDefFromMethod; - callbacks[115] = (delegate* unmanaged)&_getMethodName; - callbacks[116] = (delegate* unmanaged)&_getMethodNameFromMetadata; - callbacks[117] = (delegate* unmanaged)&_getMethodHash; - callbacks[118] = (delegate* unmanaged)&_findNameOfToken; - callbacks[119] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; - callbacks[120] = (delegate* unmanaged)&_getLoongArch64PassStructInRegisterFlags; - callbacks[121] = (delegate* unmanaged)&_getThreadTLSIndex; - callbacks[122] = (delegate* unmanaged)&_getInlinedCallFrameVptr; - callbacks[123] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; - callbacks[124] = (delegate* unmanaged)&_getHelperFtn; - callbacks[125] = (delegate* unmanaged)&_getFunctionEntryPoint; - callbacks[126] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; - callbacks[127] = (delegate* unmanaged)&_getMethodSync; - callbacks[128] = (delegate* unmanaged)&_getLazyStringLiteralHelper; - callbacks[129] = (delegate* unmanaged)&_embedModuleHandle; - callbacks[130] = (delegate* unmanaged)&_embedClassHandle; - callbacks[131] = (delegate* unmanaged)&_embedMethodHandle; - callbacks[132] = (delegate* unmanaged)&_embedFieldHandle; - callbacks[133] = (delegate* unmanaged)&_embedGenericHandle; - callbacks[134] = (delegate* unmanaged)&_getLocationOfThisType; - callbacks[135] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; - callbacks[136] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; - callbacks[137] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; - callbacks[138] = (delegate* unmanaged)&_getJustMyCodeHandle; - callbacks[139] = (delegate* unmanaged)&_GetProfilingHandle; - callbacks[140] = (delegate* unmanaged)&_getCallInfo; - callbacks[141] = (delegate* unmanaged)&_canAccessFamily; - callbacks[142] = (delegate* unmanaged)&_isRIDClassDomainID; - callbacks[143] = (delegate* unmanaged)&_getClassDomainID; - callbacks[144] = (delegate* unmanaged)&_getFieldAddress; - callbacks[145] = (delegate* unmanaged)&_getStaticFieldCurrentClass; - callbacks[146] = (delegate* unmanaged)&_getVarArgsHandle; - callbacks[147] = (delegate* unmanaged)&_canGetVarArgsHandle; - callbacks[148] = (delegate* unmanaged)&_constructStringLiteral; - callbacks[149] = (delegate* unmanaged)&_emptyStringLiteral; - callbacks[150] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; - callbacks[151] = (delegate* unmanaged)&_addActiveDependency; - callbacks[152] = (delegate* unmanaged)&_GetDelegateCtor; - callbacks[153] = (delegate* unmanaged)&_MethodCompileComplete; - callbacks[154] = (delegate* unmanaged)&_getTailCallHelpers; - callbacks[155] = (delegate* unmanaged)&_convertPInvokeCalliToCall; - callbacks[156] = (delegate* unmanaged)&_notifyInstructionSetUsage; - callbacks[157] = (delegate* unmanaged)&_updateEntryPointForTailCall; - callbacks[158] = (delegate* unmanaged)&_allocMem; - callbacks[159] = (delegate* unmanaged)&_reserveUnwindInfo; - callbacks[160] = (delegate* unmanaged)&_allocUnwindInfo; - callbacks[161] = (delegate* unmanaged)&_allocGCInfo; - callbacks[162] = (delegate* unmanaged)&_setEHcount; - callbacks[163] = (delegate* unmanaged)&_setEHinfo; - callbacks[164] = (delegate* unmanaged)&_logMsg; - callbacks[165] = (delegate* unmanaged)&_doAssert; - callbacks[166] = (delegate* unmanaged)&_reportFatalError; - callbacks[167] = (delegate* unmanaged)&_getPgoInstrumentationResults; - callbacks[168] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; - callbacks[169] = (delegate* unmanaged)&_recordCallSite; - callbacks[170] = (delegate* unmanaged)&_recordRelocation; - callbacks[171] = (delegate* unmanaged)&_getRelocTypeHint; - callbacks[172] = (delegate* unmanaged)&_getExpectedTargetArchitecture; - callbacks[173] = (delegate* unmanaged)&_getJitFlags; + callbacks[103] = (delegate* unmanaged)&_getExactClasses; + callbacks[104] = (delegate* unmanaged)&_getArgClass; + callbacks[105] = (delegate* unmanaged)&_getHFAType; + callbacks[106] = (delegate* unmanaged)&_GetErrorHRESULT; + callbacks[107] = (delegate* unmanaged)&_GetErrorMessage; + callbacks[108] = (delegate* unmanaged)&_FilterException; + callbacks[109] = (delegate* unmanaged)&_ThrowExceptionForJitResult; + callbacks[110] = (delegate* unmanaged)&_ThrowExceptionForHelper; + callbacks[111] = (delegate* unmanaged)&_runWithErrorTrap; + callbacks[112] = (delegate* unmanaged)&_runWithSPMIErrorTrap; + callbacks[113] = (delegate* unmanaged)&_getEEInfo; + callbacks[114] = (delegate* unmanaged)&_getJitTimeLogFilename; + callbacks[115] = (delegate* unmanaged)&_getMethodDefFromMethod; + callbacks[116] = (delegate* unmanaged)&_getMethodName; + callbacks[117] = (delegate* unmanaged)&_getMethodNameFromMetadata; + callbacks[118] = (delegate* unmanaged)&_getMethodHash; + callbacks[119] = (delegate* unmanaged)&_findNameOfToken; + callbacks[120] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; + callbacks[121] = (delegate* unmanaged)&_getLoongArch64PassStructInRegisterFlags; + callbacks[122] = (delegate* unmanaged)&_getThreadTLSIndex; + callbacks[123] = (delegate* unmanaged)&_getInlinedCallFrameVptr; + callbacks[124] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; + callbacks[125] = (delegate* unmanaged)&_getHelperFtn; + callbacks[126] = (delegate* unmanaged)&_getFunctionEntryPoint; + callbacks[127] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; + callbacks[128] = (delegate* unmanaged)&_getMethodSync; + callbacks[129] = (delegate* unmanaged)&_getLazyStringLiteralHelper; + callbacks[130] = (delegate* unmanaged)&_embedModuleHandle; + callbacks[131] = (delegate* unmanaged)&_embedClassHandle; + callbacks[132] = (delegate* unmanaged)&_embedMethodHandle; + callbacks[133] = (delegate* unmanaged)&_embedFieldHandle; + callbacks[134] = (delegate* unmanaged)&_embedGenericHandle; + callbacks[135] = (delegate* unmanaged)&_getLocationOfThisType; + callbacks[136] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; + callbacks[137] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; + callbacks[138] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; + callbacks[139] = (delegate* unmanaged)&_getJustMyCodeHandle; + callbacks[140] = (delegate* unmanaged)&_GetProfilingHandle; + callbacks[141] = (delegate* unmanaged)&_getCallInfo; + callbacks[142] = (delegate* unmanaged)&_canAccessFamily; + callbacks[143] = (delegate* unmanaged)&_isRIDClassDomainID; + callbacks[144] = (delegate* unmanaged)&_getClassDomainID; + callbacks[145] = (delegate* unmanaged)&_getFieldAddress; + callbacks[146] = (delegate* unmanaged)&_getStaticFieldCurrentClass; + callbacks[147] = (delegate* unmanaged)&_getVarArgsHandle; + callbacks[148] = (delegate* unmanaged)&_canGetVarArgsHandle; + callbacks[149] = (delegate* unmanaged)&_constructStringLiteral; + callbacks[150] = (delegate* unmanaged)&_emptyStringLiteral; + callbacks[151] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; + callbacks[152] = (delegate* unmanaged)&_addActiveDependency; + callbacks[153] = (delegate* unmanaged)&_GetDelegateCtor; + callbacks[154] = (delegate* unmanaged)&_MethodCompileComplete; + callbacks[155] = (delegate* unmanaged)&_getTailCallHelpers; + callbacks[156] = (delegate* unmanaged)&_convertPInvokeCalliToCall; + callbacks[157] = (delegate* unmanaged)&_notifyInstructionSetUsage; + callbacks[158] = (delegate* unmanaged)&_updateEntryPointForTailCall; + callbacks[159] = (delegate* unmanaged)&_allocMem; + callbacks[160] = (delegate* unmanaged)&_reserveUnwindInfo; + callbacks[161] = (delegate* unmanaged)&_allocUnwindInfo; + callbacks[162] = (delegate* unmanaged)&_allocGCInfo; + callbacks[163] = (delegate* unmanaged)&_setEHcount; + callbacks[164] = (delegate* unmanaged)&_setEHinfo; + callbacks[165] = (delegate* unmanaged)&_logMsg; + callbacks[166] = (delegate* unmanaged)&_doAssert; + callbacks[167] = (delegate* unmanaged)&_reportFatalError; + callbacks[168] = (delegate* unmanaged)&_getPgoInstrumentationResults; + callbacks[169] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; + callbacks[170] = (delegate* unmanaged)&_recordCallSite; + callbacks[171] = (delegate* unmanaged)&_recordRelocation; + callbacks[172] = (delegate* unmanaged)&_getRelocTypeHint; + callbacks[173] = (delegate* unmanaged)&_getExpectedTargetArchitecture; + callbacks[174] = (delegate* unmanaged)&_getJitFlags; return (IntPtr)callbacks; } diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index b5d9f38bbb4ce..9793debdc452a 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -9512,15 +9512,13 @@ int CEEInfo::getExactClasses ( MODE_ANY; } CONTRACTL_END; - int exactClassesCount; + int exactClassesCount = 0; JIT_TO_EE_TRANSITION(); // This function is currently implemented only on NativeAOT // but can be implemented for CoreCLR as well (e.g. for internal types) - exactClassesCount = 0; - EE_TO_JIT_TRANSITION(); return exactClassesCount; From 0e8adab68693bec682d3abd129821c32b41a78d7 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Tue, 19 Jul 2022 01:00:30 +0200 Subject: [PATCH 11/30] Fix importer logic --- src/coreclr/jit/importer.cpp | 37 ++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 48a500a785135..35d9a9898b2f7 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -22791,19 +22791,39 @@ void Compiler::considerGuardedDevirtualization(GenTreeCall* call, { JITDUMP("Considering guarded devirtualization at IL offset %u (0x%x)\n", ilOffset, ilOffset); + const int maxExactClasses = 1; + CORINFO_CLASS_HANDLE exactClasses[maxExactClasses] = {0}; + int exactClassesCount = 0; + + // This is currently only targets NativeAOT + if (IsTargetAbi(CORINFO_NATIVEAOT_ABI)) + { + // NativeAOT is able to tell us the exact list of classes which implement this baseClass (class/interface) + // so if we have just one (TODO: add multiple guesses) we can omit fallback + exactClassesCount = info.compCompHnd->getExactClasses(baseClass, maxExactClasses, exactClasses); + } + // We currently only get likely class guesses when there is PGO data - // with class profiles. + // with class profiles or we have exact list of classes from VM // - if ((fgPgoClassProfiles == 0) && (fgPgoMethodProfiles == 0)) + if ((fgPgoClassProfiles == 0) && (fgPgoMethodProfiles == 0) && (exactClassesCount == 0)) { JITDUMP("Not guessing for class or method: no GDV profile pgo data, or pgo disabled\n"); return; } - CORINFO_CLASS_HANDLE likelyClass; - CORINFO_METHOD_HANDLE likelyMethod; - unsigned likelihood; - pickGDV(call, ilOffset, isInterface, &likelyClass, &likelyMethod, &likelihood); + CORINFO_CLASS_HANDLE likelyClass = NO_CLASS_HANDLE; + CORINFO_METHOD_HANDLE likelyMethod = NO_METHOD_HANDLE; + unsigned likelihood = 0; + if (exactClassesCount == 0) + { + pickGDV(call, ilOffset, isInterface, &likelyClass, &likelyMethod, &likelihood); + } + else + { + likelyClass = exactClasses[0]; + likelihood = 100; + } if ((likelyClass == NO_CLASS_HANDLE) && (likelyMethod == NO_METHOD_HANDLE)) { @@ -22900,6 +22920,11 @@ void Compiler::considerGuardedDevirtualization(GenTreeCall* call, isInterface ? "interface" : call->IsDelegateInvoke() ? "delegate" : "virtual", eeGetMethodName(likelyMethod, nullptr)); + if (exactClassesCount > 0) + { + call->gtCallMoreFlags |= GTF_CALL_M_GUARDED_DEVIRT_EXACT; + } + // Add this as a potential candidate. // addGuardedDevirtualizationCandidate(call, likelyMethod, likelyClass, likelyMethodAttribs, likelyClassAttribs, From a1617c6148d2412ccf5de08e947bdcb2ef803206 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Tue, 19 Jul 2022 08:43:14 +0200 Subject: [PATCH 12/30] Update importer.cpp --- src/coreclr/jit/importer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 35d9a9898b2f7..6cae2eaf33936 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -22796,7 +22796,7 @@ void Compiler::considerGuardedDevirtualization(GenTreeCall* call, int exactClassesCount = 0; // This is currently only targets NativeAOT - if (IsTargetAbi(CORINFO_NATIVEAOT_ABI)) + if (IsTargetAbi(CORINFO_NATIVEAOT_ABI) && (baseClass != NO_CLASS_HANDLE)) { // NativeAOT is able to tell us the exact list of classes which implement this baseClass (class/interface) // so if we have just one (TODO: add multiple guesses) we can omit fallback From 46f84a5bae23f8c701ed154fef6bae75b9ced9e3 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Thu, 21 Jul 2022 17:19:17 +0200 Subject: [PATCH 13/30] Don't use GDV --- src/coreclr/jit/gentree.h | 6 -- src/coreclr/jit/importer.cpp | 64 +++++++++++---------- src/coreclr/jit/indirectcalltransformer.cpp | 9 --- 3 files changed, 33 insertions(+), 46 deletions(-) diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 98e3187bcb7d0..f81aaa779b819 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -4010,7 +4010,6 @@ enum GenTreeCallFlags : unsigned int GTF_CALL_M_UNBOXED = 0x00080000, // this call was optimized to use the unboxed entry point GTF_CALL_M_GUARDED_DEVIRT = 0x00100000, // this call is a candidate for guarded devirtualization GTF_CALL_M_GUARDED_DEVIRT_CHAIN = 0x00200000, // this call is a candidate for chained guarded devirtualization - GTF_CALL_M_GUARDED_DEVIRT_EXACT = 0x20000000, // this call was transformed by guarded devirtualization for exact classes (no fallback) GTF_CALL_M_GUARDED = 0x00400000, // this call was transformed by guarded devirtualization GTF_CALL_M_ALLOC_SIDE_EFFECTS = 0x00800000, // this is a call to an allocator with side effects GTF_CALL_M_SUPPRESS_GC_TRANSITION = 0x01000000, // suppress the GC transition (i.e. during a pinvoke) but a separate GC safe point is required. @@ -5285,11 +5284,6 @@ struct GenTreeCall final : public GenTree return (gtCallMoreFlags & GTF_CALL_M_GUARDED_DEVIRT) != 0; } - bool IsGuardedDevirtualizationCandidateForExactClasses() const - { - return (gtCallMoreFlags & GTF_CALL_M_GUARDED_DEVIRT_EXACT) != 0; - } - bool IsPure(Compiler* compiler) const; bool HasSideEffects(Compiler* compiler, bool ignoreExceptions = false, bool ignoreCctors = false) const; diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 904f42d57c297..b06338f45abad 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -21691,6 +21691,33 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, bool objIsNonNull = false; CORINFO_CLASS_HANDLE objClass = gtGetClassHandle(thisObj, &isExact, &objIsNonNull); + if (!isExact && IsTargetAbi(CORINFO_NATIVEAOT_ABI)) + { + // Ask VM if it knows the exact type behind this class/interface (currently only works for NativeAOT) + + const int maxExactClasses = 1; + CORINFO_CLASS_HANDLE exactClasses[maxExactClasses]; + int exactClassesCount = 0; + + if ((baseClass != NO_CLASS_HANDLE)) + { + exactClassesCount = info.compCompHnd->getExactClasses(baseClass, maxExactClasses, exactClasses); + if (exactClassesCount == 1) + { + objClass = exactClasses[0]; + isExact = true; + } + // TODO: Enable GDV for exact classes without fallbacks, e.g. objClass is IDisposable + // and vm returns just two exact clases: ClassA and ClassB and so we can devirtualize it as follows: + // + // IDisposable d = ... + // if (d is ClassA) + // ((ClassA)d).Dispose() + // else + // ((ClassB)d).Dispose() + } + } + // Bail if we know nothing. if (objClass == NO_CLASS_HANDLE) { @@ -22791,39 +22818,19 @@ void Compiler::considerGuardedDevirtualization(GenTreeCall* call, { JITDUMP("Considering guarded devirtualization at IL offset %u (0x%x)\n", ilOffset, ilOffset); - const int maxExactClasses = 1; - CORINFO_CLASS_HANDLE exactClasses[maxExactClasses] = {0}; - int exactClassesCount = 0; - - // This is currently only targets NativeAOT - if (IsTargetAbi(CORINFO_NATIVEAOT_ABI) && (baseClass != NO_CLASS_HANDLE)) - { - // NativeAOT is able to tell us the exact list of classes which implement this baseClass (class/interface) - // so if we have just one (TODO: add multiple guesses) we can omit fallback - exactClassesCount = info.compCompHnd->getExactClasses(baseClass, maxExactClasses, exactClasses); - } - // We currently only get likely class guesses when there is PGO data - // with class profiles or we have exact list of classes from VM + // with class profiles. // - if ((fgPgoClassProfiles == 0) && (fgPgoMethodProfiles == 0) && (exactClassesCount == 0)) + if ((fgPgoClassProfiles == 0) && (fgPgoMethodProfiles == 0)) { JITDUMP("Not guessing for class or method: no GDV profile pgo data, or pgo disabled\n"); return; } - CORINFO_CLASS_HANDLE likelyClass = NO_CLASS_HANDLE; - CORINFO_METHOD_HANDLE likelyMethod = NO_METHOD_HANDLE; - unsigned likelihood = 0; - if (exactClassesCount == 0) - { - pickGDV(call, ilOffset, isInterface, &likelyClass, &likelyMethod, &likelihood); - } - else - { - likelyClass = exactClasses[0]; - likelihood = 100; - } + CORINFO_CLASS_HANDLE likelyClass; + CORINFO_METHOD_HANDLE likelyMethod; + unsigned likelihood; + pickGDV(call, ilOffset, isInterface, &likelyClass, &likelyMethod, &likelihood); if ((likelyClass == NO_CLASS_HANDLE) && (likelyMethod == NO_METHOD_HANDLE)) { @@ -22920,11 +22927,6 @@ void Compiler::considerGuardedDevirtualization(GenTreeCall* call, isInterface ? "interface" : call->IsDelegateInvoke() ? "delegate" : "virtual", eeGetMethodName(likelyMethod, nullptr)); - if (exactClassesCount > 0) - { - call->gtCallMoreFlags |= GTF_CALL_M_GUARDED_DEVIRT_EXACT; - } - // Add this as a potential candidate. // addGuardedDevirtualizationCandidate(call, likelyMethod, likelyClass, likelyMethodAttribs, likelyClassAttribs, diff --git a/src/coreclr/jit/indirectcalltransformer.cpp b/src/coreclr/jit/indirectcalltransformer.cpp index ce2bdb951e908..ff85c71670ad1 100644 --- a/src/coreclr/jit/indirectcalltransformer.cpp +++ b/src/coreclr/jit/indirectcalltransformer.cpp @@ -539,14 +539,6 @@ class IndirectCallTransformer // checkBlock = currBlock; - // Create empty check for "Exact Classes" case. In future, it's going to support - // multiple exact classes with help of https://github.com/dotnet/runtime/pull/59604 - if (origCall->IsGuardedDevirtualizationCandidateForExactClasses()) - { - checkBlock->bbJumpKind = BBJ_NONE; - return; - } - checkBlock->bbJumpKind = BBJ_COND; CallArg* thisArg = origCall->gtArgs.GetThisArg(); @@ -1147,7 +1139,6 @@ class IndirectCallTransformer GenTreeCall* const call = root->AsCall(); if (call->IsGuardedDevirtualizationCandidate() && - !call->IsGuardedDevirtualizationCandidateForExactClasses() && // no need for chaining for now (call->gtGuardedDevirtualizationCandidateInfo->likelihood >= gdvChainLikelihood)) { JITDUMP("GDV call at [%06u] has likelihood %u >= %u; chaining (%u stmts, %u nodes to dup).\n", From f87300fefd07220b6db5b69be73d5267b39c6de5 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Thu, 21 Jul 2022 17:20:54 +0200 Subject: [PATCH 14/30] Clean up --- src/coreclr/jit/indirectcalltransformer.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/coreclr/jit/indirectcalltransformer.cpp b/src/coreclr/jit/indirectcalltransformer.cpp index ff85c71670ad1..e3d799f734b8b 100644 --- a/src/coreclr/jit/indirectcalltransformer.cpp +++ b/src/coreclr/jit/indirectcalltransformer.cpp @@ -537,8 +537,7 @@ class IndirectCallTransformer { // There's no need for a new block here. We can just append to currBlock. // - checkBlock = currBlock; - + checkBlock = currBlock; checkBlock->bbJumpKind = BBJ_COND; CallArg* thisArg = origCall->gtArgs.GetThisArg(); From cd4aa36dc73fea17ae7d8eebe43f712d9b7c3084 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Thu, 21 Jul 2022 17:49:30 +0200 Subject: [PATCH 15/30] Refactoring --- src/coreclr/jit/importer.cpp | 53 ++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index b06338f45abad..b7ec337fc1256 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -21691,22 +21691,49 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, bool objIsNonNull = false; CORINFO_CLASS_HANDLE objClass = gtGetClassHandle(thisObj, &isExact, &objIsNonNull); - if (!isExact && IsTargetAbi(CORINFO_NATIVEAOT_ABI)) + // If the objClass is sealed (final), then we may be able to devirtualize. + DWORD objClassAttribs = 0; + bool objClassIsFinal = false; + if (objClass != NO_CLASS_HANDLE) { - // Ask VM if it knows the exact type behind this class/interface (currently only works for NativeAOT) + objClassAttribs = info.compCompHnd->getClassAttribs(objClass); + objClassIsFinal = (objClassAttribs & CORINFO_FLG_FINAL) != 0; + } - const int maxExactClasses = 1; - CORINFO_CLASS_HANDLE exactClasses[maxExactClasses]; - int exactClassesCount = 0; + if (!isExact && !objClassIsFinal && IsTargetAbi(CORINFO_NATIVEAOT_ABI)) + { + // Ask VM if it knows the exact type behind this class/interface (currently only works for NativeAOT) + auto getUniqueImpl = [](COMP_HANDLE comp, CORINFO_CLASS_HANDLE cls) -> CORINFO_CLASS_HANDLE { + if (cls == NO_CLASS_HANDLE) + { + return NO_CLASS_HANDLE; + } - if ((baseClass != NO_CLASS_HANDLE)) - { - exactClassesCount = info.compCompHnd->getExactClasses(baseClass, maxExactClasses, exactClasses); - if (exactClassesCount == 1) + const int maxExactClasses = 1; + CORINFO_CLASS_HANDLE exactClasses[maxExactClasses]; + int exactClassesCount = 0; + exactClassesCount = comp->getExactClasses(cls, maxExactClasses, exactClasses); + if ((exactClassesCount == 1) && (comp->compareTypesForCast(exactClasses[0], cls) == TypeCompareState::Must)) { - objClass = exactClasses[0]; - isExact = true; + return exactClasses[0]; } + return NO_CLASS_HANDLE; + }; + + CORINFO_CLASS_HANDLE baseForUnique = objClass; + CORINFO_CLASS_HANDLE uniqueImpl = getUniqueImpl(info.compCompHnd, objClass); + if (uniqueImpl == NO_CLASS_HANDLE) + { + baseForUnique = baseClass; + uniqueImpl = getUniqueImpl(info.compCompHnd, baseClass); + } + + if (uniqueImpl != NO_CLASS_HANDLE) + { + objClass = uniqueImpl; + isExact = true; + JITDUMP("Devirtualizeing '%s' as '%s' via getExactClasses\n", eeGetClassName(baseForUnique), + eeGetClassName(uniqueImpl)); // TODO: Enable GDV for exact classes without fallbacks, e.g. objClass is IDisposable // and vm returns just two exact clases: ClassA and ClassB and so we can devirtualize it as follows: // @@ -21736,10 +21763,6 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, return; } - // If the objClass is sealed (final), then we may be able to devirtualize. - const DWORD objClassAttribs = info.compCompHnd->getClassAttribs(objClass); - const bool objClassIsFinal = (objClassAttribs & CORINFO_FLG_FINAL) != 0; - #if defined(DEBUG) const char* callKind = isInterface ? "interface" : "virtual"; const char* objClassNote = "[?]"; From d9721c035d96b6b64cbec22087a904871a4ffb40 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Thu, 21 Jul 2022 17:50:54 +0200 Subject: [PATCH 16/30] Clean up --- src/coreclr/jit/importer.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index b7ec337fc1256..ec4901ca5e230 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -21691,7 +21691,6 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, bool objIsNonNull = false; CORINFO_CLASS_HANDLE objClass = gtGetClassHandle(thisObj, &isExact, &objIsNonNull); - // If the objClass is sealed (final), then we may be able to devirtualize. DWORD objClassAttribs = 0; bool objClassIsFinal = false; if (objClass != NO_CLASS_HANDLE) From b4923736c19814b49806a3fe60d32658735663ac Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Thu, 21 Jul 2022 22:47:43 +0200 Subject: [PATCH 17/30] Update importer.cpp --- src/coreclr/jit/importer.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index ec4901ca5e230..73aac9a9fbd83 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -21719,20 +21719,18 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, return NO_CLASS_HANDLE; }; - CORINFO_CLASS_HANDLE baseForUnique = objClass; - CORINFO_CLASS_HANDLE uniqueImpl = getUniqueImpl(info.compCompHnd, objClass); - if (uniqueImpl == NO_CLASS_HANDLE) + CORINFO_CLASS_HANDLE baseForUnique = baseClass; + CORINFO_CLASS_HANDLE uniqueImpl = getUniqueImpl(info.compCompHnd, baseForUnique); + /*if (uniqueImpl == NO_CLASS_HANDLE) { baseForUnique = baseClass; - uniqueImpl = getUniqueImpl(info.compCompHnd, baseClass); - } + uniqueImpl = getUniqueImpl(info.compCompHnd, baseForUnique); + }*/ if (uniqueImpl != NO_CLASS_HANDLE) { objClass = uniqueImpl; isExact = true; - JITDUMP("Devirtualizeing '%s' as '%s' via getExactClasses\n", eeGetClassName(baseForUnique), - eeGetClassName(uniqueImpl)); // TODO: Enable GDV for exact classes without fallbacks, e.g. objClass is IDisposable // and vm returns just two exact clases: ClassA and ClassB and so we can devirtualize it as follows: // From d83f514d57ed578b14a463b553513a18be3d4960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 22 Jul 2022 17:39:19 +0900 Subject: [PATCH 18/30] Make things more conservative --- .../ILCompiler.Compiler/Compiler/ILScanner.cs | 42 +++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs index 9ec3cf5664759..0b89089d8b6a0 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs @@ -396,10 +396,46 @@ public ScannedDevirtualizationManager(ImmutableArray : IFooable { } + // class Doer : IFooable { } + // And we instantiated Fooer, but not Doer. But we do have code for Doer<__Canon>. + // We might think we can devirtualize IFooable to Fooer, but someone could + // typeof(Doer<>).MakeGenericType(typeof(string)) and break our whole program view. + // This is only a problem if canonical form of the interface exists. + skip |= baseInterface.ConvertToCanonForm(CanonicalFormKind.Specific) != baseInterface; + + if (skip) + continue; + } + + RecordImplementation(baseInterface, type); + } } bool hasNonAbstractTypeInHierarchy = canonType is not MetadataType mdType || !mdType.IsAbstract; From be28832ca29effe2f80c702afb42017820137ed6 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 22 Jul 2022 12:53:47 +0200 Subject: [PATCH 19/30] Clean up --- src/coreclr/jit/importer.cpp | 54 +++++++++++------------------------- 1 file changed, 16 insertions(+), 38 deletions(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index ec4901ca5e230..32057a689ca40 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -21697,50 +21697,28 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, { objClassAttribs = info.compCompHnd->getClassAttribs(objClass); objClassIsFinal = (objClassAttribs & CORINFO_FLG_FINAL) != 0; - } - - if (!isExact && !objClassIsFinal && IsTargetAbi(CORINFO_NATIVEAOT_ABI)) - { - // Ask VM if it knows the exact type behind this class/interface (currently only works for NativeAOT) - auto getUniqueImpl = [](COMP_HANDLE comp, CORINFO_CLASS_HANDLE cls) -> CORINFO_CLASS_HANDLE { - if (cls == NO_CLASS_HANDLE) - { - return NO_CLASS_HANDLE; - } + if (!isExact && !objClassIsFinal && IsTargetAbi(CORINFO_NATIVEAOT_ABI)) + { const int maxExactClasses = 1; CORINFO_CLASS_HANDLE exactClasses[maxExactClasses]; int exactClassesCount = 0; - exactClassesCount = comp->getExactClasses(cls, maxExactClasses, exactClasses); - if ((exactClassesCount == 1) && (comp->compareTypesForCast(exactClasses[0], cls) == TypeCompareState::Must)) + exactClassesCount = info.compCompHnd->getExactClasses(objClass, maxExactClasses, exactClasses); + if (exactClassesCount == 1) { - return exactClasses[0]; - } - return NO_CLASS_HANDLE; - }; + assert((info.compCompHnd->compareTypesForCast(exactClasses[0], objClass) == TypeCompareState::Must)); + objClass = exactClasses[0]; + isExact = true; - CORINFO_CLASS_HANDLE baseForUnique = objClass; - CORINFO_CLASS_HANDLE uniqueImpl = getUniqueImpl(info.compCompHnd, objClass); - if (uniqueImpl == NO_CLASS_HANDLE) - { - baseForUnique = baseClass; - uniqueImpl = getUniqueImpl(info.compCompHnd, baseClass); - } - - if (uniqueImpl != NO_CLASS_HANDLE) - { - objClass = uniqueImpl; - isExact = true; - JITDUMP("Devirtualizeing '%s' as '%s' via getExactClasses\n", eeGetClassName(baseForUnique), - eeGetClassName(uniqueImpl)); - // TODO: Enable GDV for exact classes without fallbacks, e.g. objClass is IDisposable - // and vm returns just two exact clases: ClassA and ClassB and so we can devirtualize it as follows: - // - // IDisposable d = ... - // if (d is ClassA) - // ((ClassA)d).Dispose() - // else - // ((ClassB)d).Dispose() + // TODO: Enable GDV for exact classes without fallbacks, e.g. objClass is IDisposable + // and vm returns just two exact clases: ClassA and ClassB and so we can devirtualize it as follows: + // + // IDisposable d = ... + // if (d is ClassA) + // ((ClassA)d).Dispose() + // else + // ((ClassB)d).Dispose() + } } } From a73bdef8731b2adcfbb0db070be8831fe56942ed Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 22 Jul 2022 13:11:50 +0200 Subject: [PATCH 20/30] Clean up --- src/coreclr/jit/importer.cpp | 22 ++++++++++++------- .../tools/Common/JitInterface/CorInfoImpl.cs | 10 --------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 32057a689ca40..38af35ca7fb9a 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -21698,17 +21698,23 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, objClassAttribs = info.compCompHnd->getClassAttribs(objClass); objClassIsFinal = (objClassAttribs & CORINFO_FLG_FINAL) != 0; + // Ask VM if it's able to tell us if this class has a unique impl/subclass if (!isExact && !objClassIsFinal && IsTargetAbi(CORINFO_NATIVEAOT_ABI)) { - const int maxExactClasses = 1; - CORINFO_CLASS_HANDLE exactClasses[maxExactClasses]; - int exactClassesCount = 0; - exactClassesCount = info.compCompHnd->getExactClasses(objClass, maxExactClasses, exactClasses); - if (exactClassesCount == 1) + CORINFO_CLASS_HANDLE exactClass; + if (info.compCompHnd->getExactClasses(objClass, 1, &exactClass) == 1) { - assert((info.compCompHnd->compareTypesForCast(exactClasses[0], objClass) == TypeCompareState::Must)); - objClass = exactClasses[0]; - isExact = true; + assert(exactClass != NO_CLASS_HANDLE); + assert((info.compCompHnd->compareTypesForCast(exactClass, objClass) == TypeCompareState::Must)); + + if (objClass != exactClass) + { + // update flags + objClassAttribs = info.compCompHnd->getClassAttribs(exactClass); + objClassIsFinal = (objClassAttribs & CORINFO_FLG_FINAL) != 0; + } + objClass = exactClass; + isExact = true; // TODO: Enable GDV for exact classes without fallbacks, e.g. objClass is IDisposable // and vm returns just two exact clases: ClassA and ClassB and so we can devirtualize it as follows: diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 302a3a915a78b..1f0ba6e03c1b4 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -2948,16 +2948,6 @@ private int getExactClasses(CORINFO_CLASS_STRUCT_* baseType, int maxExactClasses int index = 0; foreach (TypeDesc implClass in implClasses) { - Debug.Assert(!implClass.IsInterface); - - if (implClass.IsCanonicalDefinitionType(CanonicalFormKind.Any) || - implClass.IsCanonicalSubtype(CanonicalFormKind.Any) || - implClass.HasVariance || implClass.IsArray || implClass.IsNullable || implClass.IsDelegate || - implClass.IsIDynamicInterfaceCastable || implClass.HasInstantiation) - { - // Give up if we see shared types among implementations - return 0; - } exactClsRet[index++] = ObjectToHandle(implClass); } From 90b8bfae551ecf2bd799f658971091f1f0930735 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 22 Jul 2022 13:24:03 +0200 Subject: [PATCH 21/30] Clean up --- src/coreclr/jit/gentree.cpp | 11 ++++++++++ src/coreclr/jit/importer.cpp | 41 ++++-------------------------------- src/coreclr/jit/lclvars.cpp | 11 ++++++++++ 3 files changed, 26 insertions(+), 37 deletions(-) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index b15378b8e9b97..9a2b342cfe630 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -17685,6 +17685,17 @@ CORINFO_CLASS_HANDLE Compiler::gtGetClassHandle(GenTree* tree, bool* pIsExact, b } } + if ((objClass != NO_CLASS_HANDLE) && !*pIsExact && IsTargetAbi(CORINFO_NATIVEAOT_ABI)) + { + CORINFO_CLASS_HANDLE exactClass; + if ((info.compCompHnd->getExactClasses(objClass, 1, &exactClass) == 1) && + (info.compCompHnd->compareTypesForCast(exactClass, objClass) == TypeCompareState::Must)) + { + *pIsExact = true; + objClass = exactClass; + } + } + return objClass; } diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 38af35ca7fb9a..2420571e130aa 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -21691,43 +21691,6 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, bool objIsNonNull = false; CORINFO_CLASS_HANDLE objClass = gtGetClassHandle(thisObj, &isExact, &objIsNonNull); - DWORD objClassAttribs = 0; - bool objClassIsFinal = false; - if (objClass != NO_CLASS_HANDLE) - { - objClassAttribs = info.compCompHnd->getClassAttribs(objClass); - objClassIsFinal = (objClassAttribs & CORINFO_FLG_FINAL) != 0; - - // Ask VM if it's able to tell us if this class has a unique impl/subclass - if (!isExact && !objClassIsFinal && IsTargetAbi(CORINFO_NATIVEAOT_ABI)) - { - CORINFO_CLASS_HANDLE exactClass; - if (info.compCompHnd->getExactClasses(objClass, 1, &exactClass) == 1) - { - assert(exactClass != NO_CLASS_HANDLE); - assert((info.compCompHnd->compareTypesForCast(exactClass, objClass) == TypeCompareState::Must)); - - if (objClass != exactClass) - { - // update flags - objClassAttribs = info.compCompHnd->getClassAttribs(exactClass); - objClassIsFinal = (objClassAttribs & CORINFO_FLG_FINAL) != 0; - } - objClass = exactClass; - isExact = true; - - // TODO: Enable GDV for exact classes without fallbacks, e.g. objClass is IDisposable - // and vm returns just two exact clases: ClassA and ClassB and so we can devirtualize it as follows: - // - // IDisposable d = ... - // if (d is ClassA) - // ((ClassA)d).Dispose() - // else - // ((ClassB)d).Dispose() - } - } - } - // Bail if we know nothing. if (objClass == NO_CLASS_HANDLE) { @@ -21746,6 +21709,10 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, return; } + // If the objClass is sealed (final), then we may be able to devirtualize. + const DWORD objClassAttribs = info.compCompHnd->getClassAttribs(objClass); + const bool objClassIsFinal = (objClassAttribs & CORINFO_FLG_FINAL) != 0; + #if defined(DEBUG) const char* callKind = isInterface ? "interface" : "virtual"; const char* objClassNote = "[?]"; diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 53aea41cabd68..469a1c69fd050 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -3193,6 +3193,17 @@ void Compiler::lvaSetClass(unsigned varNum, CORINFO_CLASS_HANDLE clsHnd, bool is return; } + if (clsHnd != NO_CLASS_HANDLE && !isExact && IsTargetAbi(CORINFO_NATIVEAOT_ABI)) + { + CORINFO_CLASS_HANDLE exactClass; + if ((info.compCompHnd->getExactClasses(clsHnd, 1, &exactClass) == 1) && + (info.compCompHnd->compareTypesForCast(exactClass, clsHnd) == TypeCompareState::Must)) + { + isExact = true; + clsHnd = exactClass; + } + } + // Else we should have a type handle. assert(clsHnd != nullptr); From e76b472864541e4cf7a93fd9dd32ebfa261a517e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Mon, 25 Jul 2022 15:01:08 +0900 Subject: [PATCH 22/30] IDynamicInterfaceCastable and template type loader cases --- .../ILCompiler.Compiler/Compiler/ILScanner.cs | 187 +++++++++++------- 1 file changed, 114 insertions(+), 73 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs index 0b89089d8b6a0..c4cc52a5fa5fa 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs @@ -368,100 +368,138 @@ private class ScannedDevirtualizationManager : DevirtualizationManager private HashSet _unsealedTypes = new HashSet(); private HashSet _abstractButNonabstractlyOverriddenTypes = new HashSet(); private Dictionary> _interfaceImplementators = new(); + private HashSet _disqualifiedInterfaces = new(); public ScannedDevirtualizationManager(ImmutableArray> markedNodes) { foreach (var node in markedNodes) { - if (node is ConstructedEETypeNode eetypeNode) + TypeDesc type; + + if (node is CanonicalEETypeNode canonEETypeNode) { - TypeDesc type = eetypeNode.Type; + type = canonEETypeNode.Type; + + foreach (DefType baseInterface in type.RuntimeInterfaces) + { + // If the interface is implemented on a template type, there might be + // no real upper bound on the number of actual classes implementing it + // due to MakeGenericType. + if (CanAssumeWholeProgramViewOnInterfaceUse(baseInterface)) + _disqualifiedInterfaces.Add(baseInterface); + } + } + + if (node is not ConstructedEETypeNode eetypeNode) + continue; + + type = eetypeNode.Type; - if (!type.IsInterface) + if (type.IsInterface) + { + if (((MetadataType)type).IsDynamicInterfaceCastableImplementation()) { - // - // We collect this information: - // - // 1. What types got allocated - // 2. What types are the base types of other types - // This is needed for optimizations. We use this information to effectively - // seal types that are not base types for any other type. - // 3. What abstract types got derived by non-abstract types. - // This is needed for correctness. Abstract types that were never derived - // by non-abstract types should never be devirtualized into - we probably - // didn't scan the virtual methods on them. - // - - _constructedTypes.Add(type); - - TypeDesc canonType = type.ConvertToCanonForm(CanonicalFormKind.Specific); - - if (!type.IsCanonicalSubtype(CanonicalFormKind.Any)) + foreach (DefType baseInterface in type.RuntimeInterfaces) { - // Record all interfaces this class implements to _interfaceImplementators - foreach (DefType baseInterface in type.RuntimeInterfaces) - { - bool skip = false; - Instantiation interfaceInstantiation = baseInterface.Instantiation; - if (!interfaceInstantiation.IsNull) - { - foreach (GenericParameterDesc genericParam in baseInterface.GetTypeDefinition().Instantiation) - { - if (genericParam.Variance != GenericVariance.None) - { - // If the interface has any variance, this gets complicated. - // Skip for now. - skip = true; - break; - } - } - - // Interfaces implemented by arrays also behave covariantly on arrays even though - // they're not actually variant. Skip for now. - skip |= ((CompilerTypeSystemContext)type.Context).IsGenericArrayInterfaceType(baseInterface); - - // If the interface has a canonical form, we might not have a full view of all implementers. - // E.g. if we have: - // class Fooer : IFooable { } - // class Doer : IFooable { } - // And we instantiated Fooer, but not Doer. But we do have code for Doer<__Canon>. - // We might think we can devirtualize IFooable to Fooer, but someone could - // typeof(Doer<>).MakeGenericType(typeof(string)) and break our whole program view. - // This is only a problem if canonical form of the interface exists. - skip |= baseInterface.ConvertToCanonForm(CanonicalFormKind.Specific) != baseInterface; - - if (skip) - continue; - } + // If the interface is implemented through IDynamicInterfaceCastable, there might be + // no real upper bound on the number of actual classes implementing it. + if (CanAssumeWholeProgramViewOnInterfaceUse(baseInterface)) + _disqualifiedInterfaces.Add(baseInterface); + } + } + } + else + { + // + // We collect this information: + // + // 1. What types got allocated + // 2. What types are the base types of other types + // This is needed for optimizations. We use this information to effectively + // seal types that are not base types for any other type. + // 3. What abstract types got derived by non-abstract types. + // This is needed for correctness. Abstract types that were never derived + // by non-abstract types should never be devirtualized into - we probably + // didn't scan the virtual methods on them. + // 4. What types implement interfaces for which use we can assume whole + // program view. + // + + _constructedTypes.Add(type); + if (type is not MetadataType { IsAbstract: true }) + { + // Record all interfaces this class implements to _interfaceImplementators + foreach (DefType baseInterface in type.RuntimeInterfaces) + { + if (CanAssumeWholeProgramViewOnInterfaceUse(baseInterface)) + { RecordImplementation(baseInterface, type); } } + } - bool hasNonAbstractTypeInHierarchy = canonType is not MetadataType mdType || !mdType.IsAbstract; - TypeDesc baseType = canonType.BaseType; - bool added = true; - while (baseType != null && added) - { - baseType = baseType.ConvertToCanonForm(CanonicalFormKind.Specific); + TypeDesc canonType = type.ConvertToCanonForm(CanonicalFormKind.Specific); - Debug.Assert(!baseType.IsInterface); + bool hasNonAbstractTypeInHierarchy = canonType is not MetadataType mdType || !mdType.IsAbstract; + TypeDesc baseType = canonType.BaseType; + bool added = true; + while (baseType != null && added) + { + baseType = baseType.ConvertToCanonForm(CanonicalFormKind.Specific); + added = _unsealedTypes.Add(baseType); - // Record all base types this class subclasses to _interfaceImplementators - RecordImplementation(baseType, canonType); + bool currentTypeIsAbstract = ((MetadataType)baseType).IsAbstract; + if (currentTypeIsAbstract && hasNonAbstractTypeInHierarchy) + added |= _abstractButNonabstractlyOverriddenTypes.Add(baseType); + hasNonAbstractTypeInHierarchy |= !currentTypeIsAbstract; - added = _unsealedTypes.Add(baseType); + baseType = baseType.BaseType; + } + } + } + } - bool currentTypeIsAbstract = ((MetadataType)baseType).IsAbstract; - if (currentTypeIsAbstract && hasNonAbstractTypeInHierarchy) - added |= _abstractButNonabstractlyOverriddenTypes.Add(baseType); - hasNonAbstractTypeInHierarchy |= !currentTypeIsAbstract; + private static bool CanAssumeWholeProgramViewOnInterfaceUse(DefType interfaceType) + { + if (!interfaceType.HasInstantiation) + { + return true; + } - baseType = baseType.BaseType; - } - } + foreach (GenericParameterDesc genericParam in interfaceType.GetTypeDefinition().Instantiation) + { + if (genericParam.Variance != GenericVariance.None) + { + // If the interface has any variance, this gets complicated. + // Skip for now. + return false; } } + + if (((CompilerTypeSystemContext)interfaceType.Context).IsGenericArrayInterfaceType(interfaceType)) + { + // Interfaces implemented by arrays also behave covariantly on arrays even though + // they're not actually variant. Skip for now. + return false; + } + + if (interfaceType.IsCanonicalSubtype(CanonicalFormKind.Any) + || interfaceType.ConvertToCanonForm(CanonicalFormKind.Specific) != interfaceType + || interfaceType.Context.SupportsUniversalCanon) + { + // If the interface has a canonical form, we might not have a full view of all implementers. + // E.g. if we have: + // class Fooer : IFooable { } + // class Doer : IFooable { } + // And we instantiated Fooer, but not Doer. But we do have code for Doer<__Canon>. + // We might think we can devirtualize IFooable to Fooer, but someone could + // typeof(Doer<>).MakeGenericType(typeof(string)) and break our whole program view. + // This is only a problem if canonical form of the interface exists. + return false; + } + + return true; } private void RecordImplementation(TypeDesc type, TypeDesc implType) @@ -528,6 +566,9 @@ protected override MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefTyp public override TypeDesc[] GetImplementingClasses(TypeDesc type) { + if (_disqualifiedInterfaces.Contains(type)) + return null; + if (type.IsInterface && _interfaceImplementators.TryGetValue(type, out HashSet implementations)) { var types = new TypeDesc[implementations.Count]; From c30f1ee84506a4e6ab6313a0f9fde49c01929d3f Mon Sep 17 00:00:00 2001 From: EgorBo Date: Wed, 27 Jul 2022 19:25:59 +0200 Subject: [PATCH 23/30] Address feedback --- src/coreclr/jit/gentree.cpp | 3 +-- src/coreclr/jit/lclvars.cpp | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 9a2b342cfe630..6d076c84868d5 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -17688,8 +17688,7 @@ CORINFO_CLASS_HANDLE Compiler::gtGetClassHandle(GenTree* tree, bool* pIsExact, b if ((objClass != NO_CLASS_HANDLE) && !*pIsExact && IsTargetAbi(CORINFO_NATIVEAOT_ABI)) { CORINFO_CLASS_HANDLE exactClass; - if ((info.compCompHnd->getExactClasses(objClass, 1, &exactClass) == 1) && - (info.compCompHnd->compareTypesForCast(exactClass, objClass) == TypeCompareState::Must)) + if (info.compCompHnd->getExactClasses(objClass, 1, &exactClass) == 1) { *pIsExact = true; objClass = exactClass; diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 469a1c69fd050..51dc52bc134d7 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -3196,8 +3196,7 @@ void Compiler::lvaSetClass(unsigned varNum, CORINFO_CLASS_HANDLE clsHnd, bool is if (clsHnd != NO_CLASS_HANDLE && !isExact && IsTargetAbi(CORINFO_NATIVEAOT_ABI)) { CORINFO_CLASS_HANDLE exactClass; - if ((info.compCompHnd->getExactClasses(clsHnd, 1, &exactClass) == 1) && - (info.compCompHnd->compareTypesForCast(exactClass, clsHnd) == TypeCompareState::Must)) + if (info.compCompHnd->getExactClasses(clsHnd, 1, &exactClass) == 1) { isExact = true; clsHnd = exactClass; From 497b84399ac82604644e28872b75a4fcb4f77fc4 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Thu, 28 Jul 2022 15:23:06 +0200 Subject: [PATCH 24/30] ignore types which require runtime lookup --- src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 1f0ba6e03c1b4..3d608958212c5 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -2948,6 +2948,10 @@ private int getExactClasses(CORINFO_CLASS_STRUCT_* baseType, int maxExactClasses int index = 0; foreach (TypeDesc implClass in implClasses) { + if (implClass.IsRuntimeDeterminedType) + { + return 0; + } exactClsRet[index++] = ObjectToHandle(implClass); } From 755901550af54ea30751cb0a9aefa4734b10e776 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 12 Aug 2022 13:22:20 +0300 Subject: [PATCH 25/30] address feedback --- .../Compiler/DevirtualizationManager.cs | 2 +- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 6 +++ .../JitInterface/CorInfoImpl.RyuJit.cs | 38 +++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs b/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs index f640cffbe794b..2fb34cede261f 100644 --- a/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs +++ b/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs @@ -211,8 +211,8 @@ protected virtual MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefType /// so it can answer this question. /// public virtual bool CanConstructType(TypeDesc type) => true; -#endif public virtual TypeDesc[] GetImplementingClasses(TypeDesc type) => null; +#endif } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 4d07cd8e68610..f5d81d5d2d5cc 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -2969,5 +2969,11 @@ private void updateEntryPointForTailCall(ref CORINFO_CONST_LOOKUP entryPoint) entryPoint = CreateConstLookupToSymbol(newEntryPoint); } + + private int getExactClasses(CORINFO_CLASS_STRUCT_* baseType, int maxExactClasses, CORINFO_CLASS_STRUCT_** exactClsRet) + { + // Not implemented for R2R yet + return 0; + } } } diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index e54366f91906f..b29b104f41d70 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -2154,5 +2154,43 @@ private void getFieldInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_MET // TODO: We need to implement access checks for fields and methods. See JitInterface.cpp in mrtjit // and STS::AccessCheck::CanAccess. } + + private int getExactClasses(CORINFO_CLASS_STRUCT_* baseType, int maxExactClasses, CORINFO_CLASS_STRUCT_** exactClsRet) + { + MetadataType type = HandleToObject(baseType) as MetadataType; + if (type == null) + { + return 0; + } + + // type is already sealed, return it + if (_compilation.IsEffectivelySealed(type)) + { + *exactClsRet = baseType; + return 1; + } + + if (!type.IsInterface) + { + // TODO: handle classes + return 0; + } + + TypeDesc[] implClasses = _compilation.GetImplementingClasses(type); + if (implClasses == null || implClasses.Length > maxExactClasses) + { + return 0; + } + + int index = 0; + foreach (TypeDesc implClass in implClasses) + { + Debug.Assert(!implClass.IsCanonicalSubtype(CanonicalFormKind.Any)); + exactClsRet[index++] = ObjectToHandle(implClass); + } + + Debug.Assert(index <= maxExactClasses); + return index; + } } } From e4eca639293dffd700bd8b701cf05789b5a58914 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 12 Aug 2022 13:50:25 +0300 Subject: [PATCH 26/30] fix build --- .../tools/Common/JitInterface/CorInfoImpl.cs | 45 ------------------- 1 file changed, 45 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 530df88eab8fe..c1f59c124b676 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -2894,51 +2894,6 @@ private CorInfoTypeWithMod getArgType(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_ST } } - private int getExactClasses(CORINFO_CLASS_STRUCT_* baseType, int maxExactClasses, CORINFO_CLASS_STRUCT_** exactClsRet) - { -#if !READYTORUN - MetadataType type = HandleToObject(baseType) as MetadataType; - if (type == null) - { - return 0; - } - - // type is already sealed, return it - if (_compilation.IsEffectivelySealed(type)) - { - *exactClsRet = baseType; - return 1; - } - - if (!type.IsInterface) - { - // TODO: handle classes - return 0; - } - - TypeDesc[] implClasses = _compilation.GetImplementingClasses(type); - if (implClasses == null || implClasses.Length > maxExactClasses) - { - return 0; - } - - int index = 0; - foreach (TypeDesc implClass in implClasses) - { - if (implClass.IsRuntimeDeterminedType) - { - return 0; - } - exactClsRet[index++] = ObjectToHandle(implClass); - } - - Debug.Assert(index <= maxExactClasses); - return index; -#else - return 0; -#endif - } - private CORINFO_CLASS_STRUCT_* getArgClass(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_STRUCT_* args) { int index = (int)args; From d1700525a52183592125ebd13972be0bc3a16599 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Sat, 13 Aug 2022 06:08:25 +0900 Subject: [PATCH 27/30] Restore old logic --- .../ILCompiler.Compiler/Compiler/ILScanner.cs | 104 +++++++++--------- 1 file changed, 53 insertions(+), 51 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs index 05cc38b096ef4..66eece6f7a65e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs @@ -374,76 +374,78 @@ public ScannedDevirtualizationManager(ImmutableArray eetypeNode.Type, + CanonicalEETypeNode canoneetypeNode => canoneetypeNode.Type, + _ => null, + }; - type = eetypeNode.Type; - - if (type.IsInterface) + if (type != null) { - if (((MetadataType)type).IsDynamicInterfaceCastableImplementation()) + if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) { foreach (DefType baseInterface in type.RuntimeInterfaces) { - // If the interface is implemented through IDynamicInterfaceCastable, there might be - // no real upper bound on the number of actual classes implementing it. + // If the interface is implemented on a template type, there might be + // no real upper bound on the number of actual classes implementing it + // due to MakeGenericType. if (CanAssumeWholeProgramViewOnInterfaceUse(baseInterface)) _disqualifiedInterfaces.Add(baseInterface); } } - } - else - { - // - // We collect this information: - // - // 1. What types got allocated - // 2. What types are the base types of other types - // This is needed for optimizations. We use this information to effectively - // seal types that are not base types for any other type. - // 3. What types implement interfaces for which use we can assume whole - // program view. - // - - _constructedTypes.Add(type); - if (type is not MetadataType { IsAbstract: true }) + if (type.IsInterface) { - // Record all interfaces this class implements to _interfaceImplementators - foreach (DefType baseInterface in type.RuntimeInterfaces) + if (((MetadataType)type).IsDynamicInterfaceCastableImplementation()) { - if (CanAssumeWholeProgramViewOnInterfaceUse(baseInterface)) + foreach (DefType baseInterface in type.RuntimeInterfaces) { - RecordImplementation(baseInterface, type); + // If the interface is implemented through IDynamicInterfaceCastable, there might be + // no real upper bound on the number of actual classes implementing it. + if (CanAssumeWholeProgramViewOnInterfaceUse(baseInterface)) + _disqualifiedInterfaces.Add(baseInterface); } } } + else + { + // + // We collect this information: + // + // 1. What types got allocated + // 2. What types are the base types of other types + // This is needed for optimizations. We use this information to effectively + // seal types that are not base types for any other type. + // 3. What types implement interfaces for which use we can assume whole + // program view. + // + + if (!type.IsCanonicalSubtype(CanonicalFormKind.Any)) + _constructedTypes.Add(type); + + if (type is not MetadataType { IsAbstract: true }) + { + // Record all interfaces this class implements to _interfaceImplementators + foreach (DefType baseInterface in type.RuntimeInterfaces) + { + if (CanAssumeWholeProgramViewOnInterfaceUse(baseInterface)) + { + RecordImplementation(baseInterface, type); + } + } + } - TypeDesc canonType = type.ConvertToCanonForm(CanonicalFormKind.Specific); + TypeDesc canonType = type.ConvertToCanonForm(CanonicalFormKind.Specific); - TypeDesc baseType = canonType.BaseType; - bool added = true; - while (baseType != null && added) - { - baseType = baseType.ConvertToCanonForm(CanonicalFormKind.Specific); - added = _unsealedTypes.Add(baseType); - baseType = baseType.BaseType; + TypeDesc baseType = canonType.BaseType; + bool added = true; + while (baseType != null && added) + { + baseType = baseType.ConvertToCanonForm(CanonicalFormKind.Specific); + added = _unsealedTypes.Add(baseType); + baseType = baseType.BaseType; + } } } } From 6bf87dbb2a8a6819742c18a0ac4f254668ace1d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Sat, 13 Aug 2022 06:13:19 +0900 Subject: [PATCH 28/30] One more lost diff --- src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs index 66eece6f7a65e..7c6d2ab6431d6 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs @@ -437,6 +437,7 @@ public ScannedDevirtualizationManager(ImmutableArray Date: Mon, 15 Aug 2022 18:36:58 +0300 Subject: [PATCH 29/30] add JitEnableExactDevirtualization --- src/coreclr/jit/gentree.cpp | 2 +- src/coreclr/jit/jitconfigvalues.h | 1 + src/coreclr/jit/lclvars.cpp | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index a5d028f201bb7..3b7c12f25ccb1 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -17773,7 +17773,7 @@ CORINFO_CLASS_HANDLE Compiler::gtGetClassHandle(GenTree* tree, bool* pIsExact, b } } - if ((objClass != NO_CLASS_HANDLE) && !*pIsExact && IsTargetAbi(CORINFO_NATIVEAOT_ABI)) + if ((objClass != NO_CLASS_HANDLE) && !*pIsExact && JitConfig.JitEnableExactDevirtualization()) { CORINFO_CLASS_HANDLE exactClass; if (info.compCompHnd->getExactClasses(objClass, 1, &exactClass) == 1) diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index db46ca0257c91..efe1fffc690fc 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -91,6 +91,7 @@ CONFIG_INTEGER(JitEmitPrintRefRegs, W("JitEmitPrintRefRegs"), 0) CONFIG_INTEGER(JitEnableDevirtualization, W("JitEnableDevirtualization"), 1) // Enable devirtualization in importer CONFIG_INTEGER(JitEnableLateDevirtualization, W("JitEnableLateDevirtualization"), 1) // Enable devirtualization after // inlining +CONFIG_INTEGER(JitEnableExactDevirtualization, W("JitEnableExactDevirtualization"), 1) CONFIG_INTEGER(JitExpensiveDebugCheckLevel, W("JitExpensiveDebugCheckLevel"), 0) // Level indicates how much checking // beyond the default to do in debug // builds (currently 1-2) diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index e74015049aa42..4aa1b1e4d9865 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -3174,7 +3174,7 @@ void Compiler::lvaSetClass(unsigned varNum, CORINFO_CLASS_HANDLE clsHnd, bool is return; } - if (clsHnd != NO_CLASS_HANDLE && !isExact && IsTargetAbi(CORINFO_NATIVEAOT_ABI)) + if (clsHnd != NO_CLASS_HANDLE && !isExact && JitConfig.JitEnableExactDevirtualization()) { CORINFO_CLASS_HANDLE exactClass; if (info.compCompHnd->getExactClasses(clsHnd, 1, &exactClass) == 1) From 292d1d84c60a26fff22a01fba412324f56eac005 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Mon, 15 Aug 2022 19:04:05 +0300 Subject: [PATCH 30/30] fix build --- src/coreclr/jit/jitconfigvalues.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index efe1fffc690fc..3e6df61b1f71f 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -91,7 +91,6 @@ CONFIG_INTEGER(JitEmitPrintRefRegs, W("JitEmitPrintRefRegs"), 0) CONFIG_INTEGER(JitEnableDevirtualization, W("JitEnableDevirtualization"), 1) // Enable devirtualization in importer CONFIG_INTEGER(JitEnableLateDevirtualization, W("JitEnableLateDevirtualization"), 1) // Enable devirtualization after // inlining -CONFIG_INTEGER(JitEnableExactDevirtualization, W("JitEnableExactDevirtualization"), 1) CONFIG_INTEGER(JitExpensiveDebugCheckLevel, W("JitExpensiveDebugCheckLevel"), 0) // Level indicates how much checking // beyond the default to do in debug // builds (currently 1-2) @@ -566,6 +565,9 @@ CONFIG_INTEGER(JitCrossCheckDevirtualizationAndPGO, W("JitCrossCheckDevirtualiza CONFIG_INTEGER(JitNoteFailedExactDevirtualization, W("JitNoteFailedExactDevirtualization"), 0) #endif // debug +// Devirtualize virtual calls with getExactClasses (NativeAOT only for now) +CONFIG_INTEGER(JitEnableExactDevirtualization, W("JitEnableExactDevirtualization"), 1) + // Control when Virtual Calls are expanded CONFIG_INTEGER(JitExpandCallsEarly, W("JitExpandCallsEarly"), 1) // Expand Call targets early (in the global morph // phase)