diff --git a/src/coreclr/tools/Common/JitInterface/AsyncMethodDesc.cs b/src/coreclr/tools/Common/JitInterface/AsyncMethodDesc.cs new file mode 100644 index 00000000000000..b6584b5588eafb --- /dev/null +++ b/src/coreclr/tools/Common/JitInterface/AsyncMethodDesc.cs @@ -0,0 +1,109 @@ +// 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.Diagnostics; +using Internal.TypeSystem; + +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 + { + private readonly AsyncMethodDescFactory _factory; + private readonly int _jitVisibleHashCode; + + public MethodDesc Target => _wrappedMethod; + + public AsyncMethodDesc(MethodDesc wrappedMethod, AsyncMethodDescFactory factory) + : base(wrappedMethod) + { + Debug.Assert(wrappedMethod.IsTaskReturning()); + _factory = factory; + // Salt with arbitrary constant so hash space differs from underlying method. + _jitVisibleHashCode = HashCode.Combine(wrappedMethod.GetHashCode(), 0x51C0A54); + } + + public override MethodDesc GetCanonMethodTarget(CanonicalFormKind kind) + { + MethodDesc realCanonTarget = _wrappedMethod.GetCanonMethodTarget(kind); + if (realCanonTarget != _wrappedMethod) + return _factory.GetAsyncMethod(realCanonTarget); + return this; + } + + public override MethodDesc GetMethodDefinition() + { + MethodDesc real = _wrappedMethod.GetMethodDefinition(); + if (real != _wrappedMethod) + return _factory.GetAsyncMethod(real); + return this; + } + + public override MethodDesc GetTypicalMethodDefinition() + { + MethodDesc real = _wrappedMethod.GetTypicalMethodDefinition(); + if (real != _wrappedMethod) + return _factory.GetAsyncMethod(real); + return this; + } + + public override MethodDesc InstantiateSignature(Instantiation typeInstantiation, Instantiation methodInstantiation) + { + MethodDesc real = _wrappedMethod.InstantiateSignature(typeInstantiation, methodInstantiation); + if (real != _wrappedMethod) + return _factory.GetAsyncMethod(real); + return this; + } + + public override MethodSignature Signature + { + get + { + MethodSignature wrappedSignature = _wrappedMethod.Signature; + MetadataType md = (MetadataType)wrappedSignature.ReturnType; + MethodSignatureBuilder builder = new MethodSignatureBuilder(wrappedSignature); + builder.ReturnType = md.HasInstantiation ? md.Instantiation[0] : this.Context.GetWellKnownType(WellKnownType.Void); + builder.Flags = wrappedSignature.Flags | MethodSignatureFlags.AsyncCallConv; + return builder.ToSignature(); + } + } + +#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 + } + + internal static class AsyncMethodDescExtensions + { + /// + /// Returns true if the method returns Task, Task<T>, ValueTask, or ValueTask<T>, otherwise false. + /// + public static bool IsTaskReturning(this MethodDesc method) + { + TypeDesc ret = method.GetTypicalMethodDefinition().Signature.ReturnType; + + if (ret is MetadataType md + && md.Module == method.Context.SystemModule + && md.Namespace.SequenceEqual("System.Threading.Tasks"u8)) + { + 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; + } + } +} diff --git a/src/coreclr/tools/Common/JitInterface/AsyncMethodDescFactory.cs b/src/coreclr/tools/Common/JitInterface/AsyncMethodDescFactory.cs new file mode 100644 index 00000000000000..5494807192e078 --- /dev/null +++ b/src/coreclr/tools/Common/JitInterface/AsyncMethodDescFactory.cs @@ -0,0 +1,24 @@ +// 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 ab43b57640a4a5..3df47ba887b672 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3358,7 +3358,33 @@ private void getEEInfo(ref CORINFO_EE_INFO pEEInfoOut) private void getAsyncInfo(ref CORINFO_ASYNC_INFO pAsyncInfoOut) { - throw new NotImplementedException(); + DefType continuation = MethodBeingCompiled.Context.SystemModule.GetType("System.Runtime.CompilerServices"u8, "Continuation"u8); + pAsyncInfoOut.continuationClsHnd = ObjectToHandle(continuation); + // 'Next' field + pAsyncInfoOut.continuationNextFldHnd = ObjectToHandle(continuation.GetField("Next"u8)); + // 'Resume' field + pAsyncInfoOut.continuationResumeFldHnd = ObjectToHandle(continuation.GetField("Resume"u8)); + // 'State' field + pAsyncInfoOut.continuationStateFldHnd = ObjectToHandle(continuation.GetField("State"u8)); + // 'Flags' field + pAsyncInfoOut.continuationFlagsFldHnd = ObjectToHandle(continuation.GetField("Flags"u8)); + // 'Data' field + pAsyncInfoOut.continuationDataFldHnd = ObjectToHandle(continuation.GetField("Data"u8)); + // 'GCData' field + pAsyncInfoOut.continuationGCDataFldHnd = ObjectToHandle(continuation.GetField("GCData"u8)); + // Whether or not the continuation needs to be allocated through the + // helper that also takes a method handle + pAsyncInfoOut.continuationsNeedMethodHandle = false; + DefType asyncHelpers = MethodBeingCompiled.Context.SystemModule.GetType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8); + DefType executionContext = MethodBeingCompiled.Context.SystemModule.GetType("System.Threading"u8, "ExecutionContext"u8); + DefType @void = MethodBeingCompiled.Context.GetWellKnownType(WellKnownType.Void); + // Method handle for AsyncHelpers.CaptureExecutionContext + pAsyncInfoOut.captureExecutionContextMethHnd = ObjectToHandle(asyncHelpers.GetMethod("CaptureExecutionContext"u8, null)); + // Method handle for AsyncHelpers.RestoreExecutionContext + pAsyncInfoOut.restoreExecutionContextMethHnd = ObjectToHandle(asyncHelpers.GetMethod("RestoreExecutionContext"u8, null)); + pAsyncInfoOut.captureContinuationContextMethHnd = ObjectToHandle(asyncHelpers.GetMethod("CaptureContinuationContext"u8, null)); + pAsyncInfoOut.captureContextsMethHnd = ObjectToHandle(asyncHelpers.GetMethod("CaptureContexts"u8, null)); + pAsyncInfoOut.restoreContextsMethHnd = ObjectToHandle(asyncHelpers.GetMethod("RestoreContexts"u8, null)); } private mdToken getMethodDefFromMethod(CORINFO_METHOD_STRUCT_* hMethod) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index f2688c585b0e52..d8dd7e9685e460 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -878,10 +878,26 @@ public unsafe struct CORINFO_ASYNC_INFO public CORINFO_CLASS_STRUCT_* continuationClsHnd; // 'Next' field public CORINFO_FIELD_STRUCT_* continuationNextFldHnd; + // 'Resume' field + public CORINFO_FIELD_STRUCT_* continuationResumeFldHnd; + // 'State' field + public CORINFO_FIELD_STRUCT_* continuationStateFldHnd; + // 'Flags' field + public CORINFO_FIELD_STRUCT_* continuationFlagsFldHnd; // 'Data' field public CORINFO_FIELD_STRUCT_* continuationDataFldHnd; // 'GCData' field public CORINFO_FIELD_STRUCT_* continuationGCDataFldHnd; + // Whether or not the continuation needs to be allocated through the + // helper that also takes a method handle + public bool continuationsNeedMethodHandle; // byte? + // Method handle for AsyncHelpers.CaptureExecutionContext + public CORINFO_METHOD_STRUCT_* captureExecutionContextMethHnd; + // Method handle for AsyncHelpers.RestoreExecutionContext + public CORINFO_METHOD_STRUCT_* restoreExecutionContextMethHnd; + public CORINFO_METHOD_STRUCT_* captureContinuationContextMethHnd; + public CORINFO_METHOD_STRUCT_* captureContextsMethHnd; + public CORINFO_METHOD_STRUCT_* restoreContextsMethHnd; } // Flags passed from JIT to runtime. diff --git a/src/coreclr/tools/Common/TypeSystem/Common/MethodDesc.cs b/src/coreclr/tools/Common/TypeSystem/Common/MethodDesc.cs index 96d4c0b163507e..3f53e780d435e5 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/MethodDesc.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/MethodDesc.cs @@ -23,6 +23,7 @@ public enum MethodSignatureFlags Static = 0x0010, ExplicitThis = 0x0020, + AsyncCallConv = 0x0040, } public enum EmbeddedSignatureDataKind @@ -138,6 +139,14 @@ public bool IsExplicitThis } } + public bool IsAsyncCallConv + { + get + { + return (_flags & MethodSignatureFlags.AsyncCallConv) != 0; + } + } + public int GenericParameterCount { get diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index 3251ff2660ebe3..5b141cfdd059bf 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -284,7 +284,6 @@ - @@ -337,5 +336,14 @@ JitInterface\UnboxingMethodDesc.cs + + JitInterface\UnboxingMethodDescFactory.cs + + + JitInterface\AsyncMethodDesc.cs + + + JitInterface\AsyncMethodDesc.cs + diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/UnboxingMethodDescFactory.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/UnboxingMethodDescFactory.cs deleted file mode 100644 index 42f4a648fbaaef..00000000000000 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/UnboxingMethodDescFactory.cs +++ /dev/null @@ -1,30 +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.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using Internal.TypeSystem; - -namespace Internal.JitInterface -{ - internal class UnboxingMethodDescFactory : ConcurrentDictionary - { - private Func _factoryDelegate; - private UnboxingMethodDesc CreateUnboxingMethod(MethodDesc method) - { - return new UnboxingMethodDesc(method, this); - } - - public UnboxingMethodDescFactory() - { - _factoryDelegate = CreateUnboxingMethod; - } - - public UnboxingMethodDesc GetUnboxingMethod(MethodDesc method) - { - return GetOrAdd(method, _factoryDelegate); - } - } -}