From 4155fae7828f0e6a224938cb4cb8dfc2e62a955f Mon Sep 17 00:00:00 2001 From: Fadi Hanna Date: Tue, 21 Jun 2016 11:51:05 -0700 Subject: [PATCH] Generic dictionary minor performance improvement and simplification for R2R (dotnet/coreclr#5690) A set of refactoring changes to slighly improve the performance of generic dictionary access on R2R images, and simplifying the codebase: 1) Removing dependency on CEEInfo::ComputeRuntimeLookupForSharedGenericTokenStatic (and deleting the API). 2) Stop parsing the generic type/method signatures when generating the R2R dictionary lookup assembly stub. 3) Avoid re-encoding the generic type/method signatures in a new in-memory blob using a SigBuilder (signatures used directly from the R2R image) 4) Moved the parsing/loading of type/method signatures to Dictionary::PopulateEntry() 5) Dictionary index and slot number are now encoded in the generated assembly stub instead of the signature (stub takes a pointer to a data blob, which contains the signature, the dictionary index and slot, and the module pointer) Commit migrated from https://github.com/dotnet/coreclr/commit/b9f5ae88cc49836c8d31f07db7800707165cdb06 --- src/coreclr/src/inc/corinfo.h | 8 +- src/coreclr/src/vm/amd64/cgenamd64.cpp | 24 +- src/coreclr/src/vm/arm64/stubs.cpp | 24 +- src/coreclr/src/vm/codeman.cpp | 34 +++ src/coreclr/src/vm/codeman.h | 1 + src/coreclr/src/vm/crossgencompile.cpp | 2 +- src/coreclr/src/vm/genericdict.cpp | 504 ++++++++++++++++++++++++--------- src/coreclr/src/vm/genericdict.h | 40 ++- src/coreclr/src/vm/i386/cgenx86.cpp | 24 +- src/coreclr/src/vm/jithelpers.cpp | 105 +++++-- src/coreclr/src/vm/jitinterface.cpp | 366 ++++++++++-------------- src/coreclr/src/vm/jitinterface.h | 27 +- src/coreclr/src/vm/prestub.cpp | 236 ++++++++------- src/coreclr/src/vm/readytoruninfo.h | 2 +- src/coreclr/src/zap/zapinfo.cpp | 10 +- 15 files changed, 873 insertions(+), 534 deletions(-) diff --git a/src/coreclr/src/inc/corinfo.h b/src/coreclr/src/inc/corinfo.h index fbbc95e..baae699 100644 --- a/src/coreclr/src/inc/corinfo.h +++ b/src/coreclr/src/inc/corinfo.h @@ -614,10 +614,10 @@ enum CorInfoHelpFunc CORINFO_HELP_MEMSET, // Init block of memory CORINFO_HELP_MEMCPY, // Copy block of memory - CORINFO_HELP_RUNTIMEHANDLE_METHOD, // determine a type/field/method handle at run-time - CORINFO_HELP_RUNTIMEHANDLE_METHOD_LOG,// determine a type/field/method handle at run-time, with IBC logging - CORINFO_HELP_RUNTIMEHANDLE_CLASS, // determine a type/field/method handle at run-time - CORINFO_HELP_RUNTIMEHANDLE_CLASS_LOG,// determine a type/field/method handle at run-time, with IBC logging + CORINFO_HELP_RUNTIMEHANDLE_METHOD, // determine a type/field/method handle at run-time + CORINFO_HELP_RUNTIMEHANDLE_METHOD_LOG, // determine a type/field/method handle at run-time, with IBC logging + CORINFO_HELP_RUNTIMEHANDLE_CLASS, // determine a type/field/method handle at run-time + CORINFO_HELP_RUNTIMEHANDLE_CLASS_LOG, // determine a type/field/method handle at run-time, with IBC logging // These helpers are required for MDIL backward compatibility only. They are not used by current JITed code. CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE_OBSOLETE, // Convert from a TypeHandle (native structure pointer) to RuntimeTypeHandle at run-time diff --git a/src/coreclr/src/vm/amd64/cgenamd64.cpp b/src/coreclr/src/vm/amd64/cgenamd64.cpp index f8bd458..d569800 100644 --- a/src/coreclr/src/vm/amd64/cgenamd64.cpp +++ b/src/coreclr/src/vm/amd64/cgenamd64.cpp @@ -20,6 +20,7 @@ #include "fcall.h" #include "array.h" #include "virtualcallstub.h" +#include "jitinterface.h" #ifdef FEATURE_COMINTEROP #include "clrtocomcall.h" @@ -1140,19 +1141,28 @@ PCODE DynamicHelpers::CreateHelperWithTwoArgs(LoaderAllocator * pAllocator, TADD END_DYNAMIC_HELPER_EMIT(); } -PCODE DynamicHelpers::CreateDictionaryLookupHelper(LoaderAllocator * pAllocator, CORINFO_RUNTIME_LOOKUP * pLookup) +PCODE DynamicHelpers::CreateDictionaryLookupHelper(LoaderAllocator * pAllocator, CORINFO_RUNTIME_LOOKUP * pLookup, DWORD dictionaryIndexAndSlot, Module * pModule) { STANDARD_VM_CONTRACT; + PCODE helperAddress = (pLookup->helper == CORINFO_HELP_RUNTIMEHANDLE_METHOD ? + GetEEFuncEntryPoint(JIT_GenericHandleMethodWithSlotAndModule) : + GetEEFuncEntryPoint(JIT_GenericHandleClassWithSlotAndModule)); + + GenericHandleArgs * pArgs = (GenericHandleArgs *)(void *)pAllocator->GetDynamicHelpersHeap()->AllocAlignedMem(sizeof(GenericHandleArgs), DYNAMIC_HELPER_ALIGNMENT); + pArgs->dictionaryIndexAndSlot = dictionaryIndexAndSlot; + pArgs->signature = pLookup->signature; + pArgs->module = (CORINFO_MODULE_HANDLE)pModule; + // It's available only via the run-time helper function if (pLookup->indirections == CORINFO_USEHELPER) { BEGIN_DYNAMIC_HELPER_EMIT(15); // rcx/rdi contains the generic context parameter - // mov rdx/rsi,pLookup->signature - // jmp pLookup->helper - EmitHelperWithArg(p, pAllocator, (TADDR)pLookup->signature, CEEJitInfo::getHelperFtnStatic(pLookup->helper)); + // mov rdx/rsi,pArgs + // jmp helperAddress + EmitHelperWithArg(p, pAllocator, (TADDR)pArgs, helperAddress); END_DYNAMIC_HELPER_EMIT(); } @@ -1248,9 +1258,9 @@ PCODE DynamicHelpers::CreateDictionaryLookupHelper(LoaderAllocator * pAllocator, *(UINT32*)p = 0x00c18948; p += 3; // mov rcx,rax #endif - // mov rdx,pLookup->signature - // jmp pLookup->helper - EmitHelperWithArg(p, pAllocator, (TADDR)pLookup->signature, CEEJitInfo::getHelperFtnStatic(pLookup->helper)); + // mov rdx,pArgs + // jmp helperAddress + EmitHelperWithArg(p, pAllocator, (TADDR)pArgs, helperAddress); } } diff --git a/src/coreclr/src/vm/arm64/stubs.cpp b/src/coreclr/src/vm/arm64/stubs.cpp index c662e0a..01a56e3 100644 --- a/src/coreclr/src/vm/arm64/stubs.cpp +++ b/src/coreclr/src/vm/arm64/stubs.cpp @@ -13,6 +13,7 @@ #include "tls.h" #include "asmconstants.h" #include "virtualcallstub.h" +#include "jitinterface.h" #ifndef DACCESS_COMPILE //----------------------------------------------------------------------- @@ -2004,10 +2005,19 @@ PCODE DynamicHelpers::CreateHelperWithTwoArgs(LoaderAllocator * pAllocator, TADD END_DYNAMIC_HELPER_EMIT(); } -PCODE DynamicHelpers::CreateDictionaryLookupHelper(LoaderAllocator * pAllocator, CORINFO_RUNTIME_LOOKUP * pLookup) +PCODE DynamicHelpers::CreateDictionaryLookupHelper(LoaderAllocator * pAllocator, CORINFO_RUNTIME_LOOKUP * pLookup, DWORD dictionaryIndexAndSlot, Module * pModule) { STANDARD_VM_CONTRACT; + PCODE helperAddress = (pLookup->helper == CORINFO_HELP_RUNTIMEHANDLE_METHOD ? + GetEEFuncEntryPoint(JIT_GenericHandleMethodWithSlotAndModule) : + GetEEFuncEntryPoint(JIT_GenericHandleClassWithSlotAndModule)); + + GenericHandleArgs * pArgs = (GenericHandleArgs *)(void *)pAllocator->GetDynamicHelpersHeap()->AllocAlignedMem(sizeof(GenericHandleArgs), DYNAMIC_HELPER_ALIGNMENT); + pArgs->dictionaryIndexAndSlot = dictionaryIndexAndSlot; + pArgs->signature = pLookup->signature; + pArgs->module = (CORINFO_MODULE_HANDLE)pModule; + // It's available only via the run-time helper function if (pLookup->indirections == CORINFO_USEHELPER) { @@ -2015,9 +2025,9 @@ PCODE DynamicHelpers::CreateDictionaryLookupHelper(LoaderAllocator * pAllocator, // X0 already contains generic context parameter // reuse EmitHelperWithArg for below two operations - // X1 <- pLookup->signature - // branch to pLookup->helper - EmitHelperWithArg(p, pAllocator, (TADDR)pLookup->signature, CEEJitInfo::getHelperFtnStatic(pLookup->helper)); + // X1 <- pArgs + // branch to helperAddress + EmitHelperWithArg(p, pAllocator, (TADDR)pArgs, helperAddress); END_DYNAMIC_HELPER_EMIT(); } @@ -2104,9 +2114,9 @@ PCODE DynamicHelpers::CreateDictionaryLookupHelper(LoaderAllocator * pAllocator, *(DWORD*)p = 0x91000120; p += 4; // reuse EmitHelperWithArg for below two operations - // X1 <- pLookup->signature - // branch to pLookup->helper - EmitHelperWithArg(p, pAllocator, (TADDR)pLookup->signature, CEEJitInfo::getHelperFtnStatic(pLookup->helper)); + // X1 <- pArgs + // branch to helperAddress + EmitHelperWithArg(p, pAllocator, (TADDR)pArgs, helperAddress); } // datalabel: diff --git a/src/coreclr/src/vm/codeman.cpp b/src/coreclr/src/vm/codeman.cpp index 701f5da..7eea254 100644 --- a/src/coreclr/src/vm/codeman.cpp +++ b/src/coreclr/src/vm/codeman.cpp @@ -4535,6 +4535,40 @@ PTR_Module ExecutionManager::FindZapModule(TADDR currentData) } /* static */ +PTR_Module ExecutionManager::FindReadyToRunModule(TADDR currentData) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + MODE_ANY; + STATIC_CONTRACT_HOST_CALLS; + SUPPORTS_DAC; + } + CONTRACTL_END; + +#ifdef FEATURE_READYTORUN + ReaderLockHolder rlh; + + RangeSection * pRS = GetRangeSection(currentData); + if (pRS == NULL) + return NULL; + + if (pRS->flags & RangeSection::RANGE_SECTION_CODEHEAP) + return NULL; + + if (pRS->flags & RangeSection::RANGE_SECTION_READYTORUN) + return dac_cast(pRS->pHeapListOrZapModule);; + + return NULL; +#else + return NULL; +#endif +} + + +/* static */ PTR_Module ExecutionManager::FindModuleForGCRefMap(TADDR currentData) { CONTRACTL diff --git a/src/coreclr/src/vm/codeman.h b/src/coreclr/src/vm/codeman.h index f0703f3..855c151 100644 --- a/src/coreclr/src/vm/codeman.h +++ b/src/coreclr/src/vm/codeman.h @@ -1298,6 +1298,7 @@ public: } static PTR_Module FindZapModule(TADDR currentData); + static PTR_Module FindReadyToRunModule(TADDR currentData); // FindZapModule flavor to be used during GC to find GCRefMap static PTR_Module FindModuleForGCRefMap(TADDR currentData); diff --git a/src/coreclr/src/vm/crossgencompile.cpp b/src/coreclr/src/vm/crossgencompile.cpp index bb5d7fa..36ba0f6 100644 --- a/src/coreclr/src/vm/crossgencompile.cpp +++ b/src/coreclr/src/vm/crossgencompile.cpp @@ -305,7 +305,7 @@ void CRemotingServices::DestroyThunk(MethodDesc* pMD) } #endif -CORINFO_GENERIC_HANDLE JIT_GenericHandleWorker(MethodDesc * pMD, MethodTable * pMT, LPVOID signature) +CORINFO_GENERIC_HANDLE JIT_GenericHandleWorker(MethodDesc * pMD, MethodTable * pMT, LPVOID signature, DWORD dictionaryIndexAndSlot, Module* pModule) { UNREACHABLE(); } diff --git a/src/coreclr/src/vm/genericdict.cpp b/src/coreclr/src/vm/genericdict.cpp index dfb9ea9..dda70b6 100644 --- a/src/coreclr/src/vm/genericdict.cpp +++ b/src/coreclr/src/vm/genericdict.cpp @@ -108,21 +108,27 @@ DictionaryLayout::GetFirstDictionaryBucketSize( // // Optimize the case of a token being !i (for class dictionaries) or !!i (for method dictionaries) // -//static +/* static */ BOOL -DictionaryLayout::FindToken( - LoaderAllocator * pAllocator, - DWORD numGenericArgs, - DictionaryLayout * pDictLayout, - CORINFO_RUNTIME_LOOKUP * pResult, - SigBuilder * pSigBuilder, - int nFirstOffset) +DictionaryLayout::FindTokenWorker(LoaderAllocator * pAllocator, + DWORD numGenericArgs, + DictionaryLayout * pDictLayout, + CORINFO_RUNTIME_LOOKUP * pResult, + SigBuilder * pSigBuilder, + BYTE * pSig, + DWORD cbSig, + int nFirstOffset, + DictionaryEntrySignatureSource signatureSource, + WORD * pSlotOut) { CONTRACTL { STANDARD_VM_CHECK; PRECONDITION(numGenericArgs > 0); PRECONDITION(CheckPointer(pDictLayout)); + PRECONDITION(CheckPointer(pSlotOut)); + PRECONDITION(CheckPointer(pSig)); + PRECONDITION((pSigBuilder == NULL && cbSig == -1) || (CheckPointer(pSigBuilder) && cbSig > 0)); } CONTRACTL_END @@ -139,20 +145,34 @@ DictionaryLayout::FindToken( BYTE * pCandidate = (BYTE *)pDictLayout->m_slots[iSlot].m_signature; if (pCandidate != NULL) { - DWORD cbSig; - BYTE * pSig = (BYTE *)pSigBuilder->GetSignature(&cbSig); + bool signaturesMatch = false; - // Compare the signatures. We do not need to worry about the size of pCandidate. - // As long as we are comparing one byte at a time we are guaranteed to not overrun. - DWORD j; - for (j = 0; j < cbSig; j++) + if (pSigBuilder != NULL) { - if (pCandidate[j] != pSig[j]) - break; + // JIT case: compare signatures by comparing the bytes in them. We exclude + // any ReadyToRun signatures from the JIT case. + + if (pDictLayout->m_slots[iSlot].m_signatureSource != FromReadyToRunImage) + { + // Compare the signatures. We do not need to worry about the size of pCandidate. + // As long as we are comparing one byte at a time we are guaranteed to not overrun. + DWORD j; + for (j = 0; j < cbSig; j++) + { + if (pCandidate[j] != pSig[j]) + break; + } + signaturesMatch = (j == cbSig); + } + } + else + { + // ReadyToRun case: compare signatures by comparing their pointer values + signaturesMatch = (pCandidate == pSig); } // We've found it - if (j == cbSig) + if (signaturesMatch) { pResult->signature = pDictLayout->m_slots[iSlot].m_signature; @@ -163,8 +183,9 @@ DictionaryLayout::FindToken( return FALSE; } _ASSERTE(FitsIn(nFirstOffset + 1)); - pResult->indirections = static_cast(nFirstOffset+1); + pResult->indirections = static_cast(nFirstOffset + 1); pResult->offsets[nFirstOffset] = slot * sizeof(DictionaryEntry); + *pSlotOut = slot; return TRUE; } } @@ -177,15 +198,21 @@ DictionaryLayout::FindToken( if (pDictLayout->m_slots[iSlot].m_signature != NULL) goto RetryMatch; - pSigBuilder->AppendData(isFirstBucket ? slot : 0); + PVOID pResultSignature = pSig; + + if (pSigBuilder != NULL) + { + pSigBuilder->AppendData(isFirstBucket ? slot : 0); - DWORD cbSig; - PVOID pSig = pSigBuilder->GetSignature(&cbSig); + DWORD cbNewSig; + PVOID pNewSig = pSigBuilder->GetSignature(&cbNewSig); - PVOID pPersisted = pAllocator->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(cbSig)); - memcpy(pPersisted, pSig, cbSig); + pResultSignature = pAllocator->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(cbNewSig)); + memcpy(pResultSignature, pNewSig, cbNewSig); + } - *EnsureWritablePages(&(pDictLayout->m_slots[iSlot].m_signature)) = pPersisted; + *EnsureWritablePages(&(pDictLayout->m_slots[iSlot].m_signature)) = pResultSignature; + *EnsureWritablePages(&(pDictLayout->m_slots[iSlot].m_signatureSource)) = signatureSource; } pResult->signature = pDictLayout->m_slots[iSlot].m_signature; @@ -196,8 +223,9 @@ DictionaryLayout::FindToken( return FALSE; } _ASSERTE(FitsIn(nFirstOffset + 1)); - pResult->indirections = static_cast(nFirstOffset+1); + pResult->indirections = static_cast(nFirstOffset + 1); pResult->offsets[nFirstOffset] = slot * sizeof(DictionaryEntry); + *pSlotOut = slot; return TRUE; } slot++; @@ -213,6 +241,38 @@ DictionaryLayout::FindToken( isFirstBucket = FALSE; } } // DictionaryLayout::FindToken + +/* static */ +BOOL +DictionaryLayout::FindToken(LoaderAllocator * pAllocator, + DWORD numGenericArgs, + DictionaryLayout * pDictLayout, + CORINFO_RUNTIME_LOOKUP * pResult, + SigBuilder * pSigBuilder, + int nFirstOffset, + DictionaryEntrySignatureSource signatureSource) +{ + DWORD cbSig; + BYTE * pSig = (BYTE *)pSigBuilder->GetSignature(&cbSig); + + WORD slotDummy; + return FindTokenWorker(pAllocator, numGenericArgs, pDictLayout, pResult, pSigBuilder, pSig, cbSig, nFirstOffset, signatureSource, &slotDummy); +} + +/* static */ +BOOL +DictionaryLayout::FindToken(LoaderAllocator * pAllocator, + DWORD numGenericArgs, + DictionaryLayout * pDictLayout, + CORINFO_RUNTIME_LOOKUP * pResult, + BYTE * signature, + int nFirstOffset, + DictionaryEntrySignatureSource signatureSource, + WORD * pSlotOut) +{ + return FindTokenWorker(pAllocator, numGenericArgs, pDictLayout, pResult, NULL, signature, -1, nFirstOffset, signatureSource, pSlotOut); +} + #endif //!DACCESS_COMPILE //--------------------------------------------------------------------------------------- @@ -575,7 +635,9 @@ Dictionary::PopulateEntry( MethodTable * pMT, LPVOID signature, BOOL nonExpansive, - DictionaryEntry ** ppSlot) + DictionaryEntry ** ppSlot, + DWORD dictionaryIndexAndSlot, /* = -1 */ + Module * pModule /* = NULL */) { CONTRACTL { THROWS; @@ -583,14 +645,73 @@ Dictionary::PopulateEntry( } CONTRACTL_END; CORINFO_GENERIC_HANDLE result = NULL; + Dictionary * pDictionary = NULL; *ppSlot = NULL; + bool isReadyToRunModule = (pModule != NULL && pModule->IsReadyToRun()); + + ZapSig::Context zapSigContext(NULL, NULL, ZapSig::NormalTokens); + ZapSig::Context * pZapSigContext = NULL; + + ULONG kind = DictionaryEntryKind::EmptySlot; + SigPointer ptr((PCCOR_SIGNATURE)signature); - Dictionary * pDictionary = NULL; + if (isReadyToRunModule) + { + PCCOR_SIGNATURE pBlob = (PCCOR_SIGNATURE)signature; - ULONG kind; // DictionaryEntryKind - IfFailThrow(ptr.GetData(&kind)); + BYTE fixupKind = *pBlob++; + + Module * pInfoModule = pModule; + if (fixupKind & ENCODE_MODULE_OVERRIDE) + { + DWORD moduleIndex = CorSigUncompressData(pBlob); + pInfoModule = pModule->GetModuleFromIndex(moduleIndex); + fixupKind &= ~ENCODE_MODULE_OVERRIDE; + } + + _ASSERTE(fixupKind == ENCODE_DICTIONARY_LOOKUP_THISOBJ || + fixupKind == ENCODE_DICTIONARY_LOOKUP_TYPE || + fixupKind == ENCODE_DICTIONARY_LOOKUP_METHOD); + + if (fixupKind == ENCODE_DICTIONARY_LOOKUP_THISOBJ) + { + SigPointer p(pBlob); + p.SkipExactlyOne(); + pBlob = p.GetPtr(); + } + + CORCOMPILE_FIXUP_BLOB_KIND signatureKind = (CORCOMPILE_FIXUP_BLOB_KIND)CorSigUncompressData(pBlob); + switch (signatureKind) + { + case ENCODE_TYPE_HANDLE: kind = TypeHandleSlot; break; + case ENCODE_METHOD_HANDLE: kind = MethodDescSlot; break; + case ENCODE_METHOD_ENTRY: kind = MethodEntrySlot; break; + case ENCODE_VIRTUAL_ENTRY: kind = DispatchStubAddrSlot; break; + + default: + _ASSERTE(!"Unexpected CORCOMPILE_FIXUP_BLOB_KIND"); + ThrowHR(COR_E_BADIMAGEFORMAT); + } + + ptr = SigPointer(pBlob); + + zapSigContext = ZapSig::Context(pInfoModule, pModule, ZapSig::NormalTokens); + pZapSigContext = &zapSigContext; + } + else + { + ptr = SigPointer((PCCOR_SIGNATURE)signature); + IfFailThrow(ptr.GetData(&kind)); + + Module * pContainingZapModule = ExecutionManager::FindZapModule(dac_cast(signature)); + + zapSigContext = ZapSig::Context(MscorlibBinder::GetModule(), (void *)pContainingZapModule, ZapSig::NormalTokens); + pZapSigContext = (pContainingZapModule != NULL) ? &zapSigContext : NULL; + } + + Module * pLookupModule = (isReadyToRunModule) ? pZapSigContext->pInfoModule : MscorlibBinder::GetModule(); if (pMT != NULL) { @@ -601,11 +722,19 @@ Dictionary::PopulateEntry( // prepare for every possible derived type of the type containing the method). So instead we have to locate the exactly // instantiated (non-shared) super-type of the class passed in. - ULONG dictionaryIndex = 0; - IfFailThrow(ptr.GetData(&dictionaryIndex)); - pDictionary = pMT->GetDictionary(); + ULONG dictionaryIndex = 0; + + if (isReadyToRunModule) + { + dictionaryIndex = dictionaryIndexAndSlot >> 16; + } + else + { + IfFailThrow(ptr.GetData(&dictionaryIndex)); + } + // MethodTable is expected to be normalized _ASSERTE(pDictionary == pMT->GetPerInstInfo()[dictionaryIndex]); } @@ -626,15 +755,6 @@ Dictionary::PopulateEntry( SigTypeContext::InitTypeContext(pMD, &typeContext); } - - Module * pContainingZapModule = ExecutionManager::FindZapModule(dac_cast(signature)); - - ZapSig::Context zapSigContext( - MscorlibBinder::GetModule(), - (void *)pContainingZapModule, - ZapSig::NormalTokens); - ZapSig::Context * pZapSigContext = (pContainingZapModule != NULL) ? &zapSigContext : NULL; - TypeHandle constraintType; TypeHandle declaringType; @@ -643,7 +763,7 @@ Dictionary::PopulateEntry( case DeclaringTypeHandleSlot: { declaringType = ptr.GetTypeHandleThrowing( - MscorlibBinder::GetModule(), + pLookupModule, &typeContext, (nonExpansive ? ClassLoader::DontLoadTypes : ClassLoader::LoadTypes), CLASS_LOADED, @@ -663,7 +783,7 @@ Dictionary::PopulateEntry( case TypeHandleSlot: { TypeHandle th = ptr.GetTypeHandleThrowing( - MscorlibBinder::GetModule(), + pLookupModule, &typeContext, (nonExpansive ? ClassLoader::DontLoadTypes : ClassLoader::LoadTypes), CLASS_LOADED, @@ -694,7 +814,7 @@ Dictionary::PopulateEntry( case ConstrainedMethodEntrySlot: { constraintType = ptr.GetTypeHandleThrowing( - MscorlibBinder::GetModule(), + pLookupModule, &typeContext, (nonExpansive ? ClassLoader::DontLoadTypes : ClassLoader::LoadTypes), CLASS_LOADED, @@ -715,110 +835,205 @@ Dictionary::PopulateEntry( case DispatchStubAddrSlot: case MethodEntrySlot: { - TypeHandle ownerType = ptr.GetTypeHandleThrowing( - MscorlibBinder::GetModule(), - &typeContext, - (nonExpansive ? ClassLoader::DontLoadTypes : ClassLoader::LoadTypes), - CLASS_LOADED, - FALSE, - NULL, - pZapSigContext); - if (ownerType.IsNull()) - { - _ASSERTE(nonExpansive); - return NULL; - } - IfFailThrow(ptr.SkipExactlyOne()); + TypeHandle ownerType; + MethodTable * pOwnerMT = NULL; + MethodDesc * pMethod = NULL; - // wsperf: Create a path that doesn't load types or create new handles if nonExpansive is set - if (nonExpansive) - return NULL; + DWORD methodFlags = 0; + BOOL isInstantiatingStub = 0; + BOOL isUnboxingStub = 0; + BOOL fMethodNeedsInstantiation = 0; - MethodTable * pOwnerMT = ownerType.GetMethodTable(); - _ASSERTE(pOwnerMT != NULL); + DWORD methodSlot = -1; + BOOL fRequiresDispatchStub = 0; - DWORD methodFlags; - IfFailThrow(ptr.GetData(&methodFlags)); + if (isReadyToRunModule) + { + IfFailThrow(ptr.GetData(&methodFlags)); - BOOL isInstantiatingStub = ((methodFlags & ENCODE_METHOD_SIG_InstantiatingStub) != 0); - BOOL isUnboxingStub = ((methodFlags & ENCODE_METHOD_SIG_UnboxingStub) != 0); - BOOL fMethodNeedsInstantiation = ((methodFlags & ENCODE_METHOD_SIG_MethodInstantiation) != 0); + isInstantiatingStub = ((methodFlags & ENCODE_METHOD_SIG_InstantiatingStub) != 0) || (kind == MethodEntrySlot); + isUnboxingStub = ((methodFlags & ENCODE_METHOD_SIG_UnboxingStub) != 0); + fMethodNeedsInstantiation = ((methodFlags & ENCODE_METHOD_SIG_MethodInstantiation) != 0); - MethodDesc * pMethod = NULL; + if (methodFlags & ENCODE_METHOD_SIG_OwnerType) + { + ownerType = ptr.GetTypeHandleThrowing( + pZapSigContext->pInfoModule, + &typeContext, + ClassLoader::LoadTypes, + CLASS_LOADED, + FALSE, + NULL, + pZapSigContext); - if ((methodFlags & ENCODE_METHOD_SIG_SlotInsteadOfToken) != 0) - { - // get the method desc using slot number - DWORD slot; - IfFailThrow(ptr.GetData(&slot)); + IfFailThrow(ptr.SkipExactlyOne()); + } - if (kind == DispatchStubAddrSlot) + if (methodFlags & ENCODE_METHOD_SIG_SlotInsteadOfToken) { - if (NingenEnabled()) - return NULL; + // get the method desc using slot number + IfFailThrow(ptr.GetData(&methodSlot)); -#ifndef CROSSGEN_COMPILE - // Generate a dispatch stub and store it in the dictionary. + _ASSERTE(!ownerType.IsNull()); + pMethod = ownerType.GetMethodTable()->GetMethodDescForSlot(methodSlot); + } + else + { // - // We generate an indirection so we don't have to write to the dictionary - // when we do updates, and to simplify stub indirect callsites. Stubs stored in - // dictionaries use "RegisterIndirect" stub calling, e.g. "call [eax]", - // i.e. here the register "eax" would contain the value fetched from the dictionary, - // which in turn points to the stub indirection which holds the value the current stub - // address itself. If we just used "call eax" then we wouldn't know which stub indirection - // to update. If we really wanted to avoid the extra indirection we could return the _address_ of the - // dictionary entry to the caller, still using "call [eax]", and then the - // stub dispatch mechanism can update the dictitonary itself and we don't - // need an indirection. - LoaderAllocator * pDictLoaderAllocator = (pMT != NULL) ? pMT->GetLoaderAllocator() : pMD->GetLoaderAllocator(); - - VirtualCallStubManager * pMgr = pDictLoaderAllocator->GetVirtualCallStubManager(); - - // We indirect through a cell so that updates can take place atomically. - // The call stub and the indirection cell have the same lifetime as the dictionary itself, i.e. - // are allocated in the domain of the dicitonary. + // decode method token // - // In the case of overflow (where there is no dictionary, just a global hash table) then - // the entry will be placed in the overflow hash table (JitGenericHandleCache). This - // is partitioned according to domain, i.e. is scraped each time an AppDomain gets unloaded. - PCODE addr = pMgr->GetCallStub(ownerType, slot); + RID rid; + IfFailThrow(ptr.GetData(&rid)); - result = (CORINFO_GENERIC_HANDLE)pMgr->GenerateStubIndirection(addr); - break; -#endif // CROSSGEN_COMPILE + if (methodFlags & ENCODE_METHOD_SIG_MemberRefToken) + { + if (ownerType.IsNull()) + { + FieldDesc * pFDDummy = NULL; + + MemberLoader::GetDescFromMemberRef(pZapSigContext->pInfoModule, TokenFromRid(rid, mdtMemberRef), &pMethod, &pFDDummy, NULL, FALSE, &ownerType); + _ASSERTE(pMethod != NULL && pFDDummy == NULL); + } + else + { + pMethod = MemberLoader::GetMethodDescFromMemberRefAndType(pZapSigContext->pInfoModule, TokenFromRid(rid, mdtMemberRef), ownerType.GetMethodTable()); + } + } + else + { + pMethod = MemberLoader::GetMethodDescFromMethodDef(pZapSigContext->pInfoModule, TokenFromRid(rid, mdtMethodDef), FALSE); + } } - pMethod = pOwnerMT->GetMethodDescForSlot(slot); + if (ownerType.IsNull()) + ownerType = pMethod->GetMethodTable(); + + _ASSERT(!ownerType.IsNull() && !nonExpansive); + pOwnerMT = ownerType.GetMethodTable(); + + if (methodFlags & ENCODE_METHOD_SIG_Constrained) + { + _ASSERTE(!"ReadyToRun: Constrained methods dictionary entries not yet supported."); + ThrowHR(COR_E_BADIMAGEFORMAT); + } + + if (kind == DispatchStubAddrSlot && pMethod->IsVtableMethod()) + { + fRequiresDispatchStub = TRUE; + methodSlot = pMethod->GetSlot(); + } } else { - // Decode type where the method token is defined - TypeHandle thMethodDefType = ptr.GetTypeHandleThrowing( - MscorlibBinder::GetModule(), - &typeContext, - (nonExpansive ? ClassLoader::DontLoadTypes : ClassLoader::LoadTypes), - CLASS_LOADED, - FALSE, - NULL, + ownerType = ptr.GetTypeHandleThrowing( + pLookupModule, + &typeContext, + (nonExpansive ? ClassLoader::DontLoadTypes : ClassLoader::LoadTypes), + CLASS_LOADED, + FALSE, + NULL, pZapSigContext); - if (thMethodDefType.IsNull()) + if (ownerType.IsNull()) { _ASSERTE(nonExpansive); return NULL; } IfFailThrow(ptr.SkipExactlyOne()); - MethodTable * pMethodDefMT = thMethodDefType.GetMethodTable(); - _ASSERTE(pMethodDefMT != NULL); - - // decode method token - RID rid; - IfFailThrow(ptr.GetData(&rid)); - mdMethodDef token = TokenFromRid(rid, mdtMethodDef); - - // The RID map should have been filled out if we fully loaded the class - pMethod = pMethodDefMT->GetModule()->LookupMethodDef(token); - _ASSERTE(pMethod != NULL); - pMethod->CheckRestore(); + + // wsperf: Create a path that doesn't load types or create new handles if nonExpansive is set + if (nonExpansive) + return NULL; + + pOwnerMT = ownerType.GetMethodTable(); + _ASSERTE(pOwnerMT != NULL); + + IfFailThrow(ptr.GetData(&methodFlags)); + + isInstantiatingStub = ((methodFlags & ENCODE_METHOD_SIG_InstantiatingStub) != 0); + isUnboxingStub = ((methodFlags & ENCODE_METHOD_SIG_UnboxingStub) != 0); + fMethodNeedsInstantiation = ((methodFlags & ENCODE_METHOD_SIG_MethodInstantiation) != 0); + + if ((methodFlags & ENCODE_METHOD_SIG_SlotInsteadOfToken) != 0) + { + // get the method desc using slot number + IfFailThrow(ptr.GetData(&methodSlot)); + + if (kind == DispatchStubAddrSlot) + { + if (NingenEnabled()) + return NULL; + +#ifndef CROSSGEN_COMPILE + fRequiresDispatchStub = TRUE; +#endif + } + + if (!fRequiresDispatchStub) + pMethod = pOwnerMT->GetMethodDescForSlot(methodSlot); + } + else + { + // Decode type where the method token is defined + TypeHandle thMethodDefType = ptr.GetTypeHandleThrowing( + pLookupModule, + &typeContext, + (nonExpansive ? ClassLoader::DontLoadTypes : ClassLoader::LoadTypes), + CLASS_LOADED, + FALSE, + NULL, + pZapSigContext); + if (thMethodDefType.IsNull()) + { + _ASSERTE(nonExpansive); + return NULL; + } + IfFailThrow(ptr.SkipExactlyOne()); + MethodTable * pMethodDefMT = thMethodDefType.GetMethodTable(); + _ASSERTE(pMethodDefMT != NULL); + + // decode method token + RID rid; + IfFailThrow(ptr.GetData(&rid)); + mdMethodDef token = TokenFromRid(rid, mdtMethodDef); + + // The RID map should have been filled out if we fully loaded the class + pMethod = pMethodDefMT->GetModule()->LookupMethodDef(token); + _ASSERTE(pMethod != NULL); + pMethod->CheckRestore(); + } + } + + if (fRequiresDispatchStub) + { +#ifndef CROSSGEN_COMPILE + // Generate a dispatch stub and store it in the dictionary. + // + // We generate an indirection so we don't have to write to the dictionary + // when we do updates, and to simplify stub indirect callsites. Stubs stored in + // dictionaries use "RegisterIndirect" stub calling, e.g. "call [eax]", + // i.e. here the register "eax" would contain the value fetched from the dictionary, + // which in turn points to the stub indirection which holds the value the current stub + // address itself. If we just used "call eax" then we wouldn't know which stub indirection + // to update. If we really wanted to avoid the extra indirection we could return the _address_ of the + // dictionary entry to the caller, still using "call [eax]", and then the + // stub dispatch mechanism can update the dictitonary itself and we don't + // need an indirection. + LoaderAllocator * pDictLoaderAllocator = (pMT != NULL) ? pMT->GetLoaderAllocator() : pMD->GetLoaderAllocator(); + + VirtualCallStubManager * pMgr = pDictLoaderAllocator->GetVirtualCallStubManager(); + + // We indirect through a cell so that updates can take place atomically. + // The call stub and the indirection cell have the same lifetime as the dictionary itself, i.e. + // are allocated in the domain of the dicitonary. + // + // In the case of overflow (where there is no dictionary, just a global hash table) then + // the entry will be placed in the overflow hash table (JitGenericHandleCache). This + // is partitioned according to domain, i.e. is scraped each time an AppDomain gets unloaded. + PCODE addr = pMgr->GetCallStub(ownerType, methodSlot); + + result = (CORINFO_GENERIC_HANDLE)pMgr->GenerateStubIndirection(addr); + break; +#endif // CROSSGEN_COMPILE } Instantiation inst; @@ -833,17 +1048,17 @@ Dictionary::PopulateEntry( if (!ClrSafeInt::multiply(nargs, sizeof(TypeHandle), cbMem/* passed by ref */)) ThrowHR(COR_E_OVERFLOW); - - TypeHandle * pInst = (TypeHandle*) _alloca(cbMem); + + TypeHandle * pInst = (TypeHandle*)_alloca(cbMem); for (DWORD i = 0; i < nargs; i++) { pInst[i] = ptr.GetTypeHandleThrowing( - MscorlibBinder::GetModule(), - &typeContext, - ClassLoader::LoadTypes, - CLASS_LOADED, - FALSE, - NULL, + pLookupModule, + &typeContext, + ClassLoader::LoadTypes, + CLASS_LOADED, + FALSE, + NULL, pZapSigContext); IfFailThrow(ptr.SkipExactlyOne()); } @@ -859,14 +1074,17 @@ Dictionary::PopulateEntry( // stub for static methods in generic classees if needed, also for BoxedEntryPointStubs // in non-generic structs. pMethod = MethodDesc::FindOrCreateAssociatedMethodDesc( - pMethod, - pOwnerMT, - isUnboxingStub, - inst, + pMethod, + pOwnerMT, + isUnboxingStub, + inst, (!isInstantiatingStub && !isUnboxingStub)); if (kind == ConstrainedMethodEntrySlot) { + // TODO: READYTORUN: Support for constrained method entry slots + _ASSERT(!isReadyToRunModule); + _ASSERTE(!constraintType.IsNull()); MethodDesc *pResolvedMD = constraintType.GetMethodTable()->TryResolveConstraintMethodApprox(ownerType, pMethod); @@ -903,7 +1121,7 @@ Dictionary::PopulateEntry( case FieldDescSlot: { TypeHandle th = ptr.GetTypeHandleThrowing( - MscorlibBinder::GetModule(), + pLookupModule, &typeContext, (nonExpansive ? ClassLoader::DontLoadTypes : ClassLoader::LoadTypes), CLASS_LOADED, @@ -935,7 +1153,15 @@ Dictionary::PopulateEntry( } ULONG slotIndex; - IfFailThrow(ptr.GetData(&slotIndex)); + if (isReadyToRunModule) + { + _ASSERT(dictionaryIndexAndSlot != -1); + slotIndex = (ULONG)(dictionaryIndexAndSlot & 0xFFFF); + } + else + { + IfFailThrow(ptr.GetData(&slotIndex)); + } MemoryBarrier(); diff --git a/src/coreclr/src/vm/genericdict.h b/src/coreclr/src/vm/genericdict.h index e63b3c8..1cb172a 100644 --- a/src/coreclr/src/vm/genericdict.h +++ b/src/coreclr/src/vm/genericdict.h @@ -66,6 +66,13 @@ enum DictionaryEntryKind DeclaringTypeHandleSlot = 7, }; +enum DictionaryEntrySignatureSource : BYTE +{ + FromZapImage = 0, + FromReadyToRunImage = 1, + FromJIT = 2, +}; + class DictionaryEntryLayout { public: @@ -75,6 +82,8 @@ public: DictionaryEntryKind GetKind(); PTR_VOID m_signature; + + DictionaryEntrySignatureSource m_signatureSource; }; typedef DPTR(DictionaryEntryLayout) PTR_DictionaryEntryLayout; @@ -99,7 +108,19 @@ private: WORD m_numSlots; // m_numSlots of these - DictionaryEntryLayout m_slots[1]; + DictionaryEntryLayout m_slots[1]; + + static BOOL FindTokenWorker(LoaderAllocator *pAllocator, + DWORD numGenericArgs, + DictionaryLayout *pDictLayout, + CORINFO_RUNTIME_LOOKUP *pResult, + SigBuilder * pSigBuilder, + BYTE * pSig, + DWORD cbSig, + int nFirstOffset, + DictionaryEntrySignatureSource signatureSource, + WORD * pSlotOut); + public: // Create an initial dictionary layout with a single bucket containing numSlots slots @@ -114,7 +135,17 @@ public: DictionaryLayout *pDictLayout, CORINFO_RUNTIME_LOOKUP *pResult, SigBuilder * pSigBuilder, - int nFirstOffset); + int nFirstOffset, + DictionaryEntrySignatureSource signatureSource); + + static BOOL FindToken(LoaderAllocator * pAllocator, + DWORD numGenericArgs, + DictionaryLayout * pDictLayout, + CORINFO_RUNTIME_LOOKUP * pResult, + BYTE * signature, + int nFirstOffset, + DictionaryEntrySignatureSource signatureSource, + WORD * pSlotOut); DWORD GetMaxSlots(); DWORD GetNumUsedSlots(); @@ -253,7 +284,10 @@ class Dictionary MethodTable * pMT, LPVOID signature, BOOL nonExpansive, - DictionaryEntry ** ppSlot); + DictionaryEntry ** ppSlot, + DWORD dictionaryIndexAndSlot = -1, + Module * pModule = NULL); + void PrepopulateDictionary(MethodDesc * pMD, MethodTable * pMT, BOOL nonExpansive); diff --git a/src/coreclr/src/vm/i386/cgenx86.cpp b/src/coreclr/src/vm/i386/cgenx86.cpp index a9c2967..48428a1 100644 --- a/src/coreclr/src/vm/i386/cgenx86.cpp +++ b/src/coreclr/src/vm/i386/cgenx86.cpp @@ -34,6 +34,7 @@ #include "class.h" #include "virtualcallstub.h" #include "mdaassistants.h" +#include "jitinterface.h" #ifdef FEATURE_COMINTEROP #include "comtoclrcall.h" @@ -2153,19 +2154,28 @@ PCODE DynamicHelpers::CreateHelperWithTwoArgs(LoaderAllocator * pAllocator, TADD END_DYNAMIC_HELPER_EMIT(); } -PCODE DynamicHelpers::CreateDictionaryLookupHelper(LoaderAllocator * pAllocator, CORINFO_RUNTIME_LOOKUP * pLookup) +PCODE DynamicHelpers::CreateDictionaryLookupHelper(LoaderAllocator * pAllocator, CORINFO_RUNTIME_LOOKUP * pLookup, DWORD dictionaryIndexAndSlot, Module * pModule) { STANDARD_VM_CONTRACT; + PCODE helperAddress = (pLookup->helper == CORINFO_HELP_RUNTIMEHANDLE_METHOD ? + GetEEFuncEntryPoint(JIT_GenericHandleMethodWithSlotAndModule) : + GetEEFuncEntryPoint(JIT_GenericHandleClassWithSlotAndModule)); + + GenericHandleArgs * pArgs = (GenericHandleArgs *)(void *)pAllocator->GetDynamicHelpersHeap()->AllocAlignedMem(sizeof(GenericHandleArgs), DYNAMIC_HELPER_ALIGNMENT); + pArgs->dictionaryIndexAndSlot = dictionaryIndexAndSlot; + pArgs->signature = pLookup->signature; + pArgs->module = (CORINFO_MODULE_HANDLE)pModule; + // It's available only via the run-time helper function if (pLookup->indirections == CORINFO_USEHELPER) { BEGIN_DYNAMIC_HELPER_EMIT(10); // ecx contains the generic context parameter - // mov edx,pLookup->signature - // jmp pLookup->helper - EmitHelperWithArg(p, pAllocator, (TADDR)pLookup->signature, CEEJitInfo::getHelperFtnStatic(pLookup->helper)); + // mov edx,pArgs + // jmp helperAddress + EmitHelperWithArg(p, pAllocator, (TADDR)pArgs, helperAddress); END_DYNAMIC_HELPER_EMIT(); } @@ -2232,9 +2242,9 @@ PCODE DynamicHelpers::CreateDictionaryLookupHelper(LoaderAllocator * pAllocator, // mov ecx,eax *(UINT16*)p = 0xc189; p += 2; - // mov edx,pLookup->signature - // jmp pLookup->helper - EmitHelperWithArg(p, pAllocator, (TADDR)pLookup->signature, CEEJitInfo::getHelperFtnStatic(pLookup->helper)); + // mov edx,pArgs + // jmp helperAddress + EmitHelperWithArg(p, pAllocator, (TADDR)pArgs, helperAddress); } } diff --git a/src/coreclr/src/vm/jithelpers.cpp b/src/coreclr/src/vm/jithelpers.cpp index fffc6ce..1626810 100644 --- a/src/coreclr/src/vm/jithelpers.cpp +++ b/src/coreclr/src/vm/jithelpers.cpp @@ -3994,11 +3994,7 @@ void ClearJitGenericHandleCache(AppDomain *pDomain) // Factored out most of the body of JIT_GenericHandle so it could be called easily from the CER reliability code to pre-populate the // cache. -CORINFO_GENERIC_HANDLE -JIT_GenericHandleWorker( - MethodDesc * pMD, - MethodTable * pMT, - LPVOID signature) +CORINFO_GENERIC_HANDLE JIT_GenericHandleWorker(MethodDesc * pMD, MethodTable * pMT, LPVOID signature, DWORD dictionaryIndexAndSlot, Module* pModule) { CONTRACTL { THROWS; @@ -4009,20 +4005,34 @@ JIT_GenericHandleWorker( if (pMT != NULL) { - SigPointer ptr((PCCOR_SIGNATURE)signature); + ULONG dictionaryIndex = 0; - ULONG kind; // DictionaryEntryKind - IfFailThrow(ptr.GetData(&kind)); + if (pModule != NULL) + { +#ifdef _DEBUG + // Only in R2R mode are the module, dictionary index and dictionary slot provided as an input + _ASSERTE(dictionaryIndexAndSlot != -1); + _ASSERT(ExecutionManager::FindReadyToRunModule(dac_cast(signature)) == pModule); +#endif + dictionaryIndex = (dictionaryIndexAndSlot >> 16); + } + else + { + SigPointer ptr((PCCOR_SIGNATURE)signature); - // We need to normalize the class passed in (if any) for reliability purposes. That's because preparation of a code region that - // contains these handle lookups depends on being able to predict exactly which lookups are required (so we can pre-cache the - // answers and remove any possibility of failure at runtime). This is hard to do if the lookup (in this case the lookup of the - // dictionary overflow cache) is keyed off the somewhat arbitrary type of the instance on which the call is made (we'd need to - // prepare for every possible derived type of the type containing the method). So instead we have to locate the exactly - // instantiated (non-shared) super-type of the class passed in. + ULONG kind; // DictionaryEntryKind + IfFailThrow(ptr.GetData(&kind)); - ULONG dictionaryIndex = 0; - IfFailThrow(ptr.GetData(&dictionaryIndex)); + // We need to normalize the class passed in (if any) for reliability purposes. That's because preparation of a code region that + // contains these handle lookups depends on being able to predict exactly which lookups are required (so we can pre-cache the + // answers and remove any possibility of failure at runtime). This is hard to do if the lookup (in this case the lookup of the + // dictionary overflow cache) is keyed off the somewhat arbitrary type of the instance on which the call is made (we'd need to + // prepare for every possible derived type of the type containing the method). So instead we have to locate the exactly + // instantiated (non-shared) super-type of the class passed in. + + _ASSERTE(dictionaryIndexAndSlot == -1); + IfFailThrow(ptr.GetData(&dictionaryIndex)); + } pDeclaringMT = pMT; for (;;) @@ -4049,7 +4059,7 @@ JIT_GenericHandleWorker( } DictionaryEntry * pSlot; - CORINFO_GENERIC_HANDLE result = (CORINFO_GENERIC_HANDLE)Dictionary::PopulateEntry(pMD, pDeclaringMT, signature, FALSE, &pSlot); + CORINFO_GENERIC_HANDLE result = (CORINFO_GENERIC_HANDLE)Dictionary::PopulateEntry(pMD, pDeclaringMT, signature, FALSE, &pSlot, dictionaryIndexAndSlot, pModule); if (pSlot == NULL) { @@ -4076,10 +4086,12 @@ JIT_GenericHandleWorker( /*********************************************************************/ // slow helper to tail call from the fast one -NOINLINE HCIMPL3(CORINFO_GENERIC_HANDLE, JIT_GenericHandle_Framed, - CORINFO_CLASS_HANDLE classHnd, - CORINFO_METHOD_HANDLE methodHnd, - LPVOID signature) +NOINLINE HCIMPL5(CORINFO_GENERIC_HANDLE, JIT_GenericHandle_Framed, + CORINFO_CLASS_HANDLE classHnd, + CORINFO_METHOD_HANDLE methodHnd, + LPVOID signature, + DWORD dictionaryIndexAndSlot, + CORINFO_MODULE_HANDLE moduleHnd) { CONTRACTL { FCALL_CHECK; @@ -4092,11 +4104,12 @@ NOINLINE HCIMPL3(CORINFO_GENERIC_HANDLE, JIT_GenericHandle_Framed, MethodDesc * pMD = GetMethod(methodHnd); MethodTable * pMT = TypeHandle(classHnd).AsMethodTable(); + Module * pModule = GetModule(moduleHnd); // Set up a frame HELPER_METHOD_FRAME_BEGIN_RET_0(); - result = JIT_GenericHandleWorker(pMD, pMT, signature); + result = JIT_GenericHandleWorker(pMD, pMT, signature, dictionaryIndexAndSlot, pModule); HELPER_METHOD_FRAME_END(); @@ -4125,7 +4138,27 @@ HCIMPL2(CORINFO_GENERIC_HANDLE, JIT_GenericHandleMethod, CORINFO_METHOD_HANDLE // Tailcall to the slow helper ENDFORBIDGC(); - return HCCALL3(JIT_GenericHandle_Framed, NULL, methodHnd, signature); + return HCCALL5(JIT_GenericHandle_Framed, NULL, methodHnd, signature, -1, NULL); +} +HCIMPLEND + +HCIMPL2(CORINFO_GENERIC_HANDLE, JIT_GenericHandleMethodWithSlotAndModule, CORINFO_METHOD_HANDLE methodHnd, GenericHandleArgs * pArgs) +{ + CONTRACTL{ + FCALL_CHECK; + PRECONDITION(CheckPointer(methodHnd)); + PRECONDITION(GetMethod(methodHnd)->IsRestored()); + PRECONDITION(CheckPointer(pArgs)); + } CONTRACTL_END; + + JitGenericHandleCacheKey key(NULL, methodHnd, pArgs->signature); + HashDatum res; + if (g_pJitGenericHandleCache->GetValueSpeculative(&key, &res)) + return (CORINFO_GENERIC_HANDLE)(DictionaryEntry)res; + + // Tailcall to the slow helper + ENDFORBIDGC(); + return HCCALL5(JIT_GenericHandle_Framed, NULL, methodHnd, pArgs->signature, pArgs->dictionaryIndexAndSlot, pArgs->module); } HCIMPLEND #include @@ -4149,7 +4182,7 @@ HCIMPL2(CORINFO_GENERIC_HANDLE, JIT_GenericHandleMethodLogging, CORINFO_METHOD_H // Tailcall to the slow helper ENDFORBIDGC(); - return HCCALL3(JIT_GenericHandle_Framed, NULL, methodHnd, signature); + return HCCALL5(JIT_GenericHandle_Framed, NULL, methodHnd, signature, -1, NULL); } HCIMPLEND @@ -4171,7 +4204,27 @@ HCIMPL2(CORINFO_GENERIC_HANDLE, JIT_GenericHandleClass, CORINFO_CLASS_HANDLE cla // Tailcall to the slow helper ENDFORBIDGC(); - return HCCALL3(JIT_GenericHandle_Framed, classHnd, NULL, signature); + return HCCALL5(JIT_GenericHandle_Framed, classHnd, NULL, signature, -1, NULL); +} +HCIMPLEND + +HCIMPL2(CORINFO_GENERIC_HANDLE, JIT_GenericHandleClassWithSlotAndModule, CORINFO_CLASS_HANDLE classHnd, GenericHandleArgs * pArgs) +{ + CONTRACTL{ + FCALL_CHECK; + PRECONDITION(CheckPointer(classHnd)); + PRECONDITION(TypeHandle(classHnd).IsRestored()); + PRECONDITION(CheckPointer(pArgs)); + } CONTRACTL_END; + + JitGenericHandleCacheKey key(classHnd, NULL, pArgs->signature); + HashDatum res; + if (g_pJitGenericHandleCache->GetValueSpeculative(&key, &res)) + return (CORINFO_GENERIC_HANDLE)(DictionaryEntry)res; + + // Tailcall to the slow helper + ENDFORBIDGC(); + return HCCALL5(JIT_GenericHandle_Framed, classHnd, NULL, pArgs->signature, pArgs->dictionaryIndexAndSlot, pArgs->module); } HCIMPLEND #include @@ -4195,7 +4248,7 @@ HCIMPL2(CORINFO_GENERIC_HANDLE, JIT_GenericHandleClassLogging, CORINFO_CLASS_HAN // Tailcall to the slow helper ENDFORBIDGC(); - return HCCALL3(JIT_GenericHandle_Framed, classHnd, NULL, signature); + return HCCALL5(JIT_GenericHandle_Framed, classHnd, NULL, signature, -1, NULL); } HCIMPLEND diff --git a/src/coreclr/src/vm/jitinterface.cpp b/src/coreclr/src/vm/jitinterface.cpp index 99e987d..b72eaf3 100644 --- a/src/coreclr/src/vm/jitinterface.cpp +++ b/src/coreclr/src/vm/jitinterface.cpp @@ -3064,6 +3064,7 @@ static BOOL IsTypeSpecForTypicalInstantiation(SigPointer sigptr) return IsSignatureForTypicalInstantiation(sigptr, ELEMENT_TYPE_VAR, ntypars); } + void CEEInfo::ComputeRuntimeLookupForSharedGenericToken(DictionaryEntryKind entryKind, CORINFO_RESOLVED_TOKEN * pResolvedToken, CORINFO_RESOLVED_TOKEN * pConstrainedResolvedToken, @@ -3081,15 +3082,58 @@ void CEEInfo::ComputeRuntimeLookupForSharedGenericToken(DictionaryEntryKind entr pResultLookup->lookupKind.needsRuntimeLookup = true; pResultLookup->lookupKind.runtimeLookupFlags = 0; + + CORINFO_RUNTIME_LOOKUP *pResult = &pResultLookup->runtimeLookup; + pResult->signature = NULL; + + // Unless we decide otherwise, just do the lookup via a helper function + pResult->indirections = CORINFO_USEHELPER; + + MethodDesc *pContextMD = GetMethodFromContext(pResolvedToken->tokenContext); + MethodTable *pContextMT = pContextMD->GetMethodTable(); + + // Do not bother computing the runtime lookup if we are inlining. The JIT is going + // to abort the inlining attempt anyway. + if (pContextMD != m_pMethodBeingCompiled) + { + return; + } + + // There is a pathological case where invalid IL refereces __Canon type directly, but there is no dictionary availabled to store the lookup. + // All callers of ComputeRuntimeLookupForSharedGenericToken have to filter out this case. We can't do much about it here. + _ASSERTE(pContextMD->IsSharedByGenericInstantiations()); + + BOOL fInstrument = FALSE; + +#ifdef FEATURE_NATIVE_IMAGE_GENERATION + // This will make sure that when IBC logging is turned on we will go through a version + // of JIT_GenericHandle which logs the access. Note that we still want the dictionaries + // to be populated to prepopulate the types at NGen time. + if (IsCompilingForNGen() && + GetAppDomain()->ToCompilationDomain()->m_fForceInstrument) + { + fInstrument = TRUE; + } +#endif // FEATURE_NATIVE_IMAGE_GENERATION + + if (pContextMD->RequiresInstMethodDescArg()) + { + pResultLookup->lookupKind.runtimeLookupKind = CORINFO_LOOKUP_METHODPARAM; + } + else + { + if (pContextMD->RequiresInstMethodTableArg()) + pResultLookup->lookupKind.runtimeLookupKind = CORINFO_LOOKUP_CLASSPARAM; + else + pResultLookup->lookupKind.runtimeLookupKind = CORINFO_LOOKUP_THISOBJ; + } + #ifdef FEATURE_READYTORUN_COMPILER if (IsReadyToRunCompilation()) { #if defined(_TARGET_ARM_) - // TODO - ThrowHR(E_NOTIMPL); + ThrowHR(E_NOTIMPL); /* TODO - NYI */ #endif - - switch (entryKind) { case TypeHandleSlot: @@ -3126,191 +3170,138 @@ void CEEInfo::ComputeRuntimeLookupForSharedGenericToken(DictionaryEntryKind entr _ASSERTE(!"Unknown dictionary entry kind!"); IfFailThrow(E_FAIL); } + + // For R2R compilations, we don't generate the dictionary lookup signatures (dictionary lookups are done in a + // different way that is more version resilient... plus we can't have pointers to existing MTs/MDs in the sigs) + return; } #endif + // If we've got a method type parameter of any kind then we must look in the method desc arg + if (pContextMD->RequiresInstMethodDescArg()) + { + pResult->helper = fInstrument ? CORINFO_HELP_RUNTIMEHANDLE_METHOD_LOG : CORINFO_HELP_RUNTIMEHANDLE_METHOD; - CORINFO_RUNTIME_LOOKUP *pResult = &pResultLookup->runtimeLookup; - pResult->signature = NULL; + if (fInstrument) + goto NoSpecialCase; - // Unless we decide otherwise, just do the lookup via a helper function - pResult->indirections = CORINFO_USEHELPER; + // Special cases: + // (1) Naked method type variable: look up directly in instantiation hanging off runtime md + // (2) Reference to method-spec of current method (e.g. a recursive call) i.e. currentmeth + if ((entryKind == TypeHandleSlot) && (pResolvedToken->tokenType != CORINFO_TOKENKIND_Newarr)) + { + SigPointer sigptr(pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec); + CorElementType type; + IfFailThrow(sigptr.GetElemType(&type)); + if (type == ELEMENT_TYPE_MVAR) + { + pResult->indirections = 2; + pResult->testForNull = 0; +#ifdef FEATURE_PREJIT + pResult->testForFixup = 1; +#else + pResult->testForFixup = 0; +#endif + pResult->offsets[0] = offsetof(InstantiatedMethodDesc, m_pPerInstInfo); - MethodDesc *pContextMD = GetMethodFromContext(pResolvedToken->tokenContext); - MethodTable *pContextMT = pContextMD->GetMethodTable(); + ULONG data; + IfFailThrow(sigptr.GetData(&data)); + pResult->offsets[1] = sizeof(TypeHandle) * data; - // Do not bother computing the runtime lookup if we are inlining. The JIT is going - // to abort the inlining attempt anyway. - if (pContextMD != m_pMethodBeingCompiled) - { - return; - } + return; + } + } + else if (entryKind == MethodDescSlot) + { + // It's the context itself (i.e. a recursive call) + if (!pTemplateMD->HasSameMethodDefAs(pContextMD)) + goto NoSpecialCase; - // There is a pathological case where invalid IL refereces __Canon type directly, but there is no dictionary availabled to store the lookup. - // All callers of ComputeRuntimeLookupForSharedGenericToken have to filter out this case. We can't do much about it here. - _ASSERTE(pContextMD->IsSharedByGenericInstantiations()); + // Now just check that the instantiation is (!!0, ..., !!(n-1)) + if (!IsMethodSpecForTypicalInstantation(SigPointer(pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec))) + goto NoSpecialCase; - BOOL fInstrument = FALSE; + // Type instantiation has to match too if there is one + if (pContextMT->HasInstantiation()) + { + TypeHandle thTemplate(pResolvedToken->hClass); -#ifdef FEATURE_NATIVE_IMAGE_GENERATION - // This will make sure that when IBC logging is turned on we will go through a version - // of JIT_GenericHandle which logs the access. Note that we still want the dictionaries - // to be populated to prepopulate the types at NGen time. - if (IsCompilingForNGen() && - GetAppDomain()->ToCompilationDomain()->m_fForceInstrument) - { - fInstrument = TRUE; - } -#endif // FEATURE_NATIVE_IMAGE_GENERATION + if (thTemplate.IsTypeDesc() || !thTemplate.AsMethodTable()->HasSameTypeDefAs(pContextMT)) + goto NoSpecialCase; - DWORD numGenericArgs; - DictionaryLayout* pDictionaryLayout; - LoaderAllocator* pAllocator; + // This check filters out method instantiation on generic type definition, like G::M() + // We may not ever get it here. Filter it out just to be sure... + if (pResolvedToken->pTypeSpec == NULL) + goto NoSpecialCase; - if (pContextMD->RequiresInstMethodDescArg()) - { - pAllocator = pContextMD->GetLoaderAllocator(); - numGenericArgs = pContextMD->GetNumGenericMethodArgs(); - pDictionaryLayout = pContextMD->GetDictionaryLayout(); + if (!IsTypeSpecForTypicalInstantiation(SigPointer(pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec))) + goto NoSpecialCase; + } - pResultLookup->lookupKind.runtimeLookupKind = CORINFO_LOOKUP_METHODPARAM; - pResult->helper = fInstrument ? CORINFO_HELP_RUNTIMEHANDLE_METHOD_LOG : CORINFO_HELP_RUNTIMEHANDLE_METHOD; + // Just use the method descriptor that was passed in! + pResult->indirections = 0; + pResult->testForNull = 0; + pResult->testForFixup = 0; + + return; + } } + // Otherwise we must just have class type variables else { - pAllocator = pContextMT->GetLoaderAllocator(); - numGenericArgs = pContextMT->GetNumGenericArgs(); - pDictionaryLayout = pContextMT->GetClass()->GetDictionaryLayout(); + _ASSERTE(pContextMT->GetNumGenericArgs() > 0); if (pContextMD->RequiresInstMethodTableArg()) { // If we've got a vtable extra argument, go through that - pResultLookup->lookupKind.runtimeLookupKind = CORINFO_LOOKUP_CLASSPARAM; pResult->helper = fInstrument ? CORINFO_HELP_RUNTIMEHANDLE_CLASS_LOG : CORINFO_HELP_RUNTIMEHANDLE_CLASS; } // If we've got an object, go through its vtable else { _ASSERTE(pContextMD->AcquiresInstMethodTableFromThis()); - pResultLookup->lookupKind.runtimeLookupKind = CORINFO_LOOKUP_THISOBJ; pResult->helper = fInstrument ? CORINFO_HELP_RUNTIMEHANDLE_CLASS_LOG : CORINFO_HELP_RUNTIMEHANDLE_CLASS; } - } - ComputeRuntimeLookupForSharedGenericTokenStatic( - entryKind, - pResolvedToken, - pConstrainedResolvedToken, - pTemplateMD, - pAllocator, - numGenericArgs, - pDictionaryLayout, - (pResultLookup->lookupKind.runtimeLookupKind == CORINFO_LOOKUP_METHODPARAM ? 0 : pContextMT->GetNumDicts()), - pResultLookup, - TRUE, - fInstrument, - TRUE); -} - -void CEEInfo::ComputeRuntimeLookupForSharedGenericTokenStatic(DictionaryEntryKind entryKind, - CORINFO_RESOLVED_TOKEN * pResolvedToken, - CORINFO_RESOLVED_TOKEN * pConstrainedResolvedToken /* for ConstrainedMethodEntrySlot */, - MethodDesc * pTemplateMD /* for method-based slots */, - LoaderAllocator* pAllocator, - DWORD numGenericArgs, - DictionaryLayout* pDictionaryLayout, - DWORD typeDictionaryIndex, - CORINFO_LOOKUP *pResultLookup, - BOOL fEnableTypeHandleLookupOptimization, - BOOL fInstrument, - BOOL fMethodSpecContainsCallingConventionFlag) -{ - CONTRACTL{ - STANDARD_VM_CHECK; - PRECONDITION(CheckPointer(pResultLookup)); - } CONTRACTL_END; - - pResultLookup->lookupKind.needsRuntimeLookup = true; - - CORINFO_RUNTIME_LOOKUP *pResult = &pResultLookup->runtimeLookup; - pResult->signature = NULL; + if (fInstrument) + goto NoSpecialCase; - // Unless we decide otherwise, just do the lookup via a helper function - pResult->indirections = CORINFO_USEHELPER; - - // For R2R compilations, we don't generate the dictionary lookup signatures (dictionary lookups are done in a - // different way that is more version resilient... plus we can't have pointers to existing MTs/MDs in the sigs) - if (IsReadyToRunCompilation()) - return; - - if (fEnableTypeHandleLookupOptimization) - { - MethodDesc *pContextMD = GetMethodFromContext(pResolvedToken->tokenContext); - MethodTable *pContextMT = pContextMD->GetMethodTable(); - - // There is a pathological case where invalid IL refereces __Canon type directly, but there is no dictionary availabled to store the lookup. - // All callers of ComputeRuntimeLookupForSharedGenericToken have to filter out this case. We can't do much about it here. - _ASSERTE(pContextMD->IsSharedByGenericInstantiations()); - - // If we've got a method type parameter of any kind then we must look in the method desc arg - if (pContextMD->RequiresInstMethodDescArg()) + // Special cases: + // (1) Naked class type variable: look up directly in instantiation hanging off vtable + // (2) C where C is the context's class and C is sealed: just return vtable ptr + if ((entryKind == TypeHandleSlot) && (pResolvedToken->tokenType != CORINFO_TOKENKIND_Newarr)) { - if (fInstrument) - goto NoSpecialCase; - - // Special cases: - // (1) Naked method type variable: look up directly in instantiation hanging off runtime md - // (2) Reference to method-spec of current method (e.g. a recursive call) i.e. currentmeth - if ((entryKind == TypeHandleSlot) && (pResolvedToken->tokenType != CORINFO_TOKENKIND_Newarr)) + SigPointer sigptr(pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec); + CorElementType type; + IfFailThrow(sigptr.GetElemType(&type)); + if (type == ELEMENT_TYPE_VAR) { - SigPointer sigptr(pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec); - CorElementType type; - IfFailThrow(sigptr.GetElemType(&type)); - if (type == ELEMENT_TYPE_MVAR) - { - pResult->indirections = 2; - pResult->testForNull = 0; + pResult->indirections = 3; + pResult->testForNull = 0; #ifdef FEATURE_PREJIT - pResult->testForFixup = 1; + pResult->testForFixup = 1; #else - pResult->testForFixup = 0; + pResult->testForFixup = 0; #endif - pResult->offsets[0] = offsetof(InstantiatedMethodDesc, m_pPerInstInfo); + pResult->offsets[0] = MethodTable::GetOffsetOfPerInstInfo(); + pResult->offsets[1] = sizeof(TypeHandle*) * (pContextMT->GetNumDicts() - 1); + ULONG data; + IfFailThrow(sigptr.GetData(&data)); + pResult->offsets[2] = sizeof(TypeHandle) * data; - ULONG data; - IfFailThrow(sigptr.GetData(&data)); - pResult->offsets[1] = sizeof(TypeHandle) * data; - - return; - } + return; } - else if (entryKind == MethodDescSlot) + else if (type == ELEMENT_TYPE_GENERICINST && + (pContextMT->IsSealed() || pResultLookup->lookupKind.runtimeLookupKind == CORINFO_LOOKUP_CLASSPARAM)) { - // It's the context itself (i.e. a recursive call) - if (!pTemplateMD->HasSameMethodDefAs(pContextMD)) - goto NoSpecialCase; + TypeHandle thTemplate(pResolvedToken->hClass); - // Now just check that the instantiation is (!!0, ..., !!(n-1)) - if (!IsMethodSpecForTypicalInstantation(SigPointer(pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec))) + if (thTemplate.IsTypeDesc() || !thTemplate.AsMethodTable()->HasSameTypeDefAs(pContextMT)) goto NoSpecialCase; - // Type instantiation has to match too if there is one - if (pContextMT->HasInstantiation()) - { - TypeHandle thTemplate(pResolvedToken->hClass); - - if (thTemplate.IsTypeDesc() || !thTemplate.AsMethodTable()->HasSameTypeDefAs(pContextMT)) - goto NoSpecialCase; - - // This check filters out method instantiation on generic type definition, like G::M() - // We may not ever get it here. Filter it out just to be sure... - if (pResolvedToken->pTypeSpec == NULL) - goto NoSpecialCase; - - if (!IsTypeSpecForTypicalInstantiation(SigPointer(pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec))) - goto NoSpecialCase; - } + if (!IsTypeSpecForTypicalInstantiation(SigPointer(pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec))) + goto NoSpecialCase; - // Just use the method descriptor that was passed in! + // Just use the vtable pointer itself! pResult->indirections = 0; pResult->testForNull = 0; pResult->testForFixup = 0; @@ -3318,59 +3309,6 @@ void CEEInfo::ComputeRuntimeLookupForSharedGenericTokenStatic(DictionaryEntryKin return; } } - // Otherwise we must just have class type variables - else - { - _ASSERTE(pContextMT->GetNumGenericArgs() > 0); - - if (fInstrument) - goto NoSpecialCase; - - // Special cases: - // (1) Naked class type variable: look up directly in instantiation hanging off vtable - // (2) C where C is the context's class and C is sealed: just return vtable ptr - if ((entryKind == TypeHandleSlot) && (pResolvedToken->tokenType != CORINFO_TOKENKIND_Newarr)) - { - SigPointer sigptr(pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec); - CorElementType type; - IfFailThrow(sigptr.GetElemType(&type)); - if (type == ELEMENT_TYPE_VAR) - { - pResult->indirections = 3; - pResult->testForNull = 0; -#ifdef FEATURE_PREJIT - pResult->testForFixup = 1; -#else - pResult->testForFixup = 0; -#endif - pResult->offsets[0] = MethodTable::GetOffsetOfPerInstInfo(); - pResult->offsets[1] = sizeof(TypeHandle*) * (pContextMT->GetNumDicts() - 1); - ULONG data; - IfFailThrow(sigptr.GetData(&data)); - pResult->offsets[2] = sizeof(TypeHandle) * data; - - return; - } - else if (type == ELEMENT_TYPE_GENERICINST && - (pContextMT->IsSealed() || pResultLookup->lookupKind.runtimeLookupKind == CORINFO_LOOKUP_CLASSPARAM)) - { - TypeHandle thTemplate(pResolvedToken->hClass); - - if (thTemplate.IsTypeDesc() || !thTemplate.AsMethodTable()->HasSameTypeDefAs(pContextMT)) - goto NoSpecialCase; - - if (!IsTypeSpecForTypicalInstantiation(SigPointer(pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec))) - goto NoSpecialCase; - - // Just use the vtable pointer itself! - pResult->indirections = 0; - pResult->testForNull = 0; - pResult->testForFixup = 0; - - return; - } - } - } } NoSpecialCase: @@ -3381,8 +3319,8 @@ NoSpecialCase: if (pResultLookup->lookupKind.runtimeLookupKind != CORINFO_LOOKUP_METHODPARAM) { - _ASSERTE(typeDictionaryIndex > 0); - sigBuilder.AppendData(typeDictionaryIndex - 1); + _ASSERTE(pContextMT->GetNumDicts() > 0); + sigBuilder.AppendData(pContextMT->GetNumDicts() - 1); } Module * pModule = (Module *)pResolvedToken->tokenScope; @@ -3508,14 +3446,11 @@ NoSpecialCase: { SigPointer sigptr(pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec); - if (fMethodSpecContainsCallingConventionFlag) - { - BYTE etype; - IfFailThrow(sigptr.GetByte(&etype)); + BYTE etype; + IfFailThrow(sigptr.GetByte(&etype)); - // Load the generic method instantiation - THROW_BAD_FORMAT_MAYBE(etype == (BYTE)IMAGE_CEE_CS_CALLCONV_GENERICINST, 0, pModule); - } + // Load the generic method instantiation + THROW_BAD_FORMAT_MAYBE(etype == (BYTE)IMAGE_CEE_CS_CALLCONV_GENERICINST, 0, pModule); DWORD nGenericMethodArgs; IfFailThrow(sigptr.GetData(&nGenericMethodArgs)); @@ -3557,10 +3492,15 @@ NoSpecialCase: _ASSERTE(false); } + DictionaryEntrySignatureSource signatureSource = (IsCompilationProcess() ? FromZapImage : FromJIT); + // It's a method dictionary lookup if (pResultLookup->lookupKind.runtimeLookupKind == CORINFO_LOOKUP_METHODPARAM) { - if (DictionaryLayout::FindToken(pAllocator, numGenericArgs, pDictionaryLayout, pResult, &sigBuilder, 1)) + _ASSERTE(pContextMD != NULL); + _ASSERTE(pContextMD->HasMethodInstantiation()); + + if (DictionaryLayout::FindToken(pContextMD->GetLoaderAllocator(), pContextMD->GetNumGenericMethodArgs(), pContextMD->GetDictionaryLayout(), pResult, &sigBuilder, 1, signatureSource)) { pResult->testForNull = 1; pResult->testForFixup = 0; @@ -3573,7 +3513,7 @@ NoSpecialCase: // It's a class dictionary lookup (CORINFO_LOOKUP_CLASSPARAM or CORINFO_LOOKUP_THISOBJ) else { - if (DictionaryLayout::FindToken(pAllocator, numGenericArgs, pDictionaryLayout, pResult, &sigBuilder, 2)) + if (DictionaryLayout::FindToken(pContextMT->GetLoaderAllocator(), pContextMT->GetNumGenericArgs(), pContextMT->GetClass()->GetDictionaryLayout(), pResult, &sigBuilder, 2, signatureSource)) { pResult->testForNull = 1; pResult->testForFixup = 0; @@ -3582,7 +3522,7 @@ NoSpecialCase: pResult->offsets[0] = MethodTable::GetOffsetOfPerInstInfo(); // Next indirect through the dictionary appropriate to this instantiated type - pResult->offsets[1] = sizeof(TypeHandle*) * (typeDictionaryIndex - 1); + pResult->offsets[1] = sizeof(TypeHandle*) * (pContextMT->GetNumDicts() - 1); } } } diff --git a/src/coreclr/src/vm/jitinterface.h b/src/coreclr/src/vm/jitinterface.h index c712c42..03983f2 100644 --- a/src/coreclr/src/vm/jitinterface.h +++ b/src/coreclr/src/vm/jitinterface.h @@ -1151,19 +1151,6 @@ public: MethodDesc * pTemplateMD /* for method-based slots */, CORINFO_LOOKUP *pResultLookup); - static void ComputeRuntimeLookupForSharedGenericTokenStatic(DictionaryEntryKind entryKind, - CORINFO_RESOLVED_TOKEN * pResolvedToken, - CORINFO_RESOLVED_TOKEN * pConstrainedResolvedToken /* for ConstrainedMethodEntrySlot */, - MethodDesc * pTemplateMD /* for method-based slots */, - LoaderAllocator* pAllocator, - DWORD numGenericArgs, - DictionaryLayout* pDictionaryLayout, - DWORD typeDictionaryIndex, - CORINFO_LOOKUP *pResultLookup, - BOOL fEnableTypeHandleLookupOptimization, - BOOL fInstrument, - BOOL fMethodSpecContainsCallingConventionFlag); - protected: // NGen provides its own modifications to EE-JIT interface. From technical reason it cannot simply inherit // from code:CEEInfo class (because it has dependencies on VM that NGen does not want). @@ -1667,9 +1654,21 @@ struct StaticFieldAddressArgs FCDECL1(TADDR, JIT_StaticFieldAddress_Dynamic, StaticFieldAddressArgs * pArgs); FCDECL1(TADDR, JIT_StaticFieldAddressUnbox_Dynamic, StaticFieldAddressArgs * pArgs); +struct GenericHandleArgs +{ + LPVOID signature; + CORINFO_MODULE_HANDLE module; + DWORD dictionaryIndexAndSlot; +}; + +FCDECL2(CORINFO_GENERIC_HANDLE, JIT_GenericHandleMethodWithSlotAndModule, CORINFO_METHOD_HANDLE methodHnd, GenericHandleArgs * pArgs); +FCDECL2(CORINFO_GENERIC_HANDLE, JIT_GenericHandleClassWithSlotAndModule, CORINFO_CLASS_HANDLE classHnd, GenericHandleArgs * pArgs); + CORINFO_GENERIC_HANDLE JIT_GenericHandleWorker(MethodDesc *pMD, MethodTable *pMT, - LPVOID signature); + LPVOID signature, + DWORD dictionaryIndexAndSlot = -1, + Module * pModule = NULL); void ClearJitGenericHandleCache(AppDomain *pDomain); diff --git a/src/coreclr/src/vm/prestub.cpp b/src/coreclr/src/vm/prestub.cpp index 83c4015..fe2694f 100644 --- a/src/coreclr/src/vm/prestub.cpp +++ b/src/coreclr/src/vm/prestub.cpp @@ -2289,6 +2289,130 @@ TADDR GetFirstArgumentRegisterValuePtr(TransitionBlock * pTransitionBlock) return pArgument; } +void ProcessDynamicDictionaryLookup(TransitionBlock * pTransitionBlock, + Module * pModule, + Module * pInfoModule, + BYTE kind, + PCCOR_SIGNATURE pBlob, + PCCOR_SIGNATURE pBlobStart, + CORINFO_RUNTIME_LOOKUP * pResult, + DWORD * pDictionaryIndexAndSlot) +{ + TADDR genericContextPtr = *(TADDR*)GetFirstArgumentRegisterValuePtr(pTransitionBlock); + + pResult->testForFixup = pResult->testForNull = false; + pResult->signature = NULL; + pResult->indirections = CORINFO_USEHELPER; + + DWORD numGenericArgs = 0; + MethodTable* pContextMT = NULL; + MethodDesc* pContextMD = NULL; + + if (kind == ENCODE_DICTIONARY_LOOKUP_METHOD) + { + pContextMD = (MethodDesc*)genericContextPtr; + numGenericArgs = pContextMD->GetNumGenericMethodArgs(); + pResult->helper = CORINFO_HELP_RUNTIMEHANDLE_METHOD; + } + else + { + pContextMT = (MethodTable*)genericContextPtr; + + if (kind == ENCODE_DICTIONARY_LOOKUP_THISOBJ) + { + TypeHandle contextTypeHandle = ZapSig::DecodeType(pModule, pInfoModule, pBlob); + + SigPointer p(pBlob); + p.SkipExactlyOne(); + pBlob = p.GetPtr(); + + pContextMT = pContextMT->GetMethodTableMatchingParentClass(contextTypeHandle.AsMethodTable()); + } + + numGenericArgs = pContextMT->GetNumGenericArgs(); + pResult->helper = CORINFO_HELP_RUNTIMEHANDLE_CLASS; + } + + _ASSERTE(numGenericArgs > 0); + + CORCOMPILE_FIXUP_BLOB_KIND signatureKind = (CORCOMPILE_FIXUP_BLOB_KIND)CorSigUncompressData(pBlob); + + // + // Optimization cases + // + if (signatureKind == ENCODE_TYPE_HANDLE) + { + SigPointer sigptr(pBlob, -1); + + CorElementType type; + IfFailThrow(sigptr.GetElemType(&type)); + + if ((type == ELEMENT_TYPE_MVAR) && (kind == ENCODE_DICTIONARY_LOOKUP_METHOD)) + { + pResult->indirections = 2; + pResult->offsets[0] = offsetof(InstantiatedMethodDesc, m_pPerInstInfo); + + ULONG data; + IfFailThrow(sigptr.GetData(&data)); + pResult->offsets[1] = sizeof(TypeHandle) * data; + + return; + } + else if ((type == ELEMENT_TYPE_VAR) && (kind != ENCODE_DICTIONARY_LOOKUP_METHOD)) + { + pResult->indirections = 3; + pResult->offsets[0] = MethodTable::GetOffsetOfPerInstInfo(); + pResult->offsets[1] = sizeof(TypeHandle*) * (pContextMT->GetNumDicts() - 1); + + ULONG data; + IfFailThrow(sigptr.GetData(&data)); + pResult->offsets[2] = sizeof(TypeHandle) * data; + + return; + } + } + + if (pContextMT != NULL && pContextMT->GetNumDicts() > 0xFFFF) + ThrowHR(COR_E_BADIMAGEFORMAT); + + // Dictionary index and slot number are encoded in a 32-bit DWORD. The higher 16 bits + // are used for the dictionary index, and the lower 16 bits for the slot number. + *pDictionaryIndexAndSlot = (pContextMT == NULL ? 0 : pContextMT->GetNumDicts() - 1); + *pDictionaryIndexAndSlot <<= 16; + + WORD dictionarySlot; + + if (kind == ENCODE_DICTIONARY_LOOKUP_METHOD) + { + if (DictionaryLayout::FindToken(pModule->GetLoaderAllocator(), numGenericArgs, pContextMD->GetDictionaryLayout(), pResult, (BYTE*)pBlobStart, 1, FromReadyToRunImage, &dictionarySlot)) + { + pResult->testForNull = 1; + + // Indirect through dictionary table pointer in InstantiatedMethodDesc + pResult->offsets[0] = offsetof(InstantiatedMethodDesc, m_pPerInstInfo); + + *pDictionaryIndexAndSlot |= dictionarySlot; + } + } + + // It's a class dictionary lookup (CORINFO_LOOKUP_CLASSPARAM or CORINFO_LOOKUP_THISOBJ) + else + { + if (DictionaryLayout::FindToken(pModule->GetLoaderAllocator(), numGenericArgs, pContextMT->GetClass()->GetDictionaryLayout(), pResult, (BYTE*)pBlobStart, 2, FromReadyToRunImage, &dictionarySlot)) + { + pResult->testForNull = 1; + + // Indirect through dictionary table pointer in vtable + pResult->offsets[0] = MethodTable::GetOffsetOfPerInstInfo(); + + // Next indirect through the dictionary appropriate to this instantiated type + pResult->offsets[1] = sizeof(TypeHandle*) * (pContextMT->GetNumDicts() - 1); + + *pDictionaryIndexAndSlot |= dictionarySlot; + } + } +} + PCODE DynamicHelperFixup(TransitionBlock * pTransitionBlock, TADDR * pCell, DWORD sectionIndex, Module * pModule, CORCOMPILE_FIXUP_BLOB_KIND * pKind, TypeHandle * pTH, MethodDesc ** ppMD, FieldDesc ** ppFD) { STANDARD_VM_CONTRACT; @@ -2307,6 +2431,7 @@ PCODE DynamicHelperFixup(TransitionBlock * pTransitionBlock, TADDR * pCell, DWOR PTR_DWORD pSignatures = dac_cast(pNativeImage->GetRvaData(pImportSection->Signatures)); PCCOR_SIGNATURE pBlob = (BYTE *)pNativeImage->GetRvaData(pSignatures[index]); + PCCOR_SIGNATURE pBlobStart = pBlob; BYTE kind = *pBlob++; @@ -2322,7 +2447,8 @@ PCODE DynamicHelperFixup(TransitionBlock * pTransitionBlock, TADDR * pCell, DWOR TypeHandle th; MethodDesc * pMD = NULL; FieldDesc * pFD = NULL; - CORINFO_GENERICHANDLE_RESULT embedInfo; + CORINFO_RUNTIME_LOOKUP genericLookup; + DWORD dictionaryIndexAndSlot; switch (kind) { @@ -2377,111 +2503,7 @@ PCODE DynamicHelperFixup(TransitionBlock * pTransitionBlock, TADDR * pCell, DWOR case ENCODE_DICTIONARY_LOOKUP_THISOBJ: case ENCODE_DICTIONARY_LOOKUP_TYPE: case ENCODE_DICTIONARY_LOOKUP_METHOD: - { - TADDR genericContextPtr = *(TADDR*)GetFirstArgumentRegisterValuePtr(pTransitionBlock); - - DWORD numGenericArgs = 0; - MethodTable* pContextMT = NULL; - MethodDesc* pContextMD = NULL; - MethodDesc* pTemplateMD = NULL; - DictionaryLayout* pDictionaryLayout = NULL; - SigTypeContext typeOrMethodContext; - - if (kind == ENCODE_DICTIONARY_LOOKUP_METHOD) - { - pContextMD = (MethodDesc*)genericContextPtr; - numGenericArgs = pContextMD->GetNumGenericMethodArgs(); - pDictionaryLayout = pContextMD->GetDictionaryLayout(); - typeOrMethodContext = SigTypeContext(pContextMD); - embedInfo.lookup.lookupKind.runtimeLookupKind = CORINFO_LOOKUP_METHODPARAM; - embedInfo.lookup.runtimeLookup.helper = CORINFO_HELP_RUNTIMEHANDLE_METHOD; - } - else - { - pContextMT = (MethodTable*)genericContextPtr; - - if (kind == ENCODE_DICTIONARY_LOOKUP_THISOBJ) - { - TypeHandle contextTypeHandle = ZapSig::DecodeType(pModule, pInfoModule, pBlob); - - SigPointer p(pBlob); - p.SkipExactlyOne(); - pBlob = p.GetPtr(); - - pContextMT = pContextMT->GetMethodTableMatchingParentClass(contextTypeHandle.AsMethodTable()); - embedInfo.lookup.lookupKind.runtimeLookupKind = CORINFO_LOOKUP_THISOBJ; - } - else - { - embedInfo.lookup.lookupKind.runtimeLookupKind = CORINFO_LOOKUP_CLASSPARAM; - } - - numGenericArgs = pContextMT->GetNumGenericArgs(); - pDictionaryLayout = pContextMT->GetClass()->GetDictionaryLayout(); - typeOrMethodContext = SigTypeContext(pContextMT); - embedInfo.lookup.runtimeLookup.helper = CORINFO_HELP_RUNTIMEHANDLE_CLASS; - } - - CORINFO_RESOLVED_TOKEN resolvedToken; - INDEBUG(memset(&resolvedToken, 0xCC, sizeof(resolvedToken))); - resolvedToken.tokenType = CORINFO_TOKENKIND_Ldtoken; // Reasonable default value to use that works - resolvedToken.tokenScope = (CORINFO_MODULE_HANDLE)pModule; - resolvedToken.pMethodSpec = resolvedToken.pTypeSpec = NULL; - resolvedToken.cbMethodSpec = resolvedToken.cbTypeSpec = -1; - - DictionaryEntryKind entryKind = EmptySlot; - CORCOMPILE_FIXUP_BLOB_KIND signatureKind = (CORCOMPILE_FIXUP_BLOB_KIND)CorSigUncompressData(pBlob); - - switch (signatureKind) - { - case ENCODE_TYPE_HANDLE: - { - entryKind = TypeHandleSlot; - resolvedToken.pTypeSpec = pBlob; - } - break; - - case ENCODE_METHOD_HANDLE: - case ENCODE_METHOD_ENTRY: - case ENCODE_VIRTUAL_ENTRY: - { - if (signatureKind == ENCODE_METHOD_HANDLE) - entryKind = MethodDescSlot; - else if (signatureKind == ENCODE_METHOD_ENTRY) - entryKind = MethodEntrySlot; - else - entryKind = DispatchStubAddrSlot; - - pTemplateMD = ZapSig::DecodeMethod(pModule, pInfoModule, pBlob, &typeOrMethodContext, &th, &resolvedToken.pTypeSpec, &resolvedToken.pMethodSpec); - resolvedToken.hMethod = (CORINFO_METHOD_HANDLE)pTemplateMD; - resolvedToken.hClass = (CORINFO_CLASS_HANDLE)pTemplateMD->GetMethodTable_NoLogging(); - } - break; - - // TODO: Support for the rest of the dictionary signature kinds - - default: - _ASSERTE(!"Unexpected CORCOMPILE_FIXUP_BLOB_KIND"); - ThrowHR(COR_E_BADIMAGEFORMAT); - } - - CEEInfo::ComputeRuntimeLookupForSharedGenericTokenStatic( - entryKind, - &resolvedToken, - NULL, // pConstrainedResolvedToken for ConstrainedMethodEntrySlot - pTemplateMD, - pModule->GetLoaderAllocator(), - numGenericArgs, - pDictionaryLayout, - pContextMT == NULL ? 0 : pContextMT->GetNumDicts(), - &embedInfo.lookup, - FALSE, // fEnableTypeHandleLookupOptimization, - FALSE, // fInstrument - FALSE // fMethodSpecContainsCallingConventionFlag - ); - - _ASSERTE(embedInfo.lookup.lookupKind.needsRuntimeLookup); - } + ProcessDynamicDictionaryLookup(pTransitionBlock, pModule, pInfoModule, kind, pBlob, pBlobStart, &genericLookup, &dictionaryIndexAndSlot); break; default: @@ -2693,7 +2715,7 @@ PCODE DynamicHelperFixup(TransitionBlock * pTransitionBlock, TADDR * pCell, DWOR case ENCODE_DICTIONARY_LOOKUP_TYPE: case ENCODE_DICTIONARY_LOOKUP_METHOD: { - pHelper = DynamicHelpers::CreateDictionaryLookupHelper(pModule->GetLoaderAllocator(), &embedInfo.lookup.runtimeLookup); + pHelper = DynamicHelpers::CreateDictionaryLookupHelper(pModule->GetLoaderAllocator(), &genericLookup, dictionaryIndexAndSlot, pModule); } break; diff --git a/src/coreclr/src/vm/readytoruninfo.h b/src/coreclr/src/vm/readytoruninfo.h index 24bdae8..28efe01 100644 --- a/src/coreclr/src/vm/readytoruninfo.h +++ b/src/coreclr/src/vm/readytoruninfo.h @@ -138,7 +138,7 @@ public: static PCODE CreateReturnIndirConst(LoaderAllocator * pAllocator, TADDR arg, INT8 offset); static PCODE CreateHelperWithTwoArgs(LoaderAllocator * pAllocator, TADDR arg, PCODE target); static PCODE CreateHelperWithTwoArgs(LoaderAllocator * pAllocator, TADDR arg, TADDR arg2, PCODE target); - static PCODE CreateDictionaryLookupHelper(LoaderAllocator * pAllocator, CORINFO_RUNTIME_LOOKUP * pLookup); + static PCODE CreateDictionaryLookupHelper(LoaderAllocator * pAllocator, CORINFO_RUNTIME_LOOKUP * pLookup, DWORD dictionaryIndexAndSlot, Module * pModule); }; #endif // _READYTORUNINFO_H_ diff --git a/src/coreclr/src/zap/zapinfo.cpp b/src/coreclr/src/zap/zapinfo.cpp index 831c61b..232570f 100644 --- a/src/coreclr/src/zap/zapinfo.cpp +++ b/src/coreclr/src/zap/zapinfo.cpp @@ -3487,11 +3487,11 @@ bool ZapInfo::getReadyToRunHelper(CORINFO_RESOLVED_TOKEN * pResolvedToken, pImport = m_pImage->GetImportTable()->GetDictionaryLookupCell( (CORCOMPILE_FIXUP_BLOB_KIND)(ENCODE_DICTIONARY_LOOKUP_METHOD | fAtypicalCallsite), pResolvedToken, pGenericLookupKind); } - else if (pGenericLookupKind->runtimeLookupKind == CORINFO_LOOKUP_THISOBJ) - { - pImport = m_pImage->GetImportTable()->GetDictionaryLookupCell( - (CORCOMPILE_FIXUP_BLOB_KIND)(ENCODE_DICTIONARY_LOOKUP_THISOBJ | fAtypicalCallsite), pResolvedToken, pGenericLookupKind); -} + else if (pGenericLookupKind->runtimeLookupKind == CORINFO_LOOKUP_THISOBJ) + { + pImport = m_pImage->GetImportTable()->GetDictionaryLookupCell( + (CORCOMPILE_FIXUP_BLOB_KIND)(ENCODE_DICTIONARY_LOOKUP_THISOBJ | fAtypicalCallsite), pResolvedToken, pGenericLookupKind); + } else { _ASSERTE(pGenericLookupKind->runtimeLookupKind == CORINFO_LOOKUP_CLASSPARAM); -- 2.7.4