Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public enum ReadyToRunImportSectionFlags : ushort
/// Constants for method and field encoding
/// </summary>
[Flags]
public enum ReadyToRunMethodSigFlags : byte
public enum ReadyToRunMethodSigFlags : ushort
{
READYTORUN_METHOD_SIG_None = 0x00,
READYTORUN_METHOD_SIG_UnboxingStub = 0x01,
Expand All @@ -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_AsyncThunkVariant = 0x100,
}

[Flags]
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,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;

Expand Down Expand Up @@ -1395,7 +1396,7 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info)

if (info->pResolvedTokenVirtualMethod != null)
{
methodWithTokenDecl = ComputeMethodWithToken(decl, ref *info->pResolvedTokenVirtualMethod, null, false);
methodWithTokenDecl = ComputeMethodWithToken(decl, ref *info->pResolvedTokenVirtualMethod, null, false, asyncVariant: false);
}
else
{
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 isAsyncCallConv() { return ((callConv & CorInfoCallConv.CORINFO_CALLCONV_ASYNCCALL) != 0); }
};

//----------------------------------------------------------------------------
Expand Down Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions src/coreclr/tools/Common/JitInterface/UnboxingMethodDesc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ public override MethodDesc InstantiateSignature(Instantiation typeInstantiation,
return this;
}

public override AsyncMethodKind AsyncMethodKind
{
get
{
return _wrappedMethod.AsyncMethodKind;
}
}

