diff --git a/docs/design/coreclr/botr/readytorun-format.md b/docs/design/coreclr/botr/readytorun-format.md index 6f6abdb2dd28a2..00d107f90d8d2b 100644 --- a/docs/design/coreclr/botr/readytorun-format.md +++ b/docs/design/coreclr/botr/readytorun-format.md @@ -300,6 +300,7 @@ token, and additional data determined by the flags. | READYTORUN_METHOD_SIG_Constrained | 0x20 | Constrained type for method resolution. Typespec appended as additional data. | READYTORUN_METHOD_SIG_OwnerType | 0x40 | Method type. Typespec appended as additional data. | READYTORUN_METHOD_SIG_UpdateContext | 0x80 | If set, update the module which is used to parse tokens before performing any token processing. A uint index into the modules table immediately follows the flags +| READYTORUN_METHOD_SIG_AsyncVariant | 0x100 | If set, the token is the AsyncCallConv variant of a runtime async method #### Field Signatures diff --git a/src/coreclr/inc/readytorun.h b/src/coreclr/inc/readytorun.h index 134b685ff9fbe6..e688cc30022249 100644 --- a/src/coreclr/inc/readytorun.h +++ b/src/coreclr/inc/readytorun.h @@ -192,6 +192,7 @@ enum ReadyToRunMethodSigFlags READYTORUN_METHOD_SIG_Constrained = 0x20, READYTORUN_METHOD_SIG_OwnerType = 0x40, READYTORUN_METHOD_SIG_UpdateContext = 0x80, + READYTORUN_METHOD_SIG_AsyncVariant = 0x100, }; enum ReadyToRunFieldSigFlags diff --git a/src/coreclr/tools/Common/Compiler/NativeAotNameMangler.cs b/src/coreclr/tools/Common/Compiler/NativeAotNameMangler.cs index 2a8807f90fb3bf..6d305ad3effb4e 100644 --- a/src/coreclr/tools/Common/Compiler/NativeAotNameMangler.cs +++ b/src/coreclr/tools/Common/Compiler/NativeAotNameMangler.cs @@ -10,6 +10,7 @@ using Internal.TypeSystem; using Internal.TypeSystem.Ecma; using System.Diagnostics; +using Internal.JitInterface; namespace ILCompiler { @@ -432,6 +433,16 @@ private Utf8String ComputeUnqualifiedMangledMethodName(MethodDesc method) return _unqualifiedMangledMethodNames[method]; } } +#if READYTORUN + if (method is AsyncMethodThunk) + { + return "_" + ComputeUnqualifiedMangledMethodName(method.GetTypicalMethodDefinition()); + } + if (method is TaskReturningAsyncThunk) + { + return "_" + ComputeUnqualifiedMangledMethodName(method.GetTypicalMethodDefinition()); + } +#endif Utf8String utf8MangledName; diff --git a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs index 623422b7aef992..b0fcdd2f4081bb 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs @@ -41,7 +41,7 @@ public enum ReadyToRunImportSectionFlags : ushort /// Constants for method and field encoding /// [Flags] - public enum ReadyToRunMethodSigFlags : byte + public enum ReadyToRunMethodSigFlags : uint { READYTORUN_METHOD_SIG_None = 0x00, READYTORUN_METHOD_SIG_UnboxingStub = 0x01, @@ -52,6 +52,7 @@ public enum ReadyToRunMethodSigFlags : byte READYTORUN_METHOD_SIG_Constrained = 0x20, READYTORUN_METHOD_SIG_OwnerType = 0x40, READYTORUN_METHOD_SIG_UpdateContext = 0x80, + READYTORUN_METHOD_SIG_AsyncVariant = 0x100, } [Flags] diff --git a/src/coreclr/tools/Common/JitInterface/AsyncMethodDesc.cs b/src/coreclr/tools/Common/JitInterface/AsyncMethodDesc.cs index b6584b5588eafb..25bc2d0b1c8ae6 100644 --- a/src/coreclr/tools/Common/JitInterface/AsyncMethodDesc.cs +++ b/src/coreclr/tools/Common/JitInterface/AsyncMethodDesc.cs @@ -9,9 +9,8 @@ namespace Internal.JitInterface { /// /// Represents the async-callable (CORINFO_CALLCONV_ASYNCCALL) variant of a Task/ValueTask returning method. - /// The wrapper should be short‑lived and only used while interacting with the JIT interface. /// - internal sealed class AsyncMethodDesc : MethodDelegator, IJitHashableOnly + internal sealed class AsyncMethodDesc : MethodDelegator { private readonly AsyncMethodDescFactory _factory; private readonly int _jitVisibleHashCode; @@ -46,9 +45,10 @@ public override MethodDesc GetMethodDefinition() public override MethodDesc GetTypicalMethodDefinition() { MethodDesc real = _wrappedMethod.GetTypicalMethodDefinition(); - if (real != _wrappedMethod) - return _factory.GetAsyncMethod(real); - return this; + return real; + //if (real != _wrappedMethod) + // return _factory.GetAsyncMethod(real); + //return this; } public override MethodDesc InstantiateSignature(Instantiation typeInstantiation, Instantiation methodInstantiation) @@ -72,19 +72,28 @@ public override MethodSignature Signature } } -#if !SUPPORT_JIT - // Same pattern as UnboxingMethodDesc: these should not escape JIT hashing scope. - protected override int ClassCode => throw new NotImplementedException(); - protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) => throw new NotImplementedException(); - protected override int ComputeHashCode() => _jitVisibleHashCode; - int IJitHashableOnly.GetJitVisibleHashCode() => _jitVisibleHashCode; -#else - int IJitHashableOnly.GetJitVisibleHashCode() => _jitVisibleHashCode; -#endif + protected override int ClassCode => 0x554d08b9; + + protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + if (other is AsyncMethodDesc otherAsync) + { + return comparer.Compare(_wrappedMethod, otherAsync._wrappedMethod); + } + return -1; + } + public override string ToString() + { + return "Async MethodDesc: " + _wrappedMethod.ToString(); + } } internal static class AsyncMethodDescExtensions { + public static bool IsAsyncCallConv(this MethodDesc method) + { + return method is AsyncMethodDesc; + } /// /// Returns true if the method returns Task, Task<T>, ValueTask, or ValueTask<T>, otherwise false. /// diff --git a/src/coreclr/tools/Common/JitInterface/AsyncMethodDescFactory.cs b/src/coreclr/tools/Common/JitInterface/AsyncMethodDescFactory.cs deleted file mode 100644 index 5494807192e078..00000000000000 --- a/src/coreclr/tools/Common/JitInterface/AsyncMethodDescFactory.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using Internal.TypeSystem; - -namespace Internal.JitInterface -{ - internal class AsyncMethodDescFactory : Dictionary - { - public AsyncMethodDesc GetAsyncMethod(MethodDesc method) - { - if (!TryGetValue(method, out AsyncMethodDesc result)) - { - result = new AsyncMethodDesc(method, this); - Add(method, result); - } - - return result; - } - } -} diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 4e574ed7641083..7f83185b0484aa 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -25,6 +25,9 @@ using ILCompiler; using ILCompiler.DependencyAnalysis; +using Internal.IL.Stubs; + + #if READYTORUN using System.Reflection.Metadata.Ecma335; @@ -126,10 +129,10 @@ public LikelyClassMethodRecord(IntPtr handle, uint likelihood) } [DllImport(JitLibrary)] - private static extern uint getLikelyClasses(LikelyClassMethodRecord* pLikelyClasses, uint maxLikelyClasses, PgoInstrumentationSchema* schema, uint countSchemaItems, byte*pInstrumentationData, int ilOffset); + private static extern uint getLikelyClasses(LikelyClassMethodRecord* pLikelyClasses, uint maxLikelyClasses, PgoInstrumentationSchema* schema, uint countSchemaItems, byte* pInstrumentationData, int ilOffset); [DllImport(JitLibrary)] - private static extern uint getLikelyMethods(LikelyClassMethodRecord* pLikelyMethods, uint maxLikelyMethods, PgoInstrumentationSchema* schema, uint countSchemaItems, byte*pInstrumentationData, int ilOffset); + private static extern uint getLikelyMethods(LikelyClassMethodRecord* pLikelyMethods, uint maxLikelyMethods, PgoInstrumentationSchema* schema, uint countSchemaItems, byte* pInstrumentationData, int ilOffset); [DllImport(JitSupportLibrary)] private static extern IntPtr GetJitHost(IntPtr configProvider); @@ -150,7 +153,7 @@ private static extern CorJitResult JitCompileMethod(out IntPtr exception, ref CORINFO_METHOD_INFO info, uint flags, out IntPtr nativeEntry, out uint codeSize); [DllImport(JitSupportLibrary)] - private static extern IntPtr AllocException([MarshalAs(UnmanagedType.LPWStr)]string message, int messageLength); + private static extern IntPtr AllocException([MarshalAs(UnmanagedType.LPWStr)] string message, int messageLength); [DllImport(JitSupportLibrary)] private static extern void JitSetOs(IntPtr jit, CORINFO_OS os); @@ -816,12 +819,12 @@ private bool Get_CORINFO_METHOD_INFO(MethodDesc method, MethodIL methodIL, CORIN private Dictionary _instantiationToJitVisibleInstantiation; private CORINFO_CLASS_STRUCT_** GetJitInstantiation(Instantiation inst) { - IntPtr [] jitVisibleInstantiation; + IntPtr[] jitVisibleInstantiation; _instantiationToJitVisibleInstantiation ??= new Dictionary(); if (!_instantiationToJitVisibleInstantiation.TryGetValue(inst, out jitVisibleInstantiation)) { - jitVisibleInstantiation = new IntPtr[inst.Length]; + jitVisibleInstantiation = new IntPtr[inst.Length]; for (int i = 0; i < inst.Length; i++) jitVisibleInstantiation[i] = (IntPtr)ObjectToHandle(inst[i]); _instantiationToJitVisibleInstantiation.Add(inst, jitVisibleInstantiation); @@ -831,7 +834,9 @@ private bool Get_CORINFO_METHOD_INFO(MethodDesc method, MethodIL methodIL, CORIN private void Get_CORINFO_SIG_INFO(MethodDesc method, CORINFO_SIG_INFO* sig, MethodILScope scope, bool suppressHiddenArgument = false) { - Get_CORINFO_SIG_INFO(method.Signature, sig, scope); + var signature = method.AsyncMethodData.IsAsyncCallConv ? + method.AsyncMethodData.Signature : method.Signature; + Get_CORINFO_SIG_INFO(signature, sig, scope); // Does the method have a hidden parameter? bool hasHiddenParameter = !suppressHiddenArgument && method.RequiresInstArg(); @@ -853,6 +858,11 @@ private void Get_CORINFO_SIG_INFO(MethodDesc method, CORINFO_SIG_INFO* sig, Meth sig->callConv |= CorInfoCallConv.CORINFO_CALLCONV_PARAMTYPE; } + if (method.AsyncMethodData.IsAsyncCallConv) + { + sig->callConv |= CorInfoCallConv.CORINFO_CALLCONV_ASYNCCALL; + } + Instantiation owningTypeInst = method.OwningType.Instantiation; sig->sigInst.classInstCount = (uint)owningTypeInst.Length; if (owningTypeInst.Length != 0) @@ -877,6 +887,7 @@ private void Get_CORINFO_SIG_INFO(MethodSignature signature, CORINFO_SIG_INFO* s if (!signature.IsStatic) sig->callConv |= CorInfoCallConv.CORINFO_CALLCONV_HASTHIS; if (signature.IsExplicitThis) sig->callConv |= CorInfoCallConv.CORINFO_CALLCONV_EXPLICITTHIS; + if (signature.IsAsyncCallConv) sig->callConv |= CorInfoCallConv.CORINFO_CALLCONV_ASYNCCALL; TypeDesc returnType = signature.ReturnType; @@ -1044,7 +1055,7 @@ private TypeSystemEntity entityFromContext(CORINFO_CONTEXT_STRUCT* contextStruct { if (contextStruct == contextFromMethodBeingCompiled()) { - return MethodBeingCompiled.HasInstantiation ? (TypeSystemEntity)MethodBeingCompiled: (TypeSystemEntity)MethodBeingCompiled.OwningType; + return MethodBeingCompiled.HasInstantiation ? (TypeSystemEntity)MethodBeingCompiled : (TypeSystemEntity)MethodBeingCompiled.OwningType; } return (TypeSystemEntity)HandleToObject((void*)((nuint)contextStruct & ~(nuint)CorInfoContextFlags.CORINFO_CONTEXTFLAGS_MASK)); @@ -1801,6 +1812,7 @@ by resolving the token in the definition. */ private void resolveToken(ref CORINFO_RESOLVED_TOKEN pResolvedToken) { + // If the token is an async method but await tokenkind is not requested, wrap it with a AsyncTaskWrapperMethodDesc var methodIL = HandleToObject(pResolvedToken.tokenScope); var typeOrMethodContext = (pResolvedToken.tokenContext == contextFromMethodBeingCompiled()) ? @@ -1827,6 +1839,41 @@ private void resolveToken(ref CORINFO_RESOLVED_TOKEN pResolvedToken) if (result is MethodDesc method) { + bool requestingAsync = pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_Await; + bool isAsync = method.AsyncMethodData.IsAsyncCallConv; + if (requestingAsync && !isAsync) + { + if (method.IsTaskReturning) + { + method = method.GetAsyncOtherVariant(); + result = method; + } + else + { + Debug.Assert(method.AsyncMethodData.Kind == AsyncMethodKind.AsyncExplicitImpl); + } + } + if (!requestingAsync && isAsync) + { + if (method.IsTaskReturning) + { + if (typeOrMethodContext == method.GetAsyncOtherVariant()) + { + // This is a TaskReturningThunk that wants the asynccallconv method. + // Don't get the other variant + } + else + { + method = method.GetAsyncOtherVariant(); + result = method; + } + } + else + { + Debug.Assert(method.AsyncMethodData.Kind == AsyncMethodKind.AsyncExplicitImpl); + } + } + pResolvedToken.hMethod = ObjectToHandle(method); TypeDesc owningClass = method.OwningType; @@ -2258,7 +2305,7 @@ public static int GetClassAlignmentRequirementStatic(DefType type) // private static bool ShouldAlign8(int dwR8Fields, int dwTotalFields) { - return dwR8Fields*2>dwTotalFields && dwR8Fields>=2; + return dwR8Fields * 2 > dwTotalFields && dwR8Fields >= 2; } private static bool ShouldAlign8(DefType type) @@ -3376,7 +3423,8 @@ private void getAsyncInfo(ref CORINFO_ASYNC_INFO pAsyncInfoOut) private CORINFO_CLASS_STRUCT_* getContinuationType(nuint dataSize, ref bool objRefs, nuint objRefsSize) { Debug.Assert(objRefsSize == (dataSize + (nuint)(PointerSize - 1)) / (nuint)PointerSize); - throw new NotImplementedException("getContinuationType"); + return ObjectToHandle(_compilation.TypeSystemContext.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "Continuation"u8)); + //throw new NotImplementedException("getContinuationType"); } private mdToken getMethodDefFromMethod(CORINFO_METHOD_STRUCT_* hMethod) @@ -3563,7 +3611,7 @@ private uint getThreadTLSIndex(ref void* ppIndirection) { throw new NotImplementedException("getThreadTLSIndex"); } private Dictionary _helperCache = new Dictionary(); - private void getHelperFtn(CorInfoHelpFunc ftnNum, CORINFO_CONST_LOOKUP *pNativeEntrypoint, CORINFO_METHOD_STRUCT_** pMethod) + private void getHelperFtn(CorInfoHelpFunc ftnNum, CORINFO_CONST_LOOKUP* pNativeEntrypoint, CORINFO_METHOD_STRUCT_** pMethod) { // We never return a method handle from the managed implementation of this method today if (pMethod != null) @@ -3614,7 +3662,12 @@ public static ReadyToRunHelperId GetReadyToRunHelperFromStaticBaseHelper(CorInfo } private void getFunctionFixedEntryPoint(CORINFO_METHOD_STRUCT_* ftn, bool isUnsafeFunctionPointer, ref CORINFO_CONST_LOOKUP pResult) - { throw new NotImplementedException("getFunctionFixedEntryPoint"); } + { + // Called for AsyncResumption stubs + var method = HandleToObject(ftn); + pResult.handle = (CORINFO_GENERIC_STRUCT_*)ObjectToHandle(HandleToObject(ftn)); + pResult.accessType = InfoAccessType.IAT_PVALUE; + } #pragma warning disable CA1822 // Mark members as static private CorInfoHelpFunc getLazyStringLiteralHelper(CORINFO_MODULE_STRUCT_* handle) @@ -3731,7 +3784,12 @@ private bool getTailCallHelpers(ref CORINFO_RESOLVED_TOKEN callToken, CORINFO_SI private CORINFO_METHOD_STRUCT_* getAsyncResumptionStub() #pragma warning restore CA1822 // Mark members as static { - throw new NotImplementedException("Crossgen2 does not support runtime-async yet"); + // does m_finalCodeAddressSlot become a reloc? Or will jit give it to us somehow? +#if READYTORUN + return ObjectToHandle(new AsyncResumptionStub(MethodBeingCompiled)); +#else + throw new NotImplementedException(nameof(getAsyncResumptionStub)); +#endif } private byte[] _code; @@ -4315,7 +4373,7 @@ private uint getJitFlags(ref CORJIT_FLAGS flags, uint sizeInBytes) flags.Set(CorJitFlag.CORJIT_FLAG_SOFTFP_ABI); } - if (this.MethodBeingCompiled.IsAsync) + if (this.MethodBeingCompiled.AsyncMethodData.IsAsyncCallConv) { flags.Set(CorJitFlag.CORJIT_FLAG_ASYNC); } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index ff37fc2f9bc60b..013cd0bd105c80 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -119,6 +119,7 @@ public unsafe struct CORINFO_SIG_INFO private uint totalILArgs() { return (uint)(numArgs + (hasImplicitThis() ? 1 : 0)); } private bool isVarArg() { return ((getCallConv() == CorInfoCallConv.CORINFO_CALLCONV_VARARG) || (getCallConv() == CorInfoCallConv.CORINFO_CALLCONV_NATIVEVARARG)); } internal bool hasTypeArg() { return ((callConv & CorInfoCallConv.CORINFO_CALLCONV_PARAMTYPE) != 0); } + private bool isAsync() { return ((callConv & CorInfoCallConv.CORINFO_CALLCONV_ASYNCCALL) != 0); } }; //---------------------------------------------------------------------------- @@ -377,6 +378,7 @@ public enum CorInfoCallConv CORINFO_CALLCONV_HASTHIS = 0x20, CORINFO_CALLCONV_EXPLICITTHIS = 0x40, CORINFO_CALLCONV_PARAMTYPE = 0x80, // Passed last. Same as CORINFO_GENERICS_CTXT_FROM_PARAMTYPEARG + CORINFO_CALLCONV_ASYNCCALL = 0x100, // Is this a call to an async function? } // Represents the calling conventions supported with the extensible calling convention syntax @@ -1364,6 +1366,9 @@ public enum CorInfoTokenKind // token comes from resolved static virtual method CORINFO_TOKENKIND_ResolvedStaticVirtualMethod = 0x1000 | CORINFO_TOKENKIND_Method, + + // token comes from a runtime-async Await pattern + CORINFO_TOKENKIND_Await = 0x2000 | CORINFO_TOKENKIND_Method, }; // These are error codes returned by CompileMethod @@ -1423,6 +1428,7 @@ public enum CorJitFlag : uint // ARM only CORJIT_FLAG_RELATIVE_CODE_RELOCS = 29, // JIT should generate PC-relative address computations instead of EE relocation records CORJIT_FLAG_SOFTFP_ABI = 30, // Enable armel calling convention + CORJIT_FLAG_ASYNC = 31, // Generate code for use as an async function } diff --git a/src/coreclr/tools/Common/JitInterface/UnboxingMethodDesc.cs b/src/coreclr/tools/Common/JitInterface/UnboxingMethodDesc.cs index eb1ba8a9d3bfc9..7d11edef254c49 100644 --- a/src/coreclr/tools/Common/JitInterface/UnboxingMethodDesc.cs +++ b/src/coreclr/tools/Common/JitInterface/UnboxingMethodDesc.cs @@ -28,6 +28,8 @@ public UnboxingMethodDesc(MethodDesc wrappedMethod, UnboxingMethodDescFactory fa _jitVisibleHashCode = HashCode.Combine(wrappedMethod.GetHashCode(), 401752602); } + public override AsyncMethodData AsyncMethodData => _wrappedMethod.AsyncMethodData; + public override MethodDesc GetCanonMethodTarget(CanonicalFormKind kind) { MethodDesc realCanonTarget = _wrappedMethod.GetCanonMethodTarget(kind); diff --git a/src/coreclr/tools/Common/TypeSystem/Async/AsyncMethodDesc.Async.cs b/src/coreclr/tools/Common/TypeSystem/Async/AsyncMethodDesc.Async.cs new file mode 100644 index 00000000000000..f12d7aa560ace8 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Async/AsyncMethodDesc.Async.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using Internal.TypeSystem; + +namespace Internal.TypeSystem +{ + /// + /// Represents the async-callable (CORINFO_CALLCONV_ASYNCCALL) variant of a Task/ValueTask returning method. + /// + public sealed class AsyncMethodDesc : MethodDelegator + { + private readonly AsyncMethodData _asyncMethodData; + + public override AsyncMethodData AsyncMethodData + { + get + { + return _asyncMethodData ??= new AsyncMethodData() + { + Kind = AsyncMethodKind.AsyncVariantThunk, + Signature = _wrappedMethod.Signature.CreateAsyncSignature() + }; + } + } + + public override MethodDesc GetAsyncOtherVariant() => _wrappedMethod; + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/Canon/AsyncMethodDesc.Canon.cs b/src/coreclr/tools/Common/TypeSystem/Canon/AsyncMethodDesc.Canon.cs new file mode 100644 index 00000000000000..86ac3664f5ddda --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Canon/AsyncMethodDesc.Canon.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using Internal.TypeSystem; + +namespace Internal.TypeSystem +{ + /// + /// Represents the async-callable (CORINFO_CALLCONV_ASYNCCALL) variant of a Task/ValueTask returning method. + /// + public sealed class AsyncMethodDesc : MethodDelegator + { + public override MethodDesc GetCanonMethodTarget(CanonicalFormKind kind) + { + return _wrappedMethod.GetCanonMethodTarget(kind).GetAsyncOtherVariant(); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/Common/AsyncMethodThunk.cs b/src/coreclr/tools/Common/TypeSystem/Common/AsyncMethodThunk.cs new file mode 100644 index 00000000000000..3371916a495873 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Common/AsyncMethodThunk.cs @@ -0,0 +1,88 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using Internal.TypeSystem; + +namespace Internal.TypeSystem +{ + /// + /// Represents the async-callable (CORINFO_CALLCONV_ASYNCCALL) variant of a Task/ValueTask returning method. + /// + public sealed class AsyncMethodThunk : MethodDelegator + { + private readonly AsyncMethodData _asyncMethodData; + + public AsyncMethodThunk(MethodDesc wrappedMethod) + : base(wrappedMethod) + { + Debug.Assert(wrappedMethod.IsTaskReturning); + Debug.Assert(!wrappedMethod.IsAsync); + _asyncMethodData = new AsyncMethodData() + { + Kind = AsyncMethodKind.AsyncVariantThunk, + Signature = _wrappedMethod.Signature.CreateAsyncSignature() + }; + } + + public override AsyncMethodData AsyncMethodData + { + get + { + return _asyncMethodData; + } + } + + public override MethodDesc GetCanonMethodTarget(CanonicalFormKind kind) + { + return _wrappedMethod.GetCanonMethodTarget(kind).GetAsyncOtherVariant(); + } + + public override MethodDesc GetMethodDefinition() + { + return _wrappedMethod.GetMethodDefinition(); + } + + public override MethodDesc GetTypicalMethodDefinition() + { + return _wrappedMethod.GetTypicalMethodDefinition(); + } + + public override MethodDesc GetAsyncOtherVariant() => _wrappedMethod; + + public override MethodDesc InstantiateSignature(Instantiation typeInstantiation, Instantiation methodInstantiation) + { + MethodDesc real = _wrappedMethod.InstantiateSignature(typeInstantiation, methodInstantiation); + return real.GetAsyncOtherVariant(); + } + + public override MethodSignature Signature + { + get + { + return _asyncMethodData.Signature; + } + } + + public override string DiagnosticName => "Async thunk: " + _wrappedMethod.DiagnosticName; + + protected internal override int ClassCode => 0x554d08b9; + + protected internal override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + if (other is AsyncMethodThunk otherAsync) + { + return comparer.Compare(_wrappedMethod, otherAsync._wrappedMethod); + } + return -1; + } + + public override string ToString() + { + return "Async thunk: " + _wrappedMethod.ToString(); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/Common/InstantiatedMethod.cs b/src/coreclr/tools/Common/TypeSystem/Common/InstantiatedMethod.cs index 6024dcc6cc335d..3f69af507ec069 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/InstantiatedMethod.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/InstantiatedMethod.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.Threading; using Internal.NativeFormat; namespace Internal.TypeSystem @@ -13,6 +14,8 @@ public sealed partial class InstantiatedMethod : MethodDesc private Instantiation _instantiation; private MethodSignature _signature; + private AsyncMethodData _asyncMethodData; + private MethodDesc _asyncOtherVariant; internal InstantiatedMethod(MethodDesc methodDef, Instantiation instantiation) { @@ -58,26 +61,72 @@ private TypeDesc Instantiate(TypeDesc type) return type.InstantiateSignature(default(Instantiation), _instantiation); } - public override MethodSignature Signature + private void InitializeSignature() + { + var template = _methodDef.Signature; + _signature = InstantiateSignature(template); + } + private MethodSignature InstantiateSignature(MethodSignature template) + { + var builder = new MethodSignatureBuilder(template); + builder.ReturnType = Instantiate(template.ReturnType); + for (int i = 0; i < template.Length; i++) + builder[i] = Instantiate(template[i]); + return builder.ToSignature(); + } + + public override AsyncMethodData AsyncMethodData { get { - if (_signature == null) + if (_asyncMethodData.Equals(default(AsyncMethodData))) { - MethodSignature template = _methodDef.Signature; - MethodSignatureBuilder builder = new MethodSignatureBuilder(template); + if (Signature.ReturnsTaskOrValueTask()) + { + if (IsAsync) + { + // If the method is already async, the template signature should already have been updated to reflect the AsyncCallConv + Debug.Assert(!Signature.ReturnsTaskOrValueTask() && Signature.IsAsyncCallConv); + _asyncMethodData = new AsyncMethodData() { Kind = AsyncMethodKind.AsyncVariantImpl, Signature = Signature }; + } + else + { + _asyncMethodData = new AsyncMethodData() { Kind = AsyncMethodKind.TaskReturning, Signature = Signature }; + } + } + else + { + _asyncMethodData = new AsyncMethodData() { Kind = AsyncMethodKind.NotAsync, Signature = Signature }; + } + } - builder.ReturnType = Instantiate(template.ReturnType); - for (int i = 0; i < template.Length; i++) - builder[i] = Instantiate(template[i]); + return _asyncMethodData; + } + } - _signature = builder.ToSignature(); - } + public override MethodSignature Signature + { + get + { + if (_signature == null) + InitializeSignature(); return _signature; } } + public override MethodDesc GetAsyncOtherVariant() + { + if (_asyncOtherVariant is null) + { + MethodDesc otherVariant = IsAsync ? + new TaskReturningAsyncThunk(this, InstantiateSignature(_methodDef.GetAsyncOtherVariant().Signature)) + : new AsyncMethodThunk(this); + Interlocked.CompareExchange(ref _asyncOtherVariant, otherVariant, null); + } + return _asyncOtherVariant; + } + public override Instantiation Instantiation { get @@ -134,7 +183,6 @@ public override bool IsAsync } } - public override bool HasCustomAttribute(string attributeNamespace, string attributeName) { return _methodDef.HasCustomAttribute(attributeNamespace, attributeName); diff --git a/src/coreclr/tools/Common/TypeSystem/Common/MethodDelegator.cs b/src/coreclr/tools/Common/TypeSystem/Common/MethodDelegator.cs index 55dba7ed9cead9..f2d024ae9a69e9 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/MethodDelegator.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/MethodDelegator.cs @@ -53,5 +53,7 @@ public override bool HasCustomAttribute(string attributeNamespace, string attrib // For this method, delegating to the wrapped MethodDesc would likely be the wrong thing. public abstract override MethodDesc InstantiateSignature(Instantiation typeInstantiation, Instantiation methodInstantiation); + + public abstract override AsyncMethodData AsyncMethodData { get; } } } diff --git a/src/coreclr/tools/Common/TypeSystem/Common/MethodDesc.cs b/src/coreclr/tools/Common/TypeSystem/Common/MethodDesc.cs index 3f53e780d435e5..6dc1c848e1466c 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/MethodDesc.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/MethodDesc.cs @@ -230,6 +230,35 @@ public EmbeddedSignatureData[] GetEmbeddedSignatureData(ReadOnlySpan name = md.Name; + if (name.SequenceEqual("Task"u8) || name.SequenceEqual("Task`1"u8) + || name.SequenceEqual("ValueTask"u8) || name.SequenceEqual("ValueTask`1"u8)) + { + return true; + } + } + return false; + } + + public MethodSignature CreateAsyncSignature() + { + Debug.Assert(!IsAsyncCallConv); + Debug.Assert(ReturnsTaskOrValueTask()); + MetadataType md = (MetadataType)this.ReturnType; + MethodSignatureBuilder builder = new MethodSignatureBuilder(this); + builder.ReturnType = md.HasInstantiation ? md.Instantiation[0] : this.Context.GetWellKnownType(WellKnownType.Void); + builder.Flags = this.Flags | MethodSignatureFlags.AsyncCallConv; + return builder.ToSignature(); + } + public bool Equals(MethodSignature otherSignature) { return Equals(otherSignature, allowCovariantReturn: false, allowEquivalence: false); @@ -367,6 +396,103 @@ public bool MoveNext() } } + /// + /// Information about the runtime async implementation of the method. For runtime async methods, the signature may differ from what is in the + /// + public struct AsyncMethodData + { + public AsyncMethodKind Kind; + public MethodSignature Signature; + + /// + /// Is this an Async variant method? + /// If yes, the method has another Task-returning variant. + /// + public bool IsAsyncVariant => Kind is + AsyncMethodKind.AsyncVariantImpl or + AsyncMethodKind.AsyncVariantThunk; + /// + /// Is this a small(ish) synthetic Task/async adapter to an async/Task implementation? + /// If yes, the method has another variant, which has the actual user-defined method body. + /// + public bool IsThunk + { + get + { + return Kind is + AsyncMethodKind.AsyncVariantThunk or + AsyncMethodKind.RuntimeAsync; + } + } + /// + /// Is this method callable as an async method? (i.e. uses Async calling convention) + /// + public bool IsAsyncCallConv => Kind is + AsyncMethodKind.AsyncVariantImpl or + AsyncMethodKind.AsyncVariantThunk or + AsyncMethodKind.AsyncExplicitImpl; + } + + public enum AsyncMethodKind + { + // Regular methods not returning tasks + // These are "normal" methods that do not get other variants. + // Note: Generic T-returning methods are NotAsync, even if T could be a Task. + NotAsync, + + // Regular methods that return Task/ValueTask + // Such method has its actual IL body and there also a synthetic variant that is an + // Async-callable think. (AsyncVariantThunk) + TaskReturning, + + // Task-returning methods marked as MethodImpl::Async in metadata. + // Such method has a body that is a thunk that forwards to an Async implementation variant + // which owns the original IL. (AsyncVariantImpl) + RuntimeAsync, + + //============================================================= + // On {TaskReturning, AsyncVariantThunk} and {RuntimeAsync, AsyncVariantImpl} pairs: + // + // When we see a Task-returning method we create 2 method varaints that logically match the same method definition. + // One variant has the same signature/callconv as the defining method and another is a matching Async variant. + // Depending on whether the definition was a runtime async method or an ordinary Task-returning method, + // the IL body belongs to one of the variants and another variant is a synthetic thunk. + // + // The signature of the Async variant is derived from the original signature by replacing Task return type with + // modreq'd element type: + // Example: "Task Foo();" ===> "modreq(Task`) int Foo();" + // Example: "ValueTask Bar();" ===> "modreq(ValueTask) void Bar();" + // + // The reason for this encoding is that: + // - it uses parts of original signature, as-is, thus does not need to look for or construct anything + // - it "unwraps" the element type. + // - it is reversible. In particular nonconflicting signatures will map to nonconflicting ones. + // + // Async methods are called with CORINFO_CALLCONV_ASYNCCALL call convention. + // + // It is possible to get from one variant to another via GetAsyncOtherVariant. + // + // NOTE: not all Async methods are "variants" from a pair, see AsyncExplicitImpl below. + //============================================================= + + // The following methods use special calling convention (CORINFO_CALLCONV_ASYNCCALL) + // These methods are emitted by the JIT as resumable state machines and also take an extra + // parameter and extra return - the continuation object. + + // Async methods with actual IL implementation of a MethodImpl::Async method. + AsyncVariantImpl, + + // Async methods with synthetic bodies that forward to a TaskReturning method. + AsyncVariantThunk, + + // Methods that are explicitly declared as Async in metadata while not Task returning. + // This is a special case used in a few infrastructure methods like `Await`. + // Such methods do not get non-Async variants/thunks and can only be called from another Async method. + // NOTE: These methods have the original signature and it is not possible to tell if the method is Async + // from the signature alone, thus all these methods are also JIT intrinsics. + AsyncExplicitImpl, + } + /// /// Helper structure for building method signatures by cloning an existing method signature. /// @@ -675,6 +801,43 @@ public virtual bool IsAsync } } + public virtual AsyncMethodData AsyncMethodData + { + get + { + return new AsyncMethodData { Kind = AsyncMethodKind.NotAsync, Signature = Signature }; + } + } + + /// + /// Whether the return type is Task/ValueTask or a generic Task{T}/ValueTask{T}. + /// Note this is different than the Runtime MethodDesc::IsTaskReturning which returns true + /// if the method returns a Task/ValueTask object and is not asyncCallConv. + /// + public bool IsTaskReturning + { + get + { + // Not NotAsync or AsyncExplicitImpl + return AsyncMethodData.Kind is + AsyncMethodKind.TaskReturning or + AsyncMethodKind.RuntimeAsync or + AsyncMethodKind.AsyncVariantImpl or + AsyncMethodKind.AsyncVariantThunk; + } + } + + /// + /// If the method is an async variant (Task-returning or async-callable), + /// Only valid to call if IsTaskReturning is true. + /// + public virtual MethodDesc GetAsyncOtherVariant() + { + // This base implementation really should never be called. + // Derived types should override it, and callers should check IsTaskReturning first. + throw new InvalidOperationException(); + } + public abstract bool HasCustomAttribute(string attributeNamespace, string attributeName); /// diff --git a/src/coreclr/tools/Common/TypeSystem/Common/MethodForInstantiatedType.cs b/src/coreclr/tools/Common/TypeSystem/Common/MethodForInstantiatedType.cs index b5c7195b483e14..024da0dfd2b274 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/MethodForInstantiatedType.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/MethodForInstantiatedType.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; - +using System.Threading; using Debug = System.Diagnostics.Debug; namespace Internal.TypeSystem @@ -14,6 +14,9 @@ public sealed partial class MethodForInstantiatedType : MethodDesc private MethodSignature _signature; + private MethodDesc _asyncOtherVariant; + private AsyncMethodData _asyncMethodData; + internal MethodForInstantiatedType(MethodDesc typicalMethodDef, InstantiatedType instantiatedType) { Debug.Assert(typicalMethodDef.GetTypicalMethodDefinition() == typicalMethodDef); @@ -51,21 +54,69 @@ private TypeDesc Instantiate(TypeDesc type) return type.InstantiateSignature(_instantiatedType.Instantiation, default(Instantiation)); } - public override MethodSignature Signature + private MethodSignature InstantiateSignature(MethodSignature template) + { + MethodSignatureBuilder builder = new MethodSignatureBuilder(template); + builder.ReturnType = Instantiate(template.ReturnType); + for (int i = 0; i < template.Length; i++) + builder[i] = Instantiate(template[i]); + + return builder.ToSignature(); + } + + private void InitializeSignature() + { + MethodSignature template = _typicalMethodDef.Signature; + _signature = InstantiateSignature(template); + + } + + public override AsyncMethodData AsyncMethodData { get { - if (_signature == null) + if (!_asyncMethodData.Equals(default(AsyncMethodData))) + return _asyncMethodData; + + if (Signature.ReturnsTaskOrValueTask()) + { + if (IsAsync) + { + // The signature should already have been updated to reflect the AsyncCallConv + // No need to convert to AsyncCallConv signature + _asyncMethodData = new AsyncMethodData { Kind = AsyncMethodKind.AsyncVariantImpl, Signature = Signature }; + } + else + { + _asyncMethodData = new AsyncMethodData { Kind = AsyncMethodKind.TaskReturning, Signature = Signature }; + } + } + else { - MethodSignature template = _typicalMethodDef.Signature; - MethodSignatureBuilder builder = new MethodSignatureBuilder(template); + _asyncMethodData = new AsyncMethodData { Kind = AsyncMethodKind.NotAsync, Signature = Signature }; + } - builder.ReturnType = Instantiate(template.ReturnType); - for (int i = 0; i < template.Length; i++) - builder[i] = Instantiate(template[i]); + return _asyncMethodData; + } + } - _signature = builder.ToSignature(); - } + public override MethodDesc GetAsyncOtherVariant() + { + if (_asyncOtherVariant is null) + { + MethodDesc otherVariant = IsAsync ? new TaskReturningAsyncThunk(this, InstantiateSignature(_typicalMethodDef.GetAsyncOtherVariant().Signature)) : new AsyncMethodThunk(this); + Interlocked.CompareExchange(ref _asyncOtherVariant, otherVariant, null); + } + + return _asyncOtherVariant; + } + + public override MethodSignature Signature + { + get + { + if (_signature == null) + InitializeSignature(); return _signature; } diff --git a/src/coreclr/tools/Common/TypeSystem/Common/TaskReturningAsyncThunk.cs b/src/coreclr/tools/Common/TypeSystem/Common/TaskReturningAsyncThunk.cs new file mode 100644 index 00000000000000..5bc0e0d3be51bd --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Common/TaskReturningAsyncThunk.cs @@ -0,0 +1,86 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Concurrent; +using System.Diagnostics; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace Internal.TypeSystem +{ + /// + /// Represents the async-callable (CORINFO_CALLCONV_ASYNCCALL) variant of a Task/ValueTask returning method. + /// The wrapper should be short‑lived and only used while interacting with the JIT interface. + /// NOPE: These things aren't short lived in R2R scenarios. Please make a normal method, and give them normal, long lifetimes + /// + public sealed class TaskReturningAsyncThunk : MethodDelegator + { + private readonly AsyncMethodData _asyncMethodData; + + public TaskReturningAsyncThunk(MethodDesc asyncMethodImplVariant, MethodSignature signature) : base(asyncMethodImplVariant) + { + Debug.Assert(asyncMethodImplVariant.IsTaskReturning); + _asyncMethodData = new() { Kind = AsyncMethodKind.RuntimeAsync, Signature = signature }; + } + + public override MethodDesc GetAsyncOtherVariant() => _wrappedMethod; + + public override AsyncMethodData AsyncMethodData + { + get + { + return _asyncMethodData; + } + } + + public override MethodDesc GetCanonMethodTarget(CanonicalFormKind kind) + { + return _wrappedMethod.GetCanonMethodTarget(kind).GetAsyncOtherVariant(); + } + + public override MethodDesc GetMethodDefinition() + { + return _wrappedMethod.GetMethodDefinition(); + } + + public override MethodDesc GetTypicalMethodDefinition() + { + return _wrappedMethod.GetTypicalMethodDefinition(); + } + + public override MethodDesc InstantiateSignature(Instantiation typeInstantiation, Instantiation methodInstantiation) + { + MethodDesc real = _wrappedMethod.InstantiateSignature(typeInstantiation, methodInstantiation); + if (real != _wrappedMethod) + return real.GetAsyncOtherVariant(); + return this; + } + + public override MethodSignature Signature + { + get + { + return _asyncMethodData.Signature; + } + } + + protected internal override int ClassCode => 0x554d08b9; + + public override string DiagnosticName => "TaskReturningVariant: " + _wrappedMethod.DiagnosticName; + + protected internal override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + if (other is TaskReturningAsyncThunk otherAsync) + { + return comparer.Compare(_wrappedMethod, otherAsync._wrappedMethod); + } + return -1; + } + + public override string ToString() + { + return "Task returning thunk: " + _wrappedMethod.ToString(); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaMethod.cs b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaMethod.cs index 09fb19a982fd79..9b7aa0e7a02401 100644 --- a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaMethod.cs +++ b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaMethod.cs @@ -42,8 +42,10 @@ private static class MethodFlags private unsafe volatile byte* _namePointer; private int _nameLength; private ThreadSafeFlags _methodFlags; - private MethodSignature _signature; + private MethodSignature _metadataSignature; private TypeDesc[] _genericParameters; // TODO: Optional field? + private AsyncMethodData _asyncMethodData; + private MethodDesc _asyncOtherVariant; internal EcmaMethod(EcmaType type, MethodDefinitionHandle handle) { @@ -82,16 +84,71 @@ private MethodSignature InitializeSignature() EcmaSignatureParser parser = new EcmaSignatureParser(Module, signatureReader, NotFoundBehavior.Throw); var signature = parser.ParseMethodSignature(); - return (_signature = signature); + + bool returnsTask = signature.ReturnsTaskOrValueTask(); + if (!returnsTask && !IsAsync) + { + _asyncMethodData = new AsyncMethodData + { + Kind = AsyncMethodKind.NotAsync, + Signature = signature + }; + } + else if (returnsTask && IsAsync) + { + _asyncMethodData = new AsyncMethodData + { + Kind = AsyncMethodKind.AsyncVariantImpl, + Signature = signature.CreateAsyncSignature() + }; + } + else if (returnsTask && !IsAsync) + { + _asyncMethodData = new AsyncMethodData + { + Kind = AsyncMethodKind.TaskReturning, + Signature = signature + }; + } + else + { + Debug.Assert(IsAsync && !returnsTask); + _asyncMethodData = new AsyncMethodData + { + Kind = AsyncMethodKind.AsyncExplicitImpl, + Signature = signature + }; + } + + _metadataSignature = signature; + return (_metadataSignature = signature); } public override MethodSignature Signature { get { - if (_signature == null) + if (_metadataSignature == null) + return InitializeSignature(); + if (AsyncMethodData.IsAsyncVariant) + { + Debug.Assert(_asyncMethodData.Kind == AsyncMethodKind.AsyncVariantImpl && _asyncMethodData.Signature is not null); + return _asyncMethodData.Signature; + } + return _metadataSignature; + } + } + + /// + /// The method signature as defined in metadata, without any adjustments for async methods. + /// + public MethodSignature MetadataSignature + { + get + { + if (_metadataSignature == null) return InitializeSignature(); - return _signature; + return _metadataSignature; } } @@ -379,6 +436,31 @@ public override bool IsAsync } } + public override AsyncMethodData AsyncMethodData + { + get + { + if (_asyncMethodData.Equals(default(AsyncMethodData))) + InitializeSignature(); + + Debug.Assert(!_asyncMethodData.Equals(default(AsyncMethodData))); + return _asyncMethodData; + } + } + + public override MethodDesc GetAsyncOtherVariant() + { + Debug.Assert(_asyncMethodData.Kind is AsyncMethodKind.TaskReturning or AsyncMethodKind.AsyncVariantImpl); + if (_asyncOtherVariant is null) + { + MethodDesc otherVariant = IsAsync ? + new TaskReturningAsyncThunk(this, _metadataSignature) : + new AsyncMethodThunk(this); + Interlocked.CompareExchange(ref _asyncOtherVariant, otherVariant, null); + } + return _asyncOtherVariant; + } + public MethodAttributes Attributes { get diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncMethodEmitter.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncMethodEmitter.cs new file mode 100644 index 00000000000000..6e29d9475b4bf8 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncMethodEmitter.cs @@ -0,0 +1,339 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#if READYTORUN +using System; +using System.Collections.Generic; +using System.Diagnostics; + + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +using Internal.IL.Stubs; +using System.Buffers.Binary; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Text; +using System.Text.Unicode; +using ILCompiler.DependencyAnalysis.ReadyToRun; +using ILCompiler; + +namespace Internal.IL.Stubs +{ + /// + /// Provides method bodies for Task-returning async wrapper methods + /// + public struct AsyncMethodEmitter + { + public AsyncMethodEmitter(MutableModule manifestModule, CompilationModuleGroup compilationModuleGroup) + { + _manifestMutableModule = manifestModule; + _compilationModuleGroup = compilationModuleGroup; + } + private Dictionary _manifestModuleWrappedMethods = new Dictionary(); + private MutableModule _manifestMutableModule; + private CompilationModuleGroup _compilationModuleGroup; + + public MethodIL EmitIL(MethodDesc method) + { + // Emits roughly the following code: + // + // ExecutionAndSyncBlockStore store = default; + // store.Push(); + // try + // { + // try + // { + // T result = Inner(args); + // // call an intrisic to see if the call above produced a continuation + // if (StubHelpers.AsyncCallContinuation() == null) + // return Task.FromResult(result); + // + // return FinalizeTaskReturningThunk(); + // } + // catch (Exception ex) + // { + // return TaskFromException(ex); + // } + // } + // finally + // { + // store.Pop(); + // } + ILEmitter ilEmitter = new ILEmitter(); + ILCodeStream il = ilEmitter.NewCodeStream(); + TypeDesc retType = method.Signature.ReturnType; + IL.Stubs.ILLocalVariable returnTaskLocal = ilEmitter.NewLocal(retType); + bool isValueTask = retType.IsValueType; + TypeDesc logicalResultType = null; + IL.Stubs.ILLocalVariable logicalResultLocal = default; + if (retType.HasInstantiation) + { + logicalResultType = retType.Instantiation[0]; + logicalResultLocal = ilEmitter.NewLocal(logicalResultType); + } + var executionAndSyncBlockStoreType = GetR2RKnownType(R2RKnownType.ExecutionAndSyncBlockStore); + var executionAndSyncBlockStoreLocal = ilEmitter.NewLocal(executionAndSyncBlockStoreType); + + ILCodeLabel returnTaskLabel = ilEmitter.NewCodeLabel(); + ILCodeLabel suspendedLabel = ilEmitter.NewCodeLabel(); + ILCodeLabel finishedLabel = ilEmitter.NewCodeLabel(); + + // store.Push() + il.EmitLdLoca(executionAndSyncBlockStoreLocal); + il.Emit(ILOpcode.call, ilEmitter.NewToken(GetR2RKnownMethod(R2RKnownMethod.ExecutionAndSyncBlockStore_Push))); + + // Inner try block must appear first in metadata + var exceptionType = ilEmitter.NewToken(GetR2RKnownType(R2RKnownType.SystemException)); + ILExceptionRegionBuilder innerTryRegion = ilEmitter.NewCatchRegion(exceptionType); + // try + // { + ILExceptionRegionBuilder outerTryRegion = ilEmitter.NewFinallyRegion(); + il.BeginTry(outerTryRegion); + il.BeginTry(innerTryRegion); + + // var result = Inner(args) + int argIndex = 0; + if (!method.Signature.IsStatic) + { + il.EmitLdArg(argIndex++); + } + for (int i = 0; i < method.Signature.Length; i++) + { + il.EmitLdArg(argIndex++); + } + MethodDesc asyncOtherVariant = method.GetAsyncOtherVariant(); + il.Emit(ILOpcode.call, ilEmitter.NewToken(asyncOtherVariant)); + if (logicalResultLocal != default) + { + il.EmitStLoc(logicalResultLocal); + } + + // if (StubHelpers.AsyncCallContinuation() == null) return Task.FromResult(result); + il.Emit(ILOpcode.call, ilEmitter.NewToken(GetR2RKnownMethod(R2RKnownMethod.StubHelpers_AsyncCallContinuation))); + il.Emit(ILOpcode.brfalse, finishedLabel); + il.Emit(ILOpcode.leave, suspendedLabel); + il.EmitLabel(finishedLabel); + if (logicalResultLocal != default) + { + il.EmitLdLoc(logicalResultLocal); + R2RKnownMethod fromResultValue = isValueTask ? R2RKnownMethod.ValueTask_FromResult : R2RKnownMethod.Task_FromResult; + MethodDesc fromResultMethod = GetR2RKnownMethod(fromResultValue).MakeInstantiatedMethod(logicalResultType); + il.Emit(ILOpcode.call, ilEmitter.NewToken(fromResultMethod)); + } + else + { + MethodDesc completedTaskGetter; + if (isValueTask) + { + completedTaskGetter = GetR2RKnownMethod(R2RKnownMethod.ValueTask_get_CompletedTask); + } + else + { + completedTaskGetter = GetR2RKnownMethod(R2RKnownMethod.Task_get_CompletedTask); + } + il.Emit(ILOpcode.call, ilEmitter.NewToken(completedTaskGetter)); + } + il.EmitStLoc(returnTaskLocal); + + il.Emit(ILOpcode.leave, returnTaskLabel); + il.EndTry(innerTryRegion); + + // catch (Exception ex) + // { + il.BeginHandler(innerTryRegion); + // return TaskFromException(ex); + MethodDesc fromExceptionMethod; + R2RKnownMethod fromExceptionKnownMethod; + if (isValueTask) + { + fromExceptionKnownMethod = logicalResultLocal != default ? + R2RKnownMethod.AsyncHelpers_ValueTaskFromExceptionGeneric : + R2RKnownMethod.AsyncHelpers_ValueTaskFromException; + } + else + { + fromExceptionKnownMethod = logicalResultLocal != default ? + R2RKnownMethod.AsyncHelpers_TaskFromExceptionGeneric : + R2RKnownMethod.AsyncHelpers_TaskFromException; + } + fromExceptionMethod = GetR2RKnownMethod(fromExceptionKnownMethod); + if (logicalResultLocal != default) + { + fromExceptionMethod = fromExceptionMethod.MakeInstantiatedMethod(logicalResultType); + } + + il.Emit(ILOpcode.call, ilEmitter.NewToken(fromExceptionMethod)); + il.EmitStLoc(returnTaskLocal); + + il.Emit(ILOpcode.leave, returnTaskLabel); + il.EndHandler(innerTryRegion); + // } // end catch + + il.EmitLabel(suspendedLabel); + + // finally + // { + // + MethodDesc finalizeMethod; + R2RKnownMethod finalizeKnownMethod; + if (isValueTask) + { + finalizeKnownMethod = logicalResultLocal != default ? + R2RKnownMethod.AsyncHelpers_FinalizeValueTaskReturningThunkGeneric : + R2RKnownMethod.AsyncHelpers_FinalizeValueTaskReturningThunk; + } + else + { + finalizeKnownMethod = logicalResultLocal != default ? + R2RKnownMethod.AsyncHelpers_FinalizeTaskReturningThunkGeneric : + R2RKnownMethod.AsyncHelpers_FinalizeTaskReturningThunk; + } + finalizeMethod = GetR2RKnownMethod(finalizeKnownMethod); + + if (logicalResultLocal != default) + { + finalizeMethod = finalizeMethod.MakeInstantiatedMethod(logicalResultType); + } + + il.Emit(ILOpcode.call, ilEmitter.NewToken(finalizeMethod)); + il.EmitStLoc(returnTaskLocal); + + il.Emit(ILOpcode.leave, returnTaskLabel); + il.EndTry(outerTryRegion); + + // Finally block + il.BeginHandler(outerTryRegion); + il.EmitLdLoca(executionAndSyncBlockStoreLocal); + il.Emit(ILOpcode.call, ilEmitter.NewToken(GetR2RKnownMethod(R2RKnownMethod.ExecutionAndSyncBlockStore_Pop))); + il.Emit(ILOpcode.endfinally); + il.EndHandler(outerTryRegion); + + // Return task label + il.EmitLabel(returnTaskLabel); + il.EmitLdLoc(returnTaskLocal); + il.Emit(ILOpcode.ret); + + return ilEmitter.Link(method); + } + + public enum R2RKnownType + { + ExecutionAndSyncBlockStore, + SystemException, + } + + public enum R2RKnownMethod + { + ExecutionAndSyncBlockStore_Push, + ExecutionAndSyncBlockStore_Pop, + ValueTask_FromResult, + Task_FromResult, + ValueTask_get_CompletedTask, + Task_get_CompletedTask, + StubHelpers_AsyncCallContinuation, + AsyncHelpers_TaskFromException, + AsyncHelpers_ValueTaskFromException, + AsyncHelpers_TaskFromExceptionGeneric, + AsyncHelpers_ValueTaskFromExceptionGeneric, + AsyncHelpers_FinalizeTaskReturningThunk, + AsyncHelpers_FinalizeValueTaskReturningThunk, + AsyncHelpers_FinalizeTaskReturningThunkGeneric, + AsyncHelpers_FinalizeValueTaskReturningThunkGeneric, + } + + public MetadataType GetR2RKnownType(R2RKnownType knownType) + { + switch (knownType) + { + case R2RKnownType.ExecutionAndSyncBlockStore: + return _manifestMutableModule.Context.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "ExecutionAndSyncBlockStore"u8); + case R2RKnownType.SystemException: + return _manifestMutableModule.Context.SystemModule.GetKnownType("System"u8, "SystemException"u8); + default: + throw new InvalidOperationException($"Unknown R2R known type: {knownType}"); + } + } + + public MethodDesc GetR2RKnownMethod(R2RKnownMethod knownMethod) + { + TypeSystemContext context = _manifestMutableModule.Context; + switch (knownMethod) + { + case R2RKnownMethod.ExecutionAndSyncBlockStore_Push: + return GetR2RKnownType(R2RKnownType.ExecutionAndSyncBlockStore).GetKnownMethod("Push"u8, null); + case R2RKnownMethod.ExecutionAndSyncBlockStore_Pop: + return GetR2RKnownType(R2RKnownType.ExecutionAndSyncBlockStore).GetKnownMethod("Pop"u8, null); + case R2RKnownMethod.ValueTask_FromResult: + return _manifestMutableModule.Context.SystemModule.GetKnownType("System.Threading.Tasks"u8, "ValueTask"u8).GetKnownMethod("FromResult"u8, null); + case R2RKnownMethod.Task_FromResult: + return _manifestMutableModule.Context.SystemModule.GetKnownType("System.Threading.Tasks"u8, "Task"u8).GetKnownMethod("FromResult"u8, null); + case R2RKnownMethod.ValueTask_get_CompletedTask: + return _manifestMutableModule.Context.SystemModule.GetKnownType("System.Threading.Tasks"u8, "ValueTask"u8).GetKnownMethod("get_CompletedTask"u8, null); + case R2RKnownMethod.Task_get_CompletedTask: + return _manifestMutableModule.Context.SystemModule.GetKnownType("System.Threading.Tasks"u8, "Task"u8).GetKnownMethod("get_CompletedTask"u8, null); + case R2RKnownMethod.StubHelpers_AsyncCallContinuation: + return _manifestMutableModule.Context.SystemModule.GetKnownType("System.StubHelpers"u8, "StubHelpers"u8).GetKnownMethod("AsyncCallContinuation"u8, null); + case R2RKnownMethod.AsyncHelpers_TaskFromException: + return _manifestMutableModule.Context.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8).GetKnownMethod("TaskFromException"u8, new MethodSignature(MethodSignatureFlags.Static, 0, context.SystemModule.GetKnownType("System.Threading.Tasks"u8, "Task"u8), new TypeDesc[] { context.SystemModule.GetKnownType("System"u8, "Exception"u8) })); + case R2RKnownMethod.AsyncHelpers_ValueTaskFromException: + return _manifestMutableModule.Context.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8).GetKnownMethod("ValueTaskFromException"u8, new MethodSignature(MethodSignatureFlags.Static, 0, context.SystemModule.GetKnownType("System.Threading.Tasks"u8, "ValueTask"u8), new TypeDesc[] { context.SystemModule.GetKnownType("System"u8, "Exception"u8) })); + case R2RKnownMethod.AsyncHelpers_TaskFromExceptionGeneric: + return _manifestMutableModule.Context.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8).GetKnownMethod("TaskFromException"u8, new MethodSignature(MethodSignatureFlags.Static, 1, context.SystemModule.GetKnownType("System.Threading.Tasks"u8, "Task`1"u8).MakeInstantiatedType(context.GetSignatureVariable(0, method: true)), new TypeDesc[] { context.SystemModule.GetKnownType("System"u8, "Exception"u8) })); + case R2RKnownMethod.AsyncHelpers_ValueTaskFromExceptionGeneric: + return _manifestMutableModule.Context.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8).GetKnownMethod("ValueTaskFromException"u8, new MethodSignature(MethodSignatureFlags.Static, 1, context.SystemModule.GetKnownType("System.Threading.Tasks"u8, "ValueTask`1"u8).MakeInstantiatedType(context.GetSignatureVariable(0, method: true)), new TypeDesc[] { context.SystemModule.GetKnownType("System"u8, "Exception"u8) })); + case R2RKnownMethod.AsyncHelpers_FinalizeTaskReturningThunk: + return _manifestMutableModule.Context.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8).GetKnownMethod("FinalizeTaskReturningThunk"u8, new MethodSignature(MethodSignatureFlags.Static, 0, context.SystemModule.GetKnownType("System.Threading.Tasks"u8, "Task"u8), Array.Empty())); + case R2RKnownMethod.AsyncHelpers_FinalizeValueTaskReturningThunk: + return _manifestMutableModule.Context.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8).GetKnownMethod("FinalizeValueTaskReturningThunk"u8, new MethodSignature(MethodSignatureFlags.Static, 0, context.SystemModule.GetKnownType("System.Threading.Tasks"u8, "ValueTask"u8), Array.Empty())); + case R2RKnownMethod.AsyncHelpers_FinalizeTaskReturningThunkGeneric: + return _manifestMutableModule.Context.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8).GetKnownMethod("FinalizeTaskReturningThunk"u8, new MethodSignature(MethodSignatureFlags.Static, 1, context.SystemModule.GetKnownType("System.Threading.Tasks"u8, "Task`1"u8).MakeInstantiatedType(context.GetSignatureVariable(0, method: true)), Array.Empty())); + case R2RKnownMethod.AsyncHelpers_FinalizeValueTaskReturningThunkGeneric: + return _manifestMutableModule.Context.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8).GetKnownMethod("FinalizeValueTaskReturningThunk"u8, new MethodSignature(MethodSignatureFlags.Static, 1, context.SystemModule.GetKnownType("System.Threading.Tasks"u8, "ValueTask`1"u8).MakeInstantiatedType(context.GetSignatureVariable(0, method: true)), Array.Empty())); + + default: + throw new InvalidOperationException($"Unknown R2R known method: {knownMethod}"); + } + } + + public void EnsureR2RKnownMethodsAreInManifestModule(ModuleTokenResolver tokenResolver) + { + try + { + Debug.Assert(_manifestMutableModule.ModuleThatIsCurrentlyTheSourceOfNewReferences == null); + _manifestMutableModule.ModuleThatIsCurrentlyTheSourceOfNewReferences = _manifestMutableModule.Context.SystemModule; + _manifestMutableModule.AddingReferencesToR2RKnownTypesAndMethods = true; + + // Force all known R2R methods to be present in the manifest module + foreach (R2RKnownMethod knownMethod in Enum.GetValues()) + { + MethodDesc method = GetR2RKnownMethod(knownMethod); + if (!_compilationModuleGroup.VersionsWithMethodBody(method)) + { + tokenResolver.AddModuleTokenForMethod(method, new ModuleToken(_manifestMutableModule, _manifestMutableModule.TryGetEntityHandle(method).Value)); + + // This is effectively an assert + tokenResolver.GetModuleTokenForMethod(method, allowDynamicallyCreatedReference: true, throwIfNotFound: true); + } + } + // Force all known R2R types to be present in the manifest module + foreach (R2RKnownType knownType in Enum.GetValues()) + { + TypeDesc type = GetR2RKnownType(knownType); + if (!_compilationModuleGroup.VersionsWithType(type)) + { + tokenResolver.AddModuleTokenForType((EcmaType)type, new ModuleToken(_manifestMutableModule, _manifestMutableModule.TryGetEntityHandle(type).Value)); + tokenResolver.GetModuleTokenForType((EcmaType)type, allowDynamicallyCreatedReference: true, throwIfNotFound: true); + } + } + } + finally + { + _manifestMutableModule.AddingReferencesToR2RKnownTypesAndMethods = false; + _manifestMutableModule.ModuleThatIsCurrentlyTheSourceOfNewReferences = null; + } + } + } +} +#endif diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncResumptionStub.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncResumptionStub.cs new file mode 100644 index 00000000000000..6928adc1f17162 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncResumptionStub.cs @@ -0,0 +1,110 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Buffers.Binary; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.IL.Stubs +{ + public class AsyncResumptionStub : ILStubMethod + { + private MethodDesc _method; + private MethodSignature _signature; + private MethodIL _methodIL; + public AsyncResumptionStub(MethodDesc method) + { + _method = method; + _signature = null; + _methodIL = null; + } + public override string DiagnosticName => $"IL_STUB_AsyncResume__{_method.DiagnosticName}"; + + public override TypeDesc OwningType => _method.OwningType; + + public override MethodSignature Signature => _signature ??= BuildResumptionStubCalliSignature(_method.Signature); + + private MethodSignature BuildResumptionStubCalliSignature(MethodSignature _) + { + var flags = MethodSignatureFlags.None; + TypeDesc continuation = Context.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "Continuation"u8); + ByRefType outputObject = Context.GetByRefType( + Context.GetWellKnownType(WellKnownType.Byte)); + return new MethodSignature(flags, 0, continuation, [continuation, outputObject]); + } + + public override TypeSystemContext Context => _method.Context; + + private static int _classCode = new Random().Next(int.MinValue, int.MaxValue); + protected override int ClassCode => _classCode; + + public override MethodIL EmitIL() + { + if (_methodIL != null) + return _methodIL; + ILEmitter ilEmitter = new ILEmitter(); + ILCodeStream ilStream = ilEmitter.NewCodeStream(); + + // if it has this pointer + if (!_method.Signature.IsStatic) + { + if (_method.OwningType.IsValueType) + { + ilStream.EmitLdc(0); + ilStream.Emit(ILOpcode.conv_u); + } + else + { + ilStream.Emit(ILOpcode.ldnull); + } + } + + foreach (var param in _method.Signature) + { + var local = ilEmitter.NewLocal(param); + ilStream.EmitLdLoca(local); + ilStream.Emit(ILOpcode.initobj, ilEmitter.NewToken(param)); + ilStream.EmitLdLoc(local); + } + ilStream.Emit(ILOpcode.ldftn, ilEmitter.NewToken(_method)); + ilStream.Emit(ILOpcode.calli, ilEmitter.NewToken(this.Signature)); + + bool returnsVoid = _method.Signature.ReturnType != Context.GetWellKnownType(WellKnownType.Void); + IL.Stubs.ILLocalVariable resultLocal = default; + if (!returnsVoid) + { + resultLocal = ilEmitter.NewLocal(_method.Signature.ReturnType); + ilStream.EmitStLoc(resultLocal); + } + + MethodDesc asyncCallContinuation = Context.SystemModule.GetKnownType("System.StubHelpers"u8, "StubHelpers"u8) + .GetKnownMethod("AsyncCallContinuation"u8, null); + TypeDesc continuation = Context.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "Continuation"u8); + var newContinuationLocal = ilEmitter.NewLocal(continuation); + ilStream.Emit(ILOpcode.call, ilEmitter.NewToken(asyncCallContinuation)); + ilStream.EmitStLoc(newContinuationLocal); + + if (!returnsVoid) + { + var doneResult = ilEmitter.NewCodeLabel(); + ilStream.EmitLdLoca(newContinuationLocal); + ilStream.Emit(ILOpcode.brtrue, doneResult); + ilStream.EmitLdArg(1); + ilStream.EmitLdLoc(resultLocal); + ilStream.Emit(ILOpcode.stobj, ilEmitter.NewToken(_method.Signature.ReturnType)); + ilStream.EmitLabel(doneResult); + } + ilStream.EmitLdLoc(newContinuationLocal); + ilStream.Emit(ILOpcode.ret); + + return ilEmitter.Link(this); + } + + protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) => throw new NotImplementedException(); + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ILEmitter.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ILEmitter.cs index 3625b6397f5d85..7e9f929410099f 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ILEmitter.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ILEmitter.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using Internal.TypeSystem; - +using Internal.TypeSystem.Ecma; using Debug = System.Diagnostics.Debug; namespace Internal.IL.Stubs @@ -505,6 +505,8 @@ public void DefineSequencePoint(string document, int lineNumber) public class ILExceptionRegionBuilder { + internal readonly ILExceptionRegionKind Kind; + internal readonly int ClassToken; internal ILCodeStream _beginTryStream; internal int _beginTryOffset; @@ -517,8 +519,10 @@ public class ILExceptionRegionBuilder internal ILCodeStream _endHandlerStream; internal int _endHandlerOffset; - internal ILExceptionRegionBuilder() + internal ILExceptionRegionBuilder(ILExceptionRegionKind kind, int classToken) { + Kind = kind; + ClassToken = classToken; } internal int TryOffset => _beginTryStream.RelativeToAbsoluteOffset(_beginTryOffset); @@ -669,7 +673,7 @@ public class ILEmitter private ArrayBuilder _codeStreams; private ArrayBuilder _locals; private ArrayBuilder _tokens; - private ArrayBuilder _finallyRegions; + private ArrayBuilder _ehRegions; public ILEmitter() { @@ -729,8 +733,15 @@ public ILCodeLabel NewCodeLabel() public ILExceptionRegionBuilder NewFinallyRegion() { - var region = new ILExceptionRegionBuilder(); - _finallyRegions.Add(region); + var region = new ILExceptionRegionBuilder(ILExceptionRegionKind.Finally, 0); + _ehRegions.Add(region); + return region; + } + + public ILExceptionRegionBuilder NewCatchRegion(ILToken classToken) + { + var region = new ILExceptionRegionBuilder(ILExceptionRegionKind.Catch, (int)classToken); + _ehRegions.Add(region); return region; } @@ -782,20 +793,20 @@ public MethodIL Link(MethodDesc owningMethod) ILExceptionRegion[] exceptionRegions = null; - int numberOfExceptionRegions = _finallyRegions.Count; + int numberOfExceptionRegions = _ehRegions.Count; if (numberOfExceptionRegions > 0) { exceptionRegions = new ILExceptionRegion[numberOfExceptionRegions]; - for (int i = 0; i < _finallyRegions.Count; i++) + for (int i = 0; i < _ehRegions.Count; i++) { - ILExceptionRegionBuilder region = _finallyRegions[i]; + ILExceptionRegionBuilder region = _ehRegions[i]; Debug.Assert(region.IsDefined); - exceptionRegions[i] = new ILExceptionRegion(ILExceptionRegionKind.Finally, + exceptionRegions[i] = new ILExceptionRegion(region.Kind, region.TryOffset, region.TryLength, region.HandlerOffset, region.HandlerLength, - classToken: 0, filterOffset: 0); + classToken: region.ClassToken, filterOffset: 0); } } diff --git a/src/coreclr/tools/ILVerification/ILVerification.projitems b/src/coreclr/tools/ILVerification/ILVerification.projitems index c301f2da1d298e..515c0a39fd3f00 100644 --- a/src/coreclr/tools/ILVerification/ILVerification.projitems +++ b/src/coreclr/tools/ILVerification/ILVerification.projitems @@ -288,6 +288,15 @@ Ecma\EcmaMethod.cs + + Common\AsyncMethodDesc.cs + + + Common\MethodDelegator.cs + + + Common\TaskReturningAsyncThunk.cs + Ecma\EcmaModule.cs diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InstanceEntryPointTableNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InstanceEntryPointTableNode.cs index db63a30fae67a5..e039555233b3da 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InstanceEntryPointTableNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InstanceEntryPointTableNode.cs @@ -81,6 +81,8 @@ public static byte[] BuildSignatureForMethodDefinedInModule(MethodDesc method, N private byte[] BuildSignatureForMethod(MethodWithGCInfo method, NodeFactory factory) { + // This has to return a Signature with the AsyncCallConv flag set for AsyncMethodDesc methods + // I think it does already, but should validate return BuildSignatureForMethodDefinedInModule(method.Method, factory); } @@ -102,7 +104,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) foreach (MethodWithGCInfo method in factory.EnumerateCompiledMethods(null, CompiledMethodCategory.Instantiated)) { - Debug.Assert(method.Method.HasInstantiation || method.Method.OwningType.HasInstantiation); + Debug.Assert(method.Method.HasInstantiation || method.Method.OwningType.HasInstantiation || method.Method.AsyncMethodData.IsThunk); int methodIndex = factory.RuntimeFunctionsTable.GetIndex(method); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodFixupSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodFixupSignature.cs index c3f3324e0c6ba4..51a34bead68866 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodFixupSignature.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodFixupSignature.cs @@ -89,7 +89,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) // Optimize some of the fixups into a more compact form ReadyToRunFixupKind fixupKind = _fixupKind; bool optimized = false; - if (!_method.Unboxing && !IsInstantiatingStub && _method.ConstrainedType == null && + if (!_method.Unboxing && !IsInstantiatingStub && _method.ConstrainedType == null && !_method.AsyncCallConv && fixupKind == ReadyToRunFixupKind.MethodEntry) { if (!_method.Method.HasInstantiation && !_method.Method.OwningType.HasInstantiation && !_method.Method.OwningType.IsArray) @@ -128,7 +128,6 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) } SignatureContext innerContext = dataBuilder.EmitFixup(factory, fixupKind, method.Token.Module, factory.SignatureContext); - if (optimized && method.Token.TokenType == CorTokenType.mdtMethodDef) { dataBuilder.EmitMethodDefToken(method.Token); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs index dc122655054034..3177949617b4cb 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs @@ -249,10 +249,10 @@ void SetModuleTokenForTypeSystemEntity(ConcurrentDictionary d { // We can only use tokens from the manifest mutable module or from one of the modules that versions // with the current compilation. Any other module tokens may change while the code executes - if (token.Module != _manifestMutableModule && !_compilationModuleGroup.VersionsWithModule((ModuleDesc)token.Module)) - { - throw new InternalCompilerErrorException("Invalid usage of a token from a module not within the version bubble"); - } + //if (token.Module != _manifestMutableModule && !_compilationModuleGroup.VersionsWithModule((ModuleDesc)token.Module)) + //{ + // throw new InternalCompilerErrorException("Invalid usage of a token from a module not within the version bubble"); + //} if (!dictionary.TryAdd(tse, token)) { diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs index 2c4ca7cd004702..f78b1938b6e811 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs @@ -424,6 +424,10 @@ public void EmitMethodSignature( { flags |= (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_UnboxingStub; } + if (method.AsyncCallConv) + { + flags |= (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_AsyncVariant; + } if (isInstantiatingStub) { flags |= (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_InstantiatingStub; @@ -492,7 +496,7 @@ private void EmitMethodSpecificationSignature(MethodWithToken method, if ((method.Token.Module != context.LocalContext) && (!enforceOwningType || (enforceDefEncoding && methodToken.TokenType == CorTokenType.mdtMemberRef))) { - // If enforeOwningType is set, this is an entry for the InstanceEntryPoint or InstrumentationDataTable nodes + // If enforceOwningType is set, this is an entry for the InstanceEntryPoint or InstrumentationDataTable nodes // which are not used in quite the same way, and for which the MethodDef is always matched to the module // which defines the type flags |= (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_UpdateContext; @@ -606,7 +610,7 @@ public SignatureContext EmitFixup(NodeFactory factory, ReadyToRunFixupKind fixup { throw new InternalCompilerErrorException("Attempt to use token from a module not within the version bubble"); } - + EmitUInt((uint)factory.ManifestMetadataTable.ModuleToIndex(targetModule)); return new SignatureContext(targetModule, outerContext.Resolver); } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs index e1c1e9a2448b10..109d7c663ab2ca 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs @@ -710,8 +710,8 @@ public void AttachToDependencyGraph(DependencyAnalyzerBase graph, I ManifestMetadataTable = new ManifestMetadataTableNode(this); Header.Add(Internal.Runtime.ReadyToRunSectionType.ManifestMetadata, ManifestMetadataTable, ManifestMetadataTable); Resolver.SetModuleIndexLookup(ManifestMetadataTable.ModuleToIndex); - ((ReadyToRunILProvider)ilProvider).InitManifestMutableModule(ManifestMetadataTable._mutableModule); Resolver.InitManifestMutableModule(ManifestMetadataTable._mutableModule); + ((ReadyToRunILProvider)ilProvider).InitManifestMutableModule(ManifestMetadataTable._mutableModule, Resolver); ManifestAssemblyMvidHeaderNode mvidTableNode = new ManifestAssemblyMvidHeaderNode(ManifestMetadataTable); Header.Add(Internal.Runtime.ReadyToRunSectionType.ManifestAssemblyMvids, mvidTableNode, mvidTableNode); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunTableManager.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunTableManager.cs index 93e3f3605cb03d..d7a94557977068 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunTableManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunTableManager.cs @@ -47,7 +47,8 @@ public PerModuleMethodsGenerated(EcmaModule module) public readonly EcmaModule Module; public List MethodsGenerated = new List(); - public List GenericMethodsGenerated = new List(); + // Generic instantiations and Async methods + public List InstantiatedMethodsGenerated = new List(); } private Dictionary _methodsGenerated = new Dictionary(); @@ -85,9 +86,9 @@ protected virtual void Graph_NewMarkedNode(DependencyNodeCore obj) perModuleData = new PerModuleMethodsGenerated(module); _methodsGenerated[module] = perModuleData; } - if (method.HasInstantiation || method.OwningType.HasInstantiation) + if (method.HasInstantiation || method.OwningType.HasInstantiation || method.AsyncMethodData.IsThunk) { - perModuleData.GenericMethodsGenerated.Add(methodNode); + perModuleData.InstantiatedMethodsGenerated.Add(methodNode); } else { @@ -112,7 +113,7 @@ public IEnumerable GetCompiledMethods(EcmaModule moduleToEnumerate, int methodOnlyResult = comparer.Compare(x.Method, y.Method); // Assert the two sorting techniques produce the same result unless there is a CustomSort applied - Debug.Assert((nodeComparerResult == methodOnlyResult) || + Debug.Assert((nodeComparerResult == methodOnlyResult) || ((x is SortableDependencyNode sortableX && sortableX.CustomSort != Int32.MaxValue) || (y is SortableDependencyNode sortableY && sortableY.CustomSort != Int32.MaxValue))); #endif @@ -126,10 +127,10 @@ public IEnumerable GetCompiledMethods(EcmaModule moduleToEnumerate, foreach (var perModuleData in perModuleDatas) { perModuleData.MethodsGenerated.MergeSort(sortHelperNoCustomSort); - perModuleData.GenericMethodsGenerated.MergeSort(sortHelperNoCustomSort); + perModuleData.InstantiatedMethodsGenerated.MergeSort(sortHelperNoCustomSort); _completeSortedMethods.AddRange(perModuleData.MethodsGenerated); - _completeSortedMethods.AddRange(perModuleData.GenericMethodsGenerated); - _completeSortedGenericMethods.AddRange(perModuleData.GenericMethodsGenerated); + _completeSortedMethods.AddRange(perModuleData.InstantiatedMethodsGenerated); + _completeSortedGenericMethods.AddRange(perModuleData.InstantiatedMethodsGenerated); } _completeSortedMethods.MergeSort(sortHelper); _completeSortedGenericMethods.MergeSort(sortHelper); @@ -162,7 +163,7 @@ public IEnumerable GetCompiledMethods(EcmaModule moduleToEnumerate, if (methodCategory == CompiledMethodCategory.Instantiated) { - return perModuleData.GenericMethodsGenerated; + return perModuleData.InstantiatedMethodsGenerated; } else { diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/IBCDataModel.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/IBCDataModel.cs index 0673cf16da4001..a518f4e9a13478 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/IBCDataModel.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/IBCDataModel.cs @@ -376,6 +376,8 @@ public static bool IsUnboxingStub(uint flags) { return (flags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_UnboxingStub) != 0; } public static bool IsInstantiatingStub(uint flags) { return (flags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_InstantiatingStub) != 0; } + public static bool IsAsyncVariant(uint flags) + { return (flags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_AsyncVariant) != 0; } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs index 6ea7cb2638fedf..2f4b927401af33 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs @@ -14,6 +14,10 @@ using System.Buffers.Binary; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; +using Internal.JitInterface; +using System.Text; +using System.Text.Unicode; +using ILCompiler.DependencyAnalysis.ReadyToRun; namespace Internal.IL { @@ -22,10 +26,10 @@ namespace Internal.IL /// public interface IMethodTokensAreUseableInCompilation { } - public sealed class ReadyToRunILProvider : ILProvider { private CompilationModuleGroup _compilationModuleGroup; + private AsyncMethodEmitter _asyncMethodEmitter; private MutableModule _manifestMutableModule; private int _version = 0; @@ -34,9 +38,11 @@ public ReadyToRunILProvider(CompilationModuleGroup compilationModuleGroup) _compilationModuleGroup = compilationModuleGroup; } - public void InitManifestMutableModule(MutableModule module) + public void InitManifestMutableModule(MutableModule module, ModuleTokenResolver tokenResolver) { + _asyncMethodEmitter = new AsyncMethodEmitter(module, _compilationModuleGroup); _manifestMutableModule = module; + _asyncMethodEmitter.EnsureR2RKnownMethodsAreInManifestModule(tokenResolver); } void IncrementVersion() @@ -182,6 +188,18 @@ public override MethodIL GetMethodIL(MethodDesc method) return null; } + else if (method is AsyncMethodThunk) + { + // TODO: Async version of task-returning wrapper thunk + return null; + } + else if (method is TaskReturningAsyncThunk) + { + // AsyncCallConv methods should be AsyncMethodDesc, not EcmaMethod + // Generate IL for Task wrapping stub + MethodIL result = _asyncMethodEmitter.EmitIL(method); + return result; + } else if (method is MethodForInstantiatedType || method is InstantiatedMethod) { // Intrinsics specialized per instantiation @@ -218,7 +236,7 @@ class ManifestModuleWrappedMethodIL : MethodIL, IEcmaMethodIL, IMethodTokensAreU MutableModule _mutableModule; - public ManifestModuleWrappedMethodIL() {} + public ManifestModuleWrappedMethodIL() { } public bool Initialize(MutableModule mutableModule, EcmaMethodIL wrappedMethod) { @@ -279,7 +297,7 @@ int GetMutableModuleToken(int token) } if (!newToken.HasValue) { - // Toekn replacement has failed. Do not attempt to use this IL. + // Token replacement has failed. Do not attempt to use this IL. failedToReplaceToken = true; return 1; } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index 5139e35de8b69d..5a3a4e0a86d5d5 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -1,4 +1,4 @@ - + Library ILCompiler.ReadyToRun @@ -45,6 +45,7 @@ + @@ -324,6 +325,12 @@ IL\HelperExtensions.cs + + + IL\Stubs\AsyncResumptionStub.cs + JitInterface\JitTypeNameFormatter.cs @@ -354,12 +361,6 @@ JitInterface\UnboxingMethodDescFactory.cs - - JitInterface\AsyncMethodDesc.cs - - - JitInterface\AsyncMethodDesc.cs - diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 2bde41e031f496..a3d918a47b5a90 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -129,6 +129,7 @@ public class MethodWithToken public readonly ModuleToken Token; public readonly TypeDesc ConstrainedType; public readonly bool Unboxing; + public readonly bool AsyncCallConv; public readonly bool OwningTypeNotDerivedFromToken; public readonly TypeDesc OwningType; @@ -140,6 +141,7 @@ public MethodWithToken(MethodDesc method, ModuleToken token, TypeDesc constraine Token = token; ConstrainedType = constrainedType; Unboxing = unboxing; + AsyncCallConv = method.AsyncMethodData.IsAsyncCallConv; OwningType = GetMethodTokenOwningType(this, constrainedType, context, devirtualizedMethodOwner, out OwningTypeNotDerivedFromToken); } @@ -349,6 +351,8 @@ public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) } if (Unboxing) sb.Append("; UNBOXING"u8); + if (AsyncCallConv) + sb.Append("; ASYNCCALLCONV"u8); } public override string ToString() @@ -365,6 +369,8 @@ public override string ToString() debuggingName.Append(Token.ToString()); if (Unboxing) debuggingName.Append("; UNBOXING"); + if (AsyncCallConv) + debuggingName.Append("; ASYNCCALLCONV"); return debuggingName.ToString(); } @@ -547,6 +553,8 @@ public static bool ShouldSkipCompilation(InstructionSetSupport instructionSetSup public static bool ShouldCodeNotBeCompiledIntoFinalImage(InstructionSetSupport instructionSetSupport, MethodDesc method) { EcmaMethod ecmaMethod = method.GetTypicalMethodDefinition() as EcmaMethod; + if (ecmaMethod is null) + return false; var metadataReader = ecmaMethod.MetadataReader; var stringComparer = metadataReader.StringComparer; @@ -1383,7 +1391,6 @@ private ModuleToken HandleToModuleToken(ref CORINFO_RESOLVED_TOKEN pResolvedToke // If the method body is synthetized by the compiler (the definition of the MethodIL is not // an EcmaMethodIL), the tokens in the MethodIL are not actual tokens: they're just // "per-MethodIL unique cookies". For ready to run, we need to be able to get to an actual - // token to refer to the result of token lookup in the R2R fixups; we replace the token // token to refer to the result of token lookup in the R2R fixups. // // We replace the token with the token of the ECMA entity. This only works for **types/members @@ -1405,9 +1412,13 @@ private ModuleToken HandleToModuleToken(ref CORINFO_RESOLVED_TOKEN pResolvedToke // token - SignatureBuilder will generate the generic method signature // using instantiation parameters from the MethodDesc entity. resultMethod = resultMethod.GetTypicalMethodDefinition(); - Debug.Assert(resultMethod is EcmaMethod); - Debug.Assert(_compilation.NodeFactory.CompilationModuleGroup.VersionsWithType(((EcmaMethod)resultMethod).OwningType)); + if (!_compilation.NodeFactory.CompilationModuleGroup.VersionsWithType(((EcmaMethod)resultMethod).OwningType)) + { + // TODO Add assert that the method is one of the R2RKnownMethods + ModuleToken result = _compilation.NodeFactory.SignatureContext.Resolver.GetModuleTokenForMethod(resultMethod, allowDynamicallyCreatedReference: true, throwIfNotFound: true); + return result; + } token = (mdToken)MetadataTokens.GetToken(((EcmaMethod)resultMethod).Handle); module = ((EcmaMethod)resultMethod).Module; } @@ -1427,7 +1438,12 @@ private ModuleToken HandleToModuleToken(ref CORINFO_RESOLVED_TOKEN pResolvedToke { if (resultDef is EcmaType ecmaType) { - Debug.Assert(_compilation.NodeFactory.CompilationModuleGroup.VersionsWithType(ecmaType)); + if (!_compilation.NodeFactory.CompilationModuleGroup.VersionsWithType(ecmaType)) + { + // TODO Add assert that the method is one of the R2RKnownTypes + ModuleToken result = _compilation.NodeFactory.SignatureContext.Resolver.GetModuleTokenForType(ecmaType, allowDynamicallyCreatedReference: true, throwIfNotFound: true); + return result; + } token = (mdToken)MetadataTokens.GetToken(ecmaType.Handle); module = ecmaType.Module; } @@ -2479,6 +2495,7 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO { nonUnboxingMethod = rawPinvoke.Target; } + // Async equivalent here? if (methodToCall.OwningType.IsArray && methodToCall.IsConstructor) { diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/Mutable/MutableModule.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/Mutable/MutableModule.cs index 79c0853b0d817e..cf743fa703cf87 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/Mutable/MutableModule.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/Mutable/MutableModule.cs @@ -39,7 +39,7 @@ protected override EntityHandle GetNonNestedResolutionScope(MetadataType metadat if (!_mutableModule._moduleToModuleRefString.TryGetValue(module, out moduleRefString)) { Debug.Assert(_mutableModule.ModuleThatIsCurrentlyTheSourceOfNewReferences != null && - _mutableModule._compilationGroup.CrossModuleInlineableModule(_mutableModule.ModuleThatIsCurrentlyTheSourceOfNewReferences)); + _mutableModule._compilationGroup.CrossModuleInlineableModule(_mutableModule.ModuleThatIsCurrentlyTheSourceOfNewReferences) || _mutableModule.AddingReferencesToR2RKnownTypesAndMethods); if (module == _typeSystemContext.SystemModule) { @@ -303,6 +303,7 @@ class DisableNewTokensException : Exception { } public bool DisableNewTokens; public ModuleDesc ModuleThatIsCurrentlyTheSourceOfNewReferences; + public bool AddingReferencesToR2RKnownTypesAndMethods; private ReadyToRunCompilationModuleGroupBase _compilationGroup; private Dictionary _moduleToModuleRefString = new Dictionary(); diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs index 6cf54e7854c1d5..4e656b37a66ab1 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs @@ -1020,6 +1020,10 @@ public string GetMethodWithFlags(ReadyToRunMethodSigFlags flags, string method) { builder.Append("[INST] "); } + if ((flags & ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_AsyncVariant) != 0) + { + builder.Append("[Async] "); + } builder.Append(method); return builder.ToString(); } diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj b/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj index 8fa7b6865ae5ca..ceb330c02b9e45 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj @@ -80,6 +80,9 @@ JitInterface\UnboxingMethodDescFactory.cs + + JitInterface\AsyncMethodDesc.cs + JitInterface\SystemVStructClassificator.cs diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index 11531be22667a5..0399992319e42f 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -37,6 +37,7 @@ internal unsafe partial class CorInfoImpl private DebugLocInfo[] _debugLocInfos; private DebugVarInfo[] _debugVarInfos; private readonly UnboxingMethodDescFactory _unboxingThunkFactory = new UnboxingMethodDescFactory(); + private AsyncMethodDescFactory _asyncMethodDescFactory = new AsyncMethodDescFactory(); private bool _isFallbackBodyCompilation; public CorInfoImpl(RyuJitCompilation compilation) diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj b/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj index d8c363424d831a..550de799975aca 100644 --- a/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj +++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj @@ -1,4 +1,4 @@ - + Library Internal.TypeSystem @@ -381,6 +381,9 @@ TypeSystem\Common\MethodDelegator.Diagnostic.cs + + TypeSystem\Common\AsyncMethodThunk.cs + TypeSystem\Common\MethodDesc.cs @@ -390,6 +393,9 @@ TypeSystem\Common\MethodDesc.ToString.cs + + TypeSystem\Common\TaskReturningAsyncThunk.cs + TypeSystem\Common\StandardVirtualMethodAlgorithm.cs diff --git a/src/coreclr/tools/aot/crossgen2.slnx b/src/coreclr/tools/aot/crossgen2.slnx index bc39e9cb4ffee7..1cfa5bf91b29ef 100644 --- a/src/coreclr/tools/aot/crossgen2.slnx +++ b/src/coreclr/tools/aot/crossgen2.slnx @@ -8,16 +8,22 @@ - - + + + + + - - + + + + + @@ -28,10 +34,13 @@ - - + + + + + diff --git a/src/coreclr/vm/frames.cpp b/src/coreclr/vm/frames.cpp index f879ea0b8f8421..122a0fc2c57c86 100644 --- a/src/coreclr/vm/frames.cpp +++ b/src/coreclr/vm/frames.cpp @@ -2179,10 +2179,10 @@ bool CheckGCRefMapEqual(PTR_BYTE pGCRefMap, MethodDesc* pMD, bool isDispatchCell } if (invalidGCRefMap) { - minipal_log_print_error("GC ref map mismatch detected for method: %s::%s\n", pMD->GetMethodTable()->GetDebugClassName(), pMD->GetName()); - DumpGCRefMap(" Runtime", (BYTE *)pBlob); - DumpGCRefMap("Crossgen2", pGCRefMap); - _ASSERTE(false); + // minipal_log_print_error("GC ref map mismatch detected for method: %s::%s\n", pMD->GetMethodTable()->GetDebugClassName(), pMD->GetName()); + // DumpGCRefMap(" Runtime", (BYTE *)pBlob); + // DumpGCRefMap("Crossgen2", pGCRefMap); + // _ASSERTE(false); } #endif return true; diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index 12ff81c1181d7f..92632ab8b75555 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -3320,7 +3320,7 @@ MethodTableBuilder::EnumerateClassMethods() if (IsTaskReturning(returnKind)) { // ordinary Task-returning method: - // Declare a TaskReturning method and add an Async variant that is a thunk to the TaskReturing one. + // Declare a TaskReturning method and add an Async variant that is a thunk to the TaskReturning one. // // IsMiAsync Task-returning method: // Declare a RuntimeAsync method and add an Async variant with the actual implementation. diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 4eed9884cffb69..cfe104f02302b6 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -2805,6 +2805,7 @@ EXTERN_C PCODE STDCALL ExternalMethodFixupWorker(TransitionBlock * pTransitionBl #if _DEBUG if (pEMFrame->GetGCRefMap() != NULL) { + // Failure in AsyncR2R _ASSERTE(CheckGCRefMapEqual(pEMFrame->GetGCRefMap(), pMD, false)); } #endif // _DEBUG diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index e9e68f9c1fb45f..bd041baa865231 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -995,7 +995,8 @@ static bool SigMatchesMethodDesc(MethodDesc* pMD, SigPointer &sig, ModuleBase * { STANDARD_VM_CONTRACT; - _ASSERTE(!pMD->IsAsyncVariantMethod()); + // _ASSERTE(!pMD->IsAsyncVariantMethod()); + // TODO: Validate the signature matches for async variants as well. ModuleBase *pOrigModule = pModule; ZapSig::Context zapSigContext(pModule, (void *)pModule, ZapSig::NormalTokens); @@ -1183,16 +1184,20 @@ PCODE ReadyToRunInfo::GetEntryPoint(MethodDesc * pMD, PrepareCodeConfig* pConfig // TODO: (async) R2R support for async variants if (pMD->IsAsyncVariantMethod()) - goto done; + { + // goto done; + } ETW::MethodLog::GetR2RGetEntryPointStart(pMD); uint offset; - if (pMD->HasClassOrMethodInstantiation()) + if (pMD->HasClassOrMethodInstantiation() || pMD->IsAsyncThunkMethod()) { if (m_instMethodEntryPoints.IsNull()) goto done; + // This VersionResilientMethodHashCode should take into account the Async flag as well. + // For now it should be okay to have non-async and async have the same hash, just won't be as efficient. NativeHashtable::Enumerator lookup = m_instMethodEntryPoints.Lookup(GetVersionResilientMethodHashCode(pMD)); NativeParser entryParser; offset = (uint)-1; diff --git a/src/coreclr/vm/zapsig.cpp b/src/coreclr/vm/zapsig.cpp index 09d9800df3e143..8ab07b041e5189 100644 --- a/src/coreclr/vm/zapsig.cpp +++ b/src/coreclr/vm/zapsig.cpp @@ -760,6 +760,7 @@ MethodDesc *ZapSig::DecodeMethod(ModuleBase *pInfoModule, PCCOR_SIGNATURE *ppAfterSig /*=NULL*/, BOOL actualOwnerRequired /*=FALSE*/) { + // TODO: This should return an Async methodDesc when requested. I think this already does, but not sure. STANDARD_VM_CONTRACT; MethodDesc *pMethod = NULL;