diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 1716f4f33d489d..cff4b9329d9f4b 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -1485,7 +1485,6 @@ enum CorInfoTokenKind // token comes from runtime async awaiting pattern CORINFO_TOKENKIND_Await = 0x2000 | CORINFO_TOKENKIND_Method, - CORINFO_TOKENKIND_AwaitVirtual = 0x4000 | CORINFO_TOKENKIND_Method, }; struct CORINFO_RESOLVED_TOKEN @@ -2288,6 +2287,12 @@ class ICorStaticInfo CORINFO_CLASS_HANDLE* classArg ) = 0; + // Get the other variant of an async method, if possible. + virtual CORINFO_METHOD_HANDLE getAsyncOtherVariant( + CORINFO_METHOD_HANDLE ftn, + bool* variantIsThunk + ) = 0; + // Given T, return the type of the default Comparer. // Returns null if the type can't be determined exactly. virtual CORINFO_CLASS_HANDLE getDefaultComparerClass( diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index cd2823047fa2ec..7a67f9ea0ff3cd 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -104,6 +104,10 @@ CORINFO_METHOD_HANDLE getInstantiatedEntry( CORINFO_METHOD_HANDLE* methodArg, CORINFO_CLASS_HANDLE* classArg) override; +CORINFO_METHOD_HANDLE getAsyncOtherVariant( + CORINFO_METHOD_HANDLE ftn, + bool* variantIsThunk) override; + CORINFO_CLASS_HANDLE getDefaultComparerClass( CORINFO_CLASS_HANDLE elemType) override; diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index a749d3e72fd7f0..73af122681ea79 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -37,11 +37,11 @@ #include -constexpr GUID JITEEVersionIdentifier = { /* cd664ea4-e14e-4fd4-9cb8-e8fc1e1b5a0a */ - 0xcd664ea4, - 0xe14e, - 0x4fd4, - {0x9c, 0xb8, 0xe8, 0xfc, 0x1e, 0x1b, 0x5a, 0x0a} +constexpr GUID JITEEVersionIdentifier = { /* 22511e72-5ac8-4fc8-83ef-0b61688c68bb */ + 0x22511e72, + 0x5ac8, + 0x4fc8, + {0x83, 0xef, 0x0b, 0x61, 0x68, 0x8c, 0x68, 0xbb} }; #endif // JIT_EE_VERSIONING_GUID_H diff --git a/src/coreclr/interpreter/compiler.cpp b/src/coreclr/interpreter/compiler.cpp index b5924423564ee6..45a90bd932afec 100644 --- a/src/coreclr/interpreter/compiler.cpp +++ b/src/coreclr/interpreter/compiler.cpp @@ -4551,6 +4551,7 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re } else { + const uint8_t* origIP = m_ip; if (!newObj && m_methodInfo->args.isAsyncCall() && AsyncCallPeeps.FindAndApplyPeep(this)) { resolvedCallToken = m_resolvedAsyncCallToken; @@ -4576,14 +4577,30 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re m_compHnd->getCallInfo(&resolvedCallToken, pConstrainedToken, m_methodInfo->ftn, flags, &callInfo); - if (callInfo.sig.isVarArg()) + if (continuationContextHandling != ContinuationContextHandling::None && !callInfo.sig.isAsyncCall()) { - BADCODE("Vararg methods are not supported in interpreted code"); + BADCODE("We're trying to emit an async call, but the async resolved context didn't find one"); } - if (continuationContextHandling != ContinuationContextHandling::None && !callInfo.sig.isAsyncCall()) + if (continuationContextHandling != ContinuationContextHandling::None && callInfo.kind == CORINFO_CALL) { - BADCODE("We're trying to emit an async call, but the async resolved context didn't find one"); + bool isSyncCallThunk; + m_compHnd->getAsyncOtherVariant(callInfo.hMethod, &isSyncCallThunk); + if (!isSyncCallThunk) + { + // The async variant that we got is a thunk. Switch back to the + // non-async task-returning call. There is no reason to create + // and go through the thunk. + ResolveToken(token, CORINFO_TOKENKIND_Method, &resolvedCallToken); + m_ip = origIP + 5; + continuationContextHandling = ContinuationContextHandling::None; + m_compHnd->getCallInfo(&resolvedCallToken, pConstrainedToken, m_methodInfo->ftn, flags, &callInfo); + } + } + + if (callInfo.sig.isVarArg()) + { + BADCODE("Vararg methods are not supported in interpreted code"); } if (isJmp) @@ -6741,9 +6758,7 @@ int InterpCompiler::ApplyLdftnDelegateCtorPeep(const uint8_t* ip, OpcodePeepElem bool InterpCompiler::ResolveAsyncCallToken(const uint8_t* ip) { - CorInfoTokenKind tokenKind = - ip[0] == CEE_CALL ? CORINFO_TOKENKIND_Await : CORINFO_TOKENKIND_AwaitVirtual; - ResolveToken(getU4LittleEndian(ip + 1), tokenKind, &m_resolvedAsyncCallToken); + ResolveToken(getU4LittleEndian(ip + 1), CORINFO_TOKENKIND_Await, &m_resolvedAsyncCallToken); return m_resolvedAsyncCallToken.hMethod != NULL; } diff --git a/src/coreclr/jit/ICorJitInfo_names_generated.h b/src/coreclr/jit/ICorJitInfo_names_generated.h index f6d1edf75d754e..d03d03b1007970 100644 --- a/src/coreclr/jit/ICorJitInfo_names_generated.h +++ b/src/coreclr/jit/ICorJitInfo_names_generated.h @@ -23,6 +23,7 @@ DEF_CLR_API(getMethodVTableOffset) DEF_CLR_API(resolveVirtualMethod) DEF_CLR_API(getUnboxedEntry) DEF_CLR_API(getInstantiatedEntry) +DEF_CLR_API(getAsyncOtherVariant) DEF_CLR_API(getDefaultComparerClass) DEF_CLR_API(getDefaultEqualityComparerClass) DEF_CLR_API(getSZArrayHelperEnumeratorClass) diff --git a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp index b9db37ba260055..80411912d6c9cc 100644 --- a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp +++ b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp @@ -202,6 +202,16 @@ CORINFO_METHOD_HANDLE WrapICorJitInfo::getInstantiatedEntry( return temp; } +CORINFO_METHOD_HANDLE WrapICorJitInfo::getAsyncOtherVariant( + CORINFO_METHOD_HANDLE ftn, + bool* variantIsThunk) +{ + API_ENTER(getAsyncOtherVariant); + CORINFO_METHOD_HANDLE temp = wrapHnd->getAsyncOtherVariant(ftn, variantIsThunk); + API_LEAVE(getAsyncOtherVariant); + return temp; +} + CORINFO_CLASS_HANDLE WrapICorJitInfo::getDefaultComparerClass( CORINFO_CLASS_HANDLE elemType) { diff --git a/src/coreclr/jit/ee_il_dll.hpp b/src/coreclr/jit/ee_il_dll.hpp index 303340a446612b..512384824d1a1d 100644 --- a/src/coreclr/jit/ee_il_dll.hpp +++ b/src/coreclr/jit/ee_il_dll.hpp @@ -339,7 +339,12 @@ inline var_types Compiler::TypeHandleToVarType(CorInfoType jitType, CORINFO_CLAS return type; } -inline CORINFO_CALLINFO_FLAGS combine(CORINFO_CALLINFO_FLAGS flag1, CORINFO_CALLINFO_FLAGS flag2) +constexpr CORINFO_CALLINFO_FLAGS operator|(CORINFO_CALLINFO_FLAGS a, CORINFO_CALLINFO_FLAGS b) { - return (CORINFO_CALLINFO_FLAGS)(flag1 | flag2); + return (CORINFO_CALLINFO_FLAGS)((uint32_t)a | (uint32_t)b); +} + +inline CORINFO_CALLINFO_FLAGS& operator|=(CORINFO_CALLINFO_FLAGS& a, CORINFO_CALLINFO_FLAGS b) +{ + return a = (CORINFO_CALLINFO_FLAGS)((uint32_t)a | (uint32_t)b); } diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index f04f516cf663da..88b6c982bdae51 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -8556,7 +8556,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) JITDUMP(" %08X", resolvedToken.token); eeGetCallInfo(&resolvedToken, (prefixFlags & PREFIX_CONSTRAINED) ? &constrainedResolvedToken : nullptr, - combine(CORINFO_CALLINFO_SECURITYCHECKS, CORINFO_CALLINFO_LDFTN), &callInfo); + CORINFO_CALLINFO_SECURITYCHECKS | CORINFO_CALLINFO_LDFTN, &callInfo); // This check really only applies to intrinsic Array.Address methods if (callInfo.sig.callConv & CORINFO_CALLCONV_PARAMTYPE) @@ -8595,8 +8595,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) JITDUMP(" %08X", resolvedToken.token); eeGetCallInfo(&resolvedToken, nullptr /* constraint typeRef */, - combine(combine(CORINFO_CALLINFO_SECURITYCHECKS, CORINFO_CALLINFO_LDFTN), - CORINFO_CALLINFO_CALLVIRT), + CORINFO_CALLINFO_SECURITYCHECKS | CORINFO_CALLINFO_LDFTN | CORINFO_CALLINFO_CALLVIRT, &callInfo); // This check really only applies to intrinsic Array.Address methods @@ -8729,7 +8728,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) _impResolveToken(CORINFO_TOKENKIND_NewObj); eeGetCallInfo(&resolvedToken, nullptr /* constraint typeRef*/, - combine(CORINFO_CALLINFO_SECURITYCHECKS, CORINFO_CALLINFO_ALLOWINSTPARAM), &callInfo); + CORINFO_CALLINFO_SECURITYCHECKS | CORINFO_CALLINFO_ALLOWINSTPARAM, &callInfo); mflags = callInfo.methodFlags; @@ -8982,16 +8981,8 @@ void Compiler::impImportBlockCode(BasicBlock* block) if (isAwait) { - _impResolveToken(opcode == CEE_CALLVIRT ? CORINFO_TOKENKIND_AwaitVirtual - : CORINFO_TOKENKIND_Await); - if (resolvedToken.hMethod != nullptr) - { - // There is a runtime async variant that is implicitly awaitable, just call that. - // skip the await pattern to the last token. - codeAddr = codeAddrAfterMatch; - opcodeOffs = awaitOffset; - } - else + _impResolveToken(CORINFO_TOKENKIND_Await); + if (resolvedToken.hMethod == nullptr) { // This can happen in cases when the Task-returning method is not a runtime Async // function. For example "T M1(T arg) => arg" when called with a Task argument. @@ -9009,12 +9000,45 @@ void Compiler::impImportBlockCode(BasicBlock* block) _impResolveToken(CORINFO_TOKENKIND_Method); } + CORINFO_CALLINFO_FLAGS flags = CORINFO_CALLINFO_ALLOWINSTPARAM | CORINFO_CALLINFO_SECURITYCHECKS; + if (opcode == CEE_CALLVIRT) + { + flags |= CORINFO_CALLINFO_CALLVIRT; + } + eeGetCallInfo(&resolvedToken, - (prefixFlags & PREFIX_CONSTRAINED) ? &constrainedResolvedToken : nullptr, - // this is how impImportCall invokes getCallInfo - combine(combine(CORINFO_CALLINFO_ALLOWINSTPARAM, CORINFO_CALLINFO_SECURITYCHECKS), - (opcode == CEE_CALLVIRT) ? CORINFO_CALLINFO_CALLVIRT : CORINFO_CALLINFO_NONE), + (prefixFlags & PREFIX_CONSTRAINED) ? &constrainedResolvedToken : nullptr, flags, &callInfo); + + if (isAwait && (callInfo.kind == CORINFO_CALL)) + { + assert(callInfo.sig.isAsyncCall()); + bool isSyncCallThunk; + info.compCompHnd->getAsyncOtherVariant(callInfo.hMethod, &isSyncCallThunk); + if (!isSyncCallThunk) + { + // The async variant that we got is a thunk. Switch + // back to the non-async task-returning call. There + // is no reason to go through the thunk. + _impResolveToken(CORINFO_TOKENKIND_Method); + prefixFlags &= ~(PREFIX_IS_TASK_AWAIT | PREFIX_TASK_AWAIT_CONTINUE_ON_CAPTURED_CONTEXT); + isAwait = false; + + JITDUMP( + "Async variant provided by VM is a thunk, switching direct call to synchronous task-returning method\n"); + eeGetCallInfo(&resolvedToken, + (prefixFlags & PREFIX_CONSTRAINED) ? &constrainedResolvedToken : nullptr, + flags, &callInfo); + } + } + + if (isAwait) + { + // If the synchronous call is a thunk then it means the async variant is not a thunk and we + // prefer to directly call it. Skip the await pattern to the last token. + codeAddr = codeAddrAfterMatch; + opcodeOffs = awaitOffset; + } } else { diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index f82c3914a4cf57..dcf9977ec551b1 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1558,6 +1558,27 @@ static CORINFO_RESOLVED_TOKEN CreateResolvedTokenFromMethod(CorInfoImpl jitInter return null; } + private CORINFO_METHOD_STRUCT_* getAsyncOtherVariant(CORINFO_METHOD_STRUCT_* ftn, ref bool variantIsThunk) + { + MethodDesc method = HandleToObject(ftn); + if (method.IsAsyncVariant()) + { + method = method.GetTargetOfAsyncVariant(); + } + else if (method.Signature.ReturnsTaskOrValueTask()) + { + method = method.GetAsyncVariant(); + } + else + { + variantIsThunk = false; + return null; + } + + variantIsThunk = method?.IsAsyncThunk() ?? false; + return ObjectToHandle(method); + } + private CORINFO_CLASS_STRUCT_* getDefaultComparerClass(CORINFO_CLASS_STRUCT_* elemType) { TypeDesc comparand = HandleToObject(elemType); @@ -1778,7 +1799,7 @@ private object GetRuntimeDeterminedObjectForToken(ref CORINFO_RESOLVED_TOKEN pRe if (pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_Newarr) result = ((TypeDesc)result).MakeArrayType(); - if (pResolvedToken.tokenType is CorInfoTokenKind.CORINFO_TOKENKIND_Await or CorInfoTokenKind.CORINFO_TOKENKIND_AwaitVirtual) + if (pResolvedToken.tokenType is CorInfoTokenKind.CORINFO_TOKENKIND_Await) result = _compilation.TypeSystemContext.GetAsyncVariantMethod((MethodDesc)result); return result; @@ -1868,7 +1889,7 @@ private void resolveToken(ref CORINFO_RESOLVED_TOKEN pResolvedToken) _compilation.NodeFactory.MetadataManager.GetDependenciesDueToAccess(ref _additionalDependencies, _compilation.NodeFactory, (MethodIL)methodIL, method); #endif - if (pResolvedToken.tokenType is CorInfoTokenKind.CORINFO_TOKENKIND_Await or CorInfoTokenKind.CORINFO_TOKENKIND_AwaitVirtual) + if (pResolvedToken.tokenType is CorInfoTokenKind.CORINFO_TOKENKIND_Await) { // in rare cases a method that returns Task is not actually TaskReturning (i.e. returns T). // we cannot resolve to an Async variant in such case. @@ -1878,20 +1899,6 @@ private void resolveToken(ref CORINFO_RESOLVED_TOKEN pResolvedToken) // Don't get async variant of Delegate.Invoke method; the pointed to method is not an async variant either. allowAsyncVariant = allowAsyncVariant && !method.OwningType.IsDelegate; -#if !READYTORUN - if (allowAsyncVariant) - { - bool isDirect = pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_Await || method.IsCallEffectivelyDirect(); - if (isDirect && !method.IsAsync) - { - // Async variant would be a thunk. Do not resolve direct calls - // to async thunks. That just creates and JITs unnecessary - // thunks, and the thunks are harder for the JIT to optimize. - allowAsyncVariant = false; - } - } -#endif - method = allowAsyncVariant ? _compilation.TypeSystemContext.GetAsyncVariantMethod(method) : null; diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs index c38588671117c5..a1eebc3762aaa1 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs @@ -39,6 +39,7 @@ static ICorJitInfoCallbacks() s_callbacks.resolveVirtualMethod = &_resolveVirtualMethod; s_callbacks.getUnboxedEntry = &_getUnboxedEntry; s_callbacks.getInstantiatedEntry = &_getInstantiatedEntry; + s_callbacks.getAsyncOtherVariant = &_getAsyncOtherVariant; s_callbacks.getDefaultComparerClass = &_getDefaultComparerClass; s_callbacks.getDefaultEqualityComparerClass = &_getDefaultEqualityComparerClass; s_callbacks.getSZArrayHelperEnumeratorClass = &_getSZArrayHelperEnumeratorClass; @@ -220,6 +221,7 @@ static ICorJitInfoCallbacks() public delegate* unmanaged resolveVirtualMethod; public delegate* unmanaged getUnboxedEntry; public delegate* unmanaged getInstantiatedEntry; + public delegate* unmanaged getAsyncOtherVariant; public delegate* unmanaged getDefaultComparerClass; public delegate* unmanaged getDefaultEqualityComparerClass; public delegate* unmanaged getSZArrayHelperEnumeratorClass; @@ -665,6 +667,21 @@ private static byte _resolveVirtualMethod(IntPtr thisHandle, IntPtr* ppException } } + [UnmanagedCallersOnly] + private static CORINFO_METHOD_STRUCT_* _getAsyncOtherVariant(IntPtr thisHandle, IntPtr* ppException, CORINFO_METHOD_STRUCT_* ftn, bool* variantIsThunk) + { + var _this = GetThis(thisHandle); + try + { + return _this.getAsyncOtherVariant(ftn, ref *variantIsThunk); + } + catch (Exception ex) + { + *ppException = _this.AllocException(ex); + return default; + } + } + [UnmanagedCallersOnly] private static CORINFO_CLASS_STRUCT_* _getDefaultComparerClass(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* elemType) { diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 03f6049219c934..4ba39d65fefc2c 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1476,7 +1476,6 @@ public enum CorInfoTokenKind // token comes from runtime async awaiting pattern CORINFO_TOKENKIND_Await = 0x2000 | CORINFO_TOKENKIND_Method, - CORINFO_TOKENKIND_AwaitVirtual = 0x4000 | CORINFO_TOKENKIND_Method, }; // These are error codes returned by CompileMethod diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index 0b7d236841129f..8e605002f90174 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -192,6 +192,7 @@ FUNCTIONS bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info); CORINFO_METHOD_HANDLE getUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg); CORINFO_METHOD_HANDLE getInstantiatedEntry(CORINFO_METHOD_HANDLE ftn, CORINFO_METHOD_HANDLE* methodArg, CORINFO_CLASS_HANDLE* classArg); + CORINFO_METHOD_HANDLE getAsyncOtherVariant(CORINFO_METHOD_HANDLE ftn, bool* variantIsThunk); CORINFO_CLASS_HANDLE getDefaultComparerClass(CORINFO_CLASS_HANDLE elemType); CORINFO_CLASS_HANDLE getDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE elemType); CORINFO_CLASS_HANDLE getSZArrayHelperEnumeratorClass(CORINFO_CLASS_HANDLE elemType); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs index 4394d08ad8240b..870ebf9b5adbac 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs @@ -484,18 +484,6 @@ private void ImportCall(ILOpcode opcode, int token) // Don't get async variant of Delegate.Invoke method; the pointed to method is not an async variant either. allowAsyncVariant = allowAsyncVariant && !method.OwningType.IsDelegate; - if (allowAsyncVariant) - { - bool isDirect = opcode == ILOpcode.call || method.IsCallEffectivelyDirect(); - if (isDirect && !method.IsAsync) - { - // Async variant would be a thunk. Do not resolve direct calls - // to async thunks. That just creates and JITs unnecessary - // thunks, and the thunks are harder for the JIT to optimize. - allowAsyncVariant = false; - } - } - if (allowAsyncVariant && MatchTaskAwaitPattern()) { runtimeDeterminedMethod = _factory.TypeSystemContext.GetAsyncVariantMethod(runtimeDeterminedMethod); diff --git a/src/coreclr/tools/aot/crossgen2.slnx b/src/coreclr/tools/aot/crossgen2.slnx index f404d3a841f868..b79a67596c466f 100644 --- a/src/coreclr/tools/aot/crossgen2.slnx +++ b/src/coreclr/tools/aot/crossgen2.slnx @@ -7,6 +7,9 @@ + + + diff --git a/src/coreclr/tools/aot/ilc.slnx b/src/coreclr/tools/aot/ilc.slnx index ba4d00a6f349f9..9e797384a2645f 100644 --- a/src/coreclr/tools/aot/ilc.slnx +++ b/src/coreclr/tools/aot/ilc.slnx @@ -7,6 +7,9 @@ + + + diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h index eef827200da7f4..dec68b1eb53d4f 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h @@ -30,6 +30,7 @@ struct JitInterfaceCallbacks bool (* resolveVirtualMethod)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_DEVIRTUALIZATION_INFO* info); CORINFO_METHOD_HANDLE (* getUnboxedEntry)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg); CORINFO_METHOD_HANDLE (* getInstantiatedEntry)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftn, CORINFO_METHOD_HANDLE* methodArg, CORINFO_CLASS_HANDLE* classArg); + CORINFO_METHOD_HANDLE (* getAsyncOtherVariant)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftn, bool* variantIsThunk); CORINFO_CLASS_HANDLE (* getDefaultComparerClass)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE elemType); CORINFO_CLASS_HANDLE (* getDefaultEqualityComparerClass)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE elemType); CORINFO_CLASS_HANDLE (* getSZArrayHelperEnumeratorClass)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE elemType); @@ -395,6 +396,16 @@ class JitInterfaceWrapper : public ICorJitInfo return temp; } + virtual CORINFO_METHOD_HANDLE getAsyncOtherVariant( + CORINFO_METHOD_HANDLE ftn, + bool* variantIsThunk) +{ + CorInfoExceptionClass* pException = nullptr; + CORINFO_METHOD_HANDLE temp = _callbacks->getAsyncOtherVariant(_thisHandle, &pException, ftn, variantIsThunk); + if (pException != nullptr) throw pException; + return temp; +} + virtual CORINFO_CLASS_HANDLE getDefaultComparerClass( CORINFO_CLASS_HANDLE elemType) { diff --git a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h index 9240baa75f0d69..b464feef60b3f6 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h @@ -140,6 +140,7 @@ LWM(GetTypeForPrimitiveValueClass, DWORDLONG, DWORD) LWM(GetTypeForPrimitiveNumericClass, DWORDLONG, DWORD) LWM(GetUnboxedEntry, DWORDLONG, DLD); LWM(GetInstantiatedEntry, DWORDLONG, Agnostic_GetInstantiatedEntryResult); +LWM(GetAsyncOtherVariant, DWORDLONG, DLD); LWM(GetUnBoxHelper, DWORDLONG, DWORD) LWM(GetRuntimeTypePointer, DWORDLONG, DWORDLONG) LWM(IsObjectImmutable, DWORDLONG, DWORD) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index d16bb9aed25e3b..38149f1cb5a06c 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -3397,6 +3397,38 @@ CORINFO_METHOD_HANDLE MethodContext::repGetInstantiatedEntry(CORINFO_METHOD_HAND return (CORINFO_METHOD_HANDLE)(value.result); } +void MethodContext::recGetAsyncOtherVariant(CORINFO_METHOD_HANDLE ftn, + bool* variantIsThunk, + CORINFO_METHOD_HANDLE result) +{ + if (GetAsyncOtherVariant == nullptr) + { + GetAsyncOtherVariant = new LightWeightMap(); + } + + DWORDLONG key = CastHandle(ftn); + DLD value; + value.A = CastHandle(result); + value.B = (DWORD)*variantIsThunk ? 1 : 0; + GetAsyncOtherVariant->Add(key, value); + DEBUG_REC(dmpGetAsyncOtherVariant(key, value)); +} + +void MethodContext::dmpGetAsyncOtherVariant(DWORDLONG key, DLD value) +{ + printf("GetAsyncOtherVariant ftn-%016" PRIX64 ", result-%016" PRIX64 ", variantIsThunk-%u", key, value.A, value.B); +} + +CORINFO_METHOD_HANDLE MethodContext::repGetAsyncOtherVariant(CORINFO_METHOD_HANDLE ftn, bool* variantIsThunk) +{ + DWORDLONG key = CastHandle(ftn); + + DLD value = LookupByKeyOrMiss(GetAsyncOtherVariant, key, ": key %016" PRIX64 "", key); + DEBUG_REP(dmpGetAsyncOtherVariant(key, value)); + *variantIsThunk = (value.B != 0); + return (CORINFO_METHOD_HANDLE)(value.A); +} + void MethodContext::recGetDefaultComparerClass(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result) { if (GetDefaultComparerClass == nullptr) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h index 34cafcc5f986c6..5f39cdce3853e0 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h @@ -459,6 +459,10 @@ class MethodContext CORINFO_METHOD_HANDLE* methodHandle, CORINFO_CLASS_HANDLE* classHandle); + void recGetAsyncOtherVariant(CORINFO_METHOD_HANDLE ftn, bool* variantIsThunk, CORINFO_METHOD_HANDLE result); + void dmpGetAsyncOtherVariant(DWORDLONG key, DLD value); + CORINFO_METHOD_HANDLE repGetAsyncOtherVariant(CORINFO_METHOD_HANDLE ftn, bool* variantIsThunk); + void recGetDefaultComparerClass(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result); void dmpGetDefaultComparerClass(DWORDLONG key, DWORDLONG value); CORINFO_CLASS_HANDLE repGetDefaultComparerClass(CORINFO_CLASS_HANDLE cls); @@ -1217,6 +1221,7 @@ enum mcPackets Packet_GetContinuationType = 234, Packet_GetWasmTypeSymbol = 235, Packet_GetWasmLowering = 236, + Packet_GetAsyncOtherVariant = 237, }; 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 878d207c8ef34c..c5c5e47afbf684 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -263,6 +263,19 @@ CORINFO_METHOD_HANDLE interceptor_ICJI::getInstantiatedEntry(CORINFO_METHOD_HAND return result; } +CORINFO_METHOD_HANDLE interceptor_ICJI::getAsyncOtherVariant(CORINFO_METHOD_HANDLE ftn, bool* variantIsThunk) +{ + mc->cr->AddCall("getAsyncOtherVariant"); + bool localVariantIsThunk = false; + CORINFO_METHOD_HANDLE result = original_ICorJitInfo->getAsyncOtherVariant(ftn, &localVariantIsThunk); + mc->recGetAsyncOtherVariant(ftn, &localVariantIsThunk, result); + if (variantIsThunk != nullptr) + { + *variantIsThunk = localVariantIsThunk; + } + return result; +} + // Given T, return the type of the default Comparer. // Returns null if the type can't be determined exactly. CORINFO_CLASS_HANDLE interceptor_ICJI::getDefaultComparerClass(CORINFO_CLASS_HANDLE cls) 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 afadb096655c76..8058b6802159e8 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp @@ -171,6 +171,14 @@ CORINFO_METHOD_HANDLE interceptor_ICJI::getInstantiatedEntry( return original_ICorJitInfo->getInstantiatedEntry(ftn, methodArg, classArg); } +CORINFO_METHOD_HANDLE interceptor_ICJI::getAsyncOtherVariant( + CORINFO_METHOD_HANDLE ftn, + bool* variantIsThunk) +{ + mcs->AddCall("getAsyncOtherVariant"); + return original_ICorJitInfo->getAsyncOtherVariant(ftn, variantIsThunk); +} + CORINFO_CLASS_HANDLE interceptor_ICJI::getDefaultComparerClass( CORINFO_CLASS_HANDLE elemType) { 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 d90cfd56537b2c..852a318f83c225 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp @@ -152,6 +152,13 @@ CORINFO_METHOD_HANDLE interceptor_ICJI::getInstantiatedEntry( return original_ICorJitInfo->getInstantiatedEntry(ftn, methodArg, classArg); } +CORINFO_METHOD_HANDLE interceptor_ICJI::getAsyncOtherVariant( + CORINFO_METHOD_HANDLE ftn, + bool* variantIsThunk) +{ + return original_ICorJitInfo->getAsyncOtherVariant(ftn, variantIsThunk); +} + CORINFO_CLASS_HANDLE interceptor_ICJI::getDefaultComparerClass( CORINFO_CLASS_HANDLE elemType) { diff --git a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp index 589de2655a3d76..20372434076341 100644 --- a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp @@ -218,6 +218,13 @@ CORINFO_METHOD_HANDLE MyICJI::getInstantiatedEntry(CORINFO_METHOD_HANDLE ftn, CO return result; } +CORINFO_METHOD_HANDLE MyICJI::getAsyncOtherVariant(CORINFO_METHOD_HANDLE ftn, bool* variantIsThunk) +{ + jitInstance->mc->cr->AddCall("getAsyncOtherVariant"); + CORINFO_METHOD_HANDLE result = jitInstance->mc->repGetAsyncOtherVariant(ftn, variantIsThunk); + return result; +} + // Given T, return the type of the default Comparer. // Returns null if the type can't be determined exactly. CORINFO_CLASS_HANDLE MyICJI::getDefaultComparerClass(CORINFO_CLASS_HANDLE cls) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 33bea7016ae278..b87b75787bd4a6 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -1081,38 +1081,11 @@ void CEEInfo::resolveToken(/* IN, OUT */ CORINFO_RESOLVED_TOKEN * pResolvedToken break; case CORINFO_TOKENKIND_Await: - case CORINFO_TOKENKIND_AwaitVirtual: { // in rare cases a method that returns Task is not actually TaskReturning (i.e. returns T). // we cannot resolve to an Async variant in such case. // return NULL, so that caller would re-resolve as a regular method call - bool allowAsyncVariant = pMD->ReturnsTaskOrValueTask(); - if (allowAsyncVariant) - { - bool isDirect = tokenType == CORINFO_TOKENKIND_Await || pMD->IsStatic(); - if (!isDirect) - { - DWORD attrs = pMD->GetAttrs(); - if (pMD->GetMethodTable()->IsInterface()) - { - isDirect = !IsMdVirtual(attrs); - } - else - { - isDirect = !IsMdVirtual(attrs) || IsMdFinal(attrs) || pMD->GetMethodTable()->IsSealed(); - } - } - - if (isDirect && !pMD->IsAsyncThunkMethod()) - { - // Async variant would be a thunk. Do not resolve direct calls - // to async thunks. That just creates and JITs unnecessary - // thunks, and the thunks are harder for the JIT to optimize. - allowAsyncVariant = false; - } - } - - pMD = allowAsyncVariant ? pMD->GetAsyncVariant(/*allowInstParam*/FALSE) : NULL; + pMD = pMD->ReturnsTaskOrValueTask() ? pMD->GetAsyncVariant(/*allowInstParam*/FALSE) : NULL; } break; @@ -8995,6 +8968,34 @@ CORINFO_METHOD_HANDLE CEEInfo::getInstantiatedEntry( return result; } +CORINFO_METHOD_HANDLE CEEInfo::getAsyncOtherVariant( + CORINFO_METHOD_HANDLE ftn, + bool* variantIsThunk) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + MODE_PREEMPTIVE; + } CONTRACTL_END; + + CORINFO_METHOD_HANDLE result = NULL; + + JIT_TO_EE_TRANSITION(); + + MethodDesc* pMD = GetMethod(ftn); + MethodDesc* pAsyncOtherVariant = NULL; + if (pMD->HasAsyncMethodData()) + { + pAsyncOtherVariant = pMD->GetAsyncOtherVariant(); + } + result = (CORINFO_METHOD_HANDLE)pAsyncOtherVariant; + *variantIsThunk = pAsyncOtherVariant != NULL && pAsyncOtherVariant->IsAsyncThunkMethod(); + + EE_TO_JIT_TRANSITION(); + + return result; +} + /*********************************************************************/ void CEEInfo::expandRawHandleIntrinsic( CORINFO_RESOLVED_TOKEN * pResolvedToken, diff --git a/src/tests/async/simple-eh/simple-eh.cs b/src/tests/async/simple-eh/simple-eh.cs index 4fef84507c61a1..d77c669b986f7d 100644 --- a/src/tests/async/simple-eh/simple-eh.cs +++ b/src/tests/async/simple-eh/simple-eh.cs @@ -8,11 +8,13 @@ using System.Runtime; using System.Runtime.CompilerServices; using System.Threading.Tasks; +using TestLibrary; using Xunit; public class Async2SimpleEH { [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/124044", typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsCoreClrInterpreter))] public static void TestThrowAfterYield() { Task.Run(AsyncEntry).Wait(); @@ -51,6 +53,7 @@ public class IntegerException : Exception } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/124044", typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsCoreClrInterpreter))] public static int TestDefinesIntButThrows() { return TestDefinesIntButThrowsAsync().GetAwaiter().GetResult(); @@ -79,6 +82,7 @@ private static async Task IntThrows() private struct S { public long A, B, C, D; } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/124044", typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsCoreClrInterpreter))] public static int TestDefinesSButThrows() { return TestDefinesSButThrowsAsync().GetAwaiter().GetResult(); diff --git a/src/tests/async/simple-eh/simple-eh.csproj b/src/tests/async/simple-eh/simple-eh.csproj index 197767e2c4e249..7548a677889c52 100644 --- a/src/tests/async/simple-eh/simple-eh.csproj +++ b/src/tests/async/simple-eh/simple-eh.csproj @@ -1,5 +1,6 @@ +