public override string ToString()
{
return "Unboxing MethodDesc: " + _wrappedMethod.ToString();
Expand Down
124 changes: 124 additions & 0 deletions src/coreclr/tools/Common/TypeSystem/Common/AsyncMethodVariant.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// 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.Ecma;

namespace Internal.TypeSystem
{
/// <summary>
/// Either the AsyncMethodImplVariant or AsyncMethodThunkVariant of a method marked .IsAsync.
/// </summary>
public partial class AsyncMethodVariant : MethodDelegator
{
private readonly int _jitVisibleHashCode;
private MethodSignature _asyncSignature;

public AsyncMethodVariant(MethodDesc wrappedMethod)
: base(wrappedMethod)
{
Debug.Assert(wrappedMethod.Signature.ReturnsTaskOrValueTask());
_jitVisibleHashCode = HashCode.Combine(wrappedMethod.GetHashCode(), 0x310bb74f);
}

public MethodDesc Target => _wrappedMethod;

public override AsyncMethodKind AsyncMethodKind => _wrappedMethod.IsAsync ? AsyncMethodKind.AsyncVariantImpl : AsyncMethodKind.AsyncVariantThunk;

public override MethodSignature Signature
{
get
{
return _asyncSignature ??= CreateAsyncSignature(_wrappedMethod.Signature);
}
}

private MethodSignature CreateAsyncSignature(MethodSignature signature)
{
Debug.Assert(!signature.IsAsyncCallConv);
Debug.Assert(signature.ReturnsTaskOrValueTask());
TypeDesc md = signature.ReturnType;
MethodSignatureBuilder builder = new MethodSignatureBuilder(signature);
builder.ReturnType = md.HasInstantiation ? md.Instantiation[0] : this.Context.GetWellKnownType(WellKnownType.Void);
builder.Flags = signature.Flags | MethodSignatureFlags.AsyncCallingConvention;
return builder.ToSignature();
}

public override MethodDesc GetCanonMethodTarget(CanonicalFormKind kind)
{
return this;
}

public override MethodDesc GetMethodDefinition()
{
var real = _wrappedMethod.GetMethodDefinition();
if (real == _wrappedMethod)
return this;

return _wrappedMethod.Context.GetAsyncVariant(real);
}

public override MethodDesc GetTypicalMethodDefinition()
{
var real = _wrappedMethod.GetTypicalMethodDefinition();
if (real == _wrappedMethod)
return this;

return _wrappedMethod.Context.GetAsyncVariant(real);
}

public override MethodDesc InstantiateSignature(Instantiation typeInstantiation, Instantiation methodInstantiation)
{
var real = _wrappedMethod.InstantiateSignature(typeInstantiation, methodInstantiation);
if (real == _wrappedMethod)
return this;

return _wrappedMethod.Context.GetAsyncVariant(real);
}

public override string ToString() => $"Async variant ({AsyncMethodKind}): " + _wrappedMethod.ToString();

protected internal override int ClassCode => unchecked((int)0xd0fd1c1fu);

protected internal override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer)
{
var asyncOther = (AsyncMethodVariant)other;
return comparer.Compare(this._wrappedMethod, asyncOther._wrappedMethod);
}

protected override int ComputeHashCode()
{
return _jitVisibleHashCode;
}
}

public sealed class AsyncMethodVariantFactory : ConcurrentDictionary<MethodDesc, AsyncMethodVariant>
{
public AsyncMethodVariant GetOrCreateAsyncMethodImplVariant(MethodDesc wrappedMethod)
{
return GetOrAdd(wrappedMethod, static (x) => new AsyncMethodVariant(x));
}
}

public static class AsyncMethodVariantExtensions
{
/// <summary>
/// Returns true if this MethodDesc is an AsyncMethodVariant, which should not escape the jit interface.
/// </summary>
public static bool IsAsyncVariant(this MethodDesc method)
{
return method is AsyncMethodVariant;
}

/// <summary>
/// Gets the wrapped method of the AsyncMethodVariant. This method is Task-returning.
/// </summary>
public static MethodDesc GetAsyncVariantDefinition(this MethodDesc method)
{
return ((AsyncMethodVariant)method).Target;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Threading;

namespace Internal.TypeSystem
{
public sealed partial class InstantiatedMethod
{
public override AsyncMethodKind AsyncMethodKind
{
get
{
return _methodDef.AsyncMethodKind;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;

namespace Internal.TypeSystem
{
/// <summary>
/// Wraps a <see cref="MethodDesc"/> object and delegates methods to that <see cref="MethodDesc"/>.
/// </summary>
public abstract partial class MethodDelegator : MethodDesc
{
public abstract override AsyncMethodKind AsyncMethodKind { get; }
}
}
114 changes: 114 additions & 0 deletions src/coreclr/tools/Common/TypeSystem/Common/MethodDesc.Async.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System;

namespace Internal.TypeSystem
{
public sealed partial class MethodSignature
{
public bool ReturnsTaskOrValueTask()
{
TypeDesc ret = this.ReturnType;

if (ret is MetadataType md
&& md.Module == this.Context.SystemModule
&& md.Namespace.SequenceEqual("System.Threading.Tasks"u8))
{
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;
}
}

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 thunk. (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 variants 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<int> 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,
}

public abstract partial class MethodDesc : TypeSystemEntity
{
public virtual AsyncMethodKind AsyncMethodKind
{
get
{
return AsyncMethodKind.NotAsync;
}
}

/// <summary>
/// Is this synthetic Task/async adapter to an async/Task implementation?
/// If yes, the method has another variant, which has the actual user-defined method body.
/// </summary>
public bool IsAsyncThunk
{
get
{
return AsyncMethodKind is
AsyncMethodKind.AsyncVariantThunk or
AsyncMethodKind.RuntimeAsync;
}
}
}
}
4 changes: 2 additions & 2 deletions src/coreclr/tools/Common/TypeSystem/Common/MethodDesc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ public enum MethodSignatureFlags
UnmanagedCallingConventionThisCall = 0x0003,
CallingConventionVarargs = 0x0005,
UnmanagedCallingConvention = 0x0009,
AsyncCallingConvention = 0x0100,

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

public enum EmbeddedSignatureDataKind
Expand Down Expand Up @@ -143,7 +143,7 @@ public bool IsAsyncCallConv
{
get
{
return (_flags & MethodSignatureFlags.AsyncCallConv) != 0;
return (_flags & MethodSignatureFlags.AsyncCallingConvention) != 0;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// 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.Threading;
using Debug = System.Diagnostics.Debug;

namespace Internal.TypeSystem
{
public sealed partial class MethodForInstantiatedType
{
public override AsyncMethodKind AsyncMethodKind
{
get
{
return _typicalMethodDef.AsyncMethodKind;
}
}
}
}
Loading
Loading