diff --git a/src/coreclr/inc/CrstTypes.def b/src/coreclr/inc/CrstTypes.def index 4bdfd7409f8447..3d240e40cb9533 100644 --- a/src/coreclr/inc/CrstTypes.def +++ b/src/coreclr/inc/CrstTypes.def @@ -527,4 +527,8 @@ Crst PerfMap End Crst InterfaceDispatchGlobalLists +End + +Crst CallStubCache + AcquiredBefore LoaderHeap End \ No newline at end of file diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 2309db3383ecc3..bd810a4811edd7 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -3189,8 +3189,7 @@ class ICorDynamicInfo : public ICorStaticInfo CORINFO_CONST_LOOKUP * pLookup ) = 0; - // Generate a cookie based on the signature that would needs to be passed - // to CORINFO_HELP_PINVOKE_CALLI + // Generate a cookie based on the signature to pass to CORINFO_HELP_PINVOKE_CALLI virtual void* GetCookieForPInvokeCalliSig( CORINFO_SIG_INFO* szMetaSig, void** ppIndirection = NULL @@ -3202,6 +3201,10 @@ class ICorDynamicInfo : public ICorStaticInfo CORINFO_SIG_INFO* szMetaSig ) = 0; + // Generate a cookie based on the signature to pass to INTOP_CALLI in the interpreter. + virtual void* GetCookieForInterpreterCalliSig( + CORINFO_SIG_INFO* szMetaSig) = 0; + // Gets a handle that is checked to see if the current method is // included in "JustMyCode" virtual CORINFO_JUST_MY_CODE_HANDLE getJustMyCodeHandle( diff --git a/src/coreclr/inc/crsttypes_generated.h b/src/coreclr/inc/crsttypes_generated.h index 6a5ef2352d24d0..55f445271a7f0a 100644 --- a/src/coreclr/inc/crsttypes_generated.h +++ b/src/coreclr/inc/crsttypes_generated.h @@ -20,105 +20,106 @@ enum CrstType CrstAssemblyLoader = 2, CrstAvailableClass = 3, CrstAvailableParamTypes = 4, - CrstCCompRC = 5, - CrstClassFactInfoHash = 6, - CrstClassInit = 7, - CrstClrNotification = 8, - CrstCodeFragmentHeap = 9, - CrstCodeVersioning = 10, - CrstCOMCallWrapper = 11, - CrstCOMWrapperCache = 12, - CrstDataTest1 = 13, - CrstDataTest2 = 14, - CrstDbgTransport = 15, - CrstDeadlockDetection = 16, - CrstDebuggerController = 17, - CrstDebuggerFavorLock = 18, - CrstDebuggerHeapExecMemLock = 19, - CrstDebuggerHeapLock = 20, - CrstDebuggerJitInfo = 21, - CrstDebuggerMutex = 22, - CrstDynamicIL = 23, - CrstDynamicMT = 24, - CrstEtwTypeLogHash = 25, - CrstEventPipe = 26, - CrstEventStore = 27, - CrstException = 28, - CrstExecutableAllocatorLock = 29, - CrstFCall = 30, - CrstFrozenObjectHeap = 31, - CrstFuncPtrStubs = 32, - CrstFusionAppCtx = 33, - CrstGCCover = 34, - CrstGenericDictionaryExpansion = 35, - CrstGlobalStrLiteralMap = 36, - CrstHandleTable = 37, - CrstIbcProfile = 38, - CrstIJWFixupData = 39, - CrstIJWHash = 40, - CrstILStubGen = 41, - CrstInlineTrackingMap = 42, - CrstInstMethodHashTable = 43, - CrstInterfaceDispatchGlobalLists = 44, - CrstInterop = 45, - CrstInteropData = 46, - CrstIsJMCMethod = 47, - CrstISymUnmanagedReader = 48, - CrstJit = 49, - CrstJitInlineTrackingMap = 50, - CrstJitPatchpoint = 51, - CrstJumpStubCache = 52, - CrstLeafLock = 53, - CrstListLock = 54, - CrstLoaderAllocator = 55, - CrstLoaderAllocatorReferences = 56, - CrstLoaderHeap = 57, - CrstManagedObjectWrapperMap = 58, - CrstMethodDescBackpatchInfoTracker = 59, - CrstMethodTableExposedObject = 60, - CrstModule = 61, - CrstModuleLookupTable = 62, - CrstMulticoreJitHash = 63, - CrstMulticoreJitManager = 64, - CrstNativeImageEagerFixups = 65, - CrstNativeImageLoad = 66, - CrstNotifyGdb = 67, - CrstPEImage = 68, - CrstPendingTypeLoadEntry = 69, - CrstPerfMap = 70, - CrstPgoData = 71, - CrstPinnedByrefValidation = 72, - CrstPinnedHeapHandleTable = 73, - CrstProfilerGCRefDataFreeList = 74, - CrstProfilingAPIStatus = 75, - CrstRCWCache = 76, - CrstRCWCleanupList = 77, - CrstReadyToRunEntryPointToMethodDescMap = 78, - CrstReflection = 79, - CrstReJITGlobalRequest = 80, - CrstSigConvert = 81, - CrstSingleUseLock = 82, - CrstStressLog = 83, - CrstStubCache = 84, - CrstStubDispatchCache = 85, - CrstSyncBlockCache = 86, - CrstSyncHashLock = 87, - CrstSystemDomain = 88, - CrstSystemDomainDelayedUnloadList = 89, - CrstThreadIdDispenser = 90, - CrstThreadLocalStorageLock = 91, - CrstThreadStore = 92, - CrstTieredCompilation = 93, - CrstTypeEquivalenceMap = 94, - CrstTypeIDMap = 95, - CrstUMEntryThunkCache = 96, - CrstUMEntryThunkFreeListLock = 97, - CrstUniqueStack = 98, - CrstUnresolvedClassLock = 99, - CrstUnwindInfoTableLock = 100, - CrstVSDIndirectionCellLock = 101, - CrstWrapperTemplate = 102, - kNumberOfCrstTypes = 103 + CrstCallStubCache = 5, + CrstCCompRC = 6, + CrstClassFactInfoHash = 7, + CrstClassInit = 8, + CrstClrNotification = 9, + CrstCodeFragmentHeap = 10, + CrstCodeVersioning = 11, + CrstCOMCallWrapper = 12, + CrstCOMWrapperCache = 13, + CrstDataTest1 = 14, + CrstDataTest2 = 15, + CrstDbgTransport = 16, + CrstDeadlockDetection = 17, + CrstDebuggerController = 18, + CrstDebuggerFavorLock = 19, + CrstDebuggerHeapExecMemLock = 20, + CrstDebuggerHeapLock = 21, + CrstDebuggerJitInfo = 22, + CrstDebuggerMutex = 23, + CrstDynamicIL = 24, + CrstDynamicMT = 25, + CrstEtwTypeLogHash = 26, + CrstEventPipe = 27, + CrstEventStore = 28, + CrstException = 29, + CrstExecutableAllocatorLock = 30, + CrstFCall = 31, + CrstFrozenObjectHeap = 32, + CrstFuncPtrStubs = 33, + CrstFusionAppCtx = 34, + CrstGCCover = 35, + CrstGenericDictionaryExpansion = 36, + CrstGlobalStrLiteralMap = 37, + CrstHandleTable = 38, + CrstIbcProfile = 39, + CrstIJWFixupData = 40, + CrstIJWHash = 41, + CrstILStubGen = 42, + CrstInlineTrackingMap = 43, + CrstInstMethodHashTable = 44, + CrstInterfaceDispatchGlobalLists = 45, + CrstInterop = 46, + CrstInteropData = 47, + CrstIsJMCMethod = 48, + CrstISymUnmanagedReader = 49, + CrstJit = 50, + CrstJitInlineTrackingMap = 51, + CrstJitPatchpoint = 52, + CrstJumpStubCache = 53, + CrstLeafLock = 54, + CrstListLock = 55, + CrstLoaderAllocator = 56, + CrstLoaderAllocatorReferences = 57, + CrstLoaderHeap = 58, + CrstManagedObjectWrapperMap = 59, + CrstMethodDescBackpatchInfoTracker = 60, + CrstMethodTableExposedObject = 61, + CrstModule = 62, + CrstModuleLookupTable = 63, + CrstMulticoreJitHash = 64, + CrstMulticoreJitManager = 65, + CrstNativeImageEagerFixups = 66, + CrstNativeImageLoad = 67, + CrstNotifyGdb = 68, + CrstPEImage = 69, + CrstPendingTypeLoadEntry = 70, + CrstPerfMap = 71, + CrstPgoData = 72, + CrstPinnedByrefValidation = 73, + CrstPinnedHeapHandleTable = 74, + CrstProfilerGCRefDataFreeList = 75, + CrstProfilingAPIStatus = 76, + CrstRCWCache = 77, + CrstRCWCleanupList = 78, + CrstReadyToRunEntryPointToMethodDescMap = 79, + CrstReflection = 80, + CrstReJITGlobalRequest = 81, + CrstSigConvert = 82, + CrstSingleUseLock = 83, + CrstStressLog = 84, + CrstStubCache = 85, + CrstStubDispatchCache = 86, + CrstSyncBlockCache = 87, + CrstSyncHashLock = 88, + CrstSystemDomain = 89, + CrstSystemDomainDelayedUnloadList = 90, + CrstThreadIdDispenser = 91, + CrstThreadLocalStorageLock = 92, + CrstThreadStore = 93, + CrstTieredCompilation = 94, + CrstTypeEquivalenceMap = 95, + CrstTypeIDMap = 96, + CrstUMEntryThunkCache = 97, + CrstUMEntryThunkFreeListLock = 98, + CrstUniqueStack = 99, + CrstUnresolvedClassLock = 100, + CrstUnwindInfoTableLock = 101, + CrstVSDIndirectionCellLock = 102, + CrstWrapperTemplate = 103, + kNumberOfCrstTypes = 104 }; #endif // __CRST_TYPES_INCLUDED @@ -134,6 +135,7 @@ int g_rgCrstLevelMap[] = 13, // CrstAssemblyLoader 3, // CrstAvailableClass 4, // CrstAvailableParamTypes + 3, // CrstCallStubCache -1, // CrstCCompRC 14, // CrstClassFactInfoHash 10, // CrstClassInit @@ -242,6 +244,7 @@ LPCSTR g_rgCrstNameMap[] = "CrstAssemblyLoader", "CrstAvailableClass", "CrstAvailableParamTypes", + "CrstCallStubCache", "CrstCCompRC", "CrstClassFactInfoHash", "CrstClassInit", diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index 73e330058f9d2d..58556badc9fae5 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -590,6 +590,9 @@ void* GetCookieForPInvokeCalliSig( CORINFO_SIG_INFO* szMetaSig, void** ppIndirection) override; +void* GetCookieForInterpreterCalliSig( + CORINFO_SIG_INFO* szMetaSig) override; + bool canGetCookieForPInvokeCalliSig( CORINFO_SIG_INFO* szMetaSig) override; diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 736504aa9d126a..b92a98126054bd 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -37,11 +37,11 @@ #include -constexpr GUID JITEEVersionIdentifier = { /* ecc9bc7e-9223-4af6-af2f-b63e89c09279 */ - 0xecc9bc7e, - 0x9223, - 0x4af6, - {0xaf, 0x2f, 0xb6, 0x3e, 0x89, 0xc0, 0x92, 0x79} +constexpr GUID JITEEVersionIdentifier = { /* f9f14a77-1225-42d0-a21e-6d8b45506fc6 */ + 0xf9f14a77, + 0x1225, + 0x42d0, + {0xa2, 0x1e, 0x6d, 0x8b, 0x45, 0x50, 0x6f, 0xc6} }; #endif // JIT_EE_VERSIONING_GUID_H diff --git a/src/coreclr/interpreter/compiler.cpp b/src/coreclr/interpreter/compiler.cpp index 5f6b789b696519..132bdf18c6443f 100644 --- a/src/coreclr/interpreter/compiler.cpp +++ b/src/coreclr/interpreter/compiler.cpp @@ -2242,6 +2242,32 @@ InterpCompiler::InterpEmbedGenericResult InterpCompiler::EmitGenericHandle(CORIN return result; } +void InterpCompiler::EmitPushCORINFO_LOOKUP(const CORINFO_LOOKUP& lookup) +{ + PushStackType(StackTypeI, NULL); + int resultVar = m_pStackPointer[-1].var; + + CORINFO_RUNTIME_LOOKUP_KIND runtimeLookupKind = lookup.lookupKind.runtimeLookupKind; + if (runtimeLookupKind == CORINFO_LOOKUP_METHODPARAM) + { + AddIns(INTOP_GENERICLOOKUP_METHOD); + } + else if (runtimeLookupKind == CORINFO_LOOKUP_THISOBJ) + { + AddIns(INTOP_GENERICLOOKUP_THIS); + } + else + { + AddIns(INTOP_GENERICLOOKUP_CLASS); + } + CORINFO_RUNTIME_LOOKUP *pRuntimeLookup = (CORINFO_RUNTIME_LOOKUP*)AllocMethodData(sizeof(CORINFO_RUNTIME_LOOKUP)); + *pRuntimeLookup = lookup.runtimeLookup; + m_pLastNewIns->data[0] = GetDataItemIndex(pRuntimeLookup); + + m_pLastNewIns->SetSVar(getParamArgIndex()); + m_pLastNewIns->SetDVar(resultVar); +} + int InterpCompiler::EmitGenericHandleAsVar(const CORINFO_GENERICHANDLE_RESULT &embedInfo) { PushStackType(StackTypeI, NULL); @@ -2281,27 +2307,49 @@ int InterpCompiler::EmitGenericHandleAsVar(const CORINFO_GENERICHANDLE_RESULT &e return resultVar; } -void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* constrainedClass, bool readonly, bool tailcall, bool newObj) +void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* constrainedClass, bool readonly, bool tailcall, bool newObj, bool isCalli) { uint32_t token = getU4LittleEndian(m_ip + 1); bool isVirtual = (*m_ip == CEE_CALLVIRT); CORINFO_RESOLVED_TOKEN resolvedCallToken; + CORINFO_CALL_INFO callInfo; bool doCallInsteadOfNew = false; - ResolveToken(token, newObj ? CORINFO_TOKENKIND_Method : CORINFO_TOKENKIND_NewObj, &resolvedCallToken); + int callIFunctionPointerVar = -1; + void* calliCookie = NULL; - CORINFO_CALL_INFO callInfo; - CORINFO_CALLINFO_FLAGS flags = (CORINFO_CALLINFO_FLAGS)(CORINFO_CALLINFO_ALLOWINSTPARAM | CORINFO_CALLINFO_SECURITYCHECKS | CORINFO_CALLINFO_DISALLOW_STUB); - if (isVirtual) - flags = (CORINFO_CALLINFO_FLAGS)(flags | CORINFO_CALLINFO_CALLVIRT); + if (isCalli) + { + // Suppress uninitialized use warning. + memset(&resolvedCallToken, 0, sizeof(resolvedCallToken)); + memset(&callInfo, 0, sizeof(callInfo)); + + resolvedCallToken.token = token; + resolvedCallToken.tokenContext = METHOD_BEING_COMPILED_CONTEXT(); + resolvedCallToken.tokenScope = m_methodInfo->scope; - m_compHnd->getCallInfo(&resolvedCallToken, constrainedClass, m_methodInfo->ftn, flags, &callInfo); + m_compHnd->findSig(m_methodInfo->scope, token, METHOD_BEING_COMPILED_CONTEXT(), &callInfo.sig); - if (EmitCallIntrinsics(callInfo.hMethod, callInfo.sig)) + callIFunctionPointerVar = m_pStackPointer[-1].var; + m_pStackPointer--; + calliCookie = m_compHnd->GetCookieForInterpreterCalliSig(&callInfo.sig); + } + else { - m_ip += 5; - return; + + ResolveToken(token, newObj ? CORINFO_TOKENKIND_Method : CORINFO_TOKENKIND_NewObj, &resolvedCallToken); + + CORINFO_CALLINFO_FLAGS flags = (CORINFO_CALLINFO_FLAGS)(CORINFO_CALLINFO_ALLOWINSTPARAM | CORINFO_CALLINFO_SECURITYCHECKS | CORINFO_CALLINFO_DISALLOW_STUB); + if (isVirtual) + flags = (CORINFO_CALLINFO_FLAGS)(flags | CORINFO_CALLINFO_CALLVIRT); + + m_compHnd->getCallInfo(&resolvedCallToken, constrainedClass, m_methodInfo->ftn, flags, &callInfo); + if (EmitCallIntrinsics(callInfo.hMethod, callInfo.sig)) + { + m_ip += 5; + return; + } } if (callInfo.classFlags & CORINFO_FLG_VAROBJSIZE) @@ -2506,6 +2554,12 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* constrainedClass, bool rea } m_pLastNewIns->data[0] = GetDataItemIndex(callInfo.hMethod); } + else if (isCalli) + { + AddIns(INTOP_CALLI); + m_pLastNewIns->data[0] = GetDataItemIndex(calliCookie); + m_pLastNewIns->SetSVars2(CALL_ARGS_SVAR, callIFunctionPointerVar); + } else if ((callInfo.classFlags & CORINFO_FLG_ARRAY) && !readonly) { CORINFO_SIG_INFO ctorSignature; @@ -3985,14 +4039,20 @@ int InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo) break; case CEE_CALLVIRT: case CEE_CALL: - EmitCall(constrainedClass, readonly, tailcall, false /*newObj*/); + EmitCall(constrainedClass, readonly, tailcall, false /*newObj*/, false /*isCalli*/); + constrainedClass = NULL; + readonly = false; + tailcall = false; + break; + case CEE_CALLI: + EmitCall(NULL /*constrainedClass*/, false /* readonly*/, false /* tailcall*/, false /*newObj*/, true /*isCalli*/); constrainedClass = NULL; readonly = false; tailcall = false; break; case CEE_NEWOBJ: { - EmitCall(NULL /*constrainedClass*/, false /* readonly*/, false /* tailcall*/, true /*newObj*/); + EmitCall(NULL /*constrainedClass*/, false /* readonly*/, false /* tailcall*/, true /*newObj*/, false /*isCalli*/); constrainedClass = NULL; readonly = false; tailcall = false; @@ -4460,6 +4520,45 @@ int InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo) m_ip++; linkBBlocks = false; break; + + case CEE_LDFTN: + { + CORINFO_RESOLVED_TOKEN resolvedToken; + uint32_t token = getU4LittleEndian(m_ip + 1); + ResolveToken(token, CORINFO_TOKENKIND_Method, &resolvedToken); + + CORINFO_CALL_INFO callInfo; + m_compHnd->getCallInfo(&resolvedToken, constrainedClass, m_methodInfo->ftn, (CORINFO_CALLINFO_FLAGS)(CORINFO_CALLINFO_SECURITYCHECKS| CORINFO_CALLINFO_LDFTN), &callInfo); + constrainedClass = NULL; + + if (callInfo.kind == CORINFO_CALL) + { + CORINFO_CONST_LOOKUP embedInfo; + m_compHnd->getFunctionFixedEntryPoint(callInfo.hMethod, true, &embedInfo); + + switch (embedInfo.accessType) + { + case IAT_VALUE: + AddIns(INTOP_LDPTR); + break; + case IAT_PVALUE: + AddIns(INTOP_LDPTR_DEREF); + break; + default: + assert(!"Unexpected access type for function pointer"); + } + PushInterpType(InterpTypeI, NULL); + m_pLastNewIns->SetDVar(m_pStackPointer[-1].var); + m_pLastNewIns->data[0] = GetDataItemIndex(embedInfo.handle); + } + else + { + EmitPushCORINFO_LOOKUP(callInfo.codePointerLookup); + } + + m_ip += 5; + break; + } default: assert(0); break; diff --git a/src/coreclr/interpreter/compiler.h b/src/coreclr/interpreter/compiler.h index 47d38f3e1928cb..ecaf73ebb5c506 100644 --- a/src/coreclr/interpreter/compiler.h +++ b/src/coreclr/interpreter/compiler.h @@ -385,8 +385,13 @@ class InterpCompiler EmbedParent = 2, }; InterpEmbedGenericResult EmitGenericHandle(CORINFO_RESOLVED_TOKEN* resolvedToken, GenericHandleEmbedOptions options); + + // Do a generic handle lookup and acquire the result as either a var or a data item. int EmitGenericHandleAsVar(const CORINFO_GENERICHANDLE_RESULT &embedInfo); + // Emit a generic dictionary lookup and push the result onto the interpreter stack + void EmitPushCORINFO_LOOKUP(const CORINFO_LOOKUP& lookup); + void* AllocMethodData(size_t numBytes); public: // FIXME Mempool allocation currently leaks. We need to add an allocator and then @@ -484,7 +489,7 @@ class InterpCompiler void EmitUnaryArithmeticOp(int32_t opBase); void EmitShiftOp(int32_t opBase); void EmitCompareOp(int32_t opBase); - void EmitCall(CORINFO_RESOLVED_TOKEN* constrainedClass, bool readonly, bool tailcall, bool newObj); + void EmitCall(CORINFO_RESOLVED_TOKEN* constrainedClass, bool readonly, bool tailcall, bool newObj, bool isCalli); bool EmitCallIntrinsics(CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO sig); void EmitLdind(InterpType type, CORINFO_CLASS_HANDLE clsHnd, int32_t offset); void EmitStind(InterpType type, CORINFO_CLASS_HANDLE clsHnd, int32_t offset, bool reverseSVarOrder); diff --git a/src/coreclr/interpreter/intops.def b/src/coreclr/interpreter/intops.def index 22147c95d26aa8..ac75b30d256229 100644 --- a/src/coreclr/interpreter/intops.def +++ b/src/coreclr/interpreter/intops.def @@ -22,6 +22,7 @@ OPDEF(INTOP_LDC_R4, "ldc.r4", 3, 1, 0, InterpOpFloat) OPDEF(INTOP_LDC_R8, "ldc.r8", 4, 1, 0, InterpOpDouble) OPDEF(INTOP_LDPTR, "ldptr", 3, 1, 0, InterpOpLdPtr) +OPDEF(INTOP_LDPTR_DEREF, "ldptr.deref", 3, 1, 0, InterpOpLdPtr) OPDEF(INTOP_NEWARR, "newarr", 5, 1, 1, InterpOpInt) OPDEF(INTOP_NEWMDARR, "newmdarr", 5, 1, 1, InterpOpTwoInts) @@ -322,6 +323,7 @@ OPDEF(INTOP_LDFLDA, "ldflda", 4, 1, 1, InterpOpInt) // Calls OPDEF(INTOP_CALL, "call", 4, 1, 1, InterpOpMethodHandle) +OPDEF(INTOP_CALLI, "call", 5, 1, 2, InterpOpLdPtr) OPDEF(INTOP_CALLVIRT, "callvirt", 4, 1, 1, InterpOpMethodHandle) OPDEF(INTOP_NEWOBJ, "newobj", 5, 1, 1, InterpOpMethodHandle) OPDEF(INTOP_NEWOBJ_VAR, "newobj.var", 5, 1, 2, InterpOpMethodHandle) diff --git a/src/coreclr/jit/ICorJitInfo_names_generated.h b/src/coreclr/jit/ICorJitInfo_names_generated.h index cf50808bb3efe8..e8e089f0b1dd59 100644 --- a/src/coreclr/jit/ICorJitInfo_names_generated.h +++ b/src/coreclr/jit/ICorJitInfo_names_generated.h @@ -146,6 +146,7 @@ DEF_CLR_API(embedGenericHandle) DEF_CLR_API(getLocationOfThisType) DEF_CLR_API(getAddressOfPInvokeTarget) DEF_CLR_API(GetCookieForPInvokeCalliSig) +DEF_CLR_API(GetCookieForInterpreterCalliSig) DEF_CLR_API(canGetCookieForPInvokeCalliSig) DEF_CLR_API(getJustMyCodeHandle) DEF_CLR_API(GetProfilingHandle) diff --git a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp index bcc31380d5ce14..1e2de0e19a7e3e 100644 --- a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp +++ b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp @@ -1397,6 +1397,15 @@ void* WrapICorJitInfo::GetCookieForPInvokeCalliSig( return temp; } +void* WrapICorJitInfo::GetCookieForInterpreterCalliSig( + CORINFO_SIG_INFO* szMetaSig) +{ + API_ENTER(GetCookieForInterpreterCalliSig); + void* temp = wrapHnd->GetCookieForInterpreterCalliSig(szMetaSig); + API_LEAVE(GetCookieForInterpreterCalliSig); + return temp; +} + bool WrapICorJitInfo::canGetCookieForPInvokeCalliSig( CORINFO_SIG_INFO* szMetaSig) { diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index d4fd37a6b56034..ff722c78bcb7a7 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3627,6 +3627,9 @@ private void getLocationOfThisType(CORINFO_METHOD_STRUCT_* context, ref CORINFO_ } } + private void* GetCookieForInterpreterCalliSig(CORINFO_SIG_INFO* szMetaSig) + { throw new NotImplementedException("GetCookieForInterpreterCalliSig"); } + private void* GetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, ref void* ppIndirection) { throw new NotImplementedException("GetCookieForPInvokeCalliSig"); } #pragma warning disable CA1822 // Mark members as static diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs index 8b1a6f5d8d0442..2ffbf7d07ff832 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs @@ -2106,6 +2106,21 @@ private static void _getAddressOfPInvokeTarget(IntPtr thisHandle, IntPtr* ppExce } } + [UnmanagedCallersOnly] + private static void* _GetCookieForInterpreterCalliSig(IntPtr thisHandle, IntPtr* ppException, CORINFO_SIG_INFO* szMetaSig) + { + var _this = GetThis(thisHandle); + try + { + return _this.GetCookieForInterpreterCalliSig(szMetaSig); + } + catch (Exception ex) + { + *ppException = _this.AllocException(ex); + return default; + } + } + [UnmanagedCallersOnly] private static byte _canGetCookieForPInvokeCalliSig(IntPtr thisHandle, IntPtr* ppException, CORINFO_SIG_INFO* szMetaSig) { @@ -2637,7 +2652,7 @@ private static uint _getJitFlags(IntPtr thisHandle, IntPtr* ppException, CORJIT_ private static IntPtr GetUnmanagedCallbacks() { - void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 178); + void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 179); callbacks[0] = (delegate* unmanaged)&_isIntrinsic; callbacks[1] = (delegate* unmanaged)&_notifyMethodInfoUsage; @@ -2781,42 +2796,43 @@ private static IntPtr GetUnmanagedCallbacks() callbacks[139] = (delegate* unmanaged)&_getLocationOfThisType; callbacks[140] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; callbacks[141] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; - callbacks[142] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; - callbacks[143] = (delegate* unmanaged)&_getJustMyCodeHandle; - callbacks[144] = (delegate* unmanaged)&_GetProfilingHandle; - callbacks[145] = (delegate* unmanaged)&_getCallInfo; - callbacks[146] = (delegate* unmanaged)&_getStaticFieldContent; - callbacks[147] = (delegate* unmanaged)&_getObjectContent; - callbacks[148] = (delegate* unmanaged)&_getStaticFieldCurrentClass; - callbacks[149] = (delegate* unmanaged)&_getVarArgsHandle; - callbacks[150] = (delegate* unmanaged)&_canGetVarArgsHandle; - callbacks[151] = (delegate* unmanaged)&_constructStringLiteral; - callbacks[152] = (delegate* unmanaged)&_emptyStringLiteral; - callbacks[153] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; - callbacks[154] = (delegate* unmanaged)&_GetDelegateCtor; - callbacks[155] = (delegate* unmanaged)&_MethodCompileComplete; - callbacks[156] = (delegate* unmanaged)&_getTailCallHelpers; - callbacks[157] = (delegate* unmanaged)&_getAsyncResumptionStub; - callbacks[158] = (delegate* unmanaged)&_convertPInvokeCalliToCall; - callbacks[159] = (delegate* unmanaged)&_notifyInstructionSetUsage; - callbacks[160] = (delegate* unmanaged)&_updateEntryPointForTailCall; - callbacks[161] = (delegate* unmanaged)&_allocMem; - callbacks[162] = (delegate* unmanaged)&_reserveUnwindInfo; - callbacks[163] = (delegate* unmanaged)&_allocUnwindInfo; - callbacks[164] = (delegate* unmanaged)&_allocGCInfo; - callbacks[165] = (delegate* unmanaged)&_setEHcount; - callbacks[166] = (delegate* unmanaged)&_setEHinfo; - callbacks[167] = (delegate* unmanaged)&_logMsg; - callbacks[168] = (delegate* unmanaged)&_doAssert; - callbacks[169] = (delegate* unmanaged)&_reportFatalError; - callbacks[170] = (delegate* unmanaged)&_getPgoInstrumentationResults; - callbacks[171] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; - callbacks[172] = (delegate* unmanaged)&_recordCallSite; - callbacks[173] = (delegate* unmanaged)&_recordRelocation; - callbacks[174] = (delegate* unmanaged)&_getRelocTypeHint; - callbacks[175] = (delegate* unmanaged)&_getExpectedTargetArchitecture; - callbacks[176] = (delegate* unmanaged)&_getJitFlags; - callbacks[177] = (delegate* unmanaged)&_getSpecialCopyHelper; + callbacks[142] = (delegate* unmanaged)&_GetCookieForInterpreterCalliSig; + callbacks[143] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; + callbacks[144] = (delegate* unmanaged)&_getJustMyCodeHandle; + callbacks[145] = (delegate* unmanaged)&_GetProfilingHandle; + callbacks[146] = (delegate* unmanaged)&_getCallInfo; + callbacks[147] = (delegate* unmanaged)&_getStaticFieldContent; + callbacks[148] = (delegate* unmanaged)&_getObjectContent; + callbacks[149] = (delegate* unmanaged)&_getStaticFieldCurrentClass; + callbacks[150] = (delegate* unmanaged)&_getVarArgsHandle; + callbacks[151] = (delegate* unmanaged)&_canGetVarArgsHandle; + callbacks[152] = (delegate* unmanaged)&_constructStringLiteral; + callbacks[153] = (delegate* unmanaged)&_emptyStringLiteral; + callbacks[154] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; + callbacks[155] = (delegate* unmanaged)&_GetDelegateCtor; + callbacks[156] = (delegate* unmanaged)&_MethodCompileComplete; + callbacks[157] = (delegate* unmanaged)&_getTailCallHelpers; + callbacks[158] = (delegate* unmanaged)&_getAsyncResumptionStub; + callbacks[159] = (delegate* unmanaged)&_convertPInvokeCalliToCall; + callbacks[160] = (delegate* unmanaged)&_notifyInstructionSetUsage; + callbacks[161] = (delegate* unmanaged)&_updateEntryPointForTailCall; + callbacks[162] = (delegate* unmanaged)&_allocMem; + callbacks[163] = (delegate* unmanaged)&_reserveUnwindInfo; + callbacks[164] = (delegate* unmanaged)&_allocUnwindInfo; + callbacks[165] = (delegate* unmanaged)&_allocGCInfo; + callbacks[166] = (delegate* unmanaged)&_setEHcount; + callbacks[167] = (delegate* unmanaged)&_setEHinfo; + callbacks[168] = (delegate* unmanaged)&_logMsg; + callbacks[169] = (delegate* unmanaged)&_doAssert; + callbacks[170] = (delegate* unmanaged)&_reportFatalError; + callbacks[171] = (delegate* unmanaged)&_getPgoInstrumentationResults; + callbacks[172] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; + callbacks[173] = (delegate* unmanaged)&_recordCallSite; + callbacks[174] = (delegate* unmanaged)&_recordRelocation; + callbacks[175] = (delegate* unmanaged)&_getRelocTypeHint; + callbacks[176] = (delegate* unmanaged)&_getExpectedTargetArchitecture; + callbacks[177] = (delegate* unmanaged)&_getJitFlags; + callbacks[178] = (delegate* unmanaged)&_getSpecialCopyHelper; return (IntPtr)callbacks; } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 9e75ee782fab1b..2ae78da1fde8f0 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -398,12 +398,12 @@ public enum CorInfoCallConvExtension public enum CORINFO_CALLINFO_FLAGS { CORINFO_CALLINFO_NONE = 0x0000, - CORINFO_CALLINFO_ALLOWINSTPARAM = 0x0001, // Can the compiler generate code to pass an instantiation parameters? Simple compilers should not use this flag - CORINFO_CALLINFO_CALLVIRT = 0x0002, // Is it a virtual call? + CORINFO_CALLINFO_ALLOWINSTPARAM = 0x0001, // Can the compiler generate code to pass an instantiation parameters? Simple compilers should not use this flag + CORINFO_CALLINFO_CALLVIRT = 0x0002, // Is it a virtual call? // UNUSED = 0x0004, - // UNUSED = 0x0008, - CORINFO_CALLINFO_SECURITYCHECKS = 0x0010, // Perform security checks. - CORINFO_CALLINFO_LDFTN = 0x0020, // Resolving target of LDFTN + CORINFO_CALLINFO_DISALLOW_STUB = 0x0008, // Do not use a stub for this call, even if it is a virtual call. + CORINFO_CALLINFO_SECURITYCHECKS = 0x0010, // Perform security checks. + CORINFO_CALLINFO_LDFTN = 0x0020, // Resolving target of LDFTN // UNUSED = 0x0040, } diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index 6030b745b013e4..19513a96118ec9 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -307,6 +307,7 @@ FUNCTIONS void getLocationOfThisType(CORINFO_METHOD_HANDLE context, CORINFO_LOOKUP_KIND* pLookupKind); void getAddressOfPInvokeTarget(CORINFO_METHOD_HANDLE method, CORINFO_CONST_LOOKUP *pLookup); void* GetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, void ** ppIndirection); + void* GetCookieForInterpreterCalliSig(CORINFO_SIG_INFO* szMetaSig); bool canGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig); CORINFO_JUST_MY_CODE_HANDLE getJustMyCodeHandle(CORINFO_METHOD_HANDLE method, CORINFO_JUST_MY_CODE_HANDLE**ppIndirection); void GetProfilingHandle(bool* pbHookFunction, void **pProfilerHandle, bool* pbIndirectedHandles); diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h index 729a09de403eac..d082fdf6f100a4 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h @@ -153,6 +153,7 @@ struct JitInterfaceCallbacks void (* getLocationOfThisType)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE context, CORINFO_LOOKUP_KIND* pLookupKind); void (* getAddressOfPInvokeTarget)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE method, CORINFO_CONST_LOOKUP* pLookup); void* (* GetCookieForPInvokeCalliSig)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_SIG_INFO* szMetaSig, void** ppIndirection); + void* (* GetCookieForInterpreterCalliSig)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_SIG_INFO* szMetaSig); bool (* canGetCookieForPInvokeCalliSig)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_SIG_INFO* szMetaSig); CORINFO_JUST_MY_CODE_HANDLE (* getJustMyCodeHandle)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE method, CORINFO_JUST_MY_CODE_HANDLE** ppIndirection); void (* GetProfilingHandle)(void * thisHandle, CorInfoExceptionClass** ppException, bool* pbHookFunction, void** pProfilerHandle, bool* pbIndirectedHandles); @@ -1577,6 +1578,15 @@ class JitInterfaceWrapper : public ICorJitInfo return temp; } + virtual void* GetCookieForInterpreterCalliSig( + CORINFO_SIG_INFO* szMetaSig) +{ + CorInfoExceptionClass* pException = nullptr; + void* temp = _callbacks->GetCookieForInterpreterCalliSig(_thisHandle, &pException, szMetaSig); + if (pException != nullptr) throw pException; + return temp; +} + virtual bool canGetCookieForPInvokeCalliSig( CORINFO_SIG_INFO* szMetaSig) { diff --git a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h index cefd14edde954f..571380c275ba4b 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h @@ -713,6 +713,14 @@ struct GetCookieForPInvokeCalliSigValue DWORD token; }; +struct GetCookieForInterpreterCalliSigValue +{ + DWORD cbSig; + DWORD pSig_Index; + DWORDLONG scope; + DWORD token; +}; + struct CanGetCookieForPInvokeCalliSigValue { DWORDLONG scope; diff --git a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h index f81a8680ab5d74..6b9d17db03c6c3 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h @@ -74,6 +74,7 @@ LWM(GetClassSize, DWORDLONG, DWORD) LWM(GetHeapClassSize, DWORDLONG, DWORD) LWM(CanAllocateOnStack, DWORDLONG, DWORD) LWM(GetCookieForPInvokeCalliSig, GetCookieForPInvokeCalliSigValue, DLDL) +LWM(GetCookieForInterpreterCalliSig, GetCookieForInterpreterCalliSigValue, DLDL) LWM(GetDefaultComparerClass, DWORDLONG, DWORDLONG) LWM(GetDefaultEqualityComparerClass, DWORDLONG, DWORDLONG) LWM(GetSZArrayHelperEnumeratorClass, DWORDLONG, DWORDLONG) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index e414a1d0b00c21..7cca81d48e8d9a 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -6169,6 +6169,45 @@ LPVOID MethodContext::repGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig return (CORINFO_VARARGS_HANDLE)value.B; } +void MethodContext::recGetCookieForInterpreterCalliSig(CORINFO_SIG_INFO* szMetaSig, LPVOID result) +{ + if (GetCookieForInterpreterCalliSig == nullptr) + GetCookieForInterpreterCalliSig = new LightWeightMap(); + + GetCookieForInterpreterCalliSigValue key; + ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding + key.cbSig = (DWORD)szMetaSig->cbSig; + key.pSig_Index = (DWORD)GetCookieForInterpreterCalliSig->AddBuffer((unsigned char*)szMetaSig->pSig, szMetaSig->cbSig); + key.scope = CastHandle(szMetaSig->scope); + key.token = (DWORD)szMetaSig->token; + + DLDL value; + value.A = CastPointer(result); + + GetCookieForInterpreterCalliSig->Add(key, value); + DEBUG_REC(dmpGetCookieForInterpreterCalliSig(key, value)); +} + +void MethodContext::dmpGetCookieForInterpreterCalliSig(const GetCookieForInterpreterCalliSigValue& key, DLDL value) +{ + printf("GetCookieForInterpreterCalliSig NYI"); +} +LPVOID MethodContext::repGetCookieForInterpreterCalliSig(CORINFO_SIG_INFO* szMetaSig) +{ + GetCookieForInterpreterCalliSigValue key; + ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding + key.cbSig = (DWORD)szMetaSig->cbSig; + key.pSig_Index = (DWORD)GetCookieForInterpreterCalliSig->Contains((unsigned char*)szMetaSig->pSig, szMetaSig->cbSig); + key.scope = CastHandle(szMetaSig->scope); + key.token = (DWORD)szMetaSig->token; + + DLDL value = LookupByKeyOrMissNoMessage(GetCookieForInterpreterCalliSig, key); + + DEBUG_REP(dmpGetCookieForInterpreterCalliSig(key, value)); + + return (LPVOID)value.A; +} + void MethodContext::recCanGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, bool result) { if (CanGetCookieForPInvokeCalliSig == nullptr) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h index 1a25a3a24c5a96..ca8abe396a0860 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h @@ -762,6 +762,10 @@ class MethodContext void dmpGetCookieForPInvokeCalliSig(const GetCookieForPInvokeCalliSigValue& key, DLDL value); LPVOID repGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, void** ppIndirection); + LPVOID repGetCookieForInterpreterCalliSig(CORINFO_SIG_INFO* szMetaSig); + void recGetCookieForInterpreterCalliSig(CORINFO_SIG_INFO* szMetaSig, LPVOID result); + void dmpGetCookieForInterpreterCalliSig(const GetCookieForInterpreterCalliSigValue& key, DLDL value); + void recCanGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, bool result); void dmpCanGetCookieForPInvokeCalliSig(const CanGetCookieForPInvokeCalliSigValue& key, DWORD value); bool repCanGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig); @@ -1218,6 +1222,7 @@ enum mcPackets Packet_NotifyInstructionSetUsage = 229, Packet_GetAsyncInfo = 230, Packet_GetAsyncResumptionStub = 231, + Packet_GetCookieForInterpreterCalliSig = 232, }; 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 835d6a22d448de..c2d1780bb8ff8f 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -1590,8 +1590,7 @@ void interceptor_ICJI::getAddressOfPInvokeTarget(CORINFO_METHOD_HANDLE method, C mc->recGetAddressOfPInvokeTarget(method, pLookup); } -// Generate a cookie based on the signature that would needs to be passed -// to CORINFO_HELP_PINVOKE_CALLI +// Generate a cookie based on the signature to pass to CORINFO_HELP_PINVOKE_CALLI LPVOID interceptor_ICJI::GetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, void** ppIndirection) { mc->cr->AddCall("GetCookieForPInvokeCalliSig"); @@ -1600,6 +1599,16 @@ LPVOID interceptor_ICJI::GetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig return temp; } +// Generate a cookie based on the signature to pass to INTOP_CALLI +LPVOID interceptor_ICJI::GetCookieForInterpreterCalliSig(CORINFO_SIG_INFO* szMetaSig) +{ + mc->cr->AddCall("GetCookieForInterpreterCalliSig"); + LPVOID temp = original_ICorJitInfo->GetCookieForInterpreterCalliSig(szMetaSig); + mc->recGetCookieForInterpreterCalliSig(szMetaSig, temp); + return temp; +} + + // returns true if a VM cookie can be generated for it (might be false due to cross-module // inlining, in which case the inlining should be aborted) bool interceptor_ICJI::canGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig) diff --git a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp index 60d344324e98cd..96732557ece4d8 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp @@ -1149,6 +1149,13 @@ void* interceptor_ICJI::GetCookieForPInvokeCalliSig( return original_ICorJitInfo->GetCookieForPInvokeCalliSig(szMetaSig, ppIndirection); } +void* interceptor_ICJI::GetCookieForInterpreterCalliSig( + CORINFO_SIG_INFO* szMetaSig) +{ + mcs->AddCall("GetCookieForInterpreterCalliSig"); + return original_ICorJitInfo->GetCookieForInterpreterCalliSig(szMetaSig); +} + bool interceptor_ICJI::canGetCookieForPInvokeCalliSig( CORINFO_SIG_INFO* szMetaSig) { diff --git a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp index c04554b5844f63..1fd028a5cc1bf4 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp @@ -1007,6 +1007,12 @@ void* interceptor_ICJI::GetCookieForPInvokeCalliSig( return original_ICorJitInfo->GetCookieForPInvokeCalliSig(szMetaSig, ppIndirection); } +void* interceptor_ICJI::GetCookieForInterpreterCalliSig( + CORINFO_SIG_INFO* szMetaSig) +{ + return original_ICorJitInfo->GetCookieForInterpreterCalliSig(szMetaSig); +} + bool interceptor_ICJI::canGetCookieForPInvokeCalliSig( CORINFO_SIG_INFO* szMetaSig) { diff --git a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp index 98c22710cbbb9b..0380ddf67e4a3b 100644 --- a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp @@ -1378,8 +1378,7 @@ void MyICJI::getAddressOfPInvokeTarget(CORINFO_METHOD_HANDLE method, CORINFO_CON jitInstance->mc->repGetAddressOfPInvokeTarget(method, pLookup); } -// Generate a cookie based on the signature that would needs to be passed -// to CORINFO_HELP_PINVOKE_CALLI +// Generate a cookie based on the signature to pass to CORINFO_HELP_PINVOKE_CALLI LPVOID MyICJI::GetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, void** ppIndirection) { jitInstance->mc->cr->AddCall("GetCookieForPInvokeCalliSig"); @@ -1394,6 +1393,13 @@ bool MyICJI::canGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig) return jitInstance->mc->repCanGetCookieForPInvokeCalliSig(szMetaSig); } +// Generate a cookie based on the signature to pass to INTOP_CALLI +LPVOID MyICJI::GetCookieForInterpreterCalliSig(CORINFO_SIG_INFO* szMetaSig) +{ + jitInstance->mc->cr->AddCall("GetCookieForInterpreterCalliSig"); + return jitInstance->mc->repGetCookieForInterpreterCalliSig(szMetaSig); +} + // Gets a handle that is checked to see if the current method is // included in "JustMyCode" CORINFO_JUST_MY_CODE_HANDLE MyICJI::getJustMyCodeHandle(CORINFO_METHOD_HANDLE method, diff --git a/src/coreclr/vm/callstubgenerator.cpp b/src/coreclr/vm/callstubgenerator.cpp index a8558b9f58a091..d8b8771c31213f 100644 --- a/src/coreclr/vm/callstubgenerator.cpp +++ b/src/coreclr/vm/callstubgenerator.cpp @@ -1193,6 +1193,144 @@ CallStubHeader *CallStubGenerator::GenerateCallStub(MethodDesc *pMD, AllocMemTra m_interpreterToNative = interpreterToNative; MetaSig sig(pMD); + // Allocate space for the routines. The size of the array is conservatively set to twice the number of arguments + // plus one slot for the target pointer and reallocated to the real size at the end. + PCODE *pRoutines = (PCODE*)alloca(ComputeTempStorageSize(sig)); + + ComputeCallStub(sig, pRoutines); + + LoaderAllocator *pLoaderAllocator = pMD->GetLoaderAllocator(); + S_SIZE_T finalStubSize(sizeof(CallStubHeader) + m_routineIndex * sizeof(PCODE)); + void *pHeaderStorage = pamTracker->Track(pLoaderAllocator->GetHighFrequencyHeap()->AllocMem(finalStubSize)); + + CallStubHeader *pHeader = new (pHeaderStorage) CallStubHeader(m_routineIndex, pRoutines, ALIGN_UP(m_totalStackSize, STACK_ALIGN_SIZE), m_pInvokeFunction); + + return pHeader; +} + +struct CachedCallStubKey +{ + CachedCallStubKey(int32_t hashCode, int numRoutines, PCODE *pRoutines, int totalStackSize, CallStubHeader::InvokeFunctionPtr pInvokeFunction) + : HashCode(hashCode), NumRoutines(numRoutines), TotalStackSize(totalStackSize), Invoke(pInvokeFunction), Routines(pRoutines) + { + } + + bool operator==(const CachedCallStubKey& other) const + { + LIMITED_METHOD_CONTRACT; + + if (HashCode != other.HashCode || NumRoutines != other.NumRoutines || TotalStackSize != other.TotalStackSize || Invoke != other.Invoke) + return false; + + for (int i = 0; i < NumRoutines; i++) + { + if (Routines[i] != other.Routines[i]) + return false; + } + return true; + } + + const int32_t HashCode = 0; + const int NumRoutines = 0; + const int TotalStackSize = 0; + const CallStubHeader::InvokeFunctionPtr Invoke = NULL; // Pointer to the invoke function + const PCODE *Routines; +}; + +struct CachedCallStub +{ + CachedCallStub(int32_t hashCode, int numRoutines, PCODE *pRoutines, int totalStackSize, CallStubHeader::InvokeFunctionPtr pInvokeFunction) : + HashCode(hashCode), + Header(numRoutines, pRoutines, totalStackSize, pInvokeFunction) + { + } + + int32_t HashCode; + CallStubHeader Header; + + CachedCallStubKey GetKey() + { + return CachedCallStubKey( + HashCode, + Header.NumRoutines, + &Header.Routines[0], + Header.TotalStackSize, + Header.Invoke); + } + + static COUNT_T Hash(const CachedCallStubKey& key) + { + LIMITED_METHOD_CONTRACT; + return key.HashCode; + } +}; + +static CrstStatic s_callStubCrst; + +typedef PtrSHashTraits CallStubCacheTraits; + +typedef SHash CallStubCacheHash; +static CallStubCacheHash* s_callStubCache; + +void InitCallStubGenerator() +{ + STANDARD_VM_CONTRACT; + + s_callStubCrst.Init(CrstCallStubCache); + s_callStubCache = new CallStubCacheHash; +} + +CallStubHeader *CallStubGenerator::GenerateCallStubForSig(MetaSig &sig) +{ + STANDARD_VM_CONTRACT; + + // Allocate space for the routines. The size of the array is conservatively set to twice the number of arguments + // plus one slot for the target pointer and reallocated to the real size at the end. + PCODE *pRoutines = (PCODE*)alloca(ComputeTempStorageSize(sig)); + + m_interpreterToNative = true; // We always generate the interpreter to native call stub here + + ComputeCallStub(sig, pRoutines); + + xxHash hashState; + for (int i = 0; i < m_routineIndex; i++) + { + hashState.AddPointer((void*)pRoutines[i]); + } + hashState.Add(m_totalStackSize); + hashState.AddPointer((void*)m_pInvokeFunction); + + CachedCallStubKey cachedHeaderKey( + hashState.ToHashCode(), + m_routineIndex, + pRoutines, + ALIGN_UP(m_totalStackSize, STACK_ALIGN_SIZE), + m_pInvokeFunction); + + CrstHolder lockHolder(&s_callStubCrst); + CachedCallStub *pCachedHeader = s_callStubCache->Lookup(cachedHeaderKey); + if (pCachedHeader != NULL) + { + // The stub is already cached, return the cached header + return &pCachedHeader->Header; + } + else + { + AllocMemTracker amTracker; + // The stub is not cached, create a new header and add it to the cache + // We only need to allocate the actual pRoutines array, and then we can just use the cachedHeader we already constructed + void* pHeaderStorage = amTracker.Track(SystemDomain::GetGlobalLoaderAllocator()->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(CachedCallStub) + m_routineIndex * sizeof(PCODE)))); + CachedCallStub *pHeader = new (pHeaderStorage) CachedCallStub(cachedHeaderKey.HashCode, m_routineIndex, pRoutines, ALIGN_UP(m_totalStackSize, STACK_ALIGN_SIZE), m_pInvokeFunction); + s_callStubCache->Add(pHeader); + amTracker.SuppressRelease(); + + _ASSERTE(s_callStubCache->Lookup(cachedHeaderKey) == pHeader); + return &pHeader->Header; + } +}; + +void CallStubGenerator::ComputeCallStub(MetaSig &sig, PCODE *pRoutines) +{ ArgIterator argIt(&sig); m_r1 = NoRange; // indicates that there is no active range of general purpose registers @@ -1225,10 +1363,6 @@ CallStubHeader *CallStubGenerator::GenerateCallStub(MethodDesc *pMD, AllocMemTra } } - // Allocate space for the routines. The size of the array is conservatively set to twice the number of arguments - // plus one slot for the target pointer and reallocated to the real size at the end. - PCODE *pRoutines = (PCODE*)alloca(sizeof(CallStubHeader) + ((numArgs + 1) * 2 + 1) * sizeof(PCODE)); - if (argIt.HasParamType()) { // In the Interpreter calling convention the argument after the "this" pointer is the parameter type @@ -1322,26 +1456,17 @@ CallStubHeader *CallStubGenerator::GenerateCallStub(MethodDesc *pMD, AllocMemTra pRoutines[m_routineIndex++] = ((int64_t)(m_s2 - m_s1 + 1) << 32) | m_s1; } - CallStubHeader::InvokeFunctionPtr pInvokeFunction = NULL; ReturnType returnType = GetReturnType(&argIt); if (m_interpreterToNative) { - pInvokeFunction = GetInvokeFunctionPtr(returnType); + m_pInvokeFunction = GetInvokeFunctionPtr(returnType); m_routineIndex++; // Reserve one extra slot for the target method pointer } else { pRoutines[m_routineIndex++] = GetInterpreterReturnTypeHandler(returnType); } - - LoaderAllocator *pLoaderAllocator = pMD->GetLoaderAllocator(); - S_SIZE_T finalStubSize(sizeof(CallStubHeader) + m_routineIndex * sizeof(PCODE)); - void *pHeaderStorage = pamTracker->Track(pLoaderAllocator->GetHighFrequencyHeap()->AllocMem(finalStubSize)); - - CallStubHeader *pHeader = new (pHeaderStorage) CallStubHeader(m_routineIndex, pRoutines, ALIGN_UP(m_totalStackSize, STACK_ALIGN_SIZE), pInvokeFunction); - - return pHeader; } // Process the argument described by argLocDesc. This function is called for each argument in the method signature. diff --git a/src/coreclr/vm/callstubgenerator.h b/src/coreclr/vm/callstubgenerator.h index 09519287d0dcec..ee15fbcdbe4ca9 100644 --- a/src/coreclr/vm/callstubgenerator.h +++ b/src/coreclr/vm/callstubgenerator.h @@ -44,6 +44,14 @@ struct CallStubHeader _ASSERTE(target != 0); Routines[NumRoutines - 1] = target; } + + size_t GetSize() + { + LIMITED_METHOD_CONTRACT; + + // The size of the CallStubHeader is the size of the header plus the size of the routines array. + return sizeof(CallStubHeader) + (NumRoutines * sizeof(PCODE)); + } }; // This class generates the call stub for a given method. It uses the calling convention of the target CPU to determine @@ -83,20 +91,21 @@ class CallStubGenerator static const int NoRange = -1; // Current sequential range of general purpose registers used to pass arguments. - int m_r1; - int m_r2; + int m_r1 = NoRange; + int m_r2 = NoRange; // Current sequential range of floating point registers used to pass arguments. - int m_x1; - int m_x2; + int m_x1 = NoRange; + int m_x2 = NoRange; // Current sequential range of offsets of stack arguments used to pass arguments. - int m_s1; - int m_s2; + int m_s1 = NoRange; + int m_s2 = NoRange; // The index of the next routine to store in the Routines array. - int m_routineIndex; + int m_routineIndex = 0; // The total stack size used for the arguments. - int m_totalStackSize; + int m_totalStackSize = 0; - bool m_interpreterToNative; + CallStubHeader::InvokeFunctionPtr m_pInvokeFunction = NULL; + bool m_interpreterToNative = false; #ifndef UNIX_AMD64_ABI PCODE GetGPRegRefRoutine(int r); @@ -118,6 +127,20 @@ class CallStubGenerator public: // Generate the call stub for the given method. CallStubHeader *GenerateCallStub(MethodDesc *pMD, AllocMemTracker *pamTracker, bool interpreterToNative); + CallStubHeader *GenerateCallStubForSig(MetaSig &sig); + +private: + static size_t ComputeTempStorageSize(const MetaSig& sig) + { + int numArgs = sig.NumFixedArgs() + (sig.HasThis() ? 1 : 0); + + // The size of the temporary storage is the size of the CallStubHeader plus the size of the routines array. + // The size of the routines array is twice the number of arguments plus one slot for the target method pointer. + return sizeof(CallStubHeader) + ((numArgs + 1) * 2 + 1) * sizeof(PCODE); + } + void ComputeCallStub(MetaSig &sig, PCODE *pRoutines); }; +void InitCallStubGenerator(); + #endif // CALLSTUBGENERATOR_H diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index bd00b1c7a5406d..7c9e0f401049d1 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -164,6 +164,10 @@ #include "cdacplatformmetadata.hpp" #include "minipal/time.h" +#ifdef FEATURE_INTERPRETER +#include "callstubgenerator.h" +#endif + #ifndef TARGET_UNIX #include "dwreport.h" #endif // !TARGET_UNIX @@ -659,6 +663,10 @@ void EEStartupHelper() Thread::StaticInitialize(); +#ifdef FEATURE_INTERPRETER + InitCallStubGenerator(); +#endif // FEATURE_INTERPRETER + JITInlineTrackingMap::StaticInitialize(); MethodDescBackpatchInfoTracker::StaticInitialize(); CodeVersionManager::StaticInitialize(); diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 4e9d559425c164..f29ae684e12027 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -33,10 +33,10 @@ void InvokeCompiledMethod(MethodDesc *pMD, int8_t *pArgs, int8_t *pRet) } CONTRACTL_END - CallStubGenerator callStubGenerator; CallStubHeader *pHeader = pMD->GetCallStub(); if (pHeader == NULL) { + CallStubGenerator callStubGenerator; GCX_PREEMP(); AllocMemTracker amTracker; @@ -59,6 +59,26 @@ void InvokeCompiledMethod(MethodDesc *pMD, int8_t *pArgs, int8_t *pRet) pHeader->Invoke(pHeader->Routines, pArgs, pRet, pHeader->TotalStackSize); } +void InvokeCalliStub(PCODE ftn, CallStubHeader *stubHeaderTemplate, int8_t *pArgs, int8_t *pRet) +{ + CONTRACTL + { + THROWS; + MODE_ANY; + PRECONDITION(CheckPointer((void*)ftn)); + PRECONDITION(CheckPointer(stubHeaderTemplate)); + } + CONTRACTL_END + + // CallStubHeaders encode their destination addresses in the Routines array, so they need to be + // copied to a local buffer before we can actually set their target address. + uint8_t* actualCallStub = (uint8_t*)alloca(stubHeaderTemplate->GetSize()); + memcpy(actualCallStub, stubHeaderTemplate, stubHeaderTemplate->GetSize()); + CallStubHeader *pHeader = (CallStubHeader*)actualCallStub; + pHeader->SetTarget(ftn); // The method to call + pHeader->Invoke(pHeader->Routines, pArgs, pRet, pHeader->TotalStackSize); +} + // Create call stub for calling interpreted methods from JITted/AOTed code. CallStubHeader *CreateNativeToInterpreterCallStub(InterpMethod* pInterpMethod) { @@ -387,6 +407,10 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr LOCAL_VAR(ip[1], void*) = pMethod->pDataItems[ip[2]]; ip += 3; break; + case INTOP_LDPTR_DEREF: + LOCAL_VAR(ip[1], void*) = *(void**)pMethod->pDataItems[ip[2]]; + ip += 3; + break; case INTOP_RET: // Return stack slot sized value *(int64_t*)pFrame->pRetVal = LOCAL_VAR(ip[1], int64_t); @@ -1451,6 +1475,20 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr goto CALL_INTERP_METHOD; } + case INTOP_CALLI: + { + returnOffset = ip[1]; + callArgsOffset = ip[2]; + int32_t calliFunctionPointerVar = ip[3]; + int32_t calliCookie = ip[4]; + + CallStubHeader *pCallStub = (CallStubHeader*)pMethod->pDataItems[calliCookie]; + ip += 5; + + InvokeCalliStub(LOCAL_VAR(calliFunctionPointerVar, PCODE), pCallStub, stack + callArgsOffset, stack + returnOffset); + break; + } + case INTOP_CALL: { returnOffset = ip[1]; @@ -1475,7 +1513,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr pInterpreterFrame->SetTopInterpMethodContextFrame(pFrame); GCX_PREEMP(); // Attempt to setup the interpreter code for the target method. - if (!(targetMethod->IsFCall() || (targetMethod->IsCtor() && targetMethod->GetMethodTable()->IsString()))) + if (targetMethod->IsIL() || targetMethod->IsNoMetadata()) { targetMethod->PrepareInitialCode(CallerGCMode::Coop); } diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 26b9b246d58e68..dd412e765b75f1 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -58,6 +58,10 @@ #include "pgo.h" #endif +#ifdef FEATURE_INTERPRETER +#include "callstubgenerator.h" +#endif + #include "tailcallhelp.h" #include "patchpointinfo.h" @@ -11258,8 +11262,34 @@ void CEEJitInfo::SetDebugInfo(PTR_BYTE pDebugInfo) ((CodeHeader*)m_CodeHeaderRW)->SetDebugInfo(pDebugInfo); } +LPVOID CEEInfo::GetCookieForInterpreterCalliSig(CORINFO_SIG_INFO* szMetaSig) +{ + _ASSERTE(!"GetCookieForInterpreterCalliSig should not be called in CEEJitInfo"); + return NULL; +} + #ifdef FEATURE_INTERPRETER +LPVOID CInterpreterJitInfo::GetCookieForInterpreterCalliSig(CORINFO_SIG_INFO* szMetaSig) +{ + void* result = NULL; + JIT_TO_EE_TRANSITION(); + + Module* module = GetModule(szMetaSig->scope); + + Instantiation classInst = Instantiation((TypeHandle*) szMetaSig->sigInst.classInst, szMetaSig->sigInst.classInstCount); + Instantiation methodInst = Instantiation((TypeHandle*) szMetaSig->sigInst.methInst, szMetaSig->sigInst.methInstCount); + SigTypeContext typeContext = SigTypeContext(classInst, methodInst); + + MetaSig sig(szMetaSig->pSig, szMetaSig->cbSig, module, &typeContext); + CallStubGenerator callStubGenerator; + result = callStubGenerator.GenerateCallStubForSig(sig); + + EE_TO_JIT_TRANSITION(); + + return result; +} + void CInterpreterJitInfo::allocMem(AllocMemArgs *pArgs) { JIT_TO_EE_TRANSITION(); diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index ceb999589a2562..63cd935fa7aaed 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -974,6 +974,8 @@ class CInterpreterJitInfo final : public CEECodeGenInfo void BackoutJitData(EECodeGenManager * jitMgr) override; void SetDebugInfo(PTR_BYTE pDebugInfo) override; + + LPVOID GetCookieForInterpreterCalliSig(CORINFO_SIG_INFO* szMetaSig) override; }; #endif // FEATURE_INTERPRETER diff --git a/src/coreclr/vm/siginfo.hpp b/src/coreclr/vm/siginfo.hpp index b0c7b3820230a2..7b6dc043d639b9 100644 --- a/src/coreclr/vm/siginfo.hpp +++ b/src/coreclr/vm/siginfo.hpp @@ -613,7 +613,7 @@ class MetaSig // Does not count the "this" argument (which is not reflected on the // sig.) 64-bit arguments are counted as one argument. //------------------------------------------------------------------------ - UINT NumFixedArgs() + UINT NumFixedArgs() const { LIMITED_METHOD_DAC_CONTRACT; return m_nArgs; @@ -698,7 +698,7 @@ class MetaSig //---------------------------------------------------------- // Has a 'this' pointer? //---------------------------------------------------------- - BOOL HasThis() + BOOL HasThis() const { LIMITED_METHOD_CONTRACT; diff --git a/src/coreclr/vm/typehashingalgorithms.h b/src/coreclr/vm/typehashingalgorithms.h index fc428c1ef3de6a..20bfad70ca2f58 100644 --- a/src/coreclr/vm/typehashingalgorithms.h +++ b/src/coreclr/vm/typehashingalgorithms.h @@ -259,6 +259,15 @@ class xxHash uint32_t _length = 0; public: + void AddPointer(void* ptr) + { +#ifdef HOST_64BIT + Add((uint32_t)(UINT_PTR)ptr); + Add((uint32_t)(((UINT64)ptr) >> 32)); +#else + Add((uint32_t)(UINT_PTR)ptr); +#endif + } void Add(uint32_t val) { // The original xxHash works as follows: diff --git a/src/tests/JIT/interpreter/Interpreter.cs b/src/tests/JIT/interpreter/Interpreter.cs index 61b40a7d97988d..40f2d3b3657b91 100644 --- a/src/tests/JIT/interpreter/Interpreter.cs +++ b/src/tests/JIT/interpreter/Interpreter.cs @@ -860,6 +860,14 @@ public static void RunInterpreterTests() if (!TestSharedGenerics()) Environment.FailFast(null); + Console.WriteLine("TestDelegate"); + if (!TestDelegate()) + Environment.FailFast(null); + + Console.WriteLine("TestCalli"); + if (!TestCalli()) + Environment.FailFast(null); + System.GC.Collect(); Console.WriteLine("All tests passed successfully!"); @@ -2246,4 +2254,109 @@ public static bool TestMdArray() int[,] a = { { 1, 2 }, { 3, 4 } }; return a[0, 1] == 2; } + + private static int _fieldA; + private static int _fieldB; + private static int _fieldResult; + private static void MultiplyAandB() + { + _fieldResult = _fieldA * _fieldB; + } + + private static Type _typeFromFill; + + private static void Fill() + { + _typeFromFill = typeof(T); + } + + public static bool TestDelegate() + { + _fieldA = 3; + _fieldB = 1; + _fieldResult = 0; + + // This tests delegate creation, ldftn, and invocation via the "Invoke" method + Action func = new Action(MultiplyAandB); + + _fieldB = 4; + Console.WriteLine("CallingFunc first time"); + func(); + Console.WriteLine("Return CallingFunc first time"); + if (_fieldResult != 12) + { + Console.WriteLine("Delegate test failed: expected 12, got " + _fieldResult); + return false; + } + + _fieldB = 3; + Console.WriteLine("CallingFunc second time"); + func(); + Console.WriteLine("Return CallingFunc second time"); + if (_fieldResult != 9) + { + Console.WriteLine("Delegate test failed: expected 9, got " + _fieldResult); + return false; + } + return true; + } + + public unsafe static bool TestCalli() + { + delegate* func = &MultiplyAandB; + + _fieldA = 3; + _fieldB = 1; + _fieldResult = 0; + + // This tests ldftn, and calli + + _fieldB = 4; + Console.WriteLine("CallingFunc first time"); + func(); + Console.WriteLine("Return CallingFunc first time"); + if (_fieldResult != 12) + { + Console.WriteLine("Calli test failed: expected 12, got " + _fieldResult); + return false; + } + + _fieldB = 3; + Console.WriteLine("CallingFunc second time"); + func(); + Console.WriteLine("Return CallingFunc second time"); + if (_fieldResult != 9) + { + Console.WriteLine("Calli test failed: expected 9, got " + _fieldResult); + return false; + } + + GetCalliGeneric()(); + if (_typeFromFill != typeof(int)) + { + Console.WriteLine("Calli generic test failed: expected int, got " + _typeFromFill); + return false; + } + + + GetCalliGeneric()(); + if (_typeFromFill != typeof(object)) + { + Console.WriteLine("Calli generic test failed: expected object, got " + _typeFromFill); + return false; + } + + GetCalliGeneric()(); + if (_typeFromFill != typeof(string)) + { + Console.WriteLine("Calli generic test failed: expected string, got " + _typeFromFill); + return false; + } + return true; + } + + private static unsafe delegate* GetCalliGeneric() + { + return &Fill; + } }