Skip to content

Commit 0769ce2

Browse files
authored
Long-lived AsyncMethodVariant in MethodWithGCInfo/MethodWithToken (#121357)
Incorporates feedback from #121218, creating a long-lived async MethodDesc. There will be more warts that need to be worked out once we actually compile these, in particular lots of `(EcmaMethod)methodDesc.GetMethodDefinition()`, which doesn't work with `AsyncMethodVariant`.
1 parent b524fc3 commit 0769ce2

File tree

17 files changed

+196
-152
lines changed

17 files changed

+196
-152
lines changed

src/coreclr/inc/corinfo.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -673,7 +673,8 @@ enum CorInfoCallConv
673673
CORINFO_CALLCONV_HASTHIS = 0x20,
674674
CORINFO_CALLCONV_EXPLICITTHIS=0x40,
675675
CORINFO_CALLCONV_PARAMTYPE = 0x80, // Passed last. Same as CORINFO_GENERICS_CTXT_FROM_PARAMTYPEARG
676-
CORINFO_CALLCONV_ASYNCCALL = 0x100, // Is this a call to an async function?
676+
CORINFO_CALLCONV_ASYNCCALL = 0x100, // Is this a call with async calling convention?
677+
677678
};
678679

679680
// Represents the calling conventions supported with the extensible calling convention syntax
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Diagnostics;
6+
using Internal.TypeSystem;
7+
using Internal.TypeSystem.Ecma;
8+
9+
namespace ILCompiler
10+
{
11+
/// <summary>
12+
/// MethodDesc that represents async calling convention entrypoint of a Task-returning method.
13+
/// </summary>
14+
public partial class AsyncMethodVariant : MethodDelegator
15+
{
16+
private MethodSignature _asyncSignature;
17+
18+
public AsyncMethodVariant(EcmaMethod wrappedMethod)
19+
: base(wrappedMethod)
20+
{
21+
Debug.Assert(wrappedMethod.Signature.ReturnsTaskOrValueTask());
22+
}
23+
24+
public EcmaMethod Target => (EcmaMethod)_wrappedMethod;
25+
26+
public override MethodSignature Signature
27+
{
28+
get
29+
{
30+
return _asyncSignature ?? InitializeSignature();
31+
}
32+
}
33+
34+
private MethodSignature InitializeSignature()
35+
{
36+
var signature = _wrappedMethod.Signature;
37+
Debug.Assert(!signature.IsAsyncCall);
38+
Debug.Assert(signature.ReturnsTaskOrValueTask());
39+
TypeDesc md = signature.ReturnType;
40+
MethodSignatureBuilder builder = new MethodSignatureBuilder(signature);
41+
builder.ReturnType = md.HasInstantiation ? md.Instantiation[0] : this.Context.GetWellKnownType(WellKnownType.Void);
42+
builder.Flags = signature.Flags | MethodSignatureFlags.AsyncCall;
43+
return (_asyncSignature = builder.ToSignature());
44+
}
45+
46+
public override MethodDesc GetCanonMethodTarget(CanonicalFormKind kind)
47+
{
48+
// We should not be calling GetCanonMethodTarget on generic definitions of anything
49+
// and this MethodDesc is a generic definition.
50+
Debug.Assert(!HasInstantiation && !OwningType.HasInstantiation);
51+
return this;
52+
}
53+
54+
public override MethodDesc GetMethodDefinition()
55+
{
56+
return this;
57+
}
58+
59+
public override MethodDesc GetTypicalMethodDefinition()
60+
{
61+
return this;
62+
}
63+
64+
public override MethodDesc InstantiateSignature(Instantiation typeInstantiation, Instantiation methodInstantiation)
65+
{
66+
throw new NotImplementedException();
67+
}
68+
69+
public override string ToString() => $"Async variant: " + _wrappedMethod.ToString();
70+
71+
protected override int ClassCode => unchecked((int)0xd0fd1c1fu);
72+
73+
protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer)
74+
{
75+
var asyncOther = (AsyncMethodVariant)other;
76+
return comparer.Compare(_wrappedMethod, asyncOther._wrappedMethod);
77+
}
78+
}
79+
80+
public static class AsyncMethodVariantExtensions
81+
{
82+
/// <summary>
83+
/// Returns true if this MethodDesc is an AsyncMethodVariant, which should not escape the jit interface.
84+
/// </summary>
85+
public static bool IsAsyncVariant(this MethodDesc method)
86+
{
87+
return method is AsyncMethodVariant;
88+
}
89+
}
90+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Internal.IL;
5+
using Internal.TypeSystem;
6+
using Internal.TypeSystem.Ecma;
7+
8+
using Debug = System.Diagnostics.Debug;
9+
10+
namespace ILCompiler
11+
{
12+
public partial class CompilerTypeSystemContext
13+
{
14+
public MethodDesc GetAsyncVariantMethod(MethodDesc taskReturningMethod)
15+
{
16+
Debug.Assert(taskReturningMethod.Signature.ReturnsTaskOrValueTask());
17+
MethodDesc asyncMetadataMethodDef = taskReturningMethod.GetTypicalMethodDefinition();
18+
MethodDesc result = _asyncVariantImplHashtable.GetOrCreateValue((EcmaMethod)asyncMetadataMethodDef);
19+
20+
if (asyncMetadataMethodDef != taskReturningMethod)
21+
{
22+
TypeDesc owningType = taskReturningMethod.OwningType;
23+
if (owningType.HasInstantiation)
24+
result = GetMethodForInstantiatedType(result, (InstantiatedType)owningType);
25+
26+
if (taskReturningMethod.HasInstantiation)
27+
result = GetInstantiatedMethod(result, taskReturningMethod.Instantiation);
28+
}
29+
30+
return result;
31+
}
32+
33+
private sealed class AsyncVariantImplHashtable : LockFreeReaderHashtable<EcmaMethod, AsyncMethodVariant>
34+
{
35+
protected override int GetKeyHashCode(EcmaMethod key) => key.GetHashCode();
36+
protected override int GetValueHashCode(AsyncMethodVariant value) => value.Target.GetHashCode();
37+
protected override bool CompareKeyToValue(EcmaMethod key, AsyncMethodVariant value) => key == value.Target;
38+
protected override bool CompareValueToValue(AsyncMethodVariant value1, AsyncMethodVariant value2)
39+
=> value1.Target == value2.Target;
40+
protected override AsyncMethodVariant CreateValueFromKey(EcmaMethod key) => new AsyncMethodVariant(key);
41+
}
42+
private AsyncVariantImplHashtable _asyncVariantImplHashtable = new AsyncVariantImplHashtable();
43+
}
44+
}

