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);
- }
- }
-}