diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs index 600a6722f0645d..5ccc9a8d2f9dd6 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs @@ -12,6 +12,10 @@ using System.Threading; using System.Threading.Tasks; +#if NATIVEAOT +using Internal.Runtime; +#endif + namespace System.Runtime.CompilerServices { internal struct ExecutionAndSyncBlockStore @@ -160,11 +164,16 @@ private struct RuntimeAsyncAwaitState private static unsafe Continuation AllocContinuation(Continuation prevContinuation, MethodTable* contMT) { +#if NATIVEAOT + Continuation newContinuation = (Continuation)RuntimeImports.RhNewObject(contMT); +#else Continuation newContinuation = (Continuation)RuntimeTypeHandle.InternalAllocNoChecks(contMT); +#endif prevContinuation.Next = newContinuation; return newContinuation; } +#if !NATIVEAOT private static unsafe Continuation AllocContinuationMethod(Continuation prevContinuation, MethodTable* contMT, int keepAliveOffset, MethodDesc* method) { LoaderAllocator loaderAllocator = RuntimeMethodHandle.GetLoaderAllocator(new RuntimeMethodHandleInternal((IntPtr)method)); @@ -186,6 +195,7 @@ private static unsafe Continuation AllocContinuationClass(Continuation prevConti } return newContinuation; } +#endif [BypassReadyToRun] [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async)] diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 2f46b37643aaf0..01e53975c4819b 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -55,6 +55,10 @@ ExceptionStringID.cs + + + + diff --git a/src/coreclr/tools/Common/Compiler/AsyncContinuationType.cs b/src/coreclr/tools/Common/Compiler/AsyncContinuationType.cs new file mode 100644 index 00000000000000..7c65f73d8cbea2 --- /dev/null +++ b/src/coreclr/tools/Common/Compiler/AsyncContinuationType.cs @@ -0,0 +1,85 @@ +// 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 System.Text; + +using Internal.TypeSystem; + +namespace ILCompiler +{ + /// + /// An async continuation type. The code generator will request this to store local state + /// through an async suspension/resumption. We only identify these using a + /// since that's all the code generator cares about - size of the type, and where the GC pointers are. + /// + public sealed partial class AsyncContinuationType : MetadataType + { + private readonly MetadataType _continuationBaseType; + public GCPointerMap PointerMap { get; } + + public override DefType[] ExplicitlyImplementedInterfaces => []; + public override ReadOnlySpan Name => Encoding.UTF8.GetBytes(DiagnosticName); + public override ReadOnlySpan Namespace => []; + + // We don't lay these out using MetadataType metadata. + // Autolayout (which we'd get due to GC pointers) would likely not match what codegen expects. + public override bool IsExplicitLayout => throw new NotImplementedException(); + public override bool IsSequentialLayout => throw new NotImplementedException(); + public override bool IsExtendedLayout => throw new NotImplementedException(); + public override bool IsAutoLayout => throw new NotImplementedException(); + public override ClassLayoutMetadata GetClassLayout() => throw new NotImplementedException(); + + public override bool IsBeforeFieldInit => false; + public override ModuleDesc Module => _continuationBaseType.Module; + public override MetadataType BaseType => _continuationBaseType; + public override bool IsSealed => true; + public override bool IsAbstract => false; + public override MetadataType ContainingType => null; + public override PInvokeStringFormat PInvokeStringFormat => default; + public override string DiagnosticName => $"ContinuationType_{PointerMap}"; + public override string DiagnosticNamespace => ""; + protected override int ClassCode => 0x528741a; + public override TypeSystemContext Context => _continuationBaseType.Context; + + public AsyncContinuationType(MetadataType continuationBaseType, GCPointerMap pointerMap) + => (_continuationBaseType, PointerMap) = (continuationBaseType, pointerMap); + + public override bool HasCustomAttribute(string attributeNamespace, string attributeName) => false; + public override IEnumerable GetNestedTypes() => []; + public override MetadataType GetNestedType(string name) => null; + protected override MethodImplRecord[] ComputeVirtualMethodImplsForType() => []; + public override MethodImplRecord[] FindMethodsImplWithMatchingDeclName(ReadOnlySpan name) => []; + + protected override int CompareToImpl(TypeDesc other, TypeSystemComparer comparer) + { + Debug.Assert(_continuationBaseType == ((AsyncContinuationType)other)._continuationBaseType); + GCPointerMap otherPointerMap = ((AsyncContinuationType)other).PointerMap; + return PointerMap.CompareTo(otherPointerMap); + } + + public override int GetHashCode() => PointerMap.GetHashCode(); + + protected override TypeFlags ComputeTypeFlags(TypeFlags mask) + { + TypeFlags flags = 0; + + if ((mask & TypeFlags.HasGenericVarianceComputed) != 0) + { + flags |= TypeFlags.HasGenericVarianceComputed; + } + + if ((mask & TypeFlags.CategoryMask) != 0) + { + flags |= TypeFlags.Class; + } + + flags |= TypeFlags.HasFinalizerComputed; + flags |= TypeFlags.AttributeCacheComputed; + + return flags; + } + } +} diff --git a/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Async.cs b/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Async.cs index ba594e7f474ea7..0087ef7373a819 100644 --- a/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Async.cs +++ b/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Async.cs @@ -15,7 +15,7 @@ public MethodDesc GetAsyncVariantMethod(MethodDesc taskReturningMethod) { Debug.Assert(taskReturningMethod.Signature.ReturnsTaskOrValueTask()); MethodDesc asyncMetadataMethodDef = taskReturningMethod.GetTypicalMethodDefinition(); - MethodDesc result = _asyncVariantImplHashtable.GetOrCreateValue((EcmaMethod)asyncMetadataMethodDef); + MethodDesc result = _asyncVariantHashtable.GetOrCreateValue((EcmaMethod)asyncMetadataMethodDef); if (asyncMetadataMethodDef != taskReturningMethod) { @@ -30,7 +30,7 @@ public MethodDesc GetAsyncVariantMethod(MethodDesc taskReturningMethod) return result; } - private sealed class AsyncVariantImplHashtable : LockFreeReaderHashtable + private sealed class AsyncVariantHashtable : LockFreeReaderHashtable { protected override int GetKeyHashCode(EcmaMethod key) => key.GetHashCode(); protected override int GetValueHashCode(AsyncMethodVariant value) => value.Target.GetHashCode(); @@ -39,6 +39,32 @@ protected override bool CompareValueToValue(AsyncMethodVariant value1, AsyncMeth => value1.Target == value2.Target; protected override AsyncMethodVariant CreateValueFromKey(EcmaMethod key) => new AsyncMethodVariant(key); } - private AsyncVariantImplHashtable _asyncVariantImplHashtable = new AsyncVariantImplHashtable(); + private AsyncVariantHashtable _asyncVariantHashtable = new AsyncVariantHashtable(); + + public MetadataType GetContinuationType(GCPointerMap pointerMap) + { + return _continuationTypeHashtable.GetOrCreateValue(pointerMap); + } + + private sealed class ContinuationTypeHashtable : LockFreeReaderHashtable + { + private readonly CompilerTypeSystemContext _parent; + private MetadataType _continuationType; + + public ContinuationTypeHashtable(CompilerTypeSystemContext parent) + => _parent = parent; + + protected override int GetKeyHashCode(GCPointerMap key) => key.GetHashCode(); + protected override int GetValueHashCode(AsyncContinuationType value) => value.PointerMap.GetHashCode(); + protected override bool CompareKeyToValue(GCPointerMap key, AsyncContinuationType value) => key.Equals(value.PointerMap); + protected override bool CompareValueToValue(AsyncContinuationType value1, AsyncContinuationType value2) + => value1.PointerMap.Equals(value2.PointerMap); + protected override AsyncContinuationType CreateValueFromKey(GCPointerMap key) + { + _continuationType ??= _parent.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "Continuation"u8); + return new AsyncContinuationType(_continuationType, key); + } + } + private ContinuationTypeHashtable _continuationTypeHashtable; } } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 3bc5a00ce08d49..a3ca8706f9d1fe 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -685,6 +685,7 @@ private void CompileMethodCleanup() #if !READYTORUN _debugInfo = null; + _asyncResumptionStub = null; #endif _debugLocInfos = null; @@ -3401,7 +3402,19 @@ private void getAsyncInfo(ref CORINFO_ASYNC_INFO pAsyncInfoOut) private CORINFO_CLASS_STRUCT_* getContinuationType(nuint dataSize, ref bool objRefs, nuint objRefsSize) { Debug.Assert(objRefsSize == (dataSize + (nuint)(PointerSize - 1)) / (nuint)PointerSize); +#if READYTORUN throw new NotImplementedException("getContinuationType"); +#else + GCPointerMapBuilder gcMapBuilder = new GCPointerMapBuilder((int)dataSize, PointerSize); + ReadOnlySpan bools = MemoryMarshal.CreateReadOnlySpan(ref objRefs, (int)objRefsSize); + for (int i = 0; i < bools.Length; i++) + { + if (bools[i]) + gcMapBuilder.MarkGCPointer(i * PointerSize); + } + + return ObjectToHandle(_compilation.TypeSystemContext.GetContinuationType(gcMapBuilder.ToGCMap())); +#endif } private mdToken getMethodDefFromMethod(CORINFO_METHOD_STRUCT_* hMethod) @@ -3756,7 +3769,14 @@ private bool getTailCallHelpers(ref CORINFO_RESOLVED_TOKEN callToken, CORINFO_SI private CORINFO_METHOD_STRUCT_* getAsyncResumptionStub(ref void* entryPoint) #pragma warning restore CA1822 // Mark members as static { +#if READYTORUN throw new NotImplementedException("Crossgen2 does not support runtime-async yet"); +#else + _asyncResumptionStub ??= new AsyncResumptionStub(MethodBeingCompiled); + + entryPoint = (void*)ObjectToHandle(_compilation.NodeFactory.MethodEntrypoint(_asyncResumptionStub)); + return ObjectToHandle(_asyncResumptionStub); +#endif } private byte[] _code; diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncResumptionStub.Mangling.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncResumptionStub.Mangling.cs new file mode 100644 index 00000000000000..898a483159ae21 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncResumptionStub.Mangling.cs @@ -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 Internal.IL.Stubs; +using Internal.TypeSystem; + +namespace ILCompiler +{ + public partial class AsyncResumptionStub : ILStubMethod, IPrefixMangledMethod + { + MethodDesc IPrefixMangledMethod.BaseMethod => _owningMethod; + + string IPrefixMangledMethod.Prefix => "Resume"; + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncResumptionStub.Sorting.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncResumptionStub.Sorting.cs new file mode 100644 index 00000000000000..2043c2f8922535 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncResumptionStub.Sorting.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.IL.Stubs; +using Internal.TypeSystem; + +namespace ILCompiler +{ + public partial class AsyncResumptionStub : ILStubMethod + { + protected override int ClassCode => 0x773ab1; + + protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + return comparer.Compare(_owningMethod, ((AsyncResumptionStub)other)._owningMethod); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncResumptionStub.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncResumptionStub.cs new file mode 100644 index 00000000000000..29504bb6834fcf --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncResumptionStub.cs @@ -0,0 +1,53 @@ +// 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 Internal.IL; +using Internal.IL.Stubs; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler +{ + public partial class AsyncResumptionStub : ILStubMethod + { + private readonly MethodDesc _owningMethod; + private MethodSignature _signature; + + public AsyncResumptionStub(MethodDesc owningMethod) + { + Debug.Assert(owningMethod.IsAsyncVariant() + || (owningMethod.IsAsync && !owningMethod.Signature.ReturnsTaskOrValueTask())); + _owningMethod = owningMethod; + } + + public override ReadOnlySpan Name => _owningMethod.Name; + public override string DiagnosticName => _owningMethod.DiagnosticName; + + public override TypeDesc OwningType => _owningMethod.OwningType; + + public override MethodSignature Signature => _signature ??= InitializeSignature(); + + public override TypeSystemContext Context => _owningMethod.Context; + + private MethodSignature InitializeSignature() + { + TypeDesc objectType = Context.GetWellKnownType(WellKnownType.Object); + TypeDesc byrefByte = Context.GetWellKnownType(WellKnownType.Byte).MakeByRefType(); + return _signature = new MethodSignature(0, 0, objectType, [objectType, byrefByte]); + } + + public override MethodIL EmitIL() + { + var emitter = new ILEmitter(); + ILCodeStream codeStream = emitter.NewCodeStream(); + + // TODO: match getAsyncResumptionStub from CoreCLR VM + codeStream.EmitCallThrowHelper(emitter, Context.GetHelperEntryPoint("ThrowHelpers"u8, "ThrowNotSupportedException"u8)); + + return emitter.Link(this); + } + } +} 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 ce21b8e0b8a981..199c6b89782d06 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.Aot.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.Aot.cs @@ -59,6 +59,7 @@ public CompilerTypeSystemContext(TargetDetails details, SharedGenericsMode gener _typeWithRepeatedFieldsFieldLayoutAlgorithm = new TypeWithRepeatedFieldsFieldLayoutAlgorithm(_metadataFieldLayoutAlgorithm); _delegateInfoHashtable = new DelegateInfoHashtable(delegateFeatures); + _continuationTypeHashtable = new ContinuationTypeHashtable(this); _genericCycleDetector = new LazyGenericsSupport.GenericCycleDetector(genericCycleDepthCutoff, genericCycleBreadthCutoff); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AsyncContinuationEETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AsyncContinuationEETypeNode.cs new file mode 100644 index 00000000000000..5f1bc894adcdc4 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AsyncContinuationEETypeNode.cs @@ -0,0 +1,75 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.Text; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Specialized MethodTable that describes async continuation types (). + /// + public class AsyncContinuationEETypeNode : DataOnlyEETypeNode, IEETypeNode + { + public TypeDesc Type { get; } + + public AsyncContinuationEETypeNode(AsyncContinuationType type) + : base("Continuation", BuildPointerMap(type), type.BaseType, false) + { + Type = type; + } + + private static GCPointerMap BuildPointerMap(AsyncContinuationType type) + { + // The GC pointer map in the continuation type doesn't include base type. Build a GC pointer map that + // includes the base type. + MetadataType continuationBaseType = type.BaseType; + + GCPointerMap baseMap = GCPointerMap.FromInstanceLayout(continuationBaseType); + GCPointerMap dataMap = type.PointerMap; + + int baseSize = baseMap.Size; + int dataSize = dataMap.Size; + + int pointerSize = type.Context.Target.PointerSize; + + Debug.Assert(continuationBaseType.InstanceByteCountUnaligned.AsInt == baseSize * pointerSize); + +#if DEBUG + // Validate we match the expected DataOffset value. + // DataOffset is a const int32 field in the Continuation class. + EcmaField dataOffsetField = (EcmaField)continuationBaseType.GetField("DataOffset"u8); + Debug.Assert(dataOffsetField.IsLiteral); + + var reader = dataOffsetField.MetadataReader; + var constant = reader.GetConstant(reader.GetFieldDefinition(dataOffsetField.Handle).GetDefaultValue()); + Debug.Assert(constant.TypeCode == System.Reflection.Metadata.ConstantTypeCode.Int32); + int expectedDataOffset = reader.GetBlobReader(constant.Value).ReadInt32() + + pointerSize /* + MethodTable field */; + Debug.Assert(baseSize * pointerSize == expectedDataOffset); +#endif + + GCPointerMapBuilder builder = new GCPointerMapBuilder((baseSize + dataSize) * pointerSize, pointerSize); + for (int i = 0; i < baseSize + dataSize; i++) + { + bool isGCPointer = i < baseSize ? baseMap[i] : dataMap[i - baseSize]; + + if (isGCPointer) + builder.MarkGCPointer(i * pointerSize); + } + + return builder.ToGCMap(); + } + + public override int ClassCode => 9928091; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + AsyncContinuationEETypeNode otherTypeDataEETypeNode = (AsyncContinuationEETypeNode)other; + return comparer.Compare(otherTypeDataEETypeNode.Type, Type); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GCStaticEETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataOnlyEETypeNode.cs similarity index 63% rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GCStaticEETypeNode.cs rename to src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataOnlyEETypeNode.cs index 041904b3a984b8..c82b592fac0f98 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GCStaticEETypeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataOnlyEETypeNode.cs @@ -12,20 +12,23 @@ namespace ILCompiler.DependencyAnalysis { /// - /// Represents a subset of that is used to describe GC static field regions for - /// types. It only fills out enough pieces of the MethodTable structure so that the GC can operate on it. Runtime should - /// never see these. + /// MethodTable for a piece of data that can be allocated on the GC heap, with GC-reported references in it. + /// The shape of the data is described using a . The generated data structure + /// is a MethodTable that is valid for use with GC, however not all data on it is present. + /// A lot of the MethodTable API surface on this will not work at runtime . /// - public class GCStaticEETypeNode : DehydratableObjectNode, ISymbolDefinitionNode + public class DataOnlyEETypeNode : DehydratableObjectNode, ISymbolDefinitionNode { - private GCPointerMap _gcMap; - private TargetDetails _target; - private bool _requiresAlign8; + private readonly string _prefix; + protected readonly GCPointerMap _gcMap; + private readonly TypeDesc _baseType; + protected readonly bool _requiresAlign8; - public GCStaticEETypeNode(TargetDetails target, GCPointerMap gcMap, bool requiresAlign8) + public DataOnlyEETypeNode(string prefix, GCPointerMap gcMap, TypeDesc baseType, bool requiresAlign8) { + _prefix = prefix; _gcMap = gcMap; - _target = target; + _baseType = baseType; _requiresAlign8 = requiresAlign8; } @@ -33,7 +36,7 @@ public GCStaticEETypeNode(TargetDetails target, GCPointerMap gcMap, bool require protected override ObjectNodeSection GetDehydratedSection(NodeFactory factory) { - if (_target.IsWindows) + if (factory.Target.IsWindows) return ObjectNodeSection.ReadOnlyDataSection; else return ObjectNodeSection.DataSection; @@ -43,7 +46,7 @@ protected override ObjectNodeSection GetDehydratedSection(NodeFactory factory) public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { - sb.Append("__GCStaticEEType_"u8).Append(_gcMap.ToString()); + sb.Append($"__{_prefix}_").Append(_gcMap.ToString()); if (_requiresAlign8) { sb.Append("_align8"u8); @@ -55,7 +58,7 @@ int ISymbolDefinitionNode.Offset get { int numSeries = _gcMap.NumSeries; - return numSeries > 0 ? ((numSeries * 2) + 1) * _target.PointerSize : 0; + return numSeries > 0 ? ((numSeries * 2) + 1) * _baseType.Context.Target.PointerSize : 0; } } @@ -69,12 +72,8 @@ protected override ObjectData GetDehydratableData(NodeFactory factory, bool relo dataBuilder.RequireInitialPointerAlignment(); dataBuilder.AddSymbol(this); - // +1 for SyncBlock (static size already includes MethodTable) - int totalSize = (_gcMap.Size + 1) * _target.PointerSize; - - // We only need to check for containsPointers because ThreadStatics are always allocated - // on the GC heap (no matter what "HasGCStaticBase" says). - // If that ever changes, we can assume "true" and switch this to an assert. + // +1 for SyncBlock (GCMap size already includes MethodTable field) + int totalSize = (_gcMap.Size + 1) * factory.Target.PointerSize; bool containsPointers = _gcMap.NumSeries > 0; if (containsPointers) @@ -98,12 +97,10 @@ protected override ObjectData GetDehydratableData(NodeFactory factory, bool relo dataBuilder.EmitUInt(flags); - totalSize = Math.Max(totalSize, _target.PointerSize * 3); // minimum GC MethodTable size is 3 pointers + totalSize = Math.Max(totalSize, factory.Target.PointerSize * 3); // minimum GC MethodTable size is 3 pointers dataBuilder.EmitInt(totalSize); - // Related type: System.Object. This allows storing an instance of this type in an array of objects, - // or finding associated module from BulkType event source events. - dataBuilder.EmitPointerReloc(factory.NecessaryTypeSymbol(factory.TypeSystemContext.GetWellKnownType(WellKnownType.Object))); + dataBuilder.EmitPointerReloc(factory.NecessaryTypeSymbol(_baseType)); return dataBuilder.ToObjectData(); } @@ -112,11 +109,11 @@ protected override ObjectData GetDehydratableData(NodeFactory factory, bool relo public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) { - GCStaticEETypeNode otherGCStaticEETypeNode = (GCStaticEETypeNode)other; - int mapCompare = _gcMap.CompareTo(otherGCStaticEETypeNode._gcMap); + DataOnlyEETypeNode otherNode = (DataOnlyEETypeNode)other; + int mapCompare = _gcMap.CompareTo(otherNode._gcMap); if (mapCompare == 0) { - return _requiresAlign8.CompareTo(otherGCStaticEETypeNode._requiresAlign8); + return _requiresAlign8.CompareTo(otherNode._requiresAlign8); } return mapCompare; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs index 8cea4b6dee9aa9..64a1741a6d0e88 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -250,9 +250,17 @@ private void CreateNodeCaches() return new TypeThreadStaticIndexNode(type, null); }); - _GCStaticEETypes = new NodeCache<(GCPointerMap, bool), GCStaticEETypeNode>(((GCPointerMap gcMap, bool requiresAlign8) key) => + _GCStaticEETypes = new NodeCache<(GCPointerMap, bool), DataOnlyEETypeNode>(((GCPointerMap gcMap, bool requiresAlign8) key) => { - return new GCStaticEETypeNode(Target, key.gcMap, key.requiresAlign8); + // Base type: System.Object. This allows storing an instance of this type in an array of objects, + // or finding associated module from BulkType event source events. + DefType baseType = _context.GetWellKnownType(WellKnownType.Object); + return new DataOnlyEETypeNode("GCStaticEEType", key.gcMap, baseType, key.requiresAlign8); + }); + + _asyncContinuationEETypes = new NodeCache((AsyncContinuationType key) => + { + return new AsyncContinuationEETypeNode(key); }); _readOnlyDataBlobs = new NodeCache(key => @@ -713,6 +721,7 @@ public NecessaryTypeSymbolHashtable(NodeFactory factory) : base(factory) { } protected override IEETypeNode CreateValueFromKey(TypeDesc key) => _factory.CreateNecessaryTypeNode(key); } + private NodeCache _asyncContinuationEETypes; private NecessaryTypeSymbolHashtable _typeSymbols; public IEETypeNode NecessaryTypeSymbol(TypeDesc type) @@ -727,6 +736,11 @@ public IEETypeNode NecessaryTypeSymbol(TypeDesc type) return ConstructedTypeSymbol(type); } + if (type is AsyncContinuationType continuation) + { + return _asyncContinuationEETypes.GetOrAdd(continuation); + } + Debug.Assert(!TypeCannotHaveEEType(type)); return _typeSymbols.GetOrCreateValue(type); @@ -752,6 +766,9 @@ public IEETypeNode MetadataTypeSymbol(TypeDesc type) return ConstructedTypeSymbol(type); } + // Generating typeof of a continuation type is not supported; we'd need a full MethodTable. + Debug.Assert(type is not AsyncContinuationType); + Debug.Assert(!TypeCannotHaveEEType(type)); return _metadataTypeSymbols.GetOrCreateValue(type); @@ -772,6 +789,11 @@ public IEETypeNode ConstructedTypeSymbol(TypeDesc type) return ImportedEETypeSymbol(type); } + if (type is AsyncContinuationType continuation) + { + return _asyncContinuationEETypes.GetOrAdd(continuation); + } + Debug.Assert(!TypeCannotHaveEEType(type)); return _constructedTypeSymbols.GetOrCreateValue(type); @@ -893,7 +915,7 @@ public EmbeddedTrimmingDescriptorNode EmbeddedTrimmingDescriptor(EcmaModule modu return _embeddedTrimmingDescriptors.GetOrAdd(module); } - private NodeCache<(GCPointerMap, bool), GCStaticEETypeNode> _GCStaticEETypes; + private NodeCache<(GCPointerMap, bool), DataOnlyEETypeNode> _GCStaticEETypes; public ISymbolNode GCStaticEEType(GCPointerMap gcMap, bool requiredAlign8) { diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 5cd4a3f196d577..0b6f858708e99d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -33,6 +33,7 @@ + @@ -45,6 +46,9 @@ IL\DelegateInfo.cs + + + IL\Stubs\DelegateThunks.Sorting.cs @@ -377,6 +381,7 @@ + @@ -586,7 +591,7 @@ - + diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index f145cbeae61297..d326121cb517ee 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -55,6 +55,7 @@ + diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index 11531be22667a5..ab05d31d840f50 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -33,6 +33,7 @@ internal unsafe partial class CorInfoImpl private RyuJitCompilation _compilation; private MethodDebugInformation _debugInfo; + private MethodDesc _asyncResumptionStub; private MethodCodeNode _methodCodeNode; private DebugLocInfo[] _debugLocInfos; private DebugVarInfo[] _debugVarInfos; @@ -758,6 +759,9 @@ private ISymbolNode GetHelperFtnUncached(CorInfoHelpFunc ftnNum) id = ReadyToRunHelper.MonitorExit; break; + case CorInfoHelpFunc.CORINFO_HELP_ALLOC_CONTINUATION: + return _compilation.NodeFactory.MethodEntrypoint(_compilation.NodeFactory.TypeSystemContext.GetCoreLibEntryPoint("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8, "AllocContinuation"u8, null)); + case CorInfoHelpFunc.CORINFO_HELP_GETSYNCFROMCLASSHANDLE: return _compilation.NodeFactory.MethodEntrypoint(_compilation.NodeFactory.TypeSystemContext.GetCoreLibEntryPoint("System.Threading"u8, "Monitor"u8, "GetSyncObjectFromClassHandle"u8, null)); case CorInfoHelpFunc.CORINFO_HELP_GETCLASSFROMMETHODPARAM: diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.cs index ffd7b11516f138..167aca683121e2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.cs @@ -13,7 +13,7 @@ namespace System.Runtime.CompilerServices [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public static partial class AsyncHelpers { -#if CORECLR +#if CORECLR || NATIVEAOT // "BypassReadyToRun" is until AOT/R2R typesystem has support for MethodImpl.Async // Must be NoInlining because we use AsyncSuspend to manufacture an explicit suspension point. // It will not capture/restore any local state that is live across it. @@ -220,35 +220,10 @@ public static T Await(ConfiguredValueTaskAwaitable configuredAwaitable) public static void UnsafeAwaitAwaiter(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion { throw new NotImplementedException(); } [RequiresPreviewFeatures] public static void AwaitAwaiter(TAwaiter awaiter) where TAwaiter : INotifyCompletion { throw new NotImplementedException(); } -#if NATIVEAOT - [RequiresPreviewFeatures] - public static void Await(System.Threading.Tasks.Task task) - { - TaskAwaiter awaiter = task.GetAwaiter(); - if (!awaiter.IsCompleted) - { - throw new NotImplementedException(); - } - - awaiter.GetResult(); - } - [RequiresPreviewFeatures] - public static T Await(System.Threading.Tasks.Task task) - { - TaskAwaiter awaiter = task.GetAwaiter(); - if (!awaiter.IsCompleted) - { - throw new NotImplementedException(); - } - - return awaiter.GetResult(); - } -#else [RequiresPreviewFeatures] public static void Await(System.Threading.Tasks.Task task) { throw new NotImplementedException(); } [RequiresPreviewFeatures] public static T Await(System.Threading.Tasks.Task task) { throw new NotImplementedException(); } -#endif [RequiresPreviewFeatures] public static void Await(System.Threading.Tasks.ValueTask task) { throw new NotImplementedException(); } [RequiresPreviewFeatures]