Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
6658d8b
Pass async jit flag to jit in aot tools
jtschuster Oct 15, 2025
1f917e9
Add IsRuntimeAsync override to instantiated MethodDescs
jtschuster Oct 15, 2025
431bfaa
Update src/coreclr/tools/Common/TypeSystem/Common/InstantiatedMethod.cs
jkotas Oct 15, 2025
3a4ffbc
Rename IsRuntimeAsync to IsAsync, add override in MethodDelegator
jtschuster Oct 16, 2025
1030acb
Add AsyncMethodDesc for AsyncCallConv methods
jtschuster Oct 17, 2025
4cc1f5f
Merge branch 'main' of https://github.com/dotnet/runtime into AsyncMe…
jtschuster Oct 17, 2025
7516cba
Remove comments from AsyncMethodDesc
jtschuster Oct 17, 2025
42e1157
Use MethodSignatureBuilder and assert for expected invariants
jtschuster Oct 17, 2025
429f910
Use throwing cast instead of 'as' cast
jtschuster Oct 17, 2025
4e0d53d
Fix assert
jtschuster Oct 17, 2025
c75c536
Add getter to Flags for |=
jtschuster Oct 17, 2025
e8ef453
Implement getAsyncInfo in ILCompiler
jtschuster Oct 17, 2025
5ff7bc6
Use Dictionary instead of ConcurrentDictionary for AsyncMethodDescFac…
jtschuster Oct 17, 2025
e5ea4ce
Refactor AsyncMethodDesc for clarity and efficiency
jtschuster Oct 17, 2025
0cf52dc
Update src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
jtschuster Oct 17, 2025
de10bb5
Update Signature and delete GetOtherAsyncMethod
jtschuster Oct 17, 2025
ff2712d
Remove IsAsync requirement from AsyncMethodDesc
jtschuster Oct 17, 2025
9749653
Formatting and remove unused accessor
jtschuster Oct 17, 2025
c2e3c48
Merge branch 'GetAsyncInfoR2R' into AsyncMethodDesc
jtschuster Oct 17, 2025
bada361
Delete old UnboxingMethodDescFactory, move AsyncMethodDescFactory to …
jtschuster Oct 20, 2025
2aace2c
Rename ReturnsTaskLike to IsTaskReturning
jtschuster Oct 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions src/coreclr/tools/Common/JitInterface/AsyncMethodDesc.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// 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
{
/// <summary>
/// Represents the async-callable (CORINFO_CALLCONV_ASYNCCALL) variant of a Task/ValueTask returning method.
/// Mirrors the structure and usage pattern of <see cref="UnboxingMethodDesc"/>.
Comment thread
jtschuster marked this conversation as resolved.
Outdated
/// The wrapper should be short‑lived and only used while interacting with the JIT interface.
/// </summary>
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 is not null);
Debug.Assert(wrappedMethod.IsAsync && wrappedMethod.ReturnsTaskLike());
Comment thread
jkotas marked this conversation as resolved.
Outdated
_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
{
var wrappedSignature = _wrappedMethod.Signature;
var ret = wrappedSignature.ReturnType;
var md = (MetadataType)ret;
Debug.Assert(md.Namespace.SequenceEqual("System.Threading.Tasks"u8));
ReadOnlySpan<byte> name = md.Name;
TypeDesc returnType = null;
if (name.SequenceEqual("Task"u8) || name.SequenceEqual("ValueTask"u8))
Comment thread
jtschuster marked this conversation as resolved.
Outdated
{
returnType = this.Context.GetWellKnownType(WellKnownType.Void);
}
else if (name.SequenceEqual("Task`1"u8) || name.SequenceEqual("ValueTask`1"u8))
{
Debug.Assert(md.HasInstantiation);
returnType = md.Instantiation[0];
}
else
{
throw new UnreachableException("AsyncMethodDesc should not wrap a non-Task-like-returning method");
}

var builder = new MethodSignatureBuilder(_wrappedMethod.Signature);
Comment thread
jtschuster marked this conversation as resolved.
Outdated
builder.ReturnType = returnType;
builder.Flags |= MethodSignatureFlags.AsyncCallConv;
Comment thread
jtschuster marked this conversation as resolved.
Outdated
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
{
/// <summary>
/// Returns the other async variant. If the supplied method is an async-callconv wrapper, returns the wrapped (Task-returning) method.
/// If it is a Task/ValueTask returning method, returns (and possibly creates) the async-callconv variant via the factory; otherwise null.
/// </summary>
public static MethodDesc GetOtherAsyncMethod(this MethodDesc method, AsyncMethodDescFactory factory)
Comment thread
jtschuster marked this conversation as resolved.
Outdated
{
if (method is null) return null;
Comment thread
jtschuster marked this conversation as resolved.
Outdated
if (method is AsyncMethodDesc amd)
return amd.Target; // unwrap

if (method.IsAsync && ReturnsTaskLike(method))
return factory.GetAsyncMethod(method);

return null;
}

public static bool ReturnsTaskLike(this MethodDesc method)
Comment thread
jtschuster marked this conversation as resolved.
Outdated
{
TypeDesc ret = method.GetTypicalMethodDefinition().Signature.ReturnType;
if (ret == null) return false;
Comment thread
jtschuster marked this conversation as resolved.
Outdated

if (ret is MetadataType md)
{
if (md.Namespace.SequenceEqual("System.Threading.Tasks"u8))
Comment thread
jtschuster marked this conversation as resolved.
Outdated
{
ReadOnlySpan<byte> 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;
}
}
}
13 changes: 13 additions & 0 deletions src/coreclr/tools/Common/TypeSystem/Common/MethodDesc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public enum MethodSignatureFlags

Static = 0x0010,
ExplicitThis = 0x0020,
AsyncCallConv = 0x0040,
}

public enum EmbeddedSignatureDataKind
Expand Down Expand Up @@ -138,6 +139,14 @@ public bool IsExplicitThis
}
}

