From 63a83033460d1d2ed31c6487083571ddd4086f0d Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 24 Jun 2026 14:29:05 +0200 Subject: [PATCH 1/2] Ensure we do not try to inline async versions of pinvokes We normally fail inlining of pinvokes when we call getMethodInfo, but if we tried inlining an async variant of a pinvoke then that call would succeed and we would end up breaking in getEHinfo that does not handle this case. --- src/coreclr/vm/jitinterface.cpp | 31 +++++++++++-------------------- src/coreclr/vm/jitinterface.h | 1 + src/coreclr/vm/method.hpp | 13 +++++++++++++ 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 485a0c336156d8..7479db97aa4844 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -7514,6 +7514,7 @@ static bool getILIntrinsicImplementationForActivator(MethodDesc* ftn, // COR_ILMETHOD_DECODER* CEEInfo::getMethodInfoWorker( MethodDesc* ftn, + MethodDesc* ilFtn, COR_ILMETHOD_DECODER* header, CORINFO_METHOD_INFO* methInfo, CORINFO_CONTEXT_HANDLE exactContext) @@ -7530,10 +7531,6 @@ COR_ILMETHOD_DECODER* CEEInfo::getMethodInfoWorker( /* Grab information from the IL header */ SigPointer localSig{}; - // For async versions we get the IL of the ordinary variant and pass the - // JIT a flag indicating that. - MethodDesc* ilFtn = ftn->SupportsAsyncVersionCodegen() ? ftn->GetOrdinaryVariant() : ftn; - MethodInfoWorkerContext cxt{ ftn, header }; TransientMethodDetails* detailsMaybe = NULL; @@ -7740,22 +7737,20 @@ CEEInfo::getMethodInfo( JIT_TO_EE_TRANSITION(); MethodDesc* ftn = GetMethod(ftnHnd); + MethodDesc* ilFtn = ftn->GetOrdinaryVariantIfAsyncVersion(); + COR_ILMETHOD* pILHeader; - if (ftn->MayHaveILHeader() && (pILHeader = ftn->GetILHeader()) != NULL) + if (ilFtn->MayHaveILHeader() && (pILHeader = ilFtn->GetILHeader()) != NULL) { // Get the IL header and set it. - COR_ILMETHOD_DECODER header(pILHeader, ftn->GetMDImport(), NULL); - getMethodInfoWorker(ftn, &header, methInfo, context); + COR_ILMETHOD_DECODER header(pILHeader, ilFtn->GetMDImport(), NULL); + getMethodInfoWorker(ftn, ilFtn, &header, methInfo, context); result = true; } - else if (ftn->IsIL() || ftn->IsDynamicMethod()) + else if (ilFtn->IsIL() || ilFtn->IsDynamicMethod()) { // IL methods with no IL header indicate there is no implementation defined in metadata. - // NOTE: P/Invoke methods are also IL methods with no IL header, - // but it is generally not profitable to inline the marshalling IL. - // As a result, we skip inlining the marshalling IL. - // P/Invokes that don't require marshalling can still be inlined directly by the JIT. - getMethodInfoWorker(ftn, NULL, methInfo, context); + getMethodInfoWorker(ftn, ilFtn, NULL, methInfo, context); result = true; } @@ -7940,7 +7935,7 @@ CorInfoInline CEEInfo::canInline (CORINFO_METHOD_HANDLE hCaller, else if (pCallee->IsIL()) { CORINFO_METHOD_INFO methodInfo; - getMethodInfoWorker(pCallee, NULL, &methodInfo); + getMethodInfoWorker(pCallee, pCallee->GetOrdinaryVariantIfAsyncVersion(), NULL, &methodInfo); if (methodInfo.EHcount > 0) { result = INLINE_FAIL; @@ -10975,7 +10970,7 @@ CEECodeGenInfo::CEECodeGenInfo(PrepareCodeConfig* config, MethodDesc* fd, COR_IL { STANDARD_VM_CONTRACT; _ASSERTE(config != NULL); - COR_ILMETHOD_DECODER* ilHeader = getMethodInfoWorker(m_pMethodBeingCompiled, m_ILHeader, &m_MethodInfo); + COR_ILMETHOD_DECODER* ilHeader = getMethodInfoWorker(m_pMethodBeingCompiled, m_pMethodBeingCompiled->GetOrdinaryVariantIfAsyncVersion(), m_ILHeader, &m_MethodInfo); // The header maybe replaced during the call to getMethodInfoWorker. This is most probable // when the input is null (that is, no metadata), but we can also examine the method and generate @@ -13150,11 +13145,7 @@ void CEECodeGenInfo::getEHinfo( bool isMethodBeingCompiled = pMD == m_pMethodBeingCompiled; - if (pMD->SupportsAsyncVersionCodegen()) - { - // Get the EH info from the ordinary variant. - pMD = pMD->GetOrdinaryVariant(); - } + pMD = pMD->GetOrdinaryVariantIfAsyncVersion(); COR_ILMETHOD* pILHeader; if (pMD->IsDynamicMethod()) diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index aebb381a0bf37d..f35fb0a1d67986 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -428,6 +428,7 @@ class CEEInfo : public ICorJitInfo protected: COR_ILMETHOD_DECODER* getMethodInfoWorker( MethodDesc* ftn, + MethodDesc* ilFtn, COR_ILMETHOD_DECODER* header, CORINFO_METHOD_INFO* methInfo, CORINFO_CONTEXT_HANDLE exactContext = NULL); diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index 4ef12cda32dff0..e38d8b2ed9f4fc 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -1773,6 +1773,19 @@ class MethodDesc return FindOrCreateAssociatedMethodDesc(this, mt, FALSE, GetMethodInstantiation(), allowInstParam, AsyncVariantLookup::Async, FALSE, FALSE, mt->GetLoadLevel()); } + // If this method supports async version codegen, then get the ordinary non-async method. + // For async version codegen the ordinary non-async method is the method whose IL we use + // for both compilations. + MethodDesc* GetOrdinaryVariantIfAsyncVersion() + { + if (SupportsAsyncVersionCodegen()) + { + return GetOrdinaryVariant(); + } + + return this; + } + // True if a MD is an funny BoxedEntryPointStub (not from the method table) or // an MD for a generic instantiation...In other words the MethodDescs and the // MethodTable are guaranteed to be "tightly-knit", i.e. if one is present in From 0f78f6a88590988fe6e8af4c8907c7ab474f1ae2 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 24 Jun 2026 14:37:29 +0200 Subject: [PATCH 2/2] Rephrase comment --- src/coreclr/vm/jitinterface.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 7479db97aa4844..0760f6407b8787 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -7750,6 +7750,10 @@ CEEInfo::getMethodInfo( else if (ilFtn->IsIL() || ilFtn->IsDynamicMethod()) { // IL methods with no IL header indicate there is no implementation defined in metadata. + // NOTE: P/Invoke methods have marshalling IL but it is generally not + // profitable to inline it. The IsIL check above returns false for them + // and thus we will not inline them. + // P/Invokes that don't require marshalling can still be inlined directly by the JIT. getMethodInfoWorker(ftn, ilFtn, NULL, methInfo, context); result = true; }