From ff21b51924793e69e2944dc5d6fdbefca6c6c66b Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Tue, 10 Mar 2026 16:39:56 +0100 Subject: [PATCH 1/6] Avoid cookie lookup for delegates in compiler --- src/coreclr/interpreter/compiler.cpp | 24 ++++++++++++++++++++++++ src/coreclr/vm/interpexec.cpp | 14 ++++++++------ src/coreclr/vm/precode_portable.cpp | 8 ++++++++ src/coreclr/vm/precode_portable.hpp | 1 + 4 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/coreclr/interpreter/compiler.cpp b/src/coreclr/interpreter/compiler.cpp index 10c649fa958f13..b3e1c866f11a5d 100644 --- a/src/coreclr/interpreter/compiler.cpp +++ b/src/coreclr/interpreter/compiler.cpp @@ -4796,7 +4796,23 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re callIFunctionPointerVar = m_pStackPointer[-1].var; m_pStackPointer--; +#ifdef FEATURE_PORTABLE_ENTRYPOINTS + // On platforms with portable entrypoints, managed calli targets in delegate stubs are + // dispatched through the portable entrypoint path at execution time — the PE contains + // a MethodDesc from which the cookie is derived at runtime via CALL_INTERP_METHOD. + // Skip the cookie lookup for delegate stubs to avoid asserting on delegate signatures + // (e.g. "diiddil") that are not in the pre-built thunk table. + // For all other callis (PInvoke, JIT helpers, etc.) compute the cookie normally. + { + CorInfoCallConv callConv = (CorInfoCallConv)(callInfo.sig.callConv & IMAGE_CEE_CS_CALLCONV_MASK); + bool isUnmanaged = (callConv != CORINFO_CALLCONV_DEFAULT && callConv != CORINFO_CALLCONV_VARARG); + bool isDelegateStub = !isUnmanaged && + (m_compHnd->getClassAttribs(m_compHnd->getMethodClass(m_methodHnd)) & CORINFO_FLG_DELEGATE) != 0; + calliCookie = isDelegateStub ? NULL : m_compHnd->GetCookieForInterpreterCalliSig(&callInfo.sig); + } +#else calliCookie = m_compHnd->GetCookieForInterpreterCalliSig(&callInfo.sig); +#endif m_ip += 5; } else @@ -5445,7 +5461,11 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re m_pStackPointer--; int codePointerLookupResult = m_pStackPointer[0].var; +#ifdef FEATURE_PORTABLE_ENTRYPOINTS + calliCookie = NULL; +#else calliCookie = m_compHnd->GetCookieForInterpreterCalliSig(&callInfo.sig); +#endif EmitCalli(tailcall, calliCookie, codePointerLookupResult, &callInfo.sig); @@ -5488,7 +5508,11 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re m_pStackPointer--; int synthesizedLdvirtftnPtrVar = m_pStackPointer[0].var; +#ifdef FEATURE_PORTABLE_ENTRYPOINTS + calliCookie = NULL; +#else calliCookie = m_compHnd->GetCookieForInterpreterCalliSig(&callInfo.sig); +#endif EmitCalli(tailcall, calliCookie, synthesizedLdvirtftnPtrVar, &callInfo.sig); } diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 8be761a468a125..4748854d1a83a0 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -3037,13 +3037,15 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr else { #ifdef FEATURE_PORTABLE_ENTRYPOINTS - // WASM-TODO: We may end up here with native JIT helper entrypoint without MethodDesc - // that CALL_INTERP_METHOD is not able to handle. This is a potential problem for - // interpreter<->native code stub generator. - // https://github.com/dotnet/runtime/pull/119516#discussion_r2337631271 - if (!PortableEntryPoint::HasNativeEntryPoint(calliFunctionPointer)) + // On portable entry point platforms, calli targets are portable entry points. + // If the PE has a MethodDesc, route through CALL_INTERP_METHOD which derives + // the cookie at runtime. This covers both interpreted methods and FCalls + // (which have native code set but still carry an MD). + // JIT helper PEs (no MethodDesc) fall through to InvokeCalliStub using + // the compile-time cookie. + targetMethod = PortableEntryPoint::TryGetMethodDesc(calliFunctionPointer); + if (targetMethod != nullptr) { - targetMethod = PortableEntryPoint::GetMethodDesc(calliFunctionPointer); goto CALL_INTERP_METHOD; } #endif // FEATURE_PORTABLE_ENTRYPOINTS diff --git a/src/coreclr/vm/precode_portable.cpp b/src/coreclr/vm/precode_portable.cpp index 40c4bdce3958bb..3deb3cc9fd7861 100644 --- a/src/coreclr/vm/precode_portable.cpp +++ b/src/coreclr/vm/precode_portable.cpp @@ -57,6 +57,14 @@ MethodDesc* PortableEntryPoint::GetMethodDesc(PCODE addr) return portableEntryPoint->_pMD; } +MethodDesc* PortableEntryPoint::TryGetMethodDesc(PCODE addr) +{ + LIMITED_METHOD_CONTRACT; + + PortableEntryPoint* portableEntryPoint = ToPortableEntryPoint(addr); + return portableEntryPoint->_pMD; +} + void* PortableEntryPoint::GetInterpreterData(PCODE addr) { STANDARD_VM_CONTRACT; diff --git a/src/coreclr/vm/precode_portable.hpp b/src/coreclr/vm/precode_portable.hpp index 3884895f56e911..431c8a26076ecb 100644 --- a/src/coreclr/vm/precode_portable.hpp +++ b/src/coreclr/vm/precode_portable.hpp @@ -19,6 +19,7 @@ class PortableEntryPoint final static void* GetActualCode(PCODE addr); static void SetActualCode(PCODE addr, PCODE actualCode); static MethodDesc* GetMethodDesc(PCODE addr); + static MethodDesc* TryGetMethodDesc(PCODE addr); static void* GetInterpreterData(PCODE addr); static void SetInterpreterData(PCODE addr, PCODE interpreterData); From 4a3cc7c581b8e4cde851785b7c1670b1fb3f12ed Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Wed, 11 Mar 2026 18:53:04 +0100 Subject: [PATCH 2/6] Move the cookie lookup to the execution phase and cache it --- src/coreclr/interpreter/compiler.cpp | 45 +++++++++++++------ src/coreclr/interpreter/compiler.h | 2 +- .../interpreter/inc/interpretershared.h | 1 + src/coreclr/vm/interpexec.cpp | 24 +++++++++- 4 files changed, 56 insertions(+), 16 deletions(-) diff --git a/src/coreclr/interpreter/compiler.cpp b/src/coreclr/interpreter/compiler.cpp index b3e1c866f11a5d..14a1b6075843dc 100644 --- a/src/coreclr/interpreter/compiler.cpp +++ b/src/coreclr/interpreter/compiler.cpp @@ -4315,10 +4315,22 @@ bool InterpCompiler::DisallowTailCall(CORINFO_SIG_INFO* callerSig, CORINFO_SIG_I return false; } -void InterpCompiler::EmitCalli(bool isTailCall, void* calliCookie, int callIFunctionPointerVar, CORINFO_SIG_INFO* callSiteSig) +void InterpCompiler::EmitCalli(bool isTailCall, void* calliCookie, int callIFunctionPointerVar, CORINFO_SIG_INFO* callSiteSig, bool deferredCookie) { AddIns(isTailCall ? INTOP_CALLI_TAIL : INTOP_CALLI); - m_pLastNewIns->data[0] = GetDataItemIndex(calliCookie); + if (deferredCookie) + { + // Allocate two consecutive data items: sig token + cache slot (initially NULL). + // The executor uses pDataItems[index] for the sig token and pDataItems[index+1] + // for caching the resolved cookie. + m_pLastNewIns->data[0] = GetNewDataItemIndex(calliCookie); + int32_t cacheSlot = GetNewDataItemIndex(nullptr); + _ASSERTE(cacheSlot == m_pLastNewIns->data[0] + 1); + } + else + { + m_pLastNewIns->data[0] = GetDataItemIndex(calliCookie); + } // data[1] is set to 1 if the calli is calling a pinvoke, 0 otherwise bool suppressGCTransition = false; CorInfoCallConv callConv = (CorInfoCallConv)(callSiteSig->callConv & IMAGE_CEE_CS_CALLCONV_MASK); @@ -4334,7 +4346,8 @@ void InterpCompiler::EmitCalli(bool isTailCall, void* calliCookie, int callIFunc m_compHnd->getUnmanagedCallConv(nullptr, callSiteSig, &suppressGCTransition); } m_pLastNewIns->data[1] = (suppressGCTransition ? (int32_t)CalliFlags::SuppressGCTransition : 0) | - (isPInvoke ? (int32_t)CalliFlags::PInvoke : 0); + (isPInvoke ? (int32_t)CalliFlags::PInvoke : 0) | + (deferredCookie ? (int32_t)CalliFlags::DeferredCookie : 0); m_pLastNewIns->SetSVars2(CALL_ARGS_SVAR, callIFunctionPointerVar); } @@ -4774,6 +4787,7 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re int32_t callIFunctionPointerVar = -1; void* calliCookie = NULL; + bool calliDeferredCookie = false; ContinuationContextHandling continuationContextHandling = ContinuationContextHandling::None; if (isCalli) @@ -4797,18 +4811,23 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re callIFunctionPointerVar = m_pStackPointer[-1].var; m_pStackPointer--; #ifdef FEATURE_PORTABLE_ENTRYPOINTS - // On platforms with portable entrypoints, managed calli targets in delegate stubs are - // dispatched through the portable entrypoint path at execution time — the PE contains - // a MethodDesc from which the cookie is derived at runtime via CALL_INTERP_METHOD. - // Skip the cookie lookup for delegate stubs to avoid asserting on delegate signatures - // (e.g. "diiddil") that are not in the pre-built thunk table. - // For all other callis (PInvoke, JIT helpers, etc.) compute the cookie normally. + // On platforms with portable entrypoints, managed calli targets with a MethodDesc + // are dispatched via CALL_INTERP_METHOD at execution time, which derives the cookie + // from the MethodDesc. For targets without a MethodDesc (JIT helper PEs), the cookie + // is computed at execution time from the calli's signature token. { CorInfoCallConv callConv = (CorInfoCallConv)(callInfo.sig.callConv & IMAGE_CEE_CS_CALLCONV_MASK); bool isUnmanaged = (callConv != CORINFO_CALLCONV_DEFAULT && callConv != CORINFO_CALLCONV_VARARG); - bool isDelegateStub = !isUnmanaged && - (m_compHnd->getClassAttribs(m_compHnd->getMethodClass(m_methodHnd)) & CORINFO_FLG_DELEGATE) != 0; - calliCookie = isDelegateStub ? NULL : m_compHnd->GetCookieForInterpreterCalliSig(&callInfo.sig); + if (isUnmanaged) + { + calliCookie = m_compHnd->GetCookieForInterpreterCalliSig(&callInfo.sig); + } + else + { + // Store the sig token for runtime cookie resolution. + calliCookie = (void*)(size_t)token; + calliDeferredCookie = true; + } } #else calliCookie = m_compHnd->GetCookieForInterpreterCalliSig(&callInfo.sig); @@ -5349,7 +5368,7 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re } else if (isCalli) { - EmitCalli(tailcall, calliCookie, callIFunctionPointerVar, &callInfo.sig); + EmitCalli(tailcall, calliCookie, callIFunctionPointerVar, &callInfo.sig, calliDeferredCookie); if (((m_pLastNewIns->data[1] & (int32_t)CalliFlags::PInvoke) != 0) && m_pVars[dVar].interpType == InterpTypeVT) { diff --git a/src/coreclr/interpreter/compiler.h b/src/coreclr/interpreter/compiler.h index 6f58d0c065cf40..8732ce1a7b2908 100644 --- a/src/coreclr/interpreter/compiler.h +++ b/src/coreclr/interpreter/compiler.h @@ -983,7 +983,7 @@ class InterpCompiler void EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool readonly, bool tailcall, bool newObj, bool isCalli); void EmitRet(CORINFO_METHOD_INFO* methodInfo); void EmitSuspend(const CORINFO_CALL_INFO &callInfo, ContinuationContextHandling ContinuationContextHandling); - void EmitCalli(bool isTailCall, void* calliCookie, int callIFunctionPointerVar, CORINFO_SIG_INFO* callSiteSig); + void EmitCalli(bool isTailCall, void* calliCookie, int callIFunctionPointerVar, CORINFO_SIG_INFO* callSiteSig, bool deferredCookie = false); bool EmitNamedIntrinsicCall(NamedIntrinsic ni, bool nonVirtualCall, CORINFO_CLASS_HANDLE clsHnd, 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, bool enableImplicitArgConversionRules); diff --git a/src/coreclr/interpreter/inc/interpretershared.h b/src/coreclr/interpreter/inc/interpretershared.h index 35b7c23f8ac9df..18d38062f069a0 100644 --- a/src/coreclr/interpreter/inc/interpretershared.h +++ b/src/coreclr/interpreter/inc/interpretershared.h @@ -188,6 +188,7 @@ enum class CalliFlags : int32_t None = 0, SuppressGCTransition = 1 << 1, // The call is marked by the SuppressGCTransition attribute PInvoke = 1 << 2, // The call is a PInvoke call + DeferredCookie = 1 << 3, // Cookie data item contains a sig token to be resolved at runtime }; struct InterpIntervalMapEntry diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 4748854d1a83a0..fb629dddda9b29 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -208,6 +208,7 @@ void InvokeUnmanagedMethod(MethodDesc *targetMethod, int8_t *pArgs, int8_t *pRet void InvokeCalliStub(CalliStubParam* pParam); void InvokeUnmanagedCalli(PCODE ftn, void *cookie, int8_t *pArgs, int8_t *pRet); void InvokeDelegateInvokeMethod(DelegateInvokeMethodParam* pParam); +void* GetCookieForCalliSig(MetaSig metaSig); extern "C" PCODE CID_VirtualOpenDelegateDispatch(TransitionBlock * pTransitionBlock); // Filter to ignore SEH exceptions representing C++ exceptions. @@ -3041,13 +3042,32 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr // If the PE has a MethodDesc, route through CALL_INTERP_METHOD which derives // the cookie at runtime. This covers both interpreted methods and FCalls // (which have native code set but still carry an MD). - // JIT helper PEs (no MethodDesc) fall through to InvokeCalliStub using - // the compile-time cookie. + // JIT helper PEs (no MethodDesc) fall through to InvokeCalliStub — the + // cookie is resolved at runtime from the calli's signature token. targetMethod = PortableEntryPoint::TryGetMethodDesc(calliFunctionPointer); if (targetMethod != nullptr) { goto CALL_INTERP_METHOD; } + + if (flags & (int32_t)CalliFlags::DeferredCookie) + { + // pDataItems[calliCookie] = sig token (immutable) + // pDataItems[calliCookie + 1] = cached cookie (initially NULL) + cookie = pMethod->pDataItems[calliCookie + 1]; + if (cookie == nullptr) + { + mdToken sigToken = (mdToken)(size_t)pMethod->pDataItems[calliCookie]; + MethodDesc* pCallerMD = (MethodDesc*)pMethod->methodHnd; + Module* pModule = pCallerMD->GetModule(); + PCCOR_SIGNATURE pSig; + ULONG cbSig; + pModule->GetMDImport()->GetSigFromToken(sigToken, &cbSig, &pSig); + MetaSig sig(pSig, cbSig, pModule, nullptr); + cookie = GetCookieForCalliSig(sig); + pMethod->pDataItems[calliCookie + 1] = cookie; + } + } #endif // FEATURE_PORTABLE_ENTRYPOINTS CalliStubParam param = { calliFunctionPointer, cookie, callArgsAddress, returnValueAddress, pInterpreterFrame->GetContinuationPtr() }; InvokeCalliStub(¶m); From 74e493c236cb745e6547c328fa7f5804d2100ca5 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Wed, 11 Mar 2026 19:06:23 +0100 Subject: [PATCH 3/6] Remove additional cookie signatures from generator --- .../vm/wasm/callhelpers-interp-to-managed.cpp | 288 ------------------ .../coreclr/ManagedToNativeGenerator.cs | 52 +--- 2 files changed, 1 insertion(+), 339 deletions(-) diff --git a/src/coreclr/vm/wasm/callhelpers-interp-to-managed.cpp b/src/coreclr/vm/wasm/callhelpers-interp-to-managed.cpp index 950acbc6c0f5d3..2df58b868a5fec 100644 --- a/src/coreclr/vm/wasm/callhelpers-interp-to-managed.cpp +++ b/src/coreclr/vm/wasm/callhelpers-interp-to-managed.cpp @@ -19,12 +19,6 @@ namespace { - static void CallFunc_Void_RetF64(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - double (*fptr)() = (double (*)())pcode; - *((double*)pRet) = (*fptr)(); - } - static void CallFunc_F64_RetF64(PCODE pcode, int8_t* pArgs, int8_t* pRet) { double (*fptr)(double) = (double (*)(double))pcode; @@ -49,30 +43,12 @@ namespace *((double*)pRet) = (*fptr)(ARG_F64(0), ARG_I32(1)); } - static void CallFunc_F64_I32_F64_RetF64(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - double (*fptr)(double, int32_t, double) = (double (*)(double, int32_t, double))pcode; - *((double*)pRet) = (*fptr)(ARG_F64(0), ARG_I32(1), ARG_F64(2)); - } - static void CallFunc_I32_RetF64(PCODE pcode, int8_t* pArgs, int8_t* pRet) { double (*fptr)(int32_t) = (double (*)(int32_t))pcode; *((double*)pRet) = (*fptr)(ARG_I32(0)); } - static void CallFunc_I32_I32_RetF64(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - double (*fptr)(int32_t, int32_t) = (double (*)(int32_t, int32_t))pcode; - *((double*)pRet) = (*fptr)(ARG_I32(0), ARG_I32(1)); - } - - static void CallFunc_Void_RetF32(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - float (*fptr)() = (float (*)())pcode; - *((float*)pRet) = (*fptr)(); - } - static void CallFunc_F32_RetF32(PCODE pcode, int8_t* pArgs, int8_t* pRet) { float (*fptr)(float) = (float (*)(float))pcode; @@ -103,60 +79,12 @@ namespace *((int32_t*)pRet) = (*fptr)(); } - static void CallFunc_F64_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - int32_t (*fptr)(double) = (int32_t (*)(double))pcode; - *((int32_t*)pRet) = (*fptr)(ARG_F64(0)); - } - - static void CallFunc_F64_F32_I32_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - int32_t (*fptr)(double, float, int32_t) = (int32_t (*)(double, float, int32_t))pcode; - *((int32_t*)pRet) = (*fptr)(ARG_F64(0), ARG_F32(1), ARG_I32(2)); - } - - static void CallFunc_F64_I32_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - int32_t (*fptr)(double, int32_t) = (int32_t (*)(double, int32_t))pcode; - *((int32_t*)pRet) = (*fptr)(ARG_F64(0), ARG_I32(1)); - } - - static void CallFunc_F32_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - int32_t (*fptr)(float) = (int32_t (*)(float))pcode; - *((int32_t*)pRet) = (*fptr)(ARG_F32(0)); - } - - static void CallFunc_F32_F32_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - int32_t (*fptr)(float, float) = (int32_t (*)(float, float))pcode; - *((int32_t*)pRet) = (*fptr)(ARG_F32(0), ARG_F32(1)); - } - static void CallFunc_I32_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) { int32_t (*fptr)(int32_t) = (int32_t (*)(int32_t))pcode; *((int32_t*)pRet) = (*fptr)(ARG_I32(0)); } - static void CallFunc_I32_F64_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - int32_t (*fptr)(int32_t, double) = (int32_t (*)(int32_t, double))pcode; - *((int32_t*)pRet) = (*fptr)(ARG_I32(0), ARG_F64(1)); - } - - static void CallFunc_I32_F32_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - int32_t (*fptr)(int32_t, float) = (int32_t (*)(int32_t, float))pcode; - *((int32_t*)pRet) = (*fptr)(ARG_I32(0), ARG_F32(1)); - } - - static void CallFunc_I32_F32_I32_I32_F32_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - int32_t (*fptr)(int32_t, float, int32_t, int32_t, float) = (int32_t (*)(int32_t, float, int32_t, int32_t, float))pcode; - *((int32_t*)pRet) = (*fptr)(ARG_I32(0), ARG_F32(1), ARG_I32(2), ARG_I32(3), ARG_F32(4)); - } - static void CallFunc_I32_I32_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) { int32_t (*fptr)(int32_t, int32_t) = (int32_t (*)(int32_t, int32_t))pcode; @@ -199,12 +127,6 @@ namespace *((int32_t*)pRet) = (*fptr)(ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), ARG_I32(5), ARG_I32(6), ARG_I32(7), ARG_I32(8), ARG_I32(9), ARG_I32(10), ARG_I32(11), ARG_I32(12), ARG_I32(13)); } - static void CallFunc_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - int32_t (*fptr)(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t) = (int32_t (*)(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t))pcode; - *((int32_t*)pRet) = (*fptr)(ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), ARG_I32(5), ARG_I32(6), ARG_I32(7), ARG_I32(8), ARG_I32(9), ARG_I32(10), ARG_I32(11), ARG_I32(12), ARG_I32(13), ARG_I32(14), ARG_I32(15), ARG_I32(16)); - } - static void CallFunc_I32_I32_I32_I64_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) { int32_t (*fptr)(int32_t, int32_t, int32_t, int64_t) = (int32_t (*)(int32_t, int32_t, int32_t, int64_t))pcode; @@ -265,12 +187,6 @@ namespace *((int32_t*)pRet) = (*fptr)(ARG_I32(0), ARG_I64(1), ARG_I64(2), ARG_I32(3)); } - static void CallFunc_I32_IND_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - int32_t (*fptr)(int32_t, int32_t) = (int32_t (*)(int32_t, int32_t))pcode; - *((int32_t*)pRet) = (*fptr)(ARG_I32(0), ARG_IND(1)); - } - static void CallFunc_I32_IND_I32_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) { int32_t (*fptr)(int32_t, int32_t, int32_t) = (int32_t (*)(int32_t, int32_t, int32_t))pcode; @@ -283,24 +199,6 @@ namespace *((int32_t*)pRet) = (*fptr)(ARG_I32(0), ARG_IND(1), ARG_I32(2), ARG_I32(3), ARG_I32(4)); } - static void CallFunc_I32_IND_I32_IND_I32_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - int32_t (*fptr)(int32_t, int32_t, int32_t, int32_t, int32_t) = (int32_t (*)(int32_t, int32_t, int32_t, int32_t, int32_t))pcode; - *((int32_t*)pRet) = (*fptr)(ARG_I32(0), ARG_IND(1), ARG_I32(2), ARG_IND(3), ARG_I32(4)); - } - - static void CallFunc_I32_IND_IND_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - int32_t (*fptr)(int32_t, int32_t, int32_t) = (int32_t (*)(int32_t, int32_t, int32_t))pcode; - *((int32_t*)pRet) = (*fptr)(ARG_I32(0), ARG_IND(1), ARG_IND(2)); - } - - static void CallFunc_I64_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - int32_t (*fptr)(int64_t) = (int32_t (*)(int64_t))pcode; - *((int32_t*)pRet) = (*fptr)(ARG_I64(0)); - } - static void CallFunc_I64_I32_I64_I32_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) { int32_t (*fptr)(int64_t, int32_t, int64_t, int32_t) = (int32_t (*)(int64_t, int32_t, int64_t, int32_t))pcode; @@ -355,12 +253,6 @@ namespace *((int32_t*)pRet) = (*fptr)(ARG_IND(0), ARG_I32(1), ARG_IND(2), ARG_IND(3)); } - static void CallFunc_IND_IND_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - int32_t (*fptr)(int32_t, int32_t) = (int32_t (*)(int32_t, int32_t))pcode; - *((int32_t*)pRet) = (*fptr)(ARG_IND(0), ARG_IND(1)); - } - static void CallFunc_IND_IND_I32_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) { int32_t (*fptr)(int32_t, int32_t, int32_t) = (int32_t (*)(int32_t, int32_t, int32_t))pcode; @@ -385,30 +277,12 @@ namespace *((int64_t*)pRet) = (*fptr)(); } - static void CallFunc_F64_RetI64(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - int64_t (*fptr)(double) = (int64_t (*)(double))pcode; - *((int64_t*)pRet) = (*fptr)(ARG_F64(0)); - } - - static void CallFunc_F32_RetI64(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - int64_t (*fptr)(float) = (int64_t (*)(float))pcode; - *((int64_t*)pRet) = (*fptr)(ARG_F32(0)); - } - static void CallFunc_I32_RetI64(PCODE pcode, int8_t* pArgs, int8_t* pRet) { int64_t (*fptr)(int32_t) = (int64_t (*)(int32_t))pcode; *((int64_t*)pRet) = (*fptr)(ARG_I32(0)); } - static void CallFunc_I32_I32_RetI64(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - int64_t (*fptr)(int32_t, int32_t) = (int64_t (*)(int32_t, int32_t))pcode; - *((int64_t*)pRet) = (*fptr)(ARG_I32(0), ARG_I32(1)); - } - static void CallFunc_I32_I32_I32_I64_RetI64(PCODE pcode, int8_t* pArgs, int8_t* pRet) { int64_t (*fptr)(int32_t, int32_t, int32_t, int64_t) = (int64_t (*)(int32_t, int32_t, int32_t, int64_t))pcode; @@ -433,104 +307,24 @@ namespace *((int64_t*)pRet) = (*fptr)(ARG_I32(0), ARG_I64(1), ARG_I64(2)); } - static void CallFunc_I64_RetI64(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - int64_t (*fptr)(int64_t) = (int64_t (*)(int64_t))pcode; - *((int64_t*)pRet) = (*fptr)(ARG_I64(0)); - } - - static void CallFunc_I64_I32_RetI64(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - int64_t (*fptr)(int64_t, int32_t) = (int64_t (*)(int64_t, int32_t))pcode; - *((int64_t*)pRet) = (*fptr)(ARG_I64(0), ARG_I32(1)); - } - static void CallFunc_I64_I64_RetI64(PCODE pcode, int8_t* pArgs, int8_t* pRet) { int64_t (*fptr)(int64_t, int64_t) = (int64_t (*)(int64_t, int64_t))pcode; *((int64_t*)pRet) = (*fptr)(ARG_I64(0), ARG_I64(1)); } - static void CallFunc_Void_RetIND(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - int32_t (*fptr)() = (int32_t (*)())pcode; - PORTABILITY_ASSERT("Indirect struct return is not yet implemented."); - *((int32_t*)pRet) = (*fptr)(); - } - - static void CallFunc_I32_RetIND(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - int32_t (*fptr)(int32_t) = (int32_t (*)(int32_t))pcode; - PORTABILITY_ASSERT("Indirect struct return is not yet implemented."); - *((int32_t*)pRet) = (*fptr)(ARG_I32(0)); - } - - static void CallFunc_I32_I32_RetIND(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - int32_t (*fptr)(int32_t, int32_t) = (int32_t (*)(int32_t, int32_t))pcode; - PORTABILITY_ASSERT("Indirect struct return is not yet implemented."); - *((int32_t*)pRet) = (*fptr)(ARG_I32(0), ARG_I32(1)); - } - - static void CallFunc_I32_I32_I32_RetIND(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - int32_t (*fptr)(int32_t, int32_t, int32_t) = (int32_t (*)(int32_t, int32_t, int32_t))pcode; - PORTABILITY_ASSERT("Indirect struct return is not yet implemented."); - *((int32_t*)pRet) = (*fptr)(ARG_I32(0), ARG_I32(1), ARG_I32(2)); - } - - static void CallFunc_I32_IND_RetIND(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - int32_t (*fptr)(int32_t, int32_t) = (int32_t (*)(int32_t, int32_t))pcode; - PORTABILITY_ASSERT("Indirect struct return is not yet implemented."); - *((int32_t*)pRet) = (*fptr)(ARG_I32(0), ARG_IND(1)); - } - - static void CallFunc_IND_RetIND(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - int32_t (*fptr)(int32_t) = (int32_t (*)(int32_t))pcode; - PORTABILITY_ASSERT("Indirect struct return is not yet implemented."); - *((int32_t*)pRet) = (*fptr)(ARG_IND(0)); - } - - static void CallFunc_IND_I32_RetIND(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - int32_t (*fptr)(int32_t, int32_t) = (int32_t (*)(int32_t, int32_t))pcode; - PORTABILITY_ASSERT("Indirect struct return is not yet implemented."); - *((int32_t*)pRet) = (*fptr)(ARG_IND(0), ARG_I32(1)); - } - - static void CallFunc_IND_IND_RetIND(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - int32_t (*fptr)(int32_t, int32_t) = (int32_t (*)(int32_t, int32_t))pcode; - PORTABILITY_ASSERT("Indirect struct return is not yet implemented."); - *((int32_t*)pRet) = (*fptr)(ARG_IND(0), ARG_IND(1)); - } - static void CallFunc_Void_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) { void (*fptr)() = (void (*)())pcode; (*fptr)(); } - static void CallFunc_F64_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - void (*fptr)(double) = (void (*)(double))pcode; - (*fptr)(ARG_F64(0)); - } - static void CallFunc_F64_I32_I32_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) { void (*fptr)(double, int32_t, int32_t) = (void (*)(double, int32_t, int32_t))pcode; (*fptr)(ARG_F64(0), ARG_I32(1), ARG_I32(2)); } - static void CallFunc_F32_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - void (*fptr)(float) = (void (*)(float))pcode; - (*fptr)(ARG_F32(0)); - } - static void CallFunc_F32_I32_I32_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) { void (*fptr)(float, int32_t, int32_t) = (void (*)(float, int32_t, int32_t))pcode; @@ -543,12 +337,6 @@ namespace (*fptr)(ARG_I32(0)); } - static void CallFunc_I32_F64_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - void (*fptr)(int32_t, double) = (void (*)(int32_t, double))pcode; - (*fptr)(ARG_I32(0), ARG_F64(1)); - } - static void CallFunc_I32_I32_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) { void (*fptr)(int32_t, int32_t) = (void (*)(int32_t, int32_t))pcode; @@ -579,12 +367,6 @@ namespace (*fptr)(ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), ARG_I32(5)); } - static void CallFunc_I32_I32_I32_I32_I32_I32_I32_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - void (*fptr)(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t) = (void (*)(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t))pcode; - (*fptr)(ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), ARG_I32(5), ARG_I32(6)); - } - static void CallFunc_I32_I32_I32_IND_IND_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) { void (*fptr)(int32_t, int32_t, int32_t, int32_t, int32_t) = (void (*)(int32_t, int32_t, int32_t, int32_t, int32_t))pcode; @@ -597,18 +379,6 @@ namespace (*fptr)(ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_IND(3), ARG_IND(4), ARG_I32(5)); } - static void CallFunc_I32_I32_I64_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - void (*fptr)(int32_t, int32_t, int64_t) = (void (*)(int32_t, int32_t, int64_t))pcode; - (*fptr)(ARG_I32(0), ARG_I32(1), ARG_I64(2)); - } - - static void CallFunc_I32_I32_IND_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - void (*fptr)(int32_t, int32_t, int32_t) = (void (*)(int32_t, int32_t, int32_t))pcode; - (*fptr)(ARG_I32(0), ARG_I32(1), ARG_IND(2)); - } - static void CallFunc_I32_I32_IND_IND_I32_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) { void (*fptr)(int32_t, int32_t, int32_t, int32_t, int32_t) = (void (*)(int32_t, int32_t, int32_t, int32_t, int32_t))pcode; @@ -621,30 +391,12 @@ namespace (*fptr)(ARG_I32(0), ARG_I32(1), ARG_IND(2), ARG_IND(3), ARG_I32(4), ARG_I32(5)); } - static void CallFunc_I32_I64_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - void (*fptr)(int32_t, int64_t) = (void (*)(int32_t, int64_t))pcode; - (*fptr)(ARG_I32(0), ARG_I64(1)); - } - - static void CallFunc_I32_IND_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - void (*fptr)(int32_t, int32_t) = (void (*)(int32_t, int32_t))pcode; - (*fptr)(ARG_I32(0), ARG_IND(1)); - } - static void CallFunc_I32_IND_I32_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) { void (*fptr)(int32_t, int32_t, int32_t) = (void (*)(int32_t, int32_t, int32_t))pcode; (*fptr)(ARG_I32(0), ARG_IND(1), ARG_I32(2)); } - static void CallFunc_I32_IND_IND_I32_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - void (*fptr)(int32_t, int32_t, int32_t, int32_t) = (void (*)(int32_t, int32_t, int32_t, int32_t))pcode; - (*fptr)(ARG_I32(0), ARG_IND(1), ARG_IND(2), ARG_I32(3)); - } - static void CallFunc_I64_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) { void (*fptr)(int64_t) = (void (*)(int64_t))pcode; @@ -713,29 +465,17 @@ namespace } const StringToWasmSigThunk g_wasmThunks[] = { - { "d", (void*)&CallFunc_Void_RetF64 }, { "dd", (void*)&CallFunc_F64_RetF64 }, { "ddd", (void*)&CallFunc_F64_F64_RetF64 }, { "dddd", (void*)&CallFunc_F64_F64_F64_RetF64 }, { "ddi", (void*)&CallFunc_F64_I32_RetF64 }, - { "ddid", (void*)&CallFunc_F64_I32_F64_RetF64 }, { "di", (void*)&CallFunc_I32_RetF64 }, - { "dii", (void*)&CallFunc_I32_I32_RetF64 }, - { "f", (void*)&CallFunc_Void_RetF32 }, { "ff", (void*)&CallFunc_F32_RetF32 }, { "fff", (void*)&CallFunc_F32_F32_RetF32 }, { "ffff", (void*)&CallFunc_F32_F32_F32_RetF32 }, { "ffi", (void*)&CallFunc_F32_I32_RetF32 }, { "i", (void*)&CallFunc_Void_RetI32 }, - { "id", (void*)&CallFunc_F64_RetI32 }, - { "idfi", (void*)&CallFunc_F64_F32_I32_RetI32 }, - { "idi", (void*)&CallFunc_F64_I32_RetI32 }, - { "if", (void*)&CallFunc_F32_RetI32 }, - { "iff", (void*)&CallFunc_F32_F32_RetI32 }, { "ii", (void*)&CallFunc_I32_RetI32 }, - { "iid", (void*)&CallFunc_I32_F64_RetI32 }, - { "iif", (void*)&CallFunc_I32_F32_RetI32 }, - { "iifiif", (void*)&CallFunc_I32_F32_I32_I32_F32_RetI32 }, { "iii", (void*)&CallFunc_I32_I32_RetI32 }, { "iiii", (void*)&CallFunc_I32_I32_I32_RetI32 }, { "iiiii", (void*)&CallFunc_I32_I32_I32_I32_RetI32 }, @@ -743,7 +483,6 @@ const StringToWasmSigThunk g_wasmThunks[] = { { "iiiiiii", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_RetI32 }, { "iiiiiiii", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_I32_RetI32 }, { "iiiiiiiiiiiiiii", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_RetI32 }, - { "iiiiiiiiiiiiiiiiii", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_RetI32 }, { "iiiil", (void*)&CallFunc_I32_I32_I32_I64_RetI32 }, { "iiiini", (void*)&CallFunc_I32_I32_I32_IND_I32_RetI32 }, { "iiil", (void*)&CallFunc_I32_I32_I64_RetI32 }, @@ -754,12 +493,8 @@ const StringToWasmSigThunk g_wasmThunks[] = { { "iiliiil", (void*)&CallFunc_I32_I64_I32_I32_I32_I64_RetI32 }, { "iill", (void*)&CallFunc_I32_I64_I64_RetI32 }, { "iilli", (void*)&CallFunc_I32_I64_I64_I32_RetI32 }, - { "iin", (void*)&CallFunc_I32_IND_RetI32 }, { "iini", (void*)&CallFunc_I32_IND_I32_RetI32 }, { "iiniii", (void*)&CallFunc_I32_IND_I32_I32_I32_RetI32 }, - { "iinini", (void*)&CallFunc_I32_IND_I32_IND_I32_RetI32 }, - { "iinn", (void*)&CallFunc_I32_IND_IND_RetI32 }, - { "il", (void*)&CallFunc_I64_RetI32 }, { "ilili", (void*)&CallFunc_I64_I32_I64_I32_RetI32 }, { "in", (void*)&CallFunc_IND_RetI32 }, { "ini", (void*)&CallFunc_IND_I32_RetI32 }, @@ -769,53 +504,30 @@ const StringToWasmSigThunk g_wasmThunks[] = { { "iniiiii", (void*)&CallFunc_IND_I32_I32_I32_I32_I32_RetI32 }, { "inini", (void*)&CallFunc_IND_I32_IND_I32_RetI32 }, { "ininn", (void*)&CallFunc_IND_I32_IND_IND_RetI32 }, - { "inn", (void*)&CallFunc_IND_IND_RetI32 }, { "inni", (void*)&CallFunc_IND_IND_I32_RetI32 }, { "innii", (void*)&CallFunc_IND_IND_I32_I32_RetI32 }, { "innin", (void*)&CallFunc_IND_IND_I32_IND_RetI32 }, { "l", (void*)&CallFunc_Void_RetI64 }, - { "ld", (void*)&CallFunc_F64_RetI64 }, - { "lf", (void*)&CallFunc_F32_RetI64 }, { "li", (void*)&CallFunc_I32_RetI64 }, - { "lii", (void*)&CallFunc_I32_I32_RetI64 }, { "liiil", (void*)&CallFunc_I32_I32_I32_I64_RetI64 }, { "lil", (void*)&CallFunc_I32_I64_RetI64 }, { "lili", (void*)&CallFunc_I32_I64_I32_RetI64 }, { "lill", (void*)&CallFunc_I32_I64_I64_RetI64 }, - { "ll", (void*)&CallFunc_I64_RetI64 }, - { "lli", (void*)&CallFunc_I64_I32_RetI64 }, { "lll", (void*)&CallFunc_I64_I64_RetI64 }, - { "n", (void*)&CallFunc_Void_RetIND }, - { "ni", (void*)&CallFunc_I32_RetIND }, - { "nii", (void*)&CallFunc_I32_I32_RetIND }, - { "niii", (void*)&CallFunc_I32_I32_I32_RetIND }, - { "nin", (void*)&CallFunc_I32_IND_RetIND }, - { "nn", (void*)&CallFunc_IND_RetIND }, - { "nni", (void*)&CallFunc_IND_I32_RetIND }, - { "nnn", (void*)&CallFunc_IND_IND_RetIND }, { "v", (void*)&CallFunc_Void_RetVoid }, - { "vd", (void*)&CallFunc_F64_RetVoid }, { "vdii", (void*)&CallFunc_F64_I32_I32_RetVoid }, - { "vf", (void*)&CallFunc_F32_RetVoid }, { "vfii", (void*)&CallFunc_F32_I32_I32_RetVoid }, { "vi", (void*)&CallFunc_I32_RetVoid }, - { "vid", (void*)&CallFunc_I32_F64_RetVoid }, { "vii", (void*)&CallFunc_I32_I32_RetVoid }, { "viii", (void*)&CallFunc_I32_I32_I32_RetVoid }, { "viiii", (void*)&CallFunc_I32_I32_I32_I32_RetVoid }, { "viiiii", (void*)&CallFunc_I32_I32_I32_I32_I32_RetVoid }, { "viiiiii", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_RetVoid }, - { "viiiiiii", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_I32_RetVoid }, { "viiinn", (void*)&CallFunc_I32_I32_I32_IND_IND_RetVoid }, { "viiinni", (void*)&CallFunc_I32_I32_I32_IND_IND_I32_RetVoid }, - { "viil", (void*)&CallFunc_I32_I32_I64_RetVoid }, - { "viin", (void*)&CallFunc_I32_I32_IND_RetVoid }, { "viinni", (void*)&CallFunc_I32_I32_IND_IND_I32_RetVoid }, { "viinnii", (void*)&CallFunc_I32_I32_IND_IND_I32_I32_RetVoid }, - { "vil", (void*)&CallFunc_I32_I64_RetVoid }, - { "vin", (void*)&CallFunc_I32_IND_RetVoid }, { "vini", (void*)&CallFunc_I32_IND_I32_RetVoid }, - { "vinni", (void*)&CallFunc_I32_IND_IND_I32_RetVoid }, { "vl", (void*)&CallFunc_I64_RetVoid }, { "vn", (void*)&CallFunc_IND_RetVoid }, { "vni", (void*)&CallFunc_IND_I32_RetVoid }, diff --git a/src/tasks/WasmAppBuilder/coreclr/ManagedToNativeGenerator.cs b/src/tasks/WasmAppBuilder/coreclr/ManagedToNativeGenerator.cs index 88bddf47f040c6..b1722487c6a277 100644 --- a/src/tasks/WasmAppBuilder/coreclr/ManagedToNativeGenerator.cs +++ b/src/tasks/WasmAppBuilder/coreclr/ManagedToNativeGenerator.cs @@ -68,55 +68,6 @@ public override bool Execute() } } - // WASM-TODO: - // add missing signatures temporarily - // part is for runtime tests and delegates - // active issue https://github.com/dotnet/runtime/issues/121222 - private static readonly string[] missingCookies = - [ - "d", - "ddid", - "dii", - "f", - "id", - "idfi", - "idi", - "if", - "iff", - "iid", - "iif", - "iifiif", - "iiiiiiiiiiiiiiiiii", - "iin", - "iinini", - "iinn", - "il", - "inn", - "lii", - "ld", - "lf", - "ll", - "lli", - "n", - "ni", - "nii", - "niii", - "nin", - "nn", - "nni", - "nnn", - "vd", - "vf", - "viiiiiii", - "viin", - "vid", - "viil", - "vil", - "vin", - "vinni", - "iinini", - ]; - private void ExecuteInternal(LogAdapter log) { Dictionary _symbolNameFixups = new(); @@ -136,8 +87,7 @@ private void ExecuteInternal(LogAdapter log) IEnumerable cookies = Enumerable.Concat( pinvoke.Generate(PInvokeModules, PInvokeOutputPath, ReversePInvokeOutputPath), - Enumerable.Concat(icall.Generate(IcallOutputPath), - missingCookies)); + icall.Generate(IcallOutputPath)); var m2n = new InterpToNativeGenerator(log); m2n.Generate(cookies, InterpToNativeOutputPath); From 26d126c6d5838f5f215a51ca035d978054197c21 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Wed, 11 Mar 2026 19:38:27 +0100 Subject: [PATCH 4/6] Use volatile store/load and use IfFailThrow around the GetSigFromToken call --- src/coreclr/vm/interpexec.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index fb629dddda9b29..c14502fc4b86ea 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -3054,7 +3054,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr { // pDataItems[calliCookie] = sig token (immutable) // pDataItems[calliCookie + 1] = cached cookie (initially NULL) - cookie = pMethod->pDataItems[calliCookie + 1]; + cookie = VolatileLoadWithoutBarrier(&pMethod->pDataItems[calliCookie + 1]); if (cookie == nullptr) { mdToken sigToken = (mdToken)(size_t)pMethod->pDataItems[calliCookie]; @@ -3062,10 +3062,12 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr Module* pModule = pCallerMD->GetModule(); PCCOR_SIGNATURE pSig; ULONG cbSig; - pModule->GetMDImport()->GetSigFromToken(sigToken, &cbSig, &pSig); + IfFailThrow(pModule->GetMDImport()->GetSigFromToken(sigToken, &cbSig, &pSig)); + // nullptr type context is safe here — this path is only reached for + // JIT helper portable entry points which are non-generic. MetaSig sig(pSig, cbSig, pModule, nullptr); cookie = GetCookieForCalliSig(sig); - pMethod->pDataItems[calliCookie + 1] = cookie; + VolatileStoreWithoutBarrier(&pMethod->pDataItems[calliCookie + 1], cookie); } } #endif // FEATURE_PORTABLE_ENTRYPOINTS From 7a82b19a3e54800c65e29c04913867e1c7d90315 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Thu, 12 Mar 2026 18:25:33 +0100 Subject: [PATCH 5/6] Feedback --- src/coreclr/interpreter/compiler.cpp | 44 +++++++++++++++++----------- src/coreclr/interpreter/compiler.h | 2 +- src/coreclr/vm/interpexec.cpp | 14 +++++---- 3 files changed, 36 insertions(+), 24 deletions(-) diff --git a/src/coreclr/interpreter/compiler.cpp b/src/coreclr/interpreter/compiler.cpp index 14a1b6075843dc..7cd22fd3464b3d 100644 --- a/src/coreclr/interpreter/compiler.cpp +++ b/src/coreclr/interpreter/compiler.cpp @@ -4315,26 +4315,32 @@ bool InterpCompiler::DisallowTailCall(CORINFO_SIG_INFO* callerSig, CORINFO_SIG_I return false; } -void InterpCompiler::EmitCalli(bool isTailCall, void* calliCookie, int callIFunctionPointerVar, CORINFO_SIG_INFO* callSiteSig, bool deferredCookie) +void InterpCompiler::EmitCalli(bool isTailCall, void* calliCookie, int callIFunctionPointerVar, CORINFO_SIG_INFO* callSiteSig) { AddIns(isTailCall ? INTOP_CALLI_TAIL : INTOP_CALLI); + // data[1] is set to 1 if the calli is calling a pinvoke, 0 otherwise + bool suppressGCTransition = false; + CorInfoCallConv callConv = (CorInfoCallConv)(callSiteSig->callConv & IMAGE_CEE_CS_CALLCONV_MASK); + bool isPInvoke = (callConv != CORINFO_CALLCONV_DEFAULT && callConv != CORINFO_CALLCONV_VARARG); +#ifdef FEATURE_PORTABLE_ENTRYPOINTS + // On portable entry point platforms, managed callis defer cookie resolution to + // execution time. Allocate two consecutive data items: the sig token (immutable) + // and a cache slot (initially NULL) for the resolved cookie. + // calliCookie is non-NULL only for IL calli instructions where it holds + // the signature token. CODE_POINTER / LDVIRTFTN callis pass NULL because + // their targets always carry a MethodDesc resolved at execution time. + bool deferredCookie = !isPInvoke && calliCookie != NULL; if (deferredCookie) { - // Allocate two consecutive data items: sig token + cache slot (initially NULL). - // The executor uses pDataItems[index] for the sig token and pDataItems[index+1] - // for caching the resolved cookie. m_pLastNewIns->data[0] = GetNewDataItemIndex(calliCookie); int32_t cacheSlot = GetNewDataItemIndex(nullptr); _ASSERTE(cacheSlot == m_pLastNewIns->data[0] + 1); } else +#endif { m_pLastNewIns->data[0] = GetDataItemIndex(calliCookie); } - // data[1] is set to 1 if the calli is calling a pinvoke, 0 otherwise - bool suppressGCTransition = false; - CorInfoCallConv callConv = (CorInfoCallConv)(callSiteSig->callConv & IMAGE_CEE_CS_CALLCONV_MASK); - bool isPInvoke = (callConv != CORINFO_CALLCONV_DEFAULT && callConv != CORINFO_CALLCONV_VARARG); if (isPInvoke) { if (m_compHnd->pInvokeMarshalingRequired(NULL, callSiteSig)) @@ -4346,8 +4352,11 @@ void InterpCompiler::EmitCalli(bool isTailCall, void* calliCookie, int callIFunc m_compHnd->getUnmanagedCallConv(nullptr, callSiteSig, &suppressGCTransition); } m_pLastNewIns->data[1] = (suppressGCTransition ? (int32_t)CalliFlags::SuppressGCTransition : 0) | - (isPInvoke ? (int32_t)CalliFlags::PInvoke : 0) | - (deferredCookie ? (int32_t)CalliFlags::DeferredCookie : 0); + (isPInvoke ? (int32_t)CalliFlags::PInvoke : 0) +#ifdef FEATURE_PORTABLE_ENTRYPOINTS + | (deferredCookie ? (int32_t)CalliFlags::DeferredCookie : 0) +#endif + ; m_pLastNewIns->SetSVars2(CALL_ARGS_SVAR, callIFunctionPointerVar); } @@ -4787,7 +4796,6 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re int32_t callIFunctionPointerVar = -1; void* calliCookie = NULL; - bool calliDeferredCookie = false; ContinuationContextHandling continuationContextHandling = ContinuationContextHandling::None; if (isCalli) @@ -4811,10 +4819,13 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re callIFunctionPointerVar = m_pStackPointer[-1].var; m_pStackPointer--; #ifdef FEATURE_PORTABLE_ENTRYPOINTS - // On platforms with portable entrypoints, managed calli targets with a MethodDesc - // are dispatched via CALL_INTERP_METHOD at execution time, which derives the cookie - // from the MethodDesc. For targets without a MethodDesc (JIT helper PEs), the cookie - // is computed at execution time from the calli's signature token. + // On platforms with portable entry points, managed calli targets are portable + // entry points. At execution time, if the portable entry point carries a MethodDesc, + // the call is dispatched via CALL_INTERP_METHOD which derives the cookie from the + // MethodDesc. Targets without a MethodDesc — JIT helper portable entry points such + // as object allocators (e.g. CORINFO_HELP_NEWFAST) called via delegate* from BCL + // code like ActivatorCache — fall through to deferred cookie resolution using the + // calli's signature token stored here. { CorInfoCallConv callConv = (CorInfoCallConv)(callInfo.sig.callConv & IMAGE_CEE_CS_CALLCONV_MASK); bool isUnmanaged = (callConv != CORINFO_CALLCONV_DEFAULT && callConv != CORINFO_CALLCONV_VARARG); @@ -4826,7 +4837,6 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re { // Store the sig token for runtime cookie resolution. calliCookie = (void*)(size_t)token; - calliDeferredCookie = true; } } #else @@ -5368,7 +5378,7 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re } else if (isCalli) { - EmitCalli(tailcall, calliCookie, callIFunctionPointerVar, &callInfo.sig, calliDeferredCookie); + EmitCalli(tailcall, calliCookie, callIFunctionPointerVar, &callInfo.sig); if (((m_pLastNewIns->data[1] & (int32_t)CalliFlags::PInvoke) != 0) && m_pVars[dVar].interpType == InterpTypeVT) { diff --git a/src/coreclr/interpreter/compiler.h b/src/coreclr/interpreter/compiler.h index 8732ce1a7b2908..6f58d0c065cf40 100644 --- a/src/coreclr/interpreter/compiler.h +++ b/src/coreclr/interpreter/compiler.h @@ -983,7 +983,7 @@ class InterpCompiler void EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool readonly, bool tailcall, bool newObj, bool isCalli); void EmitRet(CORINFO_METHOD_INFO* methodInfo); void EmitSuspend(const CORINFO_CALL_INFO &callInfo, ContinuationContextHandling ContinuationContextHandling); - void EmitCalli(bool isTailCall, void* calliCookie, int callIFunctionPointerVar, CORINFO_SIG_INFO* callSiteSig, bool deferredCookie = false); + void EmitCalli(bool isTailCall, void* calliCookie, int callIFunctionPointerVar, CORINFO_SIG_INFO* callSiteSig); bool EmitNamedIntrinsicCall(NamedIntrinsic ni, bool nonVirtualCall, CORINFO_CLASS_HANDLE clsHnd, 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, bool enableImplicitArgConversionRules); diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index c14502fc4b86ea..5fca167506b4c4 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -3039,11 +3039,13 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr { #ifdef FEATURE_PORTABLE_ENTRYPOINTS // On portable entry point platforms, calli targets are portable entry points. - // If the PE has a MethodDesc, route through CALL_INTERP_METHOD which derives - // the cookie at runtime. This covers both interpreted methods and FCalls - // (which have native code set but still carry an MD). - // JIT helper PEs (no MethodDesc) fall through to InvokeCalliStub — the - // cookie is resolved at runtime from the calli's signature token. + // If the portable entry point has a MethodDesc, route through + // CALL_INTERP_METHOD which derives the cookie at runtime. This covers + // both interpreted methods and FCalls (which have native code set but + // still carry a MethodDesc). + // JIT helper portable entry points (no MethodDesc) fall through to + // InvokeCalliStub — the cookie is resolved at runtime from the calli's + // signature token. targetMethod = PortableEntryPoint::TryGetMethodDesc(calliFunctionPointer); if (targetMethod != nullptr) { @@ -3055,7 +3057,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr // pDataItems[calliCookie] = sig token (immutable) // pDataItems[calliCookie + 1] = cached cookie (initially NULL) cookie = VolatileLoadWithoutBarrier(&pMethod->pDataItems[calliCookie + 1]); - if (cookie == nullptr) + if (cookie == NULL) { mdToken sigToken = (mdToken)(size_t)pMethod->pDataItems[calliCookie]; MethodDesc* pCallerMD = (MethodDesc*)pMethod->methodHnd; From f1a345d9fda5834f28a55876a7d367fc9fc5a0a1 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Fri, 13 Mar 2026 10:59:11 +0100 Subject: [PATCH 6/6] Fix build after merge --- src/coreclr/vm/interpexec.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index fd25d887a8cd54..d0131af40943a0 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -208,7 +208,7 @@ void InvokeUnmanagedMethod(MethodDesc *targetMethod, int8_t *pArgs, int8_t *pRet void InvokeCalliStub(CalliStubParam* pParam); void InvokeUnmanagedCalli(PCODE ftn, void *cookie, int8_t *pArgs, int8_t *pRet); void InvokeDelegateInvokeMethod(DelegateInvokeMethodParam* pParam); -void* GetCookieForCalliSig(MetaSig metaSig); +void* GetCookieForCalliSig(MetaSig metaSig, MethodDesc *pContextMD); extern "C" PCODE CID_VirtualOpenDelegateDispatch(TransitionBlock * pTransitionBlock); // Filter to ignore SEH exceptions representing C++ exceptions. @@ -3068,7 +3068,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr // nullptr type context is safe here — this path is only reached for // JIT helper portable entry points which are non-generic. MetaSig sig(pSig, cbSig, pModule, nullptr); - cookie = GetCookieForCalliSig(sig); + cookie = GetCookieForCalliSig(sig, nullptr); VolatileStoreWithoutBarrier(&pMethod->pDataItems[calliCookie + 1], cookie); } }