public bool IsAsyncCallConv
{
get
{
return (_flags & MethodSignatureFlags.AsyncCallConv) != 0;
}
}

public int GenericParameterCount
{
get
Expand Down Expand Up @@ -386,6 +395,10 @@ public MethodSignatureBuilder(MethodSignature template)

public MethodSignatureFlags Flags
{
get
{
return _flags;
}
Comment thread
jtschuster marked this conversation as resolved.
Outdated
set
{
_flags = value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@
<Compile Include="Compiler\Logger.cs" />
<Compile Include="Compiler\ReadyToRunVisibilityRootProvider.cs" />
<Compile Include="Compiler\ReadyToRunProfilingRootProvider.cs" />
<Compile Include="JitInterface\AsyncMethodDescFactory.cs" />
<Compile Include="ObjectWriter\MapFileBuilder.cs" />
<Compile Include="CodeGen\ReadyToRunObjectWriter.cs" />
<Compile Include="Compiler\CompilationModuleGroup.ReadyToRun.cs" />
Expand Down Expand Up @@ -337,5 +338,8 @@
<Compile Include="..\..\Common\JitInterface\UnboxingMethodDesc.cs">
<Link>JitInterface\UnboxingMethodDesc.cs</Link>
</Compile>
<Compile Include="..\..\Common\JitInterface\AsyncMethodDesc.cs">
<Link>JitInterface\AsyncMethodDesc.cs</Link>
</Compile>
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// 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 AsyncMethodDescFactory : ConcurrentDictionary<MethodDesc, AsyncMethodDesc>
Comment thread
jtschuster marked this conversation as resolved.
Outdated
{
private Func<MethodDesc, AsyncMethodDesc> _factoryDelegate;
private AsyncMethodDesc CreateAsyncMethod(MethodDesc method)
{
return new AsyncMethodDesc(method, this);
}

public AsyncMethodDescFactory()
{
_factoryDelegate = CreateAsyncMethod;
}

public AsyncMethodDesc GetAsyncMethod(MethodDesc method)
{
return GetOrAdd(method, _factoryDelegate);
}
}
}
Loading