src/coreclr/tools/Common/Compiler/MethodExtensions.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System;
45
using ILCompiler.DependencyAnalysis;
5-
66
using Internal.TypeSystem;
77
using Internal.TypeSystem.Ecma;
88

@@ -135,5 +135,23 @@ public static bool NotCallableWithoutOwningEEType(this MethodDesc method)
135135
(owningType is not MetadataType mdType || !mdType.IsModuleType) && /* Compiler parks some instance methods on the <Module> type */
136136
!method.IsSharedByGenericInstantiations; /* Current impl limitation; can be lifted */
137137
}
138+
139+
public static bool ReturnsTaskOrValueTask(this MethodSignature method)
140+
{
141+
TypeDesc ret = method.ReturnType;
142+
143+
if (ret is MetadataType md
144+
&& md.Module == method.Context.SystemModule
145+
&& md.Namespace.SequenceEqual("System.Threading.Tasks"u8))
146+
{
147+
ReadOnlySpan<byte> name = md.Name;
148+
if (name.SequenceEqual("Task"u8) || name.SequenceEqual("Task`1"u8)
149+
|| name.SequenceEqual("ValueTask"u8) || name.SequenceEqual("ValueTask`1"u8))
150+
{
151+
return true;
152+
}
153+
}
154+
return false;
155+
}
138156
}
139157
}

src/coreclr/tools/Common/JitInterface/AsyncMethodDesc.cs

Lines changed: 0 additions & 109 deletions
This file was deleted.

src/coreclr/tools/Common/JitInterface/AsyncMethodDescFactory.cs

Lines changed: 0 additions & 24 deletions
This file was deleted.

