diff --git a/src/coreclr/tools/Common/Compiler/AsyncMethodVariant.cs b/src/coreclr/tools/Common/Compiler/AsyncMethodVariant.cs index a54b931195f3bf..a367004d9db7f7 100644 --- a/src/coreclr/tools/Common/Compiler/AsyncMethodVariant.cs +++ b/src/coreclr/tools/Common/Compiler/AsyncMethodVariant.cs @@ -43,9 +43,6 @@ private MethodSignature InitializeSignature() public override MethodDesc GetCanonMethodTarget(CanonicalFormKind kind) { - // We should not be calling GetCanonMethodTarget on generic definitions of anything - // and this MethodDesc is a generic definition. - Debug.Assert(!HasInstantiation && !OwningType.HasInstantiation); return this; } @@ -77,12 +74,9 @@ protected override int CompareToImpl(MethodDesc other, TypeSystemComparer compar public static class AsyncMethodVariantExtensions { - /// - /// Returns true if this MethodDesc is an AsyncMethodVariant, which should not escape the jit interface. - /// public static bool IsAsyncVariant(this MethodDesc method) { - return method is AsyncMethodVariant; + return method.GetTypicalMethodDefinition() is AsyncMethodVariant; } } } diff --git a/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Async.cs b/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Async.cs index 0087ef7373a819..50b0d413df833e 100644 --- a/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Async.cs +++ b/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Async.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; + using Internal.IL; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; @@ -11,6 +13,141 @@ namespace ILCompiler { public partial class CompilerTypeSystemContext { + private sealed class AsyncAwareVirtualMethodResolutionAlgorithm : MetadataVirtualMethodAlgorithm + { + private readonly CompilerTypeSystemContext _context; + + public AsyncAwareVirtualMethodResolutionAlgorithm(CompilerTypeSystemContext context) + => _context = context; + + private MethodDesc DecomposeAsyncVariant(MethodDesc method, out bool isAsyncVariant) + { + isAsyncVariant = method.IsAsyncVariant(); + return isAsyncVariant ? _context.GetTargetOfAsyncVariantMethod(method) : method; + } + + public override MethodDesc FindVirtualFunctionTargetMethodOnObjectType(MethodDesc targetMethod, TypeDesc objectType) + { + targetMethod = DecomposeAsyncVariant(targetMethod, out bool isAsyncSlot); + MethodDesc result = base.FindVirtualFunctionTargetMethodOnObjectType(targetMethod, objectType); + if (result != null && isAsyncSlot) + result = _context.GetAsyncVariantMethod(result); + + return result; + } + + public override DefaultInterfaceMethodResolution ResolveInterfaceMethodToDefaultImplementationOnType(MethodDesc interfaceMethod, TypeDesc currentType, out MethodDesc impl) + { + interfaceMethod = DecomposeAsyncVariant(interfaceMethod, out bool isAsyncSlot); + DefaultInterfaceMethodResolution result = base.ResolveInterfaceMethodToDefaultImplementationOnType(interfaceMethod, currentType, out impl); + if (impl != null && isAsyncSlot) + impl = _context.GetAsyncVariantMethod(impl); + + return result; + } + + public override MethodDesc ResolveInterfaceMethodToStaticVirtualMethodOnType(MethodDesc interfaceMethod, TypeDesc currentType) + { + interfaceMethod = DecomposeAsyncVariant(interfaceMethod, out bool isAsyncSlot); + MethodDesc result = base.ResolveInterfaceMethodToStaticVirtualMethodOnType(interfaceMethod, currentType); + if (result != null && isAsyncSlot) + result = _context.GetAsyncVariantMethod(result); + + return result; + } + public override MethodDesc ResolveInterfaceMethodToVirtualMethodOnType(MethodDesc interfaceMethod, TypeDesc currentType) + { + interfaceMethod = DecomposeAsyncVariant(interfaceMethod, out bool isAsyncSlot); + MethodDesc result = base.ResolveInterfaceMethodToVirtualMethodOnType(interfaceMethod, currentType); + if (result != null && isAsyncSlot) + result = _context.GetAsyncVariantMethod(result); + + return result; + } + public override DefaultInterfaceMethodResolution ResolveVariantInterfaceMethodToDefaultImplementationOnType(MethodDesc interfaceMethod, TypeDesc currentType, out MethodDesc impl) + { + interfaceMethod = DecomposeAsyncVariant(interfaceMethod, out bool isAsyncSlot); + DefaultInterfaceMethodResolution result = base.ResolveVariantInterfaceMethodToDefaultImplementationOnType(interfaceMethod, currentType, out impl); + if (impl != null && isAsyncSlot) + impl = _context.GetAsyncVariantMethod(impl); + + return result; + } + public override MethodDesc ResolveVariantInterfaceMethodToStaticVirtualMethodOnType(MethodDesc interfaceMethod, TypeDesc currentType) + { + interfaceMethod = DecomposeAsyncVariant(interfaceMethod, out bool isAsyncSlot); + MethodDesc result = base.ResolveVariantInterfaceMethodToStaticVirtualMethodOnType(interfaceMethod, currentType); + if (result != null && isAsyncSlot) + result = _context.GetAsyncVariantMethod(result); + + return result; + } + public override MethodDesc ResolveVariantInterfaceMethodToVirtualMethodOnType(MethodDesc interfaceMethod, TypeDesc currentType) + { + interfaceMethod = DecomposeAsyncVariant(interfaceMethod, out bool isAsyncSlot); + MethodDesc result = base.ResolveVariantInterfaceMethodToVirtualMethodOnType(interfaceMethod, currentType); + if (result != null && isAsyncSlot) + result = _context.GetAsyncVariantMethod(result); + + return result; + } + + public override IEnumerable ComputeAllVirtualSlots(TypeDesc type) + { + foreach (MethodDesc method in base.ComputeAllVirtualSlots(type)) + { + yield return method; + + // We create an async variant slot for any Task-returning method, not just runtime-async. + // This is not a problem in practice because the slot is still subject to dependency + // analysis and if not used, will not be generated. + // + // The reason why we need it is this: + // + // interface IFoo + // { + // [RuntimeAsyncMethodGeneration(true)] + // Task Method(); + // } + // + // class Base + // { + // [RuntimeAsyncMethodGeneration(false)] + // public virtual Task Method(); + // } + // + // class Derived : Base, IFoo + // { + // // Q: The runtime-async implementation for IFoo.Method + // // comes from Base. However Base was not runtime-async and we + // // didn't know about IFoo in Base either. Who has the slot? + // // A: Base has the runtime-async slot, despite the method not being runtime-async. + // } + if (method.GetTypicalMethodDefinition().Signature.ReturnsTaskOrValueTask()) + yield return _context.GetAsyncVariantMethod(method); + } + } + } + + public MethodDesc GetTargetOfAsyncVariantMethod(MethodDesc asyncVariantMethod) + { + var asyncMethodVariantDefinition = (AsyncMethodVariant)asyncVariantMethod.GetTypicalMethodDefinition(); + MethodDesc result = asyncMethodVariantDefinition.Target; + + // If there are generics involved, we need to specialize + if (asyncVariantMethod != asyncMethodVariantDefinition) + { + TypeDesc owningType = asyncVariantMethod.OwningType; + if (owningType != asyncMethodVariantDefinition.OwningType) + result = GetMethodForInstantiatedType(result, (InstantiatedType)owningType); + + if (asyncVariantMethod.HasInstantiation && !asyncVariantMethod.IsMethodDefinition) + result = GetInstantiatedMethod(result, asyncVariantMethod.Instantiation); + } + + return result; + } + public MethodDesc GetAsyncVariantMethod(MethodDesc taskReturningMethod) { Debug.Assert(taskReturningMethod.Signature.ReturnsTaskOrValueTask()); diff --git a/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.cs b/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.cs index 8ecd0fb034d223..db8ad465dc2fe3 100644 --- a/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.cs +++ b/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.cs @@ -17,7 +17,6 @@ namespace ILCompiler public partial class CompilerTypeSystemContext : MetadataTypeSystemContext, IMetadataStringDecoderProvider { private readonly MetadataRuntimeInterfacesAlgorithm _metadataRuntimeInterfacesAlgorithm = new MetadataRuntimeInterfacesAlgorithm(); - private readonly MetadataVirtualMethodAlgorithm _virtualMethodAlgorithm = new MetadataVirtualMethodAlgorithm(); private MetadataStringDecoder _metadataStringDecoder; diff --git a/src/coreclr/tools/Common/TypeSystem/IL/NativeAotILProvider.cs b/src/coreclr/tools/Common/TypeSystem/IL/NativeAotILProvider.cs index 8446e56e85bfe2..71fc3503b4e0b0 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/NativeAotILProvider.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/NativeAotILProvider.cs @@ -378,8 +378,7 @@ public override MethodIL GetMethodIL(MethodDesc method) } else { - // TODO: Emit thunk with async calling convention - throw new NotImplementedException(); + return AsyncThunkILEmitter.EmitAsyncMethodThunk(asyncVariantImpl, asyncVariantImpl.Target); } } else diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs index 0f048327cfdc4d..5f0610dc11dda6 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs @@ -36,5 +36,19 @@ public static MethodIL EmitTaskReturningThunk(MethodDesc taskReturningMethod, Me return emitter.Link(taskReturningMethod); } + + public static MethodIL EmitAsyncMethodThunk(MethodDesc asyncMethod, MethodDesc taskReturningMethod) + { + TypeSystemContext context = asyncMethod.Context; + + var emitter = new ILEmitter(); + var codestream = emitter.NewCodeStream(); + + // TODO: match EmitAsyncMethodThunk in CoreCLR VM + + codestream.EmitCallThrowHelper(emitter, context.GetHelperEntryPoint("ThrowHelpers"u8, "ThrowNotSupportedException"u8)); + + return emitter.Link(asyncMethod); + } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.Aot.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.Aot.cs index 199c6b89782d06..c003fadc4ed952 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.Aot.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.Aot.cs @@ -39,6 +39,8 @@ public SharedGenericsConfiguration GenericsConfig private readonly Int128FieldLayoutAlgorithm _int128FieldLayoutAlgorithm; private readonly TypeWithRepeatedFieldsFieldLayoutAlgorithm _typeWithRepeatedFieldsFieldLayoutAlgorithm; + private readonly AsyncAwareVirtualMethodResolutionAlgorithm _virtualMethodAlgorithm; + private TypeDesc[] _arrayOfTInterfaces; private TypeDesc[] _arrayEnumeratorOfTInterfaces; private ArrayOfTRuntimeInterfacesAlgorithm _arrayOfTRuntimeInterfacesAlgorithm; @@ -53,6 +55,8 @@ public CompilerTypeSystemContext(TargetDetails details, SharedGenericsMode gener { _genericsMode = genericsMode; + _virtualMethodAlgorithm = new AsyncAwareVirtualMethodResolutionAlgorithm(this); + _vectorOfTFieldLayoutAlgorithm = new VectorOfTFieldLayoutAlgorithm(_metadataFieldLayoutAlgorithm); _vectorFieldLayoutAlgorithm = new VectorFieldLayoutAlgorithm(_metadataFieldLayoutAlgorithm); _int128FieldLayoutAlgorithm = new Int128FieldLayoutAlgorithm(_metadataFieldLayoutAlgorithm); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs index 090b25a9b157d8..1189d599d2c9e5 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs @@ -11,6 +11,8 @@ namespace ILCompiler { partial class CompilerTypeSystemContext { + private readonly MetadataVirtualMethodAlgorithm _virtualMethodAlgorithm = new MetadataVirtualMethodAlgorithm(); + public CompilerTypeSystemContext(TargetDetails details, SharedGenericsMode genericsMode) : base(details) {