From 2182eeed0c6e1fab5960f45099a68643c35441cc Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 9 Jun 2025 15:15:37 -0700 Subject: [PATCH 1/9] Implementation of ldftn and calli - This implements ldftn by matching the implementation in the JIT. It appears to work correctly with shared generics, but the Interpreter stub handling of the generic param argument is missing, so that doesn't work yet - This implementation leverages our CallStubGenerator to create stubs for calli instructions. To get the appropriate call stub to the right location, we misuse the GetCookieForPInvokeCalliSig to pass the needed cookie around. - As a bonus its now possible to call delegates which take no parameters. --- src/coreclr/inc/CrstTypes.def | 4 + src/coreclr/inc/crsttypes_generated.h | 201 ++++++++++++----------- src/coreclr/interpreter/compiler.cpp | 123 ++++++++++++-- src/coreclr/interpreter/compiler.h | 3 +- src/coreclr/interpreter/intops.def | 2 + src/coreclr/vm/callstubgenerator.cpp | 194 ++++++++++++++++++---- src/coreclr/vm/callstubgenerator.h | 25 +++ src/coreclr/vm/ceemain.cpp | 8 + src/coreclr/vm/interpexec.cpp | 40 ++++- src/coreclr/vm/jitinterface.cpp | 30 ++++ src/coreclr/vm/jitinterface.h | 2 + src/coreclr/vm/siginfo.hpp | 4 +- src/coreclr/vm/typehashingalgorithms.h | 9 + src/tests/JIT/interpreter/Interpreter.cs | 142 ++++++++++++++-- 14 files changed, 626 insertions(+), 161 deletions(-) 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/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/interpreter/compiler.cpp b/src/coreclr/interpreter/compiler.cpp index b49669a476b66f..62b6f340b352dd 100644 --- a/src/coreclr/interpreter/compiler.cpp +++ b/src/coreclr/interpreter/compiler.cpp @@ -2238,6 +2238,32 @@ InterpCompiler::InterpEmbedGenericResult InterpCompiler::EmitGenericHandle(CORIN return result; } +void InterpCompiler::EmitCORINFO_LOOKUPToStack(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); @@ -2277,27 +2303,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->GetCookieForPInvokeCalliSig(&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) @@ -2502,6 +2550,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 { // Normal call @@ -3729,14 +3783,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; @@ -4204,6 +4264,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_Ldtoken, &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 + { + EmitCORINFO_LOOKUPToStack(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..5855d29cd7aa48 100644 --- a/src/coreclr/interpreter/compiler.h +++ b/src/coreclr/interpreter/compiler.h @@ -386,6 +386,7 @@ class InterpCompiler }; InterpEmbedGenericResult EmitGenericHandle(CORINFO_RESOLVED_TOKEN* resolvedToken, GenericHandleEmbedOptions options); int EmitGenericHandleAsVar(const CORINFO_GENERICHANDLE_RESULT &embedInfo); + void EmitCORINFO_LOOKUPToStack(const CORINFO_LOOKUP& lookup); void* AllocMethodData(size_t numBytes); public: @@ -484,7 +485,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 160ca3944d22ed..1a0d8d050dc4c5 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_LDELEM_I1, "ldelem.i1", 4, 1, 2, InterpOpNoArgs) @@ -279,6 +280,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/vm/callstubgenerator.cpp b/src/coreclr/vm/callstubgenerator.cpp index bdb402b8b99fe0..f92f08a8923685 100644 --- a/src/coreclr/vm/callstubgenerator.cpp +++ b/src/coreclr/vm/callstubgenerator.cpp @@ -558,6 +558,142 @@ CallStubHeader *CallStubGenerator::GenerateCallStub(MethodDesc *pMD, AllocMemTra _ASSERTE(pMD != NULL); 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)); + + ComputeCallStub(sig, pRoutines); + + xxHash hashState; + for (int i = 0; i < m_routineIndex; i++) + { + hashState.AddPointer((void*)pRoutines[i]); + } + hashState.Add(m_totalStackSize); + hashState.AddPointer(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 @@ -590,10 +726,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 @@ -687,11 +819,11 @@ CallStubHeader *CallStubGenerator::GenerateCallStub(MethodDesc *pMD, AllocMemTra pRoutines[m_routineIndex++] = ((int64_t)(m_s2 - m_s1 + 1) << 32) | m_s1; } - CallStubHeader::InvokeFunctionPtr pInvokeFunction = NULL; + m_pInvokeFunction = NULL; if (argIt.HasRetBuffArg()) { - pInvokeFunction = CallJittedMethodRetBuff; + m_pInvokeFunction = CallJittedMethodRetBuff; } else { @@ -721,14 +853,14 @@ CallStubHeader *CallStubGenerator::GenerateCallStub(MethodDesc *pMD, AllocMemTra case ELEMENT_TYPE_ARRAY: case ELEMENT_TYPE_SZARRAY: case ELEMENT_TYPE_FNPTR: - pInvokeFunction = CallJittedMethodRetI8; + m_pInvokeFunction = CallJittedMethodRetI8; break; case ELEMENT_TYPE_R4: case ELEMENT_TYPE_R8: - pInvokeFunction = CallJittedMethodRetDouble; + m_pInvokeFunction = CallJittedMethodRetDouble; break; case ELEMENT_TYPE_VOID: - pInvokeFunction = CallJittedMethodRetVoid; + m_pInvokeFunction = CallJittedMethodRetVoid; break; case ELEMENT_TYPE_VALUETYPE: #ifdef TARGET_AMD64 @@ -736,12 +868,12 @@ CallStubHeader *CallStubGenerator::GenerateCallStub(MethodDesc *pMD, AllocMemTra if (thReturnValueType.AsMethodTable()->IsIntrinsicType()) { // E.g. Vector2 - pInvokeFunction = CallJittedMethodRetDouble; + m_pInvokeFunction = CallJittedMethodRetDouble; } else { // POD structs smaller than 64 bits are returned in rax - pInvokeFunction = CallJittedMethodRetI8; + m_pInvokeFunction = CallJittedMethodRetI8; } #else // TARGET_WINDOWS if (thReturnValueType.AsMethodTable()->IsRegPassedStruct()) @@ -749,11 +881,11 @@ CallStubHeader *CallStubGenerator::GenerateCallStub(MethodDesc *pMD, AllocMemTra UINT fpReturnSize = argIt.GetFPReturnSize(); if (fpReturnSize == 0) { - pInvokeFunction = CallJittedMethodRetI8; + m_pInvokeFunction = CallJittedMethodRetI8; } else if (fpReturnSize == 8) { - pInvokeFunction = CallJittedMethodRetDouble; + m_pInvokeFunction = CallJittedMethodRetDouble; } else { @@ -764,16 +896,16 @@ CallStubHeader *CallStubGenerator::GenerateCallStub(MethodDesc *pMD, AllocMemTra switch (fpReturnSize & 0x3) { case 0: - pInvokeFunction = CallJittedMethodRetI8I8; + m_pInvokeFunction = CallJittedMethodRetI8I8; break; case 1: - pInvokeFunction = CallJittedMethodRetDoubleI8; + m_pInvokeFunction = CallJittedMethodRetDoubleI8; break; case 2: - pInvokeFunction = CallJittedMethodRetI8Double; + m_pInvokeFunction = CallJittedMethodRetI8Double; break; case 3: - pInvokeFunction = CallJittedMethodRetDoubleDouble; + m_pInvokeFunction = CallJittedMethodRetDoubleDouble; break; } } @@ -793,16 +925,16 @@ CallStubHeader *CallStubGenerator::GenerateCallStub(MethodDesc *pMD, AllocMemTra switch (thReturnValueType.GetSize()) { case 4: - pInvokeFunction = CallJittedMethodRetFloat; + m_pInvokeFunction = CallJittedMethodRetFloat; break; case 8: - pInvokeFunction = CallJittedMethodRet2Float; + m_pInvokeFunction = CallJittedMethodRet2Float; break; case 12: - pInvokeFunction = CallJittedMethodRet3Float; + m_pInvokeFunction = CallJittedMethodRet3Float; break; case 16: - pInvokeFunction = CallJittedMethodRet4Float; + m_pInvokeFunction = CallJittedMethodRet4Float; break; default: _ASSERTE(!"Should not get here"); @@ -813,16 +945,16 @@ CallStubHeader *CallStubGenerator::GenerateCallStub(MethodDesc *pMD, AllocMemTra switch (thReturnValueType.GetSize()) { case 8: - pInvokeFunction = CallJittedMethodRetDouble; + m_pInvokeFunction = CallJittedMethodRetDouble; break; case 16: - pInvokeFunction = CallJittedMethodRet2Double; + m_pInvokeFunction = CallJittedMethodRet2Double; break; case 24: - pInvokeFunction = CallJittedMethodRet3Double; + m_pInvokeFunction = CallJittedMethodRet3Double; break; case 32: - pInvokeFunction = CallJittedMethodRet4Double; + m_pInvokeFunction = CallJittedMethodRet4Double; break; default: _ASSERTE(!"Should not get here"); @@ -842,10 +974,10 @@ CallStubHeader *CallStubGenerator::GenerateCallStub(MethodDesc *pMD, AllocMemTra case 2: case 4: case 8: - pInvokeFunction = CallJittedMethodRetI8; + m_pInvokeFunction = CallJittedMethodRetI8; break; case 16: - pInvokeFunction = CallJittedMethodRet2I8; + m_pInvokeFunction = CallJittedMethodRet2I8; break; default: _ASSERTE(!"The return types that are not HFA should be <= 16 bytes in size"); @@ -863,14 +995,6 @@ CallStubHeader *CallStubGenerator::GenerateCallStub(MethodDesc *pMD, AllocMemTra } m_routineIndex++; // Reserve one extra slot for the target method pointer - - 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 bce448b5e00ce1..6abb6243d6e316 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 @@ -67,11 +75,28 @@ class CallStubGenerator // The total stack size used for the arguments. int m_totalStackSize; + CallStubHeader::InvokeFunctionPtr m_pInvokeFunction = NULL; + // Process the argument described by argLocDesc. This function is called for each argument in the method signature. void ProcessArgument(ArgIterator *pArgIt, ArgLocDesc& argLocDesc, PCODE *pRoutines); public: // Generate the call stub for the given method. CallStubHeader *GenerateCallStub(MethodDesc *pMD, AllocMemTracker *pamTracker); + + 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 9814484e4942a4..b7f4cdb9c99631 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -55,6 +55,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); +} + typedef void* (*HELPER_FTN_PP)(void*); typedef void* (*HELPER_FTN_BOX_UNBOX)(MethodTable*, void*); typedef Object* (*HELPER_FTN_NEWARR)(CORINFO_CLASS_HANDLE, intptr_t); @@ -204,6 +224,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); @@ -1162,6 +1186,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]; @@ -1186,7 +1224,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..8575da4c5143b4 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" @@ -11260,6 +11264,32 @@ void CEEJitInfo::SetDebugInfo(PTR_BYTE pDebugInfo) #ifdef FEATURE_INTERPRETER +LPVOID CInterpreterJitInfo::GetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, + void **ppIndirection) +{ + void* result = NULL; + JIT_TO_EE_TRANSITION(); + + if (ppIndirection != NULL) + { + // The JIT will not use the indirection for this cookie, so we can set it to NULL + *ppIndirection = NULL; + } + 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..ae09bb8edbf2cc 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 GetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, void **ppIndirection) 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..b82cb708d21e38 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)(((UINT64)ptr) >> 32)); +#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 f1d7baf90a6693..ec89240a56c07f 100644 --- a/src/tests/JIT/interpreter/Interpreter.cs +++ b/src/tests/JIT/interpreter/Interpreter.cs @@ -455,6 +455,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!"); @@ -615,7 +623,9 @@ public static void TestFinallyBeforeCatch() x *= 10; x += 3; } - } catch (Exception) { + } + catch (Exception) + { x *= 10; x += 4; } @@ -734,7 +744,7 @@ public static void TestCatchFinally() long ret = 1; for (int i = 0; i < n; i++) ret *= nr; - bool dummy= (int)ret == 100; + bool dummy = (int)ret == 100; x *= 10; x += 3; @@ -793,7 +803,9 @@ public static void TestFinallyWithInnerTryBeforeCatch() x *= 10; x += 5; } - } catch (Exception) { + } + catch (Exception) + { x *= 10; x += 6; } @@ -1051,16 +1063,16 @@ public static int SwitchOp(int a, int b, int op) case 2: return a * b; default: - return 42; + return 42; } } public static void TestSwitch() { - int n0 = SwitchOp (20, 6, 0); // 26 - int n1 = SwitchOp (20, 6, 1); // 14 - int n2 = SwitchOp (20, 6, 2); // 120 - int n3 = SwitchOp (20, 6, 3); // 42 + int n0 = SwitchOp(20, 6, 0); // 26 + int n1 = SwitchOp(20, 6, 1); // 14 + int n2 = SwitchOp(20, 6, 2); // 120 + int n3 = SwitchOp(20, 6, 3); // 42 if ((n0 + n1 + n2 + n3) != 202) Environment.FailFast(null); @@ -1391,11 +1403,12 @@ public static bool TestBoxing() } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] - static object BoxedSubtraction (object lhs, object rhs) { + static object BoxedSubtraction(object lhs, object rhs) + { return (int)lhs - (int)rhs; } - public static bool TestArray() + public static bool TestArray() { // sbyte if (!ArraySByte(0, 0)) return false; @@ -1701,7 +1714,114 @@ public static bool TestMdArray() // ldtoken int[,] // call System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray // The newobj currently fails because int[,].ctor isn't a real method, the interp needs to use getCallInfo to determine how to invoke it - int[,] a = {{1, 2}, {3, 4}}; + 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; + } + +/* + TODO! This will pass once we support passing parameters to the invoked methods. The GetCallStub logic needs to handle the param arg + 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; + } } From 0a4c830f8e24ff0aef1d72b1fd1e1f87a3002844 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 9 Jun 2025 15:29:06 -0700 Subject: [PATCH 2/9] Add CORINFO_CALLINFO_DISALLOW_STUB to the managed side of the JIT interface. It isn't used, but it should be there. --- src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 9e75ee782fab1b..4d6fafad9182e4 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -401,7 +401,7 @@ public enum CORINFO_CALLINFO_FLAGS 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_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, From fbb653a8d6e00b144e9ca8796b4f73eaca643ce7 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 9 Jun 2025 15:56:14 -0700 Subject: [PATCH 3/9] Fixup changes to merge with the native to interpreter argument handling pr --- src/coreclr/vm/callstubgenerator.cpp | 2 +- src/tests/JIT/interpreter/Interpreter.cs | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/coreclr/vm/callstubgenerator.cpp b/src/coreclr/vm/callstubgenerator.cpp index 0e91a2ee80689d..62ddcb79e53328 100644 --- a/src/coreclr/vm/callstubgenerator.cpp +++ b/src/coreclr/vm/callstubgenerator.cpp @@ -1203,7 +1203,7 @@ CallStubHeader *CallStubGenerator::GenerateCallStub(MethodDesc *pMD, AllocMemTra 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); + CallStubHeader *pHeader = new (pHeaderStorage) CallStubHeader(m_routineIndex, pRoutines, ALIGN_UP(m_totalStackSize, STACK_ALIGN_SIZE), m_pInvokeFunction); return pHeader; } diff --git a/src/tests/JIT/interpreter/Interpreter.cs b/src/tests/JIT/interpreter/Interpreter.cs index 5855816b84172d..1de8a6d75f03ac 100644 --- a/src/tests/JIT/interpreter/Interpreter.cs +++ b/src/tests/JIT/interpreter/Interpreter.cs @@ -2246,8 +2246,7 @@ public unsafe static bool TestCalli() return false; } -/* - TODO! This will pass once we support passing parameters to the invoked methods. The GetCallStub logic needs to handle the param arg + GetCalliGeneric()(); if (_typeFromFill != typeof(object)) { @@ -2261,7 +2260,6 @@ public unsafe static bool TestCalli() Console.WriteLine("Calli generic test failed: expected string, got " + _typeFromFill); return false; } -*/ return true; } From 0e19a228649aa73d5583efff28953baa42cb986e Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 9 Jun 2025 16:01:14 -0700 Subject: [PATCH 4/9] Update src/coreclr/vm/typehashingalgorithms.h Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/coreclr/vm/typehashingalgorithms.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/typehashingalgorithms.h b/src/coreclr/vm/typehashingalgorithms.h index b82cb708d21e38..20bfad70ca2f58 100644 --- a/src/coreclr/vm/typehashingalgorithms.h +++ b/src/coreclr/vm/typehashingalgorithms.h @@ -265,7 +265,7 @@ class xxHash Add((uint32_t)(UINT_PTR)ptr); Add((uint32_t)(((UINT64)ptr) >> 32)); #else - Add((uint32_t)(((UINT64)ptr) >> 32)); + Add((uint32_t)(UINT_PTR)ptr); #endif } void Add(uint32_t val) From d5cdc19e2b7e4b69acaf1ae0de181b189d00142b Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 10 Jun 2025 10:52:47 -0700 Subject: [PATCH 5/9] Fix unix build break --- src/coreclr/vm/callstubgenerator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/callstubgenerator.cpp b/src/coreclr/vm/callstubgenerator.cpp index 62ddcb79e53328..4bc0b26311c797 100644 --- a/src/coreclr/vm/callstubgenerator.cpp +++ b/src/coreclr/vm/callstubgenerator.cpp @@ -1296,7 +1296,7 @@ CallStubHeader *CallStubGenerator::GenerateCallStubForSig(MetaSig &sig) hashState.AddPointer((void*)pRoutines[i]); } hashState.Add(m_totalStackSize); - hashState.AddPointer(m_pInvokeFunction); + hashState.AddPointer((void*)m_pInvokeFunction); CachedCallStubKey cachedHeaderKey( hashState.ToHashCode(), From 7f1fc4e3e46deea05f321718ae770585faeec727 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 10 Jun 2025 12:05:22 -0700 Subject: [PATCH 6/9] Use new jit interface api for interpreter calli cookie instead of misusing the pinvoke one. --- src/coreclr/inc/corinfo.h | 5 ++ src/coreclr/inc/icorjitinfoimpl_generated.h | 3 + src/coreclr/inc/jiteeversionguid.h | 10 +-- src/coreclr/interpreter/compiler.cpp | 2 +- src/coreclr/jit/ICorJitInfo_names_generated.h | 1 + .../jit/ICorJitInfo_wrapper_generated.hpp | 9 ++ .../tools/Common/JitInterface/CorInfoImpl.cs | 3 + .../JitInterface/CorInfoImpl_generated.cs | 90 +++++++++++-------- .../ThunkGenerator/ThunkInput.txt | 1 + .../aot/jitinterface/jitinterface_generated.h | 10 +++ .../tools/superpmi/superpmi-shared/agnostic.h | 8 ++ .../tools/superpmi/superpmi-shared/lwmlist.h | 1 + .../superpmi-shared/methodcontext.cpp | 39 ++++++++ .../superpmi/superpmi-shared/methodcontext.h | 5 ++ .../superpmi-shim-collector/icorjitinfo.cpp | 11 +++ .../icorjitinfo_generated.cpp | 7 ++ .../icorjitinfo_generated.cpp | 6 ++ .../tools/superpmi/superpmi/icorjitinfo.cpp | 6 ++ src/coreclr/vm/jitinterface.cpp | 14 +-- src/coreclr/vm/jitinterface.h | 2 +- 20 files changed, 182 insertions(+), 51 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index b3ed7004bd27fb..1ef7aabccba44c 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -3204,6 +3204,11 @@ class ICorDynamicInfo : public ICorStaticInfo CORINFO_SIG_INFO* szMetaSig ) = 0; + // Generate a cookie based on the signature that would needs to be passed + // 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/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 982e1f9fd82eff..b92a98126054bd 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -37,11 +37,11 @@ #include -constexpr GUID JITEEVersionIdentifier = { /* f22d9c39-8d24-4e4d-86aa-7b883aecf97f */ - 0xf22d9c39, - 0x8d24, - 0x4e4d, - {0x86, 0xaa, 0x7b, 0x88, 0x3a, 0xec, 0xf9, 0x7f} +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 2b314822545312..24c478508ebb11 100644 --- a/src/coreclr/interpreter/compiler.cpp +++ b/src/coreclr/interpreter/compiler.cpp @@ -2330,7 +2330,7 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* constrainedClass, bool rea callIFunctionPointerVar = m_pStackPointer[-1].var; m_pStackPointer--; - calliCookie = m_compHnd->GetCookieForPInvokeCalliSig(&callInfo.sig); + calliCookie = m_compHnd->GetCookieForInterpreterCalliSig(&callInfo.sig); } else { 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/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..e0e4042e011ad6 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -1600,6 +1600,17 @@ LPVOID interceptor_ICJI::GetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig return temp; } +// Generate a cookie based on the signature that would needs to be passed +// 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..6379f1265d0d0f 100644 --- a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp @@ -1394,6 +1394,12 @@ bool MyICJI::canGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig) return jitInstance->mc->repCanGetCookieForPInvokeCalliSig(szMetaSig); } +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/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 8575da4c5143b4..dd412e765b75f1 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -11262,19 +11262,19 @@ 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::GetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, - void **ppIndirection) +LPVOID CInterpreterJitInfo::GetCookieForInterpreterCalliSig(CORINFO_SIG_INFO* szMetaSig) { void* result = NULL; JIT_TO_EE_TRANSITION(); - if (ppIndirection != NULL) - { - // The JIT will not use the indirection for this cookie, so we can set it to NULL - *ppIndirection = NULL; - } Module* module = GetModule(szMetaSig->scope); Instantiation classInst = Instantiation((TypeHandle*) szMetaSig->sigInst.classInst, szMetaSig->sigInst.classInstCount); diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index ae09bb8edbf2cc..63cd935fa7aaed 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -975,7 +975,7 @@ class CInterpreterJitInfo final : public CEECodeGenInfo void BackoutJitData(EECodeGenManager * jitMgr) override; void SetDebugInfo(PTR_BYTE pDebugInfo) override; - LPVOID GetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, void **ppIndirection) override; + LPVOID GetCookieForInterpreterCalliSig(CORINFO_SIG_INFO* szMetaSig) override; }; #endif // FEATURE_INTERPRETER From ffa9ba3ff85c4dcb84a0a940ff3e04176d9e2822 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 10 Jun 2025 14:54:15 -0700 Subject: [PATCH 7/9] Code review commentary --- src/coreclr/inc/corinfo.h | 6 ++---- src/coreclr/interpreter/compiler.cpp | 4 ++-- src/coreclr/interpreter/compiler.h | 6 +++++- src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs | 8 ++++---- .../superpmi/superpmi-shim-collector/icorjitinfo.cpp | 6 ++---- src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp | 4 ++-- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 1ef7aabccba44c..2ac0e3ae02b353 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -3191,8 +3191,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 @@ -3204,8 +3203,7 @@ class ICorDynamicInfo : public ICorStaticInfo CORINFO_SIG_INFO* szMetaSig ) = 0; - // Generate a cookie based on the signature that would needs to be passed - // to INTOP_CALLI in the interpreter + // Generate a cookie based on the signature to pass to INTOP_CALLI in the interpreter. virtual void* GetCookieForInterpreterCalliSig( CORINFO_SIG_INFO* szMetaSig) = 0; diff --git a/src/coreclr/interpreter/compiler.cpp b/src/coreclr/interpreter/compiler.cpp index 24c478508ebb11..4f3ac00617216e 100644 --- a/src/coreclr/interpreter/compiler.cpp +++ b/src/coreclr/interpreter/compiler.cpp @@ -2239,7 +2239,7 @@ InterpCompiler::InterpEmbedGenericResult InterpCompiler::EmitGenericHandle(CORIN return result; } -void InterpCompiler::EmitCORINFO_LOOKUPToStack(const CORINFO_LOOKUP& lookup) +void InterpCompiler::EmitPushCORINFO_LOOKUP(const CORINFO_LOOKUP& lookup) { PushStackType(StackTypeI, NULL); int resultVar = m_pStackPointer[-1].var; @@ -4311,7 +4311,7 @@ int InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo) } else { - EmitCORINFO_LOOKUPToStack(callInfo.codePointerLookup); + EmitPushCORINFO_LOOKUP(callInfo.codePointerLookup); } m_ip += 5; diff --git a/src/coreclr/interpreter/compiler.h b/src/coreclr/interpreter/compiler.h index 5855d29cd7aa48..ecaf73ebb5c506 100644 --- a/src/coreclr/interpreter/compiler.h +++ b/src/coreclr/interpreter/compiler.h @@ -385,8 +385,12 @@ 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); - void EmitCORINFO_LOOKUPToStack(const CORINFO_LOOKUP& lookup); + + // 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: diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 4d6fafad9182e4..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, 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 + CORINFO_CALLINFO_SECURITYCHECKS = 0x0010, // Perform security checks. + CORINFO_CALLINFO_LDFTN = 0x0020, // Resolving target of LDFTN // UNUSED = 0x0040, } diff --git a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp index e0e4042e011ad6..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,8 +1599,7 @@ LPVOID interceptor_ICJI::GetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig return temp; } -// Generate a cookie based on the signature that would needs to be passed -// to INTOP_CALLI +// Generate a cookie based on the signature to pass to INTOP_CALLI LPVOID interceptor_ICJI::GetCookieForInterpreterCalliSig(CORINFO_SIG_INFO* szMetaSig) { mc->cr->AddCall("GetCookieForInterpreterCalliSig"); diff --git a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp index 6379f1265d0d0f..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,7 @@ 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"); From 436efb65a79aa15391e9270b5806117d77f232b0 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 10 Jun 2025 23:51:46 +0000 Subject: [PATCH 8/9] Fix Linux Arm64 copy of this code by forcing all the fields of CallStubGenerator to be initialized to some well known value (and by explicitly setting m_interpreterToNative to true in GenerateCallStubForSig (which is what actually fixed the problem) --- src/coreclr/vm/callstubgenerator.cpp | 2 ++ src/coreclr/vm/callstubgenerator.h | 18 +++++++++--------- src/coreclr/vm/interpexec.cpp | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/coreclr/vm/callstubgenerator.cpp b/src/coreclr/vm/callstubgenerator.cpp index 4bc0b26311c797..d8b8771c31213f 100644 --- a/src/coreclr/vm/callstubgenerator.cpp +++ b/src/coreclr/vm/callstubgenerator.cpp @@ -1288,6 +1288,8 @@ CallStubHeader *CallStubGenerator::GenerateCallStubForSig(MetaSig &sig) // 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; diff --git a/src/coreclr/vm/callstubgenerator.h b/src/coreclr/vm/callstubgenerator.h index ce541c7ec807db..ee15fbcdbe4ca9 100644 --- a/src/coreclr/vm/callstubgenerator.h +++ b/src/coreclr/vm/callstubgenerator.h @@ -91,21 +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; CallStubHeader::InvokeFunctionPtr m_pInvokeFunction = NULL; - bool m_interpreterToNative; + bool m_interpreterToNative = false; #ifndef UNIX_AMD64_ABI PCODE GetGPRegRefRoutine(int r); diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index e8cf1c576e4d58..f0e96db94bd896 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -29,10 +29,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; From 21f03aa51164c67d9478987735caa1f0cde647a8 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 11 Jun 2025 15:26:26 -0700 Subject: [PATCH 9/9] We're supposed to use CORINFO_TOKENKIND_Method, not Ldtoken for this --- src/coreclr/interpreter/compiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/interpreter/compiler.cpp b/src/coreclr/interpreter/compiler.cpp index f0b460c502eaf0..132bdf18c6443f 100644 --- a/src/coreclr/interpreter/compiler.cpp +++ b/src/coreclr/interpreter/compiler.cpp @@ -4525,7 +4525,7 @@ int InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo) { CORINFO_RESOLVED_TOKEN resolvedToken; uint32_t token = getU4LittleEndian(m_ip + 1); - ResolveToken(token, CORINFO_TOKENKIND_Ldtoken, &resolvedToken); + 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);