src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -877,6 +877,7 @@ private void Get_CORINFO_SIG_INFO(MethodSignature signature, CORINFO_SIG_INFO* s
877877

878878
if (!signature.IsStatic) sig->callConv |= CorInfoCallConv.CORINFO_CALLCONV_HASTHIS;
879879
if (signature.IsExplicitThis) sig->callConv |= CorInfoCallConv.CORINFO_CALLCONV_EXPLICITTHIS;
880+
if (signature.IsAsyncCall) sig->callConv |= CorInfoCallConv.CORINFO_CALLCONV_ASYNCCALL;
880881

881882
TypeDesc returnType = signature.ReturnType;
882883

src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ public unsafe struct CORINFO_SIG_INFO
119119
private uint totalILArgs() { return (uint)(numArgs + (hasImplicitThis() ? 1 : 0)); }
120120
private bool isVarArg() { return ((getCallConv() == CorInfoCallConv.CORINFO_CALLCONV_VARARG) || (getCallConv() == CorInfoCallConv.CORINFO_CALLCONV_NATIVEVARARG)); }
121121
internal bool hasTypeArg() { return ((callConv & CorInfoCallConv.CORINFO_CALLCONV_PARAMTYPE) != 0); }
122+
private bool isAsyncCall() { return ((callConv & CorInfoCallConv.CORINFO_CALLCONV_ASYNCCALL) != 0); }
122123
};
123124

124125
//----------------------------------------------------------------------------
@@ -377,6 +378,7 @@ public enum CorInfoCallConv
377378
CORINFO_CALLCONV_HASTHIS = 0x20,
378379
CORINFO_CALLCONV_EXPLICITTHIS = 0x40,
379380
CORINFO_CALLCONV_PARAMTYPE = 0x80, // Passed last. Same as CORINFO_GENERICS_CTXT_FROM_PARAMTYPEARG
381+
CORINFO_CALLCONV_ASYNCCALL = 0x100, // Is this a call with async calling convention?
380382
}
381383

382384
// Represents the calling conventions supported with the extensible calling convention syntax

src/coreclr/tools/Common/TypeSystem/Common/MethodDesc.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ public enum MethodSignatureFlags
2323

2424
Static = 0x0010,
2525
ExplicitThis = 0x0020,
26-
AsyncCallConv = 0x0040,
26+
27+
AsyncCall = 0x0100,
2728
}
2829

2930
public enum EmbeddedSignatureDataKind
@@ -139,11 +140,11 @@ public bool IsExplicitThis
139140
}
140141
}
141142

142-
public bool IsAsyncCallConv
143+
public bool IsAsyncCall
143144
{
144145
get
145146
{
146-
return (_flags & MethodSignatureFlags.AsyncCallConv) != 0;
147+
return (_flags & MethodSignatureFlags.AsyncCall) != 0;
147148
}
148149
}
149150

src/coreclr/tools/Common/TypeSystem/Ecma/EcmaMethod.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public sealed partial class EcmaMethod : MethodDesc, EcmaModule.IEntityHandleObj
1515
{
1616
private static class MethodFlags
1717
{
18+
#pragma warning disable IDE0055 // Disable formatting to keep aligned
1819
public const int BasicMetadataCache = 0x00001;
1920
public const int Virtual = 0x00002;
2021
public const int NewSlot = 0x00004;
@@ -28,11 +29,12 @@ private static class MethodFlags
2829
public const int AggressiveOptimization = 0x00400;
2930
public const int NoOptimization = 0x00800;
3031
public const int RequireSecObject = 0x01000;
32+
public const int Async = 0x02000;
3133

32-
public const int AttributeMetadataCache = 0x02000;
33-
public const int Intrinsic = 0x04000;
34-
public const int UnmanagedCallersOnly = 0x08000;
35-
public const int Async = 0x10000;
34+
public const int AttributeMetadataCache = 0x04000;
35+
public const int Intrinsic = 0x08000;
36+
public const int UnmanagedCallersOnly = 0x10000;
37+
#pragma warning restore IDE0055
3638
};
3739

3840
private EcmaType _type;

0 commit comments

Comments
 (0)