From db314fe98c40e054451490e93467f07fbc27cc42 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 5 Feb 2026 10:22:01 -0800 Subject: [PATCH 01/22] Passing async tests --- src/coreclr/inc/corcompile.h | 1 + src/coreclr/inc/readytorun.h | 6 + src/coreclr/inc/readytorunhelpers.h | 5 + src/coreclr/jit/utils.cpp | 7 + src/coreclr/tools/.editorconfig | 2 +- .../Common/Compiler/AsyncMethodVariant.cs | 2 +- .../CompilerTypeSystemContext.Async.cs | 4 +- .../Internal/Runtime/ReadyToRunConstants.cs | 7 + .../tools/Common/JitInterface/CorInfoImpl.cs | 41 +- .../TypeSystem/IL/InstantiatedMethodIL.cs | 1 - .../IL/Stubs/AsyncResumptionStub.cs | 10 +- .../ILCompiler.Compiler/Compiler/JitHelper.cs | 10 + .../ExceptionInfoLookupTableNode.cs | 4 + .../ReadyToRun/InliningInfoNode.cs | 13 +- .../ReadyToRun/InstanceEntryPointTableNode.cs | 12 +- .../ReadyToRun/MethodFixupSignature.cs | 3 +- .../ReadyToRun/ModuleTokenResolver.cs | 40 +- .../ReadyToRun/PrecodeHelperImport.cs | 29 ++ .../ReadyToRun/SignatureBuilder.cs | 10 + .../ReadyToRun/TypeFixupSignature.cs | 64 ++- .../ReadyToRunCodegenNodeFactory.cs | 14 + .../ReadyToRunSymbolNodeFactory.cs | 17 +- .../Compiler/ReadyToRunCodegenCompilation.cs | 91 +++- .../ReadyToRunCompilationModuleGroupBase.cs | 82 +-- .../Compiler/ReadyToRunCompilerContext.cs | 1 + .../Compiler/ReadyToRunTableManager.cs | 4 +- .../IL/ReadyToRunILProvider.cs | 121 ++++- .../ILCompiler.ReadyToRun.csproj | 1 + .../JitInterface/CorInfoImpl.ReadyToRun.cs | 465 ++++++++++-------- .../PgoInfoKey.cs | 12 +- .../ReadyToRunMethod.cs | 11 + .../ReadyToRunReader.cs | 41 +- .../ReadyToRunSignature.cs | 65 ++- src/coreclr/vm/asynccontinuations.cpp | 54 +- src/coreclr/vm/asynccontinuations.h | 4 +- src/coreclr/vm/ilstubcache.cpp | 96 ++++ src/coreclr/vm/ilstubcache.h | 9 + src/coreclr/vm/jitinterface.cpp | 110 ++++- src/coreclr/vm/method.hpp | 11 +- src/coreclr/vm/methodtable.cpp | 27 +- src/coreclr/vm/readytoruninfo.cpp | 224 ++++++++- src/coreclr/vm/readytoruninfo.h | 4 + src/coreclr/vm/stackwalk.cpp | 6 +- src/coreclr/vm/zapsig.cpp | 1 + .../src/System/Reflection/CorElementType.cs | 1 + .../awaitingnotasync/awaitingnotasync.cs | 1 - .../profiler/native/rejitprofiler/sigparse.h | 107 ++-- 47 files changed, 1403 insertions(+), 448 deletions(-) diff --git a/src/coreclr/inc/corcompile.h b/src/coreclr/inc/corcompile.h index 16b688eaa57e2e..8e8c78c70b4fcf 100644 --- a/src/coreclr/inc/corcompile.h +++ b/src/coreclr/inc/corcompile.h @@ -110,6 +110,7 @@ enum EncodeMethodSigFlags ENCODE_METHOD_SIG_OwnerType = 0x40, ENCODE_METHOD_SIG_UpdateContext = 0x80, ENCODE_METHOD_SIG_AsyncVariant = 0x100, + ENCODE_METHOD_SIG_ResumptionStub = 0x200, }; enum EncodeFieldSigFlags diff --git a/src/coreclr/inc/readytorun.h b/src/coreclr/inc/readytorun.h index 8599bbdd55a2f4..6fb4987a06ef2a 100644 --- a/src/coreclr/inc/readytorun.h +++ b/src/coreclr/inc/readytorun.h @@ -296,6 +296,7 @@ enum ReadyToRunFixupKind READYTORUN_FIXUP_Check_IL_Body = 0x35, /* Check to see if an IL method is defined the same at runtime as at compile time. A failed match will cause code not to be used. */ READYTORUN_FIXUP_Verify_IL_Body = 0x36, /* Verify an IL body is defined the same at compile time and runtime. A failed match will cause a hard runtime failure. */ + READYTORUN_FIXUP_Continuation_Layout = 0x37, /* Layout of an async method continuation type */ READYTORUN_FIXUP_ModuleOverride = 0x80, /* followed by sig-encoded UInt with assemblyref index into either the assemblyref table of the MSIL metadata of the master context module for the signature or */ /* into the extra assemblyref table in the manifest metadata R2R header table (used in cases inlining brings in references to assemblies not seen in the MSIL). */ @@ -466,6 +467,11 @@ enum ReadyToRunHelper READYTORUN_HELPER_StackProbe = 0x111, READYTORUN_HELPER_GetCurrentManagedThreadId = 0x112, + + // Async continuation helpers + READYTORUN_HELPER_AllocContinuation = 0x113, + READYTORUN_HELPER_AllocContinuationClass = 0x114, + READYTORUN_HELPER_AllocContinuationMethod = 0x115, }; #include "readytoruninstructionset.h" diff --git a/src/coreclr/inc/readytorunhelpers.h b/src/coreclr/inc/readytorunhelpers.h index c6e1a09ca5eb87..730cff8ba8c645 100644 --- a/src/coreclr/inc/readytorunhelpers.h +++ b/src/coreclr/inc/readytorunhelpers.h @@ -20,6 +20,7 @@ HELPER(READYTORUN_HELPER_RngChkFail, CORINFO_HELP_RNGCHKFAIL, HELPER(READYTORUN_HELPER_FailFast, CORINFO_HELP_FAIL_FAST, OPTIMIZEFORSIZE) HELPER(READYTORUN_HELPER_ThrowNullRef, CORINFO_HELP_THROWNULLREF, OPTIMIZEFORSIZE) HELPER(READYTORUN_HELPER_ThrowDivZero, CORINFO_HELP_THROWDIVZERO, OPTIMIZEFORSIZE) +HELPER(READYTORUN_HELPER_ThrowExact, CORINFO_HELP_THROWEXACT, OPTIMIZEFORSIZE) HELPER(READYTORUN_HELPER_WriteBarrier, CORINFO_HELP_ASSIGN_REF, ) HELPER(READYTORUN_HELPER_CheckedWriteBarrier, CORINFO_HELP_CHECKED_ASSIGN_REF, ) @@ -127,5 +128,9 @@ HELPER(READYTORUN_HELPER_StackProbe, CORINFO_HELP_STACK_PROBE, HELPER(READYTORUN_HELPER_GetCurrentManagedThreadId, CORINFO_HELP_GETCURRENTMANAGEDTHREADID, ) +HELPER(READYTORUN_HELPER_AllocContinuation, CORINFO_HELP_ALLOC_CONTINUATION, ) +HELPER(READYTORUN_HELPER_AllocContinuationClass, CORINFO_HELP_ALLOC_CONTINUATION_CLASS, ) +HELPER(READYTORUN_HELPER_AllocContinuationMethod, CORINFO_HELP_ALLOC_CONTINUATION_METHOD, ) + #undef HELPER #undef OPTIMIZEFORSPEED diff --git a/src/coreclr/jit/utils.cpp b/src/coreclr/jit/utils.cpp index 5923cafe07657a..c8c67e283e75d2 100644 --- a/src/coreclr/jit/utils.cpp +++ b/src/coreclr/jit/utils.cpp @@ -1843,6 +1843,13 @@ void HelperCallProperties::init() exceptions = ExceptionSetFlags::NullReferenceException; break; + case CORINFO_HELP_ALLOC_CONTINUATION: + case CORINFO_HELP_ALLOC_CONTINUATION_CLASS: + case CORINFO_HELP_ALLOC_CONTINUATION_METHOD: + mutatesHeap = true; + isAllocator = true; + break; + default: // The most pessimistic results are returned for these helpers. mutatesHeap = true; diff --git a/src/coreclr/tools/.editorconfig b/src/coreclr/tools/.editorconfig index 2604c0c3f48d8a..e097bb1bda5fc9 100644 --- a/src/coreclr/tools/.editorconfig +++ b/src/coreclr/tools/.editorconfig @@ -3,4 +3,4 @@ [*.cs] # TODO: Update tools to use LibraryImport instead of DllImport -dotnet_diagnostic.SYSLIB1054.severity = suggestion +dotnet_diagnostic.SYSLIB1054.severity = suggestion \ No newline at end of file diff --git a/src/coreclr/tools/Common/Compiler/AsyncMethodVariant.cs b/src/coreclr/tools/Common/Compiler/AsyncMethodVariant.cs index f27466aaefa469..6b7d0f39a0c3d2 100644 --- a/src/coreclr/tools/Common/Compiler/AsyncMethodVariant.cs +++ b/src/coreclr/tools/Common/Compiler/AsyncMethodVariant.cs @@ -81,7 +81,7 @@ public static bool IsAsyncVariant(this MethodDesc method) public static bool IsAsyncThunk(this MethodDesc method) { - return method.IsAsyncVariant() ^ method.IsAsync; + return (method.IsAsyncVariant() ^ method.IsAsync) || method is AsyncResumptionStub; } public static MethodDesc GetAsyncVariant(this MethodDesc method) diff --git a/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Async.cs b/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Async.cs index e252fc5fbebd23..7a5e7fc3c8c78a 100644 --- a/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Async.cs +++ b/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Async.cs @@ -202,7 +202,9 @@ protected override bool CompareValueToValue(AsyncMethodVariant value1, AsyncMeth public MetadataType GetContinuationType(GCPointerMap pointerMap) { - return _continuationTypeHashtable.GetOrCreateValue(pointerMap); + var cont = _continuationTypeHashtable.GetOrCreateValue(pointerMap); + _validTypes.TryAdd(cont); + return cont; } private sealed class ContinuationTypeHashtable : LockFreeReaderHashtable diff --git a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs index 026213b58b4366..a1b709e7e0f0ca 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs @@ -54,6 +54,7 @@ public enum ReadyToRunMethodSigFlags : uint READYTORUN_METHOD_SIG_OwnerType = 0x40, READYTORUN_METHOD_SIG_UpdateContext = 0x80, READYTORUN_METHOD_SIG_AsyncVariant = 0x100, + READYTORUN_METHOD_SIG_ResumptionStub = 0x200, } [Flags] @@ -187,6 +188,8 @@ public enum ReadyToRunFixupKind Check_IL_Body = 0x35, /* Check to see if an IL method is defined the same at runtime as at compile time. A failed match will cause code not to be used. */ Verify_IL_Body = 0x36, /* Verify an IL body is defined the same at compile time and runtime. A failed match will cause a hard runtime failure. */ + ContinuationLayout = 0x37, /* Layout of an async method continuation type */ + ModuleOverride = 0x80, // followed by sig-encoded UInt with assemblyref index into either the assemblyref // table of the MSIL metadata of the master context module for the signature or @@ -353,6 +356,10 @@ public enum ReadyToRunHelper GetCurrentManagedThreadId = 0x112, + AllocContinuation = 0x113, + AllocContinuationClass = 0x114, + AllocContinuationMethod = 0x115, + // ********************************************************************************************** // // These are not actually part of the R2R file format. We have them here because it's convenient. diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 3273e558163c0c..31b02783262741 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -27,6 +27,7 @@ using ILCompiler.DependencyAnalysis; #if READYTORUN +using ILCompiler.ReadyToRun.TypeSystem; using System.Reflection.Metadata.Ecma335; using ILCompiler.DependencyAnalysis.ReadyToRun; #endif @@ -115,10 +116,10 @@ public LikelyClassMethodRecord(IntPtr handle, uint likelihood) } [DllImport(JitLibrary)] - private static extern uint getLikelyClasses(LikelyClassMethodRecord* pLikelyClasses, uint maxLikelyClasses, PgoInstrumentationSchema* schema, uint countSchemaItems, byte*pInstrumentationData, int ilOffset); + private static extern uint getLikelyClasses(LikelyClassMethodRecord* pLikelyClasses, uint maxLikelyClasses, PgoInstrumentationSchema* schema, uint countSchemaItems, byte* pInstrumentationData, int ilOffset); [DllImport(JitLibrary)] - private static extern uint getLikelyMethods(LikelyClassMethodRecord* pLikelyMethods, uint maxLikelyMethods, PgoInstrumentationSchema* schema, uint countSchemaItems, byte*pInstrumentationData, int ilOffset); + private static extern uint getLikelyMethods(LikelyClassMethodRecord* pLikelyMethods, uint maxLikelyMethods, PgoInstrumentationSchema* schema, uint countSchemaItems, byte* pInstrumentationData, int ilOffset); [DllImport(JitSupportLibrary)] private static extern IntPtr GetJitHost(IntPtr configProvider); @@ -139,7 +140,7 @@ private static extern CorJitResult JitCompileMethod(out IntPtr exception, ref CORINFO_METHOD_INFO info, uint flags, out IntPtr nativeEntry, out uint codeSize); [DllImport(JitSupportLibrary)] - private static extern IntPtr AllocException([MarshalAs(UnmanagedType.LPWStr)]string message, int messageLength); + private static extern IntPtr AllocException([MarshalAs(UnmanagedType.LPWStr)] string message, int messageLength); [DllImport(JitSupportLibrary)] private static extern void JitSetOs(IntPtr jit, CORINFO_OS os); @@ -838,12 +839,12 @@ private bool Get_CORINFO_METHOD_INFO(MethodDesc method, MethodIL methodIL, CORIN private Dictionary _instantiationToJitVisibleInstantiation; private CORINFO_CLASS_STRUCT_** GetJitInstantiation(Instantiation inst) { - IntPtr [] jitVisibleInstantiation; + IntPtr[] jitVisibleInstantiation; _instantiationToJitVisibleInstantiation ??= new Dictionary(); if (!_instantiationToJitVisibleInstantiation.TryGetValue(inst, out jitVisibleInstantiation)) { - jitVisibleInstantiation = new IntPtr[inst.Length]; + jitVisibleInstantiation = new IntPtr[inst.Length]; for (int i = 0; i < inst.Length; i++) jitVisibleInstantiation[i] = (IntPtr)ObjectToHandle(inst[i]); _instantiationToJitVisibleInstantiation.Add(inst, jitVisibleInstantiation); @@ -1066,7 +1067,7 @@ private TypeSystemEntity entityFromContext(CORINFO_CONTEXT_STRUCT* contextStruct { if (contextStruct == contextFromMethodBeingCompiled()) { - return MethodBeingCompiled.HasInstantiation ? (TypeSystemEntity)MethodBeingCompiled: (TypeSystemEntity)MethodBeingCompiled.OwningType; + return MethodBeingCompiled.HasInstantiation ? (TypeSystemEntity)MethodBeingCompiled : (TypeSystemEntity)MethodBeingCompiled.OwningType; } return (TypeSystemEntity)HandleToObject((void*)((nuint)contextStruct & ~(nuint)CorInfoContextFlags.CORINFO_CONTEXTFLAGS_MASK)); @@ -1837,14 +1838,14 @@ private void resolveToken(ref CORINFO_RESOLVED_TOKEN pResolvedToken) #if READYTORUN TypeDesc owningType = methodIL.OwningMethod.GetTypicalMethodDefinition().OwningType; - bool recordToken; + bool recordToken = owningType is EcmaType; if (!_compilation.CompilationModuleGroup.VersionsWithMethodBody(methodIL.OwningMethod.GetTypicalMethodDefinition())) { - recordToken = (methodIL.GetMethodILScopeDefinition() is IMethodTokensAreUseableInCompilation) && owningType is EcmaType; + recordToken &= (methodIL.GetMethodILScopeDefinition() is IMethodTokensAreUseableInCompilation); } else { - recordToken = (_compilation.CompilationModuleGroup.VersionsWithType(owningType) || _compilation.CompilationModuleGroup.CrossModuleInlineableType(owningType)) && owningType is EcmaType; + recordToken &= _compilation.CompilationModuleGroup.VersionsWithType(owningType) || _compilation.CompilationModuleGroup.CrossModuleInlineableType(owningType); } #endif @@ -1892,8 +1893,7 @@ private void resolveToken(ref CORINFO_RESOLVED_TOKEN pResolvedToken) pResolvedToken.hClass = null; } } - else - if (result is FieldDesc) + else if (result is FieldDesc) { FieldDesc field = result as FieldDesc; @@ -1980,8 +1980,7 @@ private void findCallSiteSig(CORINFO_MODULE_STRUCT_* module, uint methTOK, CORIN { result = WellKnownType.RuntimeMethodHandle; } - else - if (pResolvedToken.hField != null) + else if (pResolvedToken.hField != null) { result = WellKnownType.RuntimeFieldHandle; } @@ -2310,7 +2309,7 @@ public static int GetClassAlignmentRequirementStatic(DefType type) // private static bool ShouldAlign8(int dwR8Fields, int dwTotalFields) { - return dwR8Fields*2>dwTotalFields && dwR8Fields>=2; + return dwR8Fields * 2 > dwTotalFields && dwR8Fields >= 2; } private static bool ShouldAlign8(DefType type) @@ -3447,9 +3446,6 @@ 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++) @@ -3459,7 +3455,6 @@ private void getAsyncInfo(ref CORINFO_ASYNC_INFO pAsyncInfoOut) } return ObjectToHandle(_compilation.TypeSystemContext.GetContinuationType(gcMapBuilder.ToGCMap())); -#endif } private mdToken getMethodDefFromMethod(CORINFO_METHOD_STRUCT_* hMethod) @@ -3646,7 +3641,7 @@ private uint getThreadTLSIndex(ref void* ppIndirection) { throw new NotImplementedException("getThreadTLSIndex"); } private Dictionary _helperCache = new Dictionary(); - private void getHelperFtn(CorInfoHelpFunc ftnNum, CORINFO_CONST_LOOKUP *pNativeEntrypoint, CORINFO_METHOD_STRUCT_** pMethod) + private void getHelperFtn(CorInfoHelpFunc ftnNum, CORINFO_CONST_LOOKUP* pNativeEntrypoint, CORINFO_METHOD_STRUCT_** pMethod) { // We never return a method handle from the managed implementation of this method today if (pMethod != null) @@ -3807,7 +3802,12 @@ private bool getTailCallHelpers(ref CORINFO_RESOLVED_TOKEN callToken, CORINFO_SI #pragma warning restore CA1822 // Mark members as static { #if READYTORUN - throw new NotImplementedException("Crossgen2 does not support runtime-async yet"); + var resumptionStub = new AsyncResumptionStub(MethodBeingCompiled, MethodBeingCompiled.OwningType); + var tokenSource = MethodBeingCompiled.GetPrimaryMethodDesc().GetTypicalMethodDefinition(); + // CompiledMethodNode instead of MethodEntrypoint for the pointer to the code instead of a fixup + entryPoint = (void*)ObjectToHandle(_compilation.NodeFactory.CompiledMethodNode(resumptionStub)); + return ObjectToHandle(resumptionStub); + #else _asyncResumptionStub ??= new AsyncResumptionStub(MethodBeingCompiled, _compilation.TypeSystemContext.GeneratedAssembly.GetGlobalModuleType()); @@ -3821,7 +3821,6 @@ private bool getTailCallHelpers(ref CORINFO_RESOLVED_TOKEN callToken, CORINFO_SI private int _codeAlignment; private byte[] _roData; - private MethodReadOnlyDataNode _roDataBlob; private int _roDataAlignment; diff --git a/src/coreclr/tools/Common/TypeSystem/IL/InstantiatedMethodIL.cs b/src/coreclr/tools/Common/TypeSystem/IL/InstantiatedMethodIL.cs index 5d4fb39c953a11..d2da424faa21af 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/InstantiatedMethodIL.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/InstantiatedMethodIL.cs @@ -18,7 +18,6 @@ public InstantiatedMethodIL(MethodDesc owningMethod, MethodIL methodIL) { Debug.Assert(methodIL.GetMethodILDefinition() == methodIL); Debug.Assert(owningMethod.HasInstantiation || owningMethod.OwningType.HasInstantiation); - Debug.Assert(owningMethod.GetTypicalMethodDefinition() == methodIL.OwningMethod); _methodIL = methodIL; _method = owningMethod; diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncResumptionStub.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncResumptionStub.cs index 16154f87c97d25..a3cd88b7511bf7 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncResumptionStub.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncResumptionStub.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; - +using System.Collections.Immutable; using Internal.IL; using Internal.IL.Stubs; using Internal.TypeSystem; @@ -17,16 +17,18 @@ public partial class AsyncResumptionStub : ILStubMethod private readonly MethodDesc _targetMethod; private readonly TypeDesc _owningType; private MethodSignature _signature; + private ImmutableArray _name; public AsyncResumptionStub(MethodDesc targetMethod, TypeDesc owningType) { Debug.Assert(targetMethod.IsAsyncCall()); _targetMethod = targetMethod; _owningType = owningType; + _name = [.. "RESUME_"u8, .. _targetMethod.Name]; } - public override ReadOnlySpan Name => _targetMethod.Name; - public override string DiagnosticName => _targetMethod.DiagnosticName; + public override ReadOnlySpan Name => _name.AsSpan(); + public override string DiagnosticName => "RESUME_" + _targetMethod.DiagnosticName; public override TypeDesc OwningType => _owningType; @@ -36,6 +38,7 @@ public AsyncResumptionStub(MethodDesc targetMethod, TypeDesc owningType) public MethodDesc TargetMethod => _targetMethod; + private MethodSignature InitializeSignature() { TypeDesc objectType = Context.GetWellKnownType(WellKnownType.Object); @@ -109,6 +112,7 @@ public override MethodIL EmitIL() } ilStream.EmitLdLoc(newContinuationLocal); ilStream.Emit(ILOpcode.ret); + ilEmitter.SetHasGeneratedTokens(); return ilEmitter.Link(this); } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs index ca46321c4759ef..4bc0141382369d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs @@ -334,6 +334,16 @@ public static void GetEntryPoint(TypeSystemContext context, ReadyToRunHelper id, methodDesc = context.GetCoreLibEntryPoint("System"u8, "RuntimeTypeHandle"u8, "GetRuntimeTypeHandleFromMethodTable"u8, null); break; + case ReadyToRunHelper.AllocContinuation: + methodDesc = context.GetCoreLibEntryPoint("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8, "AllocContinuation"u8, null); + break; + case ReadyToRunHelper.AllocContinuationMethod: + methodDesc = context.GetCoreLibEntryPoint("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8, "AllocContinuationMethod"u8, null); + break; + case ReadyToRunHelper.AllocContinuationClass: + methodDesc = context.GetCoreLibEntryPoint("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8, "AllocContinuationClass"u8, null); + break; + case ReadyToRunHelper.GetCurrentManagedThreadId: methodDesc = context.SystemModule.GetKnownType("System"u8, "Environment"u8).GetKnownMethod("get_CurrentManagedThreadId"u8, null); break; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ExceptionInfoLookupTableNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ExceptionInfoLookupTableNode.cs index f9fe3657851a16..e5c3a71595c810 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ExceptionInfoLookupTableNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ExceptionInfoLookupTableNode.cs @@ -94,6 +94,10 @@ internal void LayoutMethodsWithEHInfo() foreach (MethodWithGCInfo method in _nodeFactory.EnumerateCompiledMethods()) { + if (method.Method is AsyncResumptionStub) + { + continue; + } ObjectData ehInfo = method.EHInfo; if (ehInfo != null && ehInfo.Data.Length != 0) { diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InliningInfoNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InliningInfoNode.cs index d28bd07e04921c..2df6065fb4a8f1 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InliningInfoNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InliningInfoNode.cs @@ -6,7 +6,7 @@ using System.Diagnostics; using System.IO; using System.Reflection.Metadata.Ecma335; - +using ILCompiler.ReadyToRun.TypeSystem; using Internal; using Internal.NativeFormat; using Internal.ReadyToRunConstants; @@ -67,8 +67,17 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) foreach (MethodWithGCInfo methodNode in factory.EnumerateCompiledMethods(_module, CompiledMethodCategory.All)) { MethodDesc[] inlinees = methodNode.InlinedMethods; + if (inlinees.Length == 0) + { + continue; + } MethodDesc inliner = methodNode.Method; - EcmaMethod inlinerDefinition = (EcmaMethod)inliner.GetTypicalMethodDefinition(); + if (inliner.IsAsyncThunk()) + { + // Async thunks are generated by crossgen and diagnostic tools don't need to worry about them + continue; + } + EcmaMethod inlinerDefinition = (EcmaMethod)inliner.GetPrimaryMethodDesc().GetTypicalMethodDefinition(); if (inlinerDefinition.IsNonVersionable()) { diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InstanceEntryPointTableNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InstanceEntryPointTableNode.cs index db63a30fae67a5..4ea76908662708 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InstanceEntryPointTableNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InstanceEntryPointTableNode.cs @@ -7,7 +7,7 @@ using System.IO; using System.Linq; using System.Reflection.Metadata.Ecma335; - +using ILCompiler.ReadyToRun.TypeSystem; using Internal; using Internal.JitInterface; using Internal.NativeFormat; @@ -53,17 +53,17 @@ public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilde public static byte[] BuildSignatureForMethodDefinedInModule(MethodDesc method, NodeFactory factory) { - EcmaMethod typicalMethod = (EcmaMethod)method.GetTypicalMethodDefinition(); + EcmaMethod ecmaMethod = (EcmaMethod)method.GetPrimaryMethodDesc().GetTypicalMethodDefinition(); ModuleToken moduleToken; - if (factory.CompilationModuleGroup.VersionsWithMethodBody(typicalMethod)) + if (factory.CompilationModuleGroup.VersionsWithMethodBody(ecmaMethod)) { - moduleToken = new ModuleToken(typicalMethod.Module, typicalMethod.Handle); + moduleToken = new ModuleToken(ecmaMethod.Module, ecmaMethod.Handle); } else { MutableModule manifestMetadata = factory.ManifestMetadataTable._mutableModule; - var handle = manifestMetadata.TryGetExistingEntityHandle(method.GetTypicalMethodDefinition()); + var handle = manifestMetadata.TryGetExistingEntityHandle(ecmaMethod); Debug.Assert(handle.HasValue); moduleToken = new ModuleToken(factory.ManifestMetadataTable._mutableModule, handle.Value); } @@ -102,7 +102,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) foreach (MethodWithGCInfo method in factory.EnumerateCompiledMethods(null, CompiledMethodCategory.Instantiated)) { - Debug.Assert(method.Method.HasInstantiation || method.Method.OwningType.HasInstantiation); + Debug.Assert(method.Method.HasInstantiation || method.Method.OwningType.HasInstantiation || method.Method.IsAsyncVariant() || method.Method is AsyncResumptionStub); int methodIndex = factory.RuntimeFunctionsTable.GetIndex(method); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodFixupSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodFixupSignature.cs index e117bb2fb3be87..89a91aa6298f36 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodFixupSignature.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodFixupSignature.cs @@ -113,7 +113,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) MethodWithToken method = _method; - if (factory.CompilationModuleGroup.VersionsWithMethodBody(method.Method) && !method.Method.IsAsyncVariant()) + if (factory.CompilationModuleGroup.VersionsWithMethodBody(method.Method) && !method.Method.IsAsyncVariant() && method.Method is not AsyncResumptionStub) { if (method.Token.TokenType == CorTokenType.mdtMethodSpec) { @@ -130,6 +130,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) SignatureContext innerContext = dataBuilder.EmitFixup(factory, fixupKind, method.Token.Module, factory.SignatureContext); + // We should emit AsyncVariants and ResumptionStubs differently even if the ModuleToken is a Def or Ref if (optimized && method.Token.TokenType == CorTokenType.mdtMethodDef) { dataBuilder.EmitMethodDefToken(method.Token); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs index 48bf4b1c067fa3..45978765f0b638 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs @@ -12,6 +12,7 @@ using Internal.TypeSystem.Ecma; using Internal.CorConstants; using System.Diagnostics; +using ILCompiler.ReadyToRun.TypeSystem; namespace ILCompiler.DependencyAnalysis.ReadyToRun { @@ -61,7 +62,6 @@ public ModuleToken GetModuleTokenForType(TypeDesc type, bool allowDynamicallyCre { return new ModuleToken(ecmaType.Module, (mdToken)MetadataTokens.GetToken(ecmaType.Handle)); } - if (_typeToRefTokens.TryGetValue(ecmaType, out token)) { return token; @@ -100,7 +100,7 @@ public ModuleToken GetModuleTokenForMethod(MethodDesc method, bool allowDynamica { method = method.GetCanonMethodTarget(CanonicalFormKind.Specific); - if (method.GetTypicalMethodDefinition() is EcmaMethod ecmaMethod) + if (method.GetPrimaryMethodDesc().GetTypicalMethodDefinition() is EcmaMethod ecmaMethod) { if (_compilationModuleGroup.VersionsWithMethodBody(ecmaMethod)) { @@ -154,6 +154,38 @@ public void AddModuleTokenForMethod(MethodDesc method, ModuleToken token) } } + public ModuleToken GetModuleTokenForField(FieldDesc field, bool allowDynamicallyCreatedReference, bool throwIfNotFound) + { + if (field.GetTypicalFieldDefinition() is EcmaField ecmaField) + { + if (_compilationModuleGroup.VersionsWithType(ecmaField.OwningType)) + { + return new ModuleToken(ecmaField.Module, ecmaField.Handle); + } + + // If that didn't work, it may be in the manifest module used for version resilient cross module inlining + if (allowDynamicallyCreatedReference) + { + var handle = _manifestMutableModule.TryGetExistingEntityHandle(ecmaField); + if (handle.HasValue) + { + return new ModuleToken(_manifestMutableModule, handle.Value); + } + } + } + + // Reverse lookup failed + if (throwIfNotFound) + { + throw new NotImplementedException(field.ToString()); + } + else + { + return default(ModuleToken); + } + } + + private void DecodeMethodSpecificationSignatureToDiscoverUsedTypeTokens(BlobHandle signatureHandle, ModuleToken token) { MetadataReader metadataReader = token.MetadataReader; @@ -300,6 +332,10 @@ public void AddModuleTokenForType(TypeDesc type, ModuleToken token) SetModuleTokenForTypeSystemEntity(_typeToRefTokens, ecmaType, token); } } + else if (type.IsCanonicalDefinitionType(CanonicalFormKind.Specific)) + { + return; + } else if (!specialTypeFound) { throw new NotImplementedException(type.ToString()); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/PrecodeHelperImport.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/PrecodeHelperImport.cs index 63fead7047f3a4..a026381b9e89d2 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/PrecodeHelperImport.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/PrecodeHelperImport.cs @@ -37,6 +37,35 @@ public override void EncodeData(ref ObjectDataBuilder dataBuilder, NodeFactory f } + // This is just here in case of future extension (there are no fields specific to this class) + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return base.CompareToImpl(other, comparer); + } + } + + public class CodeDataImport : Import + { + public CodeDataImport(NodeFactory factory, Signature signature) + : base(factory.CodeDataImports, signature) + { + } + + protected override string GetName(NodeFactory factory) + { + return "CodeDataImport->" + ImportSignature.GetMangledName(factory.NameMangler); + } + + public override int ClassCode => 667823013; + + public override void EncodeData(ref ObjectDataBuilder dataBuilder, NodeFactory factory, bool relocsOnly) + { + // This needs to be an empty target pointer since it will be filled in with Module* + // when loaded by CoreCLR + dataBuilder.EmitZeroPointer(); + } + + // This is just here in case of future extension (there are no fields specific to this class) public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) { diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs index 5fec36a7aee391..2224060906e119 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs @@ -303,6 +303,11 @@ public void EmitTypeSignature(TypeDesc typeDesc, SignatureContext context) { EmitElementType(CorElementType.ELEMENT_TYPE_CANON_ZAPSIG); } + else if (typeDesc is AsyncContinuationType act) + { + // We should never try to encode a continuation on this path + throw new InvalidOperationException(); + } else { ModuleToken token = context.GetModuleTokenForType((EcmaType)typeDesc); @@ -440,6 +445,11 @@ public void EmitMethodSignature( { flags |= (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_AsyncVariant; } + if (method.Method is AsyncResumptionStub) + { + flags |= (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_ResumptionStub; + } + EmitMethodSpecificationSignature(method, flags, enforceDefEncoding, enforceOwningType, context); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs index 4a45d024afcf89..25ea93992a0dbc 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs @@ -10,6 +10,7 @@ using Internal.TypeSystem.Interop; using Internal.ReadyToRunConstants; using Internal.CorConstants; +using Internal.JitInterface; namespace ILCompiler.DependencyAnalysis.ReadyToRun { @@ -46,13 +47,24 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) IEcmaModule targetModule = factory.SignatureContext.GetTargetModule(_typeDesc); SignatureContext innerContext = dataBuilder.EmitFixup(factory, fixupKind, targetModule, factory.SignatureContext); - dataBuilder.EmitTypeSignature(_typeDesc, innerContext); - if ((fixupKind == ReadyToRunFixupKind.Check_TypeLayout) || (fixupKind == ReadyToRunFixupKind.Verify_TypeLayout)) { + dataBuilder.EmitTypeSignature(_typeDesc, innerContext); + Debug.Assert(_typeDesc.IsValueType); EncodeTypeLayout(dataBuilder, _typeDesc); } + else if (fixupKind == ReadyToRunFixupKind.ContinuationLayout) + { + var act = _typeDesc as AsyncContinuationType; + // Emit EcmaType Continuation type + dataBuilder.EmitTypeSignature(act.BaseType, innerContext); + EncodeContinuationTypeLayout(dataBuilder, act); + } + else + { + dataBuilder.EmitTypeSignature(_typeDesc, innerContext); + } } return dataBuilder.ToObjectData(); @@ -60,7 +72,6 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) private static void EncodeTypeLayout(ObjectDataSignatureBuilder dataBuilder, TypeDesc type) { - Debug.Assert(type.IsValueType); MetadataType defType = (MetadataType)type; int pointerSize = type.Context.Target.PointerSize; @@ -109,6 +120,53 @@ private static void EncodeTypeLayout(ObjectDataSignatureBuilder dataBuilder, Typ // Encode the GC pointer map GCPointerMap gcMap = GCPointerMap.FromInstanceLayout(defType); + byte[] encodedGCRefMap = new byte[((size + 7) / pointerSize + 7) / 8]; + int bitIndex = 0; + foreach (bool bit in gcMap) + { + if (bit) + { + encodedGCRefMap[bitIndex / 8] |= (byte)(1 << (bitIndex & 7)); + } + + ++bitIndex; + } + + dataBuilder.EmitBytes(encodedGCRefMap); + } + } + + private static void EncodeContinuationTypeLayout(ObjectDataSignatureBuilder dataBuilder, AsyncContinuationType type) + { + int pointerSize = type.Context.Target.PointerSize; + int size = type.PointerMap.Size * pointerSize; + int alignment = pointerSize; + ReadyToRunTypeLayoutFlags flags = ReadyToRunTypeLayoutFlags.READYTORUN_LAYOUT_Alignment | ReadyToRunTypeLayoutFlags.READYTORUN_LAYOUT_GCLayout; + Debug.Assert(alignment == pointerSize); + flags |= ReadyToRunTypeLayoutFlags.READYTORUN_LAYOUT_Alignment_Native; + + bool gcLayoutEmpty = true; + foreach(bool hasPointer in type.PointerMap) + { + if (hasPointer) + { + gcLayoutEmpty = false; + break; + } + } + if (gcLayoutEmpty) + { + flags |= ReadyToRunTypeLayoutFlags.READYTORUN_LAYOUT_GCLayout_Empty; + } + + dataBuilder.EmitUInt((uint)flags); + dataBuilder.EmitUInt((uint)size); + + if (!gcLayoutEmpty) + { + // Encode the GC pointer map + GCPointerMap gcMap = type.PointerMap; + byte[] encodedGCRefMap = new byte[(size / pointerSize + 7) / 8]; int bitIndex = 0; foreach (bool bit in gcMap) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs index 273df1e98e29ef..a54cfc5a3b9d8e 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs @@ -411,6 +411,8 @@ private void CreateNodeCaches() public ImportSectionNode PrecodeImports; + public ImportSectionNode CodeDataImports; + public ImportSectionNode ILBodyPrecodeImports; private NodeCache _constructedHelpers; @@ -477,6 +479,8 @@ public IEnumerable EnumerateCompiledMethods(EcmaModule moduleT { MethodDesc method = methodNode.Method; MethodWithGCInfo methodCodeNode = methodNode as MethodWithGCInfo; + //if (methodCodeNode.Method is AsyncResumptionStub) + // continue; #if DEBUG if ((!methodCodeNode.IsEmpty || CompilationModuleGroup.VersionsWithMethodBody(method)) && method.IsPrimaryMethodDesc()) { @@ -897,6 +901,15 @@ bool HasAnyProfileDataForInput() emitGCRefMap: false); ImportSectionsTable.AddEmbeddedObject(PrecodeImports); + CodeDataImports = new ImportSectionNode( + "CodeDataImports", + ReadyToRunImportSectionType.Unknown, + ReadyToRunImportSectionFlags.PCode, + (byte)Target.PointerSize, + emitPrecode: true, + emitGCRefMap: false); + ImportSectionsTable.AddEmbeddedObject(CodeDataImports); + StringImports = new ImportSectionNode( "StringImports", ReadyToRunImportSectionType.StringHandle, @@ -913,6 +926,7 @@ bool HasAnyProfileDataForInput() graph.AddRoot(DispatchImports, "Dispatch imports are always generated"); graph.AddRoot(HelperImports, "Helper imports are always generated"); graph.AddRoot(PrecodeImports, "Precode helper imports are always generated"); + graph.AddRoot(CodeDataImports, "Code data imports are always generated"); graph.AddRoot(ILBodyPrecodeImports, "IL body precode imports are always generated"); graph.AddRoot(StringImports, "String imports are always generated"); graph.AddRoot(Header, "ReadyToRunHeader is always generated"); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs index f21e780cc97fce..9e2cb3a08d01a2 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs @@ -176,6 +176,21 @@ private void CreateNodeCaches() key.MethodWithToken, isInstantiatingStub: false)); }); + + _continuationTypeFixups = new NodeCache((key) => + { + return new PrecodeHelperImport( + _codegenNodeFactory, + _codegenNodeFactory.TypeSignature(ReadyToRunFixupKind.ContinuationLayout, key) + ); + }); + } + + private NodeCache _continuationTypeFixups; + + public ISymbolNode ContinuationTypeSymbol(AsyncContinuationType key) + { + return _continuationTypeFixups.GetOrAdd(key); } private NodeCache _importStrings; @@ -398,7 +413,7 @@ private ISymbolNode CreateMethodDictionary(MethodWithToken method) return new PrecodeHelperImport( _codegenNodeFactory, _codegenNodeFactory.MethodSignature( - ReadyToRunFixupKind.MethodDictionary, + ReadyToRunFixupKind.MethodDictionary, method, isInstantiatingStub: true)); } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs index fa51077ac5204e..be9fdb41fe3009 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs @@ -21,6 +21,8 @@ using ILCompiler.DependencyAnalysisFramework; using ILCompiler.Reflection.ReadyToRun; using Internal.TypeSystem.Ecma; +using System.Linq; +using ILCompiler.ReadyToRun.TypeSystem; namespace ILCompiler { @@ -108,9 +110,8 @@ public bool CanInline(MethodDesc caller, MethodDesc callee) } } - if (callee.IsAsyncThunk()) + if (callee.IsAsyncThunk() || callee.IsAsyncCall() || caller.IsAsyncThunk() || caller.IsAsyncCall()) { - // Async thunks require special handling in the compiler and should not be inlined return false; } @@ -301,7 +302,7 @@ public sealed class ReadyToRunCodegenCompilation : Compilation private readonly ProfileDataManager _profileData; private readonly FileLayoutOptimizer _fileLayoutOptimizer; - private readonly HashSet _methodsWhichNeedMutableILBodies = new HashSet(); + private readonly HashSet _methodsWhichNeedMutableILBodies = new HashSet(); private readonly HashSet _methodsToRecompile = new HashSet(); public ProfileDataManager ProfileData => _profileData; @@ -672,6 +673,7 @@ public void PrepareForCompilationRetry(MethodWithGCInfo methodToBeRecompiled, IE private int _finishedThreadCount; private ManualResetEventSlim _compilationSessionComplete = new ManualResetEventSlim(); private bool _hasCreatedCompilationThreads = false; + private bool _hasAddedAsyncReferences = false; protected override void ComputeDependencyNodeDependencies(List> obj) { @@ -693,12 +695,79 @@ protected override void ComputeDependencyNodeDependencies(List comparison = (EcmaMethod a, EcmaMethod b) => comparer.Compare(a, b); + Comparison comparison = (MethodDesc a, MethodDesc b) => comparer.Compare(a, b); Array.Sort(mutableMethodBodyNeedList, comparison); var ilProvider = (ReadyToRunILProvider)_methodILCache.ILProvider; @@ -845,7 +914,7 @@ void CompilationThread(object objThreadId) while (true) { _compilationThreadSemaphore.Wait(); - lock(this) + lock (this) { if (_doneAllCompiling) return; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs index 96f78ba0309247..3b93fa3f53a6c8 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs @@ -16,6 +16,8 @@ using Debug = System.Diagnostics.Debug; using Internal.ReadyToRunConstants; using Internal.JitInterface; +using System.Reflection.Metadata.Ecma335; +using System.Reflection.Metadata; namespace ILCompiler { @@ -616,18 +618,18 @@ private bool IsNonVersionableWithILTokensThatDoNotNeedTranslationUncached(EcmaMe case ILOpcode.ldfld: case ILOpcode.ldflda: case ILOpcode.stfld: - { - int token = ilReader.ReadILToken(); - FieldDesc field = methodIL.GetObject(token) as FieldDesc; - if (field == null) - return false; - if (field.IsStatic) - return false; - MetadataType owningMetadataType = field.OwningType; - if (!owningMetadataType.IsNonVersionable()) - return false; - break; - } + { + int token = ilReader.ReadILToken(); + FieldDesc field = methodIL.GetObject(token) as FieldDesc; + if (field == null) + return false; + if (field.IsStatic) + return false; + MetadataType owningMetadataType = field.OwningType; + if (!owningMetadataType.IsNonVersionable()) + return false; + break; + } case ILOpcode.ldelem: case ILOpcode.ldelema: @@ -636,37 +638,37 @@ private bool IsNonVersionableWithILTokensThatDoNotNeedTranslationUncached(EcmaMe case ILOpcode.initobj: case ILOpcode.cpobj: case ILOpcode.sizeof_: - { - int token = ilReader.ReadILToken(); - TypeDesc type = methodIL.GetObject(token) as TypeDesc; - if (type == null) - return false; + { + int token = ilReader.ReadILToken(); + TypeDesc type = methodIL.GetObject(token) as TypeDesc; + if (type == null) + return false; - MetadataType metadataType = type as MetadataType; - if (metadataType == null) - continue; // Types which are not metadata types are all well defined in size + MetadataType metadataType = type as MetadataType; + if (metadataType == null) + continue; // Types which are not metadata types are all well defined in size - if (!metadataType.IsValueType) - continue; // Reference types are all well defined in size for the sizeof instruction + if (!metadataType.IsValueType) + continue; // Reference types are all well defined in size for the sizeof instruction - if (metadataType.IsNonVersionable()) - continue; - return false; - } + if (metadataType.IsNonVersionable()) + continue; + return false; + } case ILOpcode.stelem: - { - int token = ilReader.ReadILToken(); - MetadataType type = methodIL.GetObject(token) as MetadataType; - if (type == null) - return false; - - if (!type.IsValueType) - return false; - if (!type.IsNonVersionable()) - return false; - break; - } + { + int token = ilReader.ReadILToken(); + MetadataType type = methodIL.GetObject(token) as MetadataType; + if (type == null) + return false; + + if (!type.IsValueType) + return false; + if (!type.IsNonVersionable()) + return false; + break; + } // IL instructions which refer to tokens which are not safe for NonVersionable methods case ILOpcode.box: @@ -725,7 +727,7 @@ public sealed override bool TryGetModuleTokenForExternalType(TypeDesc type, out if (_typeRefsInCompilationModuleSet == null) { - lock(_compilationModuleSet) + lock (_compilationModuleSet) { if (_typeRefsInCompilationModuleSet == null) { @@ -733,7 +735,7 @@ public sealed override bool TryGetModuleTokenForExternalType(TypeDesc type, out foreach (var module in _compilationModuleSet) { - EcmaModule ecmaModule = (EcmaModule)module; + EcmaModule ecmaModule = module; foreach (var typeRefHandle in ecmaModule.MetadataReader.TypeReferences) { try diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs index 83de288c82efbc..e0aa30e78165ea 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs @@ -16,6 +16,7 @@ partial class CompilerTypeSystemContext public CompilerTypeSystemContext(TargetDetails details, SharedGenericsMode genericsMode) : base(details) { + _continuationTypeHashtable = new(this); _genericsMode = genericsMode; } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunTableManager.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunTableManager.cs index 25bec37f74584a..b9b86646d30aa9 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunTableManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunTableManager.cs @@ -79,13 +79,13 @@ protected virtual void Graph_NewMarkedNode(DependencyNodeCore obj) { Debug.Assert(!_sortedMethods); MethodDesc method = methodNode.Method; - EcmaModule module = (EcmaModule)((EcmaMethod)method.GetTypicalMethodDefinition().GetPrimaryMethodDesc()).Module; + EcmaModule module = (EcmaModule)((EcmaMethod)method.GetPrimaryMethodDesc().GetTypicalMethodDefinition().GetTypicalMethodDefinition()).Module; if (!_methodsGenerated.TryGetValue(module, out var perModuleData)) { perModuleData = new PerModuleMethodsGenerated(module); _methodsGenerated[module] = perModuleData; } - if (method.HasInstantiation || method.OwningType.HasInstantiation) + if (method.HasInstantiation || method.OwningType.HasInstantiation || method.IsAsyncVariant() || method is AsyncResumptionStub) { perModuleData.GenericMethodsGenerated.Add(methodNode); } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs index bbfcc01d500442..01fbbdacf9e8e1 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs @@ -14,6 +14,7 @@ using System.Buffers.Binary; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; +using ILCompiler.ReadyToRun.TypeSystem; namespace Internal.IL { @@ -122,18 +123,45 @@ private MethodIL TryGetPerInstantiationIntrinsicMethodIL(MethodDesc method) return null; } - private Dictionary _manifestModuleWrappedMethods = new Dictionary(); + private Dictionary _manifestModuleWrappedMethods = new Dictionary(); // Create the cross module inlineable tokens for a method // This method is order dependent, and must be called during the single threaded portion of compilation - public void CreateCrossModuleInlineableTokensForILBody(EcmaMethod method) + public void CreateCrossModuleInlineableTokensForILBody(MethodDesc method) { Debug.Assert(_manifestMutableModule != null); var wrappedMethodIL = new ManifestModuleWrappedMethodIL(); if (method.IsAsync) { - if (!wrappedMethodIL.Initialize(_manifestMutableModule, GetMethodILForAsyncMethod(method), method, false)) + if (!wrappedMethodIL.Initialize(_manifestMutableModule, GetMethodILForAsyncMethod(method), (EcmaMethod)method, false)) + { + // If we could not initialize the wrapped method IL, we should store a null. + // That will result in the IL code for the method being unavailable for use in + // the compilation, which is version safe. + wrappedMethodIL = null; + } + } + else if (method.IsAsyncVariant()) + { + if (!wrappedMethodIL.Initialize(_manifestMutableModule, + AsyncThunkILEmitter.EmitAsyncMethodThunk(method, method.GetTargetOfAsyncVariant()), + (EcmaMethod)method.GetTargetOfAsyncVariant(), + false)) + { + // If we could not initialize the wrapped method IL, we should store a null. + // That will result in the IL code for the method being unavailable for use in + // the compilation, which is version safe. + wrappedMethodIL = null; + } + } + else if (method is AsyncResumptionStub ars) + { + if (!wrappedMethodIL.Initialize( + _manifestMutableModule, + ars.EmitIL(), + (EcmaMethod)ars.TargetMethod.GetPrimaryMethodDesc().GetTypicalMethodDefinition(), + false)) { // If we could not initialize the wrapped method IL, we should store a null. // That will result in the IL code for the method being unavailable for use in @@ -146,7 +174,7 @@ public void CreateCrossModuleInlineableTokensForILBody(EcmaMethod method) Debug.Assert(!_compilationModuleGroup.VersionsWithMethodBody(method) && _compilationModuleGroup.CrossModuleInlineable(method)); - if (!wrappedMethodIL.Initialize(_manifestMutableModule, EcmaMethodIL.Create(method))) + if (!wrappedMethodIL.Initialize(_manifestMutableModule, EcmaMethodIL.Create((EcmaMethod)method))) { // If we could not initialize the wrapped method IL, we should store a null. // That will result in the IL code for the method being unavailable for use in @@ -159,38 +187,48 @@ public void CreateCrossModuleInlineableTokensForILBody(EcmaMethod method) IncrementVersion(); } - public bool NeedsCrossModuleInlineableTokens(EcmaMethod method) + public bool NeedsCrossModuleInlineableTokens(MethodDesc method) { - if (((!_compilationModuleGroup.VersionsWithMethodBody(method) && - _compilationModuleGroup.CrossModuleInlineable(method)) - || NeedsTaskReturningThunk(method)) - && !_manifestModuleWrappedMethods.ContainsKey(method)) + if (((!_compilationModuleGroup.VersionsWithMethodBody(method) + && _compilationModuleGroup.CrossModuleInlineable(method)) + || (NeedsTaskReturningThunk(method) || NeedsAsyncThunk(method) || method is AsyncResumptionStub)) + && !_manifestModuleWrappedMethods.ContainsKey(method)) { return true; } return false; } - bool NeedsTaskReturningThunk(EcmaMethod method) + bool NeedsTaskReturningThunk(MethodDesc method) { + if (method is not EcmaMethod ecmaMethod) + return false; + if (!method.IsAsync) return false; if (method.Signature.ReturnsTaskOrValueTask()) return true; - if (method.OwningType.Module != method.Context.SystemModule) + if (ecmaMethod.OwningType.Module != ecmaMethod.Context.SystemModule) return true; return false; } - MethodIL GetMethodILForAsyncMethod(EcmaMethod method) + bool NeedsAsyncThunk(MethodDesc method) + { + if (method is not AsyncMethodVariant) + return false; + return !method.IsAsync; + } + + MethodIL GetMethodILForAsyncMethod(MethodDesc method) { - Debug.Assert(method.IsAsync); + Debug.Assert(method.IsAsync && method is EcmaMethod); if (method.Signature.ReturnsTaskOrValueTask()) { - return AsyncThunkILEmitter.EmitTaskReturningThunk(method, ((CompilerTypeSystemContext)method.Context).GetAsyncVariantMethod(method)); + return AsyncThunkILEmitter.EmitTaskReturningThunk(method, method.GetAsyncVariant()); } // We only allow non-Task returning runtime async methods in CoreLib // Skip this method @@ -215,22 +253,21 @@ public override MethodIL GetMethodIL(MethodDesc method) // portion of compilation, and CreateCrossModuleInlineableTokensForILBody // will produce tokens which are order dependent thus violating the determinism // principles of the compiler. - if (!_manifestModuleWrappedMethods.TryGetValue(ecmaMethod, out var methodIL)) - { - if (NeedsTaskReturningThunk(ecmaMethod)) - { - methodIL = GetMethodILForAsyncMethod(ecmaMethod); - } - else - { - methodIL = EcmaMethodIL.Create(ecmaMethod); - } - } + if (_manifestModuleWrappedMethods.TryGetValue(ecmaMethod, out var methodIL)) + return methodIL; - if (methodIL != null) + return NeedsTaskReturningThunk(ecmaMethod) ? + GetMethodILForAsyncMethod(ecmaMethod) + : EcmaMethodIL.Create(ecmaMethod); + } + else if (method is AsyncMethodVariant amv) + { + if (_manifestModuleWrappedMethods.TryGetValue(amv, out var methodIL)) return methodIL; - return null; + return NeedsAsyncThunk(amv) ? + AsyncThunkILEmitter.EmitAsyncMethodThunk(amv, method.GetTargetOfAsyncVariant()) + : new AsyncEcmaMethodIL(amv, EcmaMethodIL.Create((EcmaMethod)method.GetTargetOfAsyncVariant())); } else if (method is MethodForInstantiatedType || method is InstantiatedMethod) { @@ -247,6 +284,13 @@ public override MethodIL GetMethodIL(MethodDesc method) return null; return new InstantiatedMethodIL(method, methodDefinitionIL); } + else if (method is AsyncResumptionStub ars) + { + if (_manifestModuleWrappedMethods.TryGetValue(ars, out var methodil)) + return methodil; + CreateCrossModuleInlineableTokensForILBody(ars); + return _manifestModuleWrappedMethods[ars]; + } else { return null; @@ -412,5 +456,28 @@ public override object GetObject(int token, NotFoundBehavior notFoundBehavior = return result; } } + + public sealed class AsyncEcmaMethodIL : MethodIL, IEcmaMethodIL + { + private readonly AsyncMethodVariant _variant; + private readonly EcmaMethodIL _ecmaIL; + + public AsyncEcmaMethodIL(AsyncMethodVariant variant, EcmaMethodIL ecmaIL) + => (_variant, _ecmaIL) = (variant, ecmaIL); + + // This is the reason we need this class - the method that owns the IL is the variant. + public override MethodDesc OwningMethod => _variant; + + // Everything else dispatches to EcmaMethodIL + public override MethodDebugInformation GetDebugInfo() => _ecmaIL.GetDebugInfo(); + public override ILExceptionRegion[] GetExceptionRegions() => _ecmaIL.GetExceptionRegions(); + public override byte[] GetILBytes() => _ecmaIL.GetILBytes(); + public override LocalVariableDefinition[] GetLocals() => _ecmaIL.GetLocals(); + public override object GetObject(int token, NotFoundBehavior notFoundBehavior = NotFoundBehavior.Throw) => _ecmaIL.GetObject(token, notFoundBehavior); + public override bool IsInitLocals => _ecmaIL.IsInitLocals; + public override int MaxStack => _ecmaIL.MaxStack; + + public IEcmaModule Module => _ecmaIL.Module; + } } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index 630cb594c6b04b..7471371934b4c0 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -61,6 +61,7 @@ + diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index c65197b6681a94..3e5fc315e491a1 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -63,14 +63,14 @@ public FieldWithToken(FieldDesc field, ModuleToken token) var memberRef = token.MetadataReader.GetMemberReference((MemberReferenceHandle)token.Handle); switch (memberRef.Parent.Kind) { - case HandleKind.TypeDefinition: - case HandleKind.TypeReference: - case HandleKind.TypeSpecification: - OwningTypeNotDerivedFromToken = token.Module.GetType(memberRef.Parent) != field.OwningType; - break; + case HandleKind.TypeDefinition: + case HandleKind.TypeReference: + case HandleKind.TypeSpecification: + OwningTypeNotDerivedFromToken = token.Module.GetType(memberRef.Parent) != field.OwningType; + break; - default: - break; + default: + break; } } } @@ -133,8 +133,7 @@ public class MethodWithToken public readonly bool OwningTypeNotDerivedFromToken; public readonly TypeDesc OwningType; - - public MethodWithToken(MethodDesc method, ModuleToken token, TypeDesc constrainedType, bool unboxing, object context, TypeDesc devirtualizedMethodOwner = null) + public MethodWithToken(MethodDesc method, ModuleToken token, TypeDesc constrainedType, bool unboxing, TypeSystemEntity context, TypeDesc devirtualizedMethodOwner = null) { Debug.Assert(!method.IsUnboxingThunk()); Method = method; @@ -144,13 +143,17 @@ public MethodWithToken(MethodDesc method, ModuleToken token, TypeDesc constraine OwningType = GetMethodTokenOwningType(this, constrainedType, context, devirtualizedMethodOwner, out OwningTypeNotDerivedFromToken); if (method.IsAsync && method.IsAsyncVariant() && token.Module is MutableModule) { - var ecmaMethod = (EcmaMethod)method.GetTypicalMethodDefinition().GetPrimaryMethodDesc(); - Token = new (ecmaMethod.Module, ecmaMethod.Handle); - OwningTypeNotDerivedFromToken = true; + var ecmaMethod = (EcmaMethod)method.GetPrimaryMethodDesc().GetTypicalMethodDefinition(); + var newToken = new ModuleToken(ecmaMethod.Module, ecmaMethod.Handle); + Token = newToken; + if (Token.Module.GetObject((EntityHandle)Token.Handle) != newToken.Module.GetObject((EntityHandle)newToken.Handle)) + { + OwningTypeNotDerivedFromToken = true; + } } } - private static TypeDesc GetMethodTokenOwningType(MethodWithToken methodToken, TypeDesc constrainedType, object context, TypeDesc devirtualizedMethodOwner, out bool owningTypeNotDerivedFromToken) + private static TypeDesc GetMethodTokenOwningType(MethodWithToken methodToken, TypeDesc constrainedType, TypeSystemEntity context, TypeDesc devirtualizedMethodOwner, out bool owningTypeNotDerivedFromToken) { ModuleToken moduleToken = methodToken.Token; owningTypeNotDerivedFromToken = false; @@ -176,16 +179,16 @@ private static TypeDesc GetMethodTokenOwningType(MethodWithToken methodToken, Ty case HandleKind.TypeDefinition: case HandleKind.TypeReference: case HandleKind.TypeSpecification: - { - Debug.Assert(devirtualizedMethodOwner == null); // Devirtualization is expected to always use a methoddef token - return HandleContext(moduleToken.Module, memberRef.Parent, methodToken.Method.OwningType, constrainedType, context, null, ref owningTypeNotDerivedFromToken); - } + { + Debug.Assert(devirtualizedMethodOwner == null); // Devirtualization is expected to always use a methoddef token + return HandleContext(moduleToken.Module, memberRef.Parent, methodToken.Method.OwningType, constrainedType, context, null, ref owningTypeNotDerivedFromToken); + } default: return methodToken.Method.OwningType; } - TypeDesc HandleContext(IEcmaModule module, EntityHandle handle, TypeDesc methodTargetOwner, TypeDesc constrainedType, object context, TypeDesc devirtualizedMethodOwner, ref bool owningTypeNotDerivedFromToken) + TypeDesc HandleContext(IEcmaModule module, EntityHandle handle, TypeDesc methodTargetOwner, TypeDesc constrainedType, TypeSystemEntity context, TypeDesc devirtualizedMethodOwner, ref bool owningTypeNotDerivedFromToken) { var tokenOnlyOwningType = module.GetType(handle); TypeDesc actualOwningType; @@ -331,7 +334,7 @@ public bool Equals(MethodWithToken methodWithToken) && Unboxing == methodWithToken.Unboxing; if (equals) { - Debug.Assert(OwningTypeNotDerivedFromToken == methodWithToken.OwningTypeNotDerivedFromToken); + //Debug.Assert(OwningTypeNotDerivedFromToken == methodWithToken.OwningTypeNotDerivedFromToken); Debug.Assert(OwningType == methodWithToken.OwningType); } @@ -358,6 +361,8 @@ public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) sb.Append("; UNBOXING"u8); if (Method.IsAsyncVariant()) sb.Append("; ASYNC"u8); + if (Method is AsyncResumptionStub) + sb.Append("; RESUME"u8); } public override string ToString() @@ -564,8 +569,7 @@ public static bool ShouldSkipCompilation(InstructionSetSupport instructionSetSup public static bool ShouldCodeNotBeCompiledIntoFinalImage(InstructionSetSupport instructionSetSupport, MethodDesc method) { - EcmaMethod ecmaMethod = method.GetTypicalMethodDefinition().GetPrimaryMethodDesc() as EcmaMethod; - + EcmaMethod ecmaMethod = (EcmaMethod)(method.GetPrimaryMethodDesc().GetTypicalMethodDefinition()); var metadataReader = ecmaMethod.MetadataReader; var stringComparer = metadataReader.StringComparer; @@ -850,96 +854,96 @@ private bool getReadyToRunHelper(ref CORINFO_RESOLVED_TOKEN pResolvedToken, ref switch (id) { case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_NEW: - { - var type = HandleToObject(pResolvedToken.hClass); - Debug.Assert(type.IsDefType); - if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) - return false; + { + var type = HandleToObject(pResolvedToken.hClass); + Debug.Assert(type.IsDefType); + if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) + return false; - pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.NewHelper, type)); - } - break; + pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.NewHelper, type)); + } + break; case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_NEWARR_1: - { - var type = HandleToObject(pResolvedToken.hClass); - Debug.Assert(type.IsSzArray); - if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) - return false; + { + var type = HandleToObject(pResolvedToken.hClass); + Debug.Assert(type.IsSzArray); + if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) + return false; - pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.NewArr1, type)); - } - break; + pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.NewArr1, type)); + } + break; case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_ISINSTANCEOF: - { - var type = HandleToObject(pResolvedToken.hClass); - if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) - return false; + { + var type = HandleToObject(pResolvedToken.hClass); + if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) + return false; - // ECMA-335 III.4.3: If typeTok is a nullable type, Nullable, it is interpreted as "boxed" T - if (type.IsNullable) - type = type.Instantiation[0]; + // ECMA-335 III.4.3: If typeTok is a nullable type, Nullable, it is interpreted as "boxed" T + if (type.IsNullable) + type = type.Instantiation[0]; - pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.IsInstanceOf, type)); - } - break; + pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.IsInstanceOf, type)); + } + break; case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_CHKCAST: - { - var type = HandleToObject(pResolvedToken.hClass); - if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) - return false; + { + var type = HandleToObject(pResolvedToken.hClass); + if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) + return false; - // ECMA-335 III.4.3: If typeTok is a nullable type, Nullable, it is interpreted as "boxed" T - if (type.IsNullable) - type = type.Instantiation[0]; + // ECMA-335 III.4.3: If typeTok is a nullable type, Nullable, it is interpreted as "boxed" T + if (type.IsNullable) + type = type.Instantiation[0]; - pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.CastClass, type)); - } - break; + pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.CastClass, type)); + } + break; case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_GCSTATIC_BASE: case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_NONGCSTATIC_BASE: case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_THREADSTATIC_BASE: case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_NONGCTHREADSTATIC_BASE: + { + var type = HandleToObject(pResolvedToken.hClass); + if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) + return false; + var helperId = GetReadyToRunHelperFromStaticBaseHelper(id); + pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(helperId, type)); + } + break; + case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_GENERIC_HANDLE: + { + Debug.Assert(pGenericLookupKind.needsRuntimeLookup); + + ReadyToRunHelperId helperId = (ReadyToRunHelperId)pGenericLookupKind.runtimeLookupFlags; + TypeDesc constrainedType = null; + if (helperId == ReadyToRunHelperId.MethodEntry && pGenericLookupKind.runtimeLookupArgs != null) { - var type = HandleToObject(pResolvedToken.hClass); - if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) - return false; - var helperId = GetReadyToRunHelperFromStaticBaseHelper(id); - pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(helperId, type)); + constrainedType = (TypeDesc)GetRuntimeDeterminedObjectForToken(ref *(CORINFO_RESOLVED_TOKEN*)pGenericLookupKind.runtimeLookupArgs); + _compilation.NodeFactory.DetectGenericCycles(MethodBeingCompiled, constrainedType); } - break; - case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_GENERIC_HANDLE: + object helperArg = GetRuntimeDeterminedObjectForToken(ref pResolvedToken); + if (helperArg is MethodDesc methodDesc) { - Debug.Assert(pGenericLookupKind.needsRuntimeLookup); - - ReadyToRunHelperId helperId = (ReadyToRunHelperId)pGenericLookupKind.runtimeLookupFlags; - TypeDesc constrainedType = null; - if (helperId == ReadyToRunHelperId.MethodEntry && pGenericLookupKind.runtimeLookupArgs != null) - { - constrainedType = (TypeDesc)GetRuntimeDeterminedObjectForToken(ref *(CORINFO_RESOLVED_TOKEN*)pGenericLookupKind.runtimeLookupArgs); - _compilation.NodeFactory.DetectGenericCycles(MethodBeingCompiled, constrainedType); - } - object helperArg = GetRuntimeDeterminedObjectForToken(ref pResolvedToken); - if (helperArg is MethodDesc methodDesc) - { - var methodIL = HandleToObject(pResolvedToken.tokenScope); - MethodDesc sharedMethod = methodIL.OwningMethod.GetSharedRuntimeFormMethodTarget(); - _compilation.NodeFactory.DetectGenericCycles(MethodBeingCompiled, sharedMethod); - helperArg = new MethodWithToken(methodDesc, HandleToModuleToken(ref pResolvedToken), constrainedType, unboxing: false, context: sharedMethod); - } - else if (helperArg is FieldDesc fieldDesc) - { - helperArg = new FieldWithToken(fieldDesc, HandleToModuleToken(ref pResolvedToken)); - } - - var methodContext = new GenericContext(HandleToObject(callerHandle)); - ISymbolNode helper = _compilation.SymbolNodeFactory.GenericLookupHelper( - pGenericLookupKind.runtimeLookupKind, - helperId, - helperArg, - methodContext); - pLookup = CreateConstLookupToSymbol(helper); + var methodIL = HandleToObject(pResolvedToken.tokenScope); + MethodDesc sharedMethod = methodIL.OwningMethod.GetSharedRuntimeFormMethodTarget(); + _compilation.NodeFactory.DetectGenericCycles(MethodBeingCompiled, sharedMethod); + helperArg = new MethodWithToken(methodDesc, HandleToModuleToken(ref pResolvedToken), constrainedType, unboxing: false, context: sharedMethod); } - break; + else if (helperArg is FieldDesc fieldDesc) + { + helperArg = new FieldWithToken(fieldDesc, HandleToModuleToken(ref pResolvedToken)); + } + + var methodContext = new GenericContext(HandleToObject(callerHandle)); + ISymbolNode helper = _compilation.SymbolNodeFactory.GenericLookupHelper( + pGenericLookupKind.runtimeLookupKind, + helperId, + helperArg, + methodContext); + pLookup = CreateConstLookupToSymbol(helper); + } + break; default: throw new NotImplementedException("ReadyToRun: " + id.ToString()); } @@ -959,8 +963,8 @@ private void getReadyToRunDelegateCtorHelper(ref CORINFO_RESOLVED_TOKEN pTargetM MethodDesc targetMethodDesc = HandleToObject(pTargetMethod.hMethod); Debug.Assert(!targetMethodDesc.IsUnboxingThunk()); - var typeOrMethodContext = (pTargetMethod.tokenContext == contextFromMethodBeingCompiled()) ? - MethodBeingCompiled : HandleToObject((void*)pTargetMethod.tokenContext); + TypeSystemEntity typeOrMethodContext = (TypeSystemEntity)((pTargetMethod.tokenContext == contextFromMethodBeingCompiled()) ? + MethodBeingCompiled : HandleToObject((void*)pTargetMethod.tokenContext)); TypeDesc constrainedType = null; if (targetConstraint != 0) @@ -990,6 +994,9 @@ private ISymbolNode GetHelperFtnUncached(CorInfoHelpFunc ftnNum) case CorInfoHelpFunc.CORINFO_HELP_THROW: id = ReadyToRunHelper.Throw; break; + case CorInfoHelpFunc.CORINFO_HELP_THROWEXACT: + id = ReadyToRunHelper.ThrowExact; + break; case CorInfoHelpFunc.CORINFO_HELP_RETHROW: id = ReadyToRunHelper.Rethrow; break; @@ -1279,6 +1286,33 @@ private ISymbolNode GetHelperFtnUncached(CorInfoHelpFunc ftnNum) id = ReadyToRunHelper.ReversePInvokeExit; break; + case CorInfoHelpFunc.CORINFO_HELP_ALLOC_CONTINUATION: + { + + var method = _compilation.NodeFactory.TypeSystemContext.GetCoreLibEntryPoint("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8, "AllocContinuation"u8, null); + var methodWithToken = new MethodWithToken(method, _compilation.CompilationModuleGroup.Resolver.GetModuleTokenForMethod(method, true, true), null, false, null); + return _compilation.NodeFactory.MethodEntrypoint(methodWithToken, false, false, false); + //return _compilation.NodeFactory.MethodEntrypoint() + //id = ReadyToRunHelper.AllocContinuation; + //break; + } + case CorInfoHelpFunc.CORINFO_HELP_ALLOC_CONTINUATION_METHOD: + { + var method = _compilation.NodeFactory.TypeSystemContext.GetCoreLibEntryPoint("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8, "AllocContinuationMethod"u8, null); + var methodWithToken = new MethodWithToken(method, _compilation.CompilationModuleGroup.Resolver.GetModuleTokenForMethod(method, true, true), null, false, null); + return _compilation.NodeFactory.MethodEntrypoint(methodWithToken, false, false, false); + //id = ReadyToRunHelper.AllocContinuationMethod; + //break; + } + case CorInfoHelpFunc.CORINFO_HELP_ALLOC_CONTINUATION_CLASS: + { + var method = _compilation.NodeFactory.TypeSystemContext.GetCoreLibEntryPoint("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8, "AllocContinuationClass"u8, null); + var methodWithToken = new MethodWithToken(method, _compilation.CompilationModuleGroup.Resolver.GetModuleTokenForMethod(method, true, true), null, false, null); + return _compilation.NodeFactory.MethodEntrypoint(methodWithToken, false, false, false); + //id = ReadyToRunHelper.AllocContinuationClass; + //break; + } + case CorInfoHelpFunc.CORINFO_HELP_INITCLASS: case CorInfoHelpFunc.CORINFO_HELP_INITINSTCLASS: case CorInfoHelpFunc.CORINFO_HELP_GETSYNCFROMCLASSHANDLE: @@ -1307,7 +1341,9 @@ private ISymbolNode GetHelperFtnUncached(CorInfoHelpFunc ftnNum) private void getFunctionEntryPoint(CORINFO_METHOD_STRUCT_* ftn, ref CORINFO_CONST_LOOKUP pResult, CORINFO_ACCESS_FLAGS accessFlags) { - throw new RequiresRuntimeJitException(HandleToObject(ftn).ToString()); + var method = HandleToObject(ftn); + var entrypoint = _compilation.NodeFactory.MethodEntrypoint(new MethodWithToken(method, _compilation.NodeFactory.Resolver.GetModuleTokenForMethod(method, true, true), null, false, MethodBeingCompiled), false, false, false); + pResult = CreateConstLookupToSymbol(entrypoint); } private bool canTailCall(CORINFO_METHOD_STRUCT_* callerHnd, CORINFO_METHOD_STRUCT_* declaredCalleeHnd, CORINFO_METHOD_STRUCT_* exactCalleeHnd, bool fIsTailPrefix) @@ -1354,7 +1390,7 @@ private FieldWithToken ComputeFieldWithToken(FieldDesc field, ref CORINFO_RESOLV private MethodWithToken ComputeMethodWithToken(MethodDesc method, ref CORINFO_RESOLVED_TOKEN pResolvedToken, TypeDesc constrainedType, bool unboxing) { - ModuleToken token = HandleToModuleToken(ref pResolvedToken, method, out object context, ref constrainedType); + ModuleToken token = HandleToModuleToken(ref pResolvedToken, method, out TypeSystemEntity context, ref constrainedType); TypeDesc devirtualizedMethodOwner = null; if (pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_DevirtualizedMethod) @@ -1365,7 +1401,7 @@ private MethodWithToken ComputeMethodWithToken(MethodDesc method, ref CORINFO_RE return new MethodWithToken(method, token, constrainedType: constrainedType, unboxing: unboxing, context: context, devirtualizedMethodOwner: devirtualizedMethodOwner); } - private ModuleToken HandleToModuleToken(ref CORINFO_RESOLVED_TOKEN pResolvedToken, MethodDesc methodDesc, out object context, ref TypeDesc constrainedType) + private ModuleToken HandleToModuleToken(ref CORINFO_RESOLVED_TOKEN pResolvedToken, MethodDesc methodDesc, out TypeSystemEntity context, ref TypeDesc constrainedType) { if (methodDesc != null && (_compilation.NodeFactory.CompilationModuleGroup.VersionsWithMethodBody(methodDesc) || (pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_DevirtualizedMethod) @@ -1424,7 +1460,7 @@ private ModuleToken HandleToModuleToken(ref CORINFO_RESOLVED_TOKEN pResolvedToke // It's okay to strip the instantiation away because we don't need a MethodSpec // token - SignatureBuilder will generate the generic method signature // using instantiation parameters from the MethodDesc entity. - resultMethod = resultMethod.GetTypicalMethodDefinition().GetPrimaryMethodDesc(); + resultMethod = resultMethod.GetPrimaryMethodDesc().GetTypicalMethodDefinition(); if (!_compilation.NodeFactory.CompilationModuleGroup.VersionsWithType(resultMethod.OwningType)) { @@ -1449,24 +1485,7 @@ private ModuleToken HandleToModuleToken(ref CORINFO_RESOLVED_TOKEN pResolvedToke } else { - if (resultDef is EcmaType ecmaType) - { - if (!_compilation.NodeFactory.CompilationModuleGroup.VersionsWithType(ecmaType)) - { - ModuleToken result = _compilation.NodeFactory.Resolver.GetModuleTokenForType(ecmaType, allowDynamicallyCreatedReference: true, throwIfNotFound: true); - return result; - } - token = (mdToken)MetadataTokens.GetToken(ecmaType.Handle); - module = ecmaType.Module; - } - else - { - // To replace !!0, we need to find the token for a !!0 TypeSpec within the image. - Debug.Assert(resultDef is SignatureMethodVariable); - Debug.Assert(((SignatureMethodVariable)resultDef).Index == 0); - module = (EcmaModule)((MetadataType)methodILDef.OwningMethod.OwningType).Module; - token = FindGenericMethodArgTypeSpec((EcmaModule)module); - } + return GetModuleTokenForType((TypeSystemEntity)resultDef); } } else @@ -1475,6 +1494,30 @@ private ModuleToken HandleToModuleToken(ref CORINFO_RESOLVED_TOKEN pResolvedToke } return new ModuleToken(module, token); + + ModuleToken GetModuleTokenForType(TypeSystemEntity resultDef) + { + switch (resultDef) + { + case SignatureMethodVariable sigMethod: + Debug.Assert(sigMethod.Index == 0); + module = (EcmaModule)((MetadataType)methodILDef.OwningMethod.OwningType).Module; + token = FindGenericMethodArgTypeSpec((EcmaModule)module); + return new ModuleToken(module, token); + case ParameterizedType paramType: + return _compilation.NodeFactory.Resolver.GetModuleTokenForType(paramType, allowDynamicallyCreatedReference: true, throwIfNotFound: true); + case EcmaType ecmaType: + if (!_compilation.NodeFactory.CompilationModuleGroup.VersionsWithType(ecmaType)) + { + return _compilation.NodeFactory.Resolver.GetModuleTokenForType(ecmaType, allowDynamicallyCreatedReference: true, throwIfNotFound: true); + } + token = (mdToken)MetadataTokens.GetToken(ecmaType.Handle); + module = ecmaType.Module; + return new ModuleToken(module, token); + default: + throw new NotImplementedException($"Unsupported token resolution for {resultDef.GetType()}"); + } + } } private InfoAccessType constructStringLiteral(CORINFO_MODULE_STRUCT_* module, mdToken metaTok, ref void* ppValue) @@ -1815,18 +1858,18 @@ private void getFieldInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_MET fieldOffset = 0; } else - if (helperId != ReadyToRunHelperId.Invalid) - { - if (_compilation.SymbolNodeFactory.VerifyTypeAndFieldLayout && (fieldOffset <= FieldFixupSignature.MaxCheckableOffset)) + if (helperId != ReadyToRunHelperId.Invalid) { - // ENCODE_CHECK_FIELD_OFFSET - AddPrecodeFixup(_compilation.SymbolNodeFactory.CheckFieldOffset(ComputeFieldWithToken(field, ref pResolvedToken))); - } + if (_compilation.SymbolNodeFactory.VerifyTypeAndFieldLayout && (fieldOffset <= FieldFixupSignature.MaxCheckableOffset)) + { + // ENCODE_CHECK_FIELD_OFFSET + AddPrecodeFixup(_compilation.SymbolNodeFactory.CheckFieldOffset(ComputeFieldWithToken(field, ref pResolvedToken))); + } - pResult->fieldLookup = CreateConstLookupToSymbol( - _compilation.SymbolNodeFactory.CreateReadyToRunHelper(helperId, field.OwningType) - ); - } + pResult->fieldLookup = CreateConstLookupToSymbol( + _compilation.SymbolNodeFactory.CreateReadyToRunHelper(helperId, field.OwningType) + ); + } } } else @@ -1921,7 +1964,7 @@ private void ceeInfoGetCallInfo( throw new RequiresRuntimeJitException(callerMethod.ToString() + " -> " + originalMethod.ToString()); } - callerModule = ((EcmaMethod)callerMethod.GetTypicalMethodDefinition()).Module; + callerModule = ((EcmaMethod)callerMethod.GetPrimaryMethodDesc().GetTypicalMethodDefinition()).Module; bool isCallVirt = (flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_CALLVIRT) != 0; bool isLdftn = (flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_LDFTN) != 0; bool isStaticVirtual = (originalMethod.Signature.IsStatic && originalMethod.IsVirtual); @@ -2473,21 +2516,21 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO switch (pResult->kind) { case CORINFO_CALL_KIND.CORINFO_VIRTUALCALL_STUB: + { + if (pResult->codePointerOrStubLookup.lookupKind.needsRuntimeLookup) { - if (pResult->codePointerOrStubLookup.lookupKind.needsRuntimeLookup) - { - return; - } + return; + } - pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol( - _compilation.SymbolNodeFactory.InterfaceDispatchCell( - ComputeMethodWithToken(targetMethod, ref pResolvedToken, constrainedType: null, unboxing: false), - MethodBeingCompiled)); + pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol( + _compilation.SymbolNodeFactory.InterfaceDispatchCell( + ComputeMethodWithToken(targetMethod, ref pResolvedToken, constrainedType: null, unboxing: false), + MethodBeingCompiled)); - // If the abi of the method isn't stable, this will cause a usage of the RequiresRuntimeJitSymbol, which will trigger a RequiresRuntimeJitException - UpdateConstLookupWithRequiresRuntimeJitSymbolIfNeeded(ref pResult->codePointerOrStubLookup.constLookup, targetMethod); - } - break; + // If the abi of the method isn't stable, this will cause a usage of the RequiresRuntimeJitSymbol, which will trigger a RequiresRuntimeJitException + UpdateConstLookupWithRequiresRuntimeJitSymbolIfNeeded(ref pResult->codePointerOrStubLookup.constLookup, targetMethod); + } + break; case CORINFO_CALL_KIND.CORINFO_CALL_CODE_POINTER: @@ -2501,41 +2544,41 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO return; case CORINFO_CALL_KIND.CORINFO_CALL: - { - // Constrained token is not interesting with this transforms - if (pResult->thisTransform != CORINFO_THIS_TRANSFORM.CORINFO_NO_THIS_TRANSFORM) - constrainedType = null; - - MethodDesc nonUnboxingMethod = methodToCall; - bool isUnboxingStub = methodToCall.IsUnboxingThunk(); - if (isUnboxingStub) - { - nonUnboxingMethod = methodToCall.GetUnboxedMethod(); - } - if (nonUnboxingMethod is IL.Stubs.PInvokeTargetNativeMethod rawPinvoke) - { - nonUnboxingMethod = rawPinvoke.Target; - } + { + // Constrained token is not interesting with this transforms + if (pResult->thisTransform != CORINFO_THIS_TRANSFORM.CORINFO_NO_THIS_TRANSFORM) + constrainedType = null; - if (methodToCall.OwningType.IsArray && methodToCall.IsConstructor) - { - pResult->codePointerOrStubLookup.constLookup = default; - } - else - { - // READYTORUN: FUTURE: Direct calls if possible - pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol( - _compilation.NodeFactory.MethodEntrypoint( - ComputeMethodWithToken(nonUnboxingMethod, ref pResolvedToken, constrainedType, unboxing: isUnboxingStub), - isInstantiatingStub: useInstantiatingStub, - isPrecodeImportRequired: (flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_LDFTN) != 0, - isJumpableImportRequired: false)); - } + MethodDesc nonUnboxingMethod = methodToCall; + bool isUnboxingStub = methodToCall.IsUnboxingThunk(); + if (isUnboxingStub) + { + nonUnboxingMethod = methodToCall.GetUnboxedMethod(); + } + if (nonUnboxingMethod is IL.Stubs.PInvokeTargetNativeMethod rawPinvoke) + { + nonUnboxingMethod = rawPinvoke.Target; + } - // If the abi of the method isn't stable, this will cause a usage of the RequiresRuntimeJitSymbol, which will trigger a RequiresRuntimeJitException - UpdateConstLookupWithRequiresRuntimeJitSymbolIfNeeded(ref pResult->codePointerOrStubLookup.constLookup, targetMethod); + if (methodToCall.OwningType.IsArray && methodToCall.IsConstructor) + { + pResult->codePointerOrStubLookup.constLookup = default; } - break; + else + { + // READYTORUN: FUTURE: Direct calls if possible + pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol( + _compilation.NodeFactory.MethodEntrypoint( + ComputeMethodWithToken(nonUnboxingMethod, ref pResolvedToken, constrainedType, unboxing: isUnboxingStub), + isInstantiatingStub: useInstantiatingStub, + isPrecodeImportRequired: (flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_LDFTN) != 0, + isJumpableImportRequired: false)); + } + + // If the abi of the method isn't stable, this will cause a usage of the RequiresRuntimeJitSymbol, which will trigger a RequiresRuntimeJitException + UpdateConstLookupWithRequiresRuntimeJitSymbolIfNeeded(ref pResult->codePointerOrStubLookup.constLookup, targetMethod); + } + break; case CORINFO_CALL_KIND.CORINFO_VIRTUALCALL_VTABLE: // Only calls within the CoreLib version bubble support fragile NI codegen with vtable based calls, for better performance (because @@ -2575,6 +2618,10 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO MethodDesc canonMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); if (canonMethod.RequiresInstMethodDescArg()) { + //if (canonMethod.IsAsyncCall()) + //{ + // throw new RequiresRuntimeJitException(""); + //} pResult->instParamLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper( ReadyToRunHelperId.MethodDictionary, ComputeMethodWithToken(targetMethod, ref pResolvedToken, constrainedType: constrainedType, unboxing: false))); @@ -2650,17 +2697,17 @@ private void ComputeRuntimeLookupForSharedGenericToken( case DictionaryEntryKind.MethodEntrySlot: case DictionaryEntryKind.ConstrainedMethodEntrySlot: case DictionaryEntryKind.DispatchStubAddrSlot: - { - if (entryKind == DictionaryEntryKind.MethodDescSlot) - pResultLookup.lookupKind.runtimeLookupFlags = (ushort)ReadyToRunHelperId.MethodHandle; - else if (entryKind == DictionaryEntryKind.MethodEntrySlot || entryKind == DictionaryEntryKind.ConstrainedMethodEntrySlot) - pResultLookup.lookupKind.runtimeLookupFlags = (ushort)ReadyToRunHelperId.MethodEntry; - else - pResultLookup.lookupKind.runtimeLookupFlags = (ushort)ReadyToRunHelperId.VirtualDispatchCell; + { + if (entryKind == DictionaryEntryKind.MethodDescSlot) + pResultLookup.lookupKind.runtimeLookupFlags = (ushort)ReadyToRunHelperId.MethodHandle; + else if (entryKind == DictionaryEntryKind.MethodEntrySlot || entryKind == DictionaryEntryKind.ConstrainedMethodEntrySlot) + pResultLookup.lookupKind.runtimeLookupFlags = (ushort)ReadyToRunHelperId.MethodEntry; + else + pResultLookup.lookupKind.runtimeLookupFlags = (ushort)ReadyToRunHelperId.VirtualDispatchCell; - pResultLookup.lookupKind.runtimeLookupArgs = pConstrainedResolvedToken; - break; - } + pResultLookup.lookupKind.runtimeLookupArgs = pConstrainedResolvedToken; + break; + } case DictionaryEntryKind.FieldDescSlot: pResultLookup.lookupKind.runtimeLookupFlags = (ushort)ReadyToRunHelperId.FieldHandle; @@ -2774,6 +2821,17 @@ private void ceeInfoEmbedGenericHandle(ref CORINFO_RESOLVED_TOKEN pResolvedToken private CORINFO_CLASS_STRUCT_* embedClassHandle(CORINFO_CLASS_STRUCT_* handle, ref void* ppIndirection) { TypeDesc type = HandleToObject(handle); + // Continuations require special handling to encode the layout of the type. + // The TypeSignature format used in TypeHandles is not sufficient for encoding specific continuation layouts + // PrecodeHelper( + // TypeHandle(ReadyToRunFixupKind.ContinuationLayout) + // ) + if (type is AsyncContinuationType act) + { + Import import = (Import)_compilation.SymbolNodeFactory.ContinuationTypeSymbol(act); + ppIndirection = (void*)ObjectToHandle(import); + return null; + } if (!_compilation.CompilationModuleGroup.VersionsWithType(type)) throw new RequiresRuntimeJitException(type.ToString()); @@ -2811,26 +2869,26 @@ private void embedGenericHandle(ref CORINFO_RESOLVED_TOKEN pResolvedToken, bool break; case CorInfoGenericHandleType.CORINFO_HANDLETYPE_METHOD: - { - MethodDesc md = HandleToObject(pResolvedToken.hMethod); - TypeDesc td = HandleToObject(pResolvedToken.hClass); - - bool unboxingStub = false; - // - // This logic should be kept in sync with MethodTableBuilder::NeedsTightlyBoundUnboxingStub - // Essentially all ValueType virtual methods will require an Unboxing Stub - // - if ((td.IsValueType) && !md.Signature.IsStatic - && md.IsVirtual) - { - unboxingStub = true; - } + { + MethodDesc md = HandleToObject(pResolvedToken.hMethod); + TypeDesc td = HandleToObject(pResolvedToken.hClass); - symbolNode = _compilation.SymbolNodeFactory.CreateReadyToRunHelper( - ReadyToRunHelperId.MethodHandle, - ComputeMethodWithToken(md, ref pResolvedToken, constrainedType: null, unboxing: unboxingStub)); + bool unboxingStub = false; + // + // This logic should be kept in sync with MethodTableBuilder::NeedsTightlyBoundUnboxingStub + // Essentially all ValueType virtual methods will require an Unboxing Stub + // + if ((td.IsValueType) && !md.Signature.IsStatic + && md.IsVirtual) + { + unboxingStub = true; } - break; + + symbolNode = _compilation.SymbolNodeFactory.CreateReadyToRunHelper( + ReadyToRunHelperId.MethodHandle, + ComputeMethodWithToken(md, ref pResolvedToken, constrainedType: null, unboxing: unboxingStub)); + } + break; case CorInfoGenericHandleType.CORINFO_HANDLETYPE_FIELD: symbolNode = _compilation.SymbolNodeFactory.CreateReadyToRunHelper( @@ -2977,7 +3035,7 @@ private bool TypeCannotUseBasePlusOffsetEncoding(MetadataType type) private void getGSCookie(IntPtr* pCookieVal, IntPtr** ppCookieVal) { *pCookieVal = IntPtr.Zero; - *ppCookieVal = (IntPtr *)ObjectToHandle(_compilation.NodeFactory.GetReadyToRunHelperCell(ReadyToRunHelper.GSCookie)); + *ppCookieVal = (IntPtr*)ObjectToHandle(_compilation.NodeFactory.GetReadyToRunHelperCell(ReadyToRunHelper.GSCookie)); } private int* getAddrOfCaptureThreadGlobal(ref void* ppIndirection) @@ -3242,7 +3300,7 @@ private void reportInliningDecision(CORINFO_METHOD_STRUCT_* inlinerHnd, CORINFO_ // Disable async methods in cross module inlines for now, we need to trigger the CheckILBodyFixupSignature in the right situations, and that hasn't been implemented // yet. Currently, we'll correctly trigger the _ilBodiesNeeded logic below, but we also need to avoid triggering the ILBodyFixupSignature for the async thunks, but we ALSO need to make // sure we generate the CheckILBodyFixupSignature for the actual runtime-async body in which case I think the typicalMethod will be an AsyncVariantMethod, which doesn't appear - // to be handled here. This check is here in the place where I believe we actually would behave incorrectly, but we also have a check in CrossModuleInlineable which disallows + // to be handled here. This check is here in the place where I believe we actually would behave incorrectly, but we also have a check in CrossModuleInlineable which disallows // the cross module inline of async methods currently. throw new Exception("Inlining async methods is not supported in ReadyToRun compilation."); } @@ -3276,7 +3334,6 @@ private void reportInliningDecision(CORINFO_METHOD_STRUCT_* inlinerHnd, CORINFO_ // 2. If at any time, the set of methods that are inlined includes a method which has an IL body without // tokens that are useable in compilation, record that information, and once the multi-threaded portion // of the build finishes, it will then compute the IL bodies for those methods, then run the compilation again. - if (needsTokenTranslation && !(methodIL is IMethodTokensAreUseableInCompilation) && methodIL is EcmaMethodIL) { // We may have already acquired the right type of MethodIL here, or be working with a method that is an IL Intrinsic diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PgoInfoKey.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PgoInfoKey.cs index e25f3779d9b2d6..f3df030fdcd9fc 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PgoInfoKey.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PgoInfoKey.cs @@ -33,7 +33,7 @@ public class PgoInfoKey : IEquatable /// public string SignatureString { get; } - public PgoInfoKey(IAssemblyMetadata componentReader, string owningType, EntityHandle methodHandle, string[] instanceArgs) + public PgoInfoKey(IAssemblyMetadata componentReader, string owningType, EntityHandle methodHandle, string[] instanceArgs, string[] modifiers) { ComponentReader = componentReader; EntityHandle owningTypeHandle; @@ -76,6 +76,14 @@ public PgoInfoKey(IAssemblyMetadata componentReader, string owningType, EntityHa } StringBuilder sb = new StringBuilder(); + if (modifiers != null) + { + foreach (var modifier in modifiers) + { + sb.Append(modifier); + sb.Append(" "); + } + } sb.Append(Signature.ReturnType); sb.Append(" "); sb.Append(DeclaringType); @@ -130,7 +138,7 @@ public bool Equals(PgoInfoKey other) public static PgoInfoKey FromReadyToRunMethod(ReadyToRunMethod method) { - var key = new PgoInfoKey(method.ComponentReader, method.DeclaringType, method.MethodHandle, method.InstanceArgs); + var key = new PgoInfoKey(method.ComponentReader, method.DeclaringType, method.MethodHandle, method.InstanceArgs, method.Modifiers); Debug.Assert(key.SignatureString == method.SignatureString); return key; } diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunMethod.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunMethod.cs index b5c7b3b6e10436..9cbaf1104a69ad 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunMethod.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunMethod.cs @@ -367,6 +367,7 @@ public IReadOnlyList Fixups public int RuntimeFunctionCount { get; set; } public int ColdRuntimeFunctionCount { get; set; } + public string[] Modifiers { get; } /// /// Extracts the method signature from the metadata by rid @@ -379,6 +380,7 @@ public ReadyToRunMethod( string owningType, string constrainedType, string[] instanceArgs, + string[] modifiers, int? fixupOffset) { InstanceArgs = (string[])instanceArgs?.Clone(); @@ -441,6 +443,15 @@ public ReadyToRunMethod( } StringBuilder sb = new StringBuilder(); + if (modifiers != null) + { + Modifiers = modifiers; + foreach (var modifier in modifiers) + { + sb.Append(modifier); + sb.Append(" "); + } + } sb.Append(Signature.ReturnType); sb.Append(" "); sb.Append(DeclaringType); diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs index 1b184a1a8b61f1..7ec332730a5da5 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs @@ -860,7 +860,7 @@ private void ParseMethodDefEntrypointsSection(ReadyToRunSection section, IAssemb int runtimeFunctionId; int? fixupOffset; GetRuntimeFunctionIndexFromOffset(offset, out runtimeFunctionId, out fixupOffset); - ReadyToRunMethod method = new ReadyToRunMethod(this, componentReader, methodHandle, runtimeFunctionId, owningType: null, constrainedType: null, instanceArgs: null, fixupOffset: fixupOffset); + ReadyToRunMethod method = new ReadyToRunMethod(this, componentReader, methodHandle, runtimeFunctionId, owningType: null, constrainedType: null, instanceArgs: null, modifiers: [], fixupOffset: fixupOffset); if (method.EntryPointRuntimeFunctionId < 0 || method.EntryPointRuntimeFunctionId >= isEntryPoint.Length) { @@ -1014,6 +1014,24 @@ private void ParseInstanceMethodEntrypoints(bool[] isEntryPoint) constrainedType = decoder.ReadTypeSignatureNoEmit(); } + List modifiers = []; + if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_AsyncVariant) != 0) + { + modifiers.Add("[Async]"); + } + if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_ResumptionStub) != 0) + { + modifiers.Add("[Resume]"); + } + if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_InstantiatingStub) != 0) + { + throw new NotImplementedException("Crossgen2 should not emit code for Instantiating stubs."); + } + if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_UnboxingStub) != 0) + { + throw new NotImplementedException("Crossgen2 should not emit code for Unboxing stubs."); + } + int runtimeFunctionId; int? fixupOffset; GetRuntimeFunctionIndexFromOffset((int)decoder.Offset, out runtimeFunctionId, out fixupOffset); @@ -1025,6 +1043,7 @@ private void ParseInstanceMethodEntrypoints(bool[] isEntryPoint) owningType, constrainedType, methodTypeArgs, + modifiers.ToArray(), fixupOffset); if (method.EntryPointRuntimeFunctionId >= 0 && method.EntryPointRuntimeFunctionId < isEntryPoint.Length) { @@ -1134,9 +1153,27 @@ private void ParsePgoMethods() constrainedType = decoder.ReadTypeSignatureNoEmit(); } + List modifiers = []; + if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_AsyncVariant) != 0) + { + modifiers.Add("[Async]"); + } + if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_ResumptionStub) != 0) + { + modifiers.Add("[Resume]"); + } + if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_InstantiatingStub) != 0) + { + throw new NotImplementedException("Crossgen2 should not emit code for Instantiating stubs."); + } + if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_UnboxingStub) != 0) + { + throw new NotImplementedException("Crossgen2 should not emit code for Unboxing stubs."); + } + GetPgoOffsetAndVersion(decoder.Offset, out int pgoFormatVersion, out int pgoOffset); - PgoInfoKey key = new PgoInfoKey(mdReader, owningType, methodHandle, methodTypeArgs); + PgoInfoKey key = new PgoInfoKey(mdReader, owningType, methodHandle, methodTypeArgs, modifiers.ToArray()); PgoInfo info = new PgoInfo(key, this, pgoFormatVersion, Image, pgoOffset); // Since we do non-assembly qualified name based matching for generic instantiations, we can have conflicts. diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs index 802f855b793411..e7c7527ee100af 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs @@ -1024,6 +1024,10 @@ public string GetMethodWithFlags(ReadyToRunMethodSigFlags flags, string method) { builder.Append("[ASYNC] "); } + if ((flags & ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_ResumptionStub) != 0) + { + builder.Append("[RESUME] "); + } builder.Append(method); return builder.ToString(); } @@ -1377,14 +1381,13 @@ private ReadyToRunSignature ParseSignature(ReadyToRunFixupKind fixupType, String if (!layoutFlags.HasFlag(ReadyToRunTypeLayoutFlags.READYTORUN_LAYOUT_GCLayout_Empty)) { int cbGCRefMap = (actualSize / _contextReader.TargetPointerSize + 7) / 8; - builder.Append(" GCLayout "); + builder.Append(" GCLayout 0x"); for (int i = 0; i < cbGCRefMap; i++) { builder.Append(ReadByte().ToString("X")); } } } - if (fixupType == ReadyToRunFixupKind.Check_TypeLayout) builder.Append(" (CHECK_TYPE_LAYOUT)"); else @@ -1474,6 +1477,46 @@ private ReadyToRunSignature ParseSignature(ReadyToRunFixupKind fixupType, String builder.Append(" (VERIFY_IL_BODY)"); break; + case ReadyToRunFixupKind.ContinuationLayout: + var typeBuilder = new StringBuilder(); + ParseType(typeBuilder); + builder.Append($"{typeBuilder.ToString()}"); + ReadyToRunTypeLayoutFlags layoutFlags2 = (ReadyToRunTypeLayoutFlags)ReadUInt(); + builder.Append($" Flags {layoutFlags2}"); + int actualSize2 = (int)ReadUInt(); + builder.Append($" Size {actualSize2}"); + + if (layoutFlags2.HasFlag(ReadyToRunTypeLayoutFlags.READYTORUN_LAYOUT_HFA)) + { + throw new BadImageFormatException("ContinuationLayout fixup should not have READYTORUN_LAYOUT_HFA flag set"); + } + + if (layoutFlags2.HasFlag(ReadyToRunTypeLayoutFlags.READYTORUN_LAYOUT_Alignment) + && !layoutFlags2.HasFlag(ReadyToRunTypeLayoutFlags.READYTORUN_LAYOUT_Alignment_Native)) + { + throw new BadImageFormatException("ContinuationLayout fixup should not have non-native alignment"); + } + + if (!layoutFlags2.HasFlag(ReadyToRunTypeLayoutFlags.READYTORUN_LAYOUT_GCLayout)) + { + throw new BadImageFormatException("ContinuationLayout fixup should have GCLayout"); + } + if (!layoutFlags2.HasFlag(ReadyToRunTypeLayoutFlags.READYTORUN_LAYOUT_GCLayout_Empty)) + { + int cbGCRefMap = (actualSize2 / _contextReader.TargetPointerSize + 7) / 8; + builder.Append(" GCLayout 0x"); + for (int i = 0; i < cbGCRefMap; i++) + { + builder.Append(ReadByte().ToString("X")); + } + } + else + { + builder.Append(" GCLayout 0x00 (Empty)"); + } + builder.Append(" (ContinuationLayout)"); + break; + default: throw new BadImageFormatException(); } @@ -1639,6 +1682,10 @@ private void ParseHelper(StringBuilder builder) builder.Append("THROW"); break; + case ReadyToRunHelper.ThrowExact: + builder.Append("THROW_EXACT"); + break; + case ReadyToRunHelper.Rethrow: builder.Append("RETHROW"); break; @@ -1959,6 +2006,18 @@ private void ParseHelper(StringBuilder builder) builder.Append("PERSONALITY_ROUTINE_FILTER_FUNCLET"); break; + case ReadyToRunHelper.AllocContinuation: + builder.Append("ALLOC_CONTINUATION"); + break; + + case ReadyToRunHelper.AllocContinuationMethod: + builder.Append("ALLOC_CONTINUATION_METHOD"); + break; + + case ReadyToRunHelper.AllocContinuationClass: + builder.Append("ALLOC_CONTINUATION_CLASS"); + break; + // // Deprecated/legacy // @@ -2011,7 +2070,7 @@ private void ParseHelper(StringBuilder builder) break; default: - throw new BadImageFormatException(); + throw new BadImageFormatException(helperType.ToString()); } } diff --git a/src/coreclr/vm/asynccontinuations.cpp b/src/coreclr/vm/asynccontinuations.cpp index c9f954015d76c6..f55e276c402709 100644 --- a/src/coreclr/vm/asynccontinuations.cpp +++ b/src/coreclr/vm/asynccontinuations.cpp @@ -188,37 +188,7 @@ MethodTable* AsyncContinuationsManager::CreateNewContinuationMethodTable( return pMT; } -MethodTable* AsyncContinuationsManager::CreateNewContinuationMethodTable( - unsigned dataSize, - const bool* objRefs, - MethodDesc* asyncMethod, - AllocMemTracker* pamTracker) -{ - MethodTable* pMT = CreateNewContinuationMethodTable( - dataSize, - objRefs, - GetOrCreateSingletonSubContinuationEEClass(), - m_allocator, - asyncMethod->GetLoaderModule(), - pamTracker); - -#ifdef DEBUG - StackSString debugName; - PrintContinuationName( - pMT, - [&](LPCSTR str, LPCWSTR wstr) { debugName.AppendUTF8(str); }, - [&](unsigned num) { debugName.AppendPrintf("%u", num); }); - const char* debugNameUTF8 = debugName.GetUTF8(); - size_t len = strlen(debugNameUTF8) + 1; - char* name = (char*)pamTracker->Track(m_allocator->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(len))); - strcpy_s(name, len, debugNameUTF8); - pMT->SetDebugClassName(name); -#endif - - return pMT; -} - -MethodTable* AsyncContinuationsManager::LookupOrCreateContinuationMethodTable(unsigned dataSize, const bool* objRefs, MethodDesc* asyncMethod) +MethodTable* AsyncContinuationsManager::LookupOrCreateContinuationMethodTable(unsigned dataSize, const bool* objRefs, Module* loaderModule) { STANDARD_VM_CONTRACT; @@ -237,7 +207,27 @@ MethodTable* AsyncContinuationsManager::LookupOrCreateContinuationMethodTable(un #endif AllocMemTracker amTracker; - MethodTable* pNewMT = CreateNewContinuationMethodTable(dataSize, objRefs, asyncMethod, &amTracker); + MethodTable* pNewMT = CreateNewContinuationMethodTable( + dataSize, + objRefs, + GetOrCreateSingletonSubContinuationEEClass(), + m_allocator, + loaderModule, + &amTracker); + +#ifdef DEBUG + StackSString debugName; + PrintContinuationName( + pNewMT, + [&](LPCSTR str, LPCWSTR wstr) { debugName.AppendUTF8(str); }, + [&](unsigned num) { debugName.AppendPrintf("%u", num); }); + const char* debugNameUTF8 = debugName.GetUTF8(); + size_t len = strlen(debugNameUTF8) + 1; + char* name = (char*)amTracker.Track(m_allocator->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(len))); + strcpy_s(name, len, debugNameUTF8); + pNewMT->SetDebugClassName(name); +#endif + MethodTable* pReturnedMT = pNewMT; { CrstHolder lock(&m_layoutsLock); diff --git a/src/coreclr/vm/asynccontinuations.h b/src/coreclr/vm/asynccontinuations.h index 7c4e5ccd9c03ae..5b0415b2767af9 100644 --- a/src/coreclr/vm/asynccontinuations.h +++ b/src/coreclr/vm/asynccontinuations.h @@ -47,14 +47,12 @@ class AsyncContinuationsManager CrstExplicitInit m_layoutsLock; ContinuationLayoutHashTable m_layouts; - MethodTable* CreateNewContinuationMethodTable(unsigned dataSize, const bool* objRefs, MethodDesc* asyncMethod, AllocMemTracker* pamTracker); - static MethodTable* CreateNewContinuationMethodTable(unsigned dataSize, const bool* objRefs, EEClass* eeClass, LoaderAllocator* allocator, Module* loaderModule, AllocMemTracker* pamTracker); static PTR_EEClass GetOrCreateSingletonSubContinuationEEClass(); static PTR_EEClass CreateSingletonSubContinuationEEClass(); public: AsyncContinuationsManager(LoaderAllocator* allocator); - MethodTable* LookupOrCreateContinuationMethodTable(unsigned dataSize, const bool* objRefs, MethodDesc* asyncMethod); + MethodTable* LookupOrCreateContinuationMethodTable(unsigned dataSize, const bool* objRefs, Module* loaderModule); void NotifyUnloadingClasses(); template diff --git a/src/coreclr/vm/ilstubcache.cpp b/src/coreclr/vm/ilstubcache.cpp index 7fe8b6d3951945..9ec8b3a1286333 100644 --- a/src/coreclr/vm/ilstubcache.cpp +++ b/src/coreclr/vm/ilstubcache.cpp @@ -137,6 +137,7 @@ namespace case DynamicMethodDesc::StubVirtualStaticMethodDispatch: return "IL_STUB_VirtualStaticMethodDispatch"; case DynamicMethodDesc::StubDelegateShuffleThunk: return "IL_STUB_DelegateShuffleThunk"; case DynamicMethodDesc::StubAsyncResume: return "IL_STUB_AsyncResume"; + case DynamicMethodDesc::StubAsyncResumeR2R: return "IL_STUB_AsyncResumeR2R"; default: UNREACHABLE_MSG("Unknown stub type"); } @@ -356,6 +357,101 @@ MethodDesc* ILStubCache::CreateNewMethodDesc(LoaderHeap* pCreationHeap, MethodTa RETURN pMD; } +// Creates a DynamicMethodDesc that wraps pre-compiled R2R async resumption stub code. +// Unlike regular IL stubs, this does not create a resolver or precode - it points +// directly to the R2R native code. +// +// The stub signature is: object ResumptionStub(object continuation, ref byte resultLocation) +// +// static +MethodDesc* ILStubCache::CreateR2RBackedAsyncResumptionStub( + LoaderAllocator* pAllocator, + MethodTable* pMT, + PCODE r2rEntryPoint, + AllocMemTracker* pamTracker) +{ + CONTRACT(MethodDesc*) + { + STANDARD_VM_CHECK; + PRECONDITION(CheckPointer(pAllocator)); + PRECONDITION(CheckPointer(pMT)); + PRECONDITION(r2rEntryPoint != (PCODE)NULL); + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + LoaderHeap* pCreationHeap = pAllocator->GetHighFrequencyHeap(); + + // Allocate the MethodDescChunk + // - mcDynamic: This is a dynamic method + // - fNonVtableSlot: Dynamic methods don't have vtable slots + // - fNativeCodeSlot: We need a slot for the native code pointer + // - fHasAsyncMethodData: This is an async resumption stub + MethodDescChunk* pChunk = MethodDescChunk::CreateChunk( + pCreationHeap, + 1, // count + mcDynamic, // classification + TRUE, // fNonVtableSlot + TRUE, // fNativeCodeSlot + TRUE, // fHasAsyncMethodData + pMT, + pamTracker); + + DynamicMethodDesc* pMD = (DynamicMethodDesc*)pChunk->GetFirstMethodDesc(); + + // Initialize basic MethodDesc state + pMD->SetMemberDef(0); + pMD->SetSlot(MethodTable::NO_SLOT); + pMD->SetStatic(); + + // Initialize DynamicMethodDesc flags - this is an IL stub that is public and static + pMD->InitializeFlags(DynamicMethodDesc::FlagPublic | + DynamicMethodDesc::FlagStatic | + DynamicMethodDesc::FlagIsILStub); + pMD->SetILStubType(DynamicMethodDesc::StubAsyncResumeR2R); + + // No resolver needed - code already exists in R2R image + pMD->m_pResolver = nullptr; + + // Set the method name + pMD->m_pszMethodName = GetStubMethodName(DynamicMethodDesc::StubAsyncResumeR2R); + + // Build and set the stub signature: object(object, ref byte) + // This matches BuildResumptionStubSignature in jitinterface.cpp + SigBuilder sigBuilder; + sigBuilder.AppendByte(IMAGE_CEE_CS_CALLCONV_DEFAULT); + sigBuilder.AppendData(2); // 2 arguments + sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT); // return type: object + sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT); // arg0: object (continuation) + sigBuilder.AppendElementType(ELEMENT_TYPE_BYREF); // arg1: ref byte (result location) + sigBuilder.AppendElementType(ELEMENT_TYPE_U1); + + DWORD cbSig; + PVOID pSigBytes = sigBuilder.GetSignature(&cbSig); + + PVOID pSig = pamTracker->Track(pCreationHeap->AllocMem(S_SIZE_T(cbSig))); + memcpy(pSig, pSigBytes, cbSig); + + pMD->SetStoredMethodSig((PCCOR_SIGNATURE)pSig, cbSig); + + // Set async method data flags + pMD->SetHasAsyncMethodData(); + pMD->GetAddrOfAsyncMethodData()->flags = AsyncMethodFlags::AsyncCall; + + // Set the native code directly - no precode needed since code already exists + // Use SetNativeCodeInterlocked to atomically set the entry point + pMD->SetNativeCodeInterlocked(r2rEntryPoint); + +#ifdef _DEBUG + pMD->m_pszDebugMethodName = pMD->m_pszMethodName; + pMD->m_pszDebugClassName = "ILStubClass"; + pMD->m_pszDebugMethodSignature = "object(object, ref byte)"; + pMD->m_pDebugMethodTable = pMT; +#endif // _DEBUG + + RETURN pMD; +} + // // This will get or create a MethodTable in the Module/AppDomain on which // we can place a new IL stub MethodDesc. diff --git a/src/coreclr/vm/ilstubcache.h b/src/coreclr/vm/ilstubcache.h index 52be99d9fb8ef1..5a70ab0a36167b 100644 --- a/src/coreclr/vm/ilstubcache.h +++ b/src/coreclr/vm/ilstubcache.h @@ -76,6 +76,15 @@ class ILStubCache final ILStubLinker* pStubLinker, BOOL isAsync = FALSE); + // Creates a DynamicMethodDesc that wraps pre-compiled R2R async resumption stub code. + // Unlike regular IL stubs, this does not create a resolver or precode - it points + // directly to the R2R native code. + static MethodDesc* CreateR2RBackedAsyncResumptionStub( + LoaderAllocator* pAllocator, + MethodTable* pMT, + PCODE r2rEntryPoint, + AllocMemTracker* pamTracker); + MethodTable * GetStubMethodTable() { LIMITED_METHOD_CONTRACT; diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index d93d1d588fcbf3..9a7b83fb152110 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -2482,7 +2482,7 @@ bool CEEInfo::getSystemVAmd64PassStructInRegisterDescriptor( #if defined(UNIX_AMD64_ABI) SystemVStructRegisterPassingHelper helper((unsigned int)th.GetSize()); bool result = methodTablePtr->ClassifyEightBytes(&helper, useNativeLayout); - + // The answer must be true at this point. _ASSERTE(result); #endif // UNIX_AMD64_ABI @@ -8818,13 +8818,13 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info) { pExactMT = pDevirtMD->GetExactDeclaringType(pObjMT); } - + // This is generic virtual method devirtualization. if (!isArray && pBaseMD->HasMethodInstantiation()) { pDevirtMD = pDevirtMD->FindOrCreateAssociatedMethodDesc( pDevirtMD, pExactMT, pExactMT->IsValueType() && !pDevirtMD->IsStatic(), pBaseMD->GetMethodInstantiation(), true); - + // We still can't handle shared generic methods because we don't have // the right generic context for runtime lookup. // TODO: Remove this limitation. @@ -10317,10 +10317,11 @@ void CEEInfo::getAsyncInfo(CORINFO_ASYNC_INFO* pAsyncInfoOut) EE_TO_JIT_TRANSITION(); } -CORINFO_CLASS_HANDLE CEEInfo::getContinuationType( +MethodTable* getContinuationType( size_t dataSize, bool* objRefs, - size_t objRefsSize) + size_t objRefsSize, + Module* loaderModule) { CONTRACTL { THROWS; @@ -10330,18 +10331,36 @@ CORINFO_CLASS_HANDLE CEEInfo::getContinuationType( CORINFO_CLASS_HANDLE result = NULL; - JIT_TO_EE_TRANSITION(); - _ASSERTE(objRefsSize == (dataSize + (TARGET_POINTER_SIZE - 1)) / TARGET_POINTER_SIZE); - LoaderAllocator* allocator = m_pMethodBeingCompiled->GetLoaderAllocator(); + LoaderAllocator* allocator = loaderModule->GetLoaderAllocator(); AsyncContinuationsManager* asyncConts = allocator->GetAsyncContinuationsManager(); - result = (CORINFO_CLASS_HANDLE)asyncConts->LookupOrCreateContinuationMethodTable((unsigned)dataSize, objRefs, m_pMethodBeingCompiled); + MethodTable* mt = asyncConts->LookupOrCreateContinuationMethodTable((unsigned)dataSize, objRefs, loaderModule); #ifdef DEBUG - CORINFO_CLASS_HANDLE result2 = (CORINFO_CLASS_HANDLE)asyncConts->LookupOrCreateContinuationMethodTable((unsigned)dataSize, objRefs, m_pMethodBeingCompiled); - _ASSERTE(result2 == result); + MethodTable* mt2 = asyncConts->LookupOrCreateContinuationMethodTable((unsigned)dataSize, objRefs, loaderModule); + _ASSERTE(mt2 == mt); #endif + return mt; +} + +CORINFO_CLASS_HANDLE CEEInfo::getContinuationType( + size_t dataSize, + bool* objRefs, + size_t objRefsSize) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + MODE_PREEMPTIVE; + } CONTRACTL_END; + + CORINFO_CLASS_HANDLE result = NULL; + + JIT_TO_EE_TRANSITION(); + + result = (CORINFO_CLASS_HANDLE)::getContinuationType(dataSize, objRefs, objRefsSize, m_pMethodBeingCompiled->GetLoaderModule()); + EE_TO_JIT_TRANSITION(); return result; @@ -13686,6 +13705,59 @@ void ComputeGCRefMap(MethodTable * pMT, BYTE * pGCRefMap, size_t cbGCRefMap) } while (cur >= last); } + +MethodTable* GetContinuationTypeFromLayout(PCCOR_SIGNATURE pBlob, Module* currentModule) +{ + STANDARD_VM_CONTRACT; + + SigPointer p(pBlob); + // Skip Continuation type signature + IfFailThrow(p.SkipExactlyOne()); + + uint32_t dwFlags; + IfFailThrow(p.GetData(&dwFlags)); + + uint32_t dwExpectedSize; + IfFailThrow(p.GetData(&dwExpectedSize)); + + if (!(dwFlags & READYTORUN_LAYOUT_GCLayout)) + { + return nullptr; + } + + if ((dwExpectedSize % TARGET_POINTER_SIZE) != 0) + { + COMPlusThrowHR(COR_E_BADIMAGEFORMAT); + } + + LoaderAllocator* allocator = currentModule->GetLoaderAllocator(); + AsyncContinuationsManager* asyncConts = allocator->GetAsyncContinuationsManager(); + + if (dwFlags & READYTORUN_LAYOUT_GCLayout_Empty) + { + // No GC references, don't read a bitmap + return ::getContinuationType(dwExpectedSize, nullptr, 0, currentModule); + } + else + { + uint8_t* pGCRefMapBlob = (uint8_t*)p.GetPtr(); + size_t objRefsSize = (dwExpectedSize / TARGET_POINTER_SIZE); + bool* objRefs = (bool*)_alloca(objRefsSize * sizeof(bool)); + size_t bytesInBlob = (objRefsSize + 7) / 8; + // Expand bitmap to bool array for use with getContinuationType + for(size_t byteIndex = 0; byteIndex < bytesInBlob; byteIndex++) + { + uint8_t b = pGCRefMapBlob[byteIndex]; + for (int bit = 0; bit < 8 && byteIndex * 8 + bit < objRefsSize; bit++) + { + objRefs[byteIndex * 8 + bit] = (b & (1 << bit)) != 0; + } + } + return ::getContinuationType(dwExpectedSize, objRefs, objRefsSize, currentModule); + } +} + + // // Type layout check verifies that there was no incompatible change in the value type layout. // If there was one, we will fall back to JIT instead of using the pre-generated code from the ready to run image. @@ -14084,6 +14156,7 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, default: STRESS_LOG1(LF_ZAP, LL_WARNING, "Unknown READYTORUN_HELPER %d\n", helperNum); + printf("Unknown READYTORUN_HELPER %d", helperNum); _ASSERTE(!"Unknown READYTORUN_HELPER"); return FALSE; } @@ -14157,6 +14230,21 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, } break; + case READYTORUN_FIXUP_Continuation_Layout: + { + MethodTable* continuationTypeMethodTable = GetContinuationTypeFromLayout(pBlob, currentModule); + if (continuationTypeMethodTable != NULL) + { + TypeHandle th = TypeHandle(continuationTypeMethodTable); + result = (size_t)th.AsPtr(); + } + else + { + result = 0; + } + } + break; + case READYTORUN_FIXUP_Check_FieldOffset: { DWORD dwExpectedOffset = CorSigUncompressData(pBlob); diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index d8a4181d34766a..e84ca60c8c2b58 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -262,7 +262,8 @@ using PTR_MethodDescCodeData = DPTR(MethodDescCodeData); enum class AsyncVariantLookup { MatchingAsyncVariant = 0, - AsyncOtherVariant + AsyncOtherVariant, + AsyncResumptionStub }; enum class MethodReturnKind @@ -2822,6 +2823,7 @@ class DynamicMethodDesc : public StoredSigMethodDesc StubDelegateInvokeMethod, StubAsyncResume, + StubAsyncResumeR2R, // R2R-backed async resumption stub (no IL, points directly to R2R code) StubLast }; @@ -3016,6 +3018,13 @@ class DynamicMethodDesc : public StoredSigMethodDesc _ASSERTE(IsILStub()); return GetILStubType() == DynamicMethodDesc::StubDelegateShuffleThunk; } + bool IsAsyncResumptionStub() const + { + LIMITED_METHOD_DAC_CONTRACT; + _ASSERTE(IsILStub()); + ILStubType type = GetILStubType(); + return type == DynamicMethodDesc::StubAsyncResume || type == DynamicMethodDesc::StubAsyncResumeR2R; + } // Whether the stub takes a context argument that is an interop MethodDesc. // See RequiresMDContextArg() for the non-stub version. diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 470d35b23cbbfc..959a7ca2f57a82 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -1762,7 +1762,7 @@ bool MethodTable::InterfaceMapIterator::CurrentInterfaceEquivalentTo(MethodTable if (pCurrentMethodTable == pMT) return true; - + if (pCurrentMethodTable->IsSpecialMarkerTypeForGenericCasting() && !pMTOwner->GetAuxiliaryData()->MayHaveOpenInterfacesInInterfaceMap() && pCurrentMethodTable->HasSameTypeDefAs(pMT)) { // Any matches need to use the special marker type logic @@ -1779,7 +1779,7 @@ bool MethodTable::InterfaceMapIterator::CurrentInterfaceEquivalentTo(MethodTable #ifndef DACCESS_COMPILE if (pMT->IsFullyLoaded()) SetInterface(pMT); -#endif +#endif return true; } else @@ -4354,7 +4354,7 @@ VOID DoAccessibilityCheckForConstraintSignature(Module *pModule, SigPointer *pSi case ELEMENT_TYPE_TYPEDBYREF: // Primitive types and such. Nothing to check break; - + case ELEMENT_TYPE_VAR: case ELEMENT_TYPE_MVAR: { @@ -4780,7 +4780,7 @@ void MethodTable::DoFullyLoad(Generics::RecursionGraph * const pVisited, const for (DWORD i = 0; i < formalParams.GetNumArgs(); i++) { - // This call to Bounded/DoAccessibilityCheckForConstraints will also cause constraint Variance rules to be checked + // This call to Bounded/DoAccessibilityCheckForConstraints will also cause constraint Variance rules to be checked // via the call to GetConstraints which will eventually call EEClass::CheckVarianceInSig BOOL Bounded(TypeVarTypeDesc *tyvar, DWORD depth); @@ -8007,6 +8007,25 @@ MethodDesc* MethodTable::GetParallelMethodDesc(MethodDesc* pDefMD, AsyncVariantL } CONTRACTL_END; + if (asyncVariantLookup == AsyncVariantLookup::AsyncResumptionStub) + { + mdMethodDef tkMethod = pDefMD->GetMemberDef(); + Module* mod = pDefMD->GetModule(); + bool isAsyncVariantMethod = pDefMD->IsAsyncVariantMethod(); + + MethodTable::IntroducedMethodIterator it(this); + for (; it.IsValid(); it.Next()) + { + MethodDesc* pMD = it.GetMethodDesc(); + if (pMD->GetMemberDef() == tkMethod + && pMD->GetModule() == mod + && pMD->IsAsyncVariantMethod() != isAsyncVariantMethod) + { + return pMD; + } + } + return NULL; + } if (asyncVariantLookup == AsyncVariantLookup::MatchingAsyncVariant) { #ifdef FEATURE_METADATA_UPDATER diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index 3cd42e18b8ff5a..7c369ce190a843 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -17,6 +17,7 @@ #include "wellknownattributes.h" #include "nativeimage.h" #include "dn-stdio.h" +#include "ilstubcache.h" #ifdef FEATURE_PERFMAP #include "perfmap.h" @@ -1010,8 +1011,6 @@ static bool SigMatchesMethodDesc(MethodDesc* pMD, SigPointer &sig, ModuleBase * { STANDARD_VM_CONTRACT; - _ASSERTE(!pMD->IsAsyncVariantMethod()); - ModuleBase *pOrigModule = pModule; ZapSig::Context zapSigContext(pModule, (void *)pModule, ZapSig::NormalTokens); ZapSig::Context * pZapSigContext = &zapSigContext; @@ -1099,10 +1098,192 @@ static bool SigMatchesMethodDesc(MethodDesc* pMD, SigPointer &sig, ModuleBase * IfFailThrow(sig.SkipExactlyOne()); } } + bool sigIsAsync = (methodFlags & ENCODE_METHOD_SIG_AsyncVariant) != 0; + if (sigIsAsync != pMD->IsAsyncVariantMethod()) + { + return false; + } + + bool sigIsResume = (methodFlags & ENCODE_METHOD_SIG_ResumptionStub) != 0; + bool methodIsResume = pMD->IsDynamicMethod() && ((DynamicMethodDesc*)pMD)->IsILStub() && ((DynamicMethodDesc*)pMD)->IsAsyncResumptionStub(); + if (sigIsResume != methodIsResume) + { + return false; + } + + return true; +} + +// Checks if sig matches pMD for resumption stub lookup. +// This is similar to SigMatchesMethodDesc but specifically looks for +// entries with ENCODE_METHOD_SIG_ResumptionStub flag that correspond +// to the given async variant method. +static bool SigMatchesResumptionStubForMethod(MethodDesc* pAsyncMD, SigPointer &sig, ModuleBase * pModule) +{ + STANDARD_VM_CONTRACT; + + _ASSERTE(pAsyncMD->IsAsyncVariantMethod()); + + ModuleBase *pOrigModule = pModule; + ZapSig::Context zapSigContext(pModule, (void *)pModule, ZapSig::NormalTokens); + ZapSig::Context * pZapSigContext = &zapSigContext; + + uint32_t methodFlags; + IfFailThrow(sig.GetData(&methodFlags)); + + // Must have ResumptionStub flag set + if ((methodFlags & ENCODE_METHOD_SIG_ResumptionStub) == 0) + return false; + + _ASSERTE((methodFlags & ENCODE_METHOD_SIG_SlotInsteadOfToken) == 0); + _ASSERTE(((methodFlags & (ENCODE_METHOD_SIG_MemberRefToken | ENCODE_METHOD_SIG_UpdateContext)) == 0) || + ((methodFlags & (ENCODE_METHOD_SIG_MemberRefToken | ENCODE_METHOD_SIG_UpdateContext)) == (ENCODE_METHOD_SIG_MemberRefToken | ENCODE_METHOD_SIG_UpdateContext))); + + if ( methodFlags & ENCODE_METHOD_SIG_UpdateContext) + { + uint32_t updatedModuleIndex; + IfFailThrow(sig.GetData(&updatedModuleIndex)); + pModule = pZapSigContext->GetZapSigModule()->GetModuleFromIndex(updatedModuleIndex); + } + + if (methodFlags & ENCODE_METHOD_SIG_OwnerType) + { + PCCOR_SIGNATURE pSigType; + uint32_t cbSigType; + sig.GetSignature(&pSigType, &cbSigType); + if (!ZapSig::CompareSignatureToTypeHandle(pSigType, pModule, TypeHandle(pAsyncMD->GetMethodTable()), pZapSigContext)) + return false; + + IfFailThrow(sig.SkipExactlyOne()); + } + + RID rid; + IfFailThrow(sig.GetData(&rid)); + + if ((methodFlags & ENCODE_METHOD_SIG_MemberRefToken) != 0) + { + IMDInternalImport * pInternalImport = pModule->GetMDImport(); + + LPCUTF8 szMember; + PCCOR_SIGNATURE pSig; + DWORD cSig; + + IfFailThrow(pInternalImport->GetNameAndSigOfMemberRef(TokenFromRid(rid, mdtMemberRef), &pSig, &cSig, &szMember)); + + _ASSERTE(!isCallConv(MetaSig::GetCallingConvention(Signature(pSig, cSig)), IMAGE_CEE_CS_CALLCONV_FIELD)); + + if (strcmp(szMember, pAsyncMD->GetName()) != 0) + return false; + + PCCOR_SIGNATURE pTargetMethodSig; + DWORD cTargetMethodSig; + + pAsyncMD->GetSig(&pTargetMethodSig, &cTargetMethodSig); + if (!MetaSig::CompareMethodSigs(pSig, cSig, pModule, NULL, pTargetMethodSig, cTargetMethodSig, pAsyncMD->GetModule(), NULL, FALSE)) + return false; + + rid = RidFromToken(pAsyncMD->GetMemberDef()); + } + + if (RidFromToken(pAsyncMD->GetMemberDef()) != rid) + return false; + + if (methodFlags & ENCODE_METHOD_SIG_MethodInstantiation) + { + uint32_t numGenericArgs; + IfFailThrow(sig.GetData(&numGenericArgs)); + Instantiation inst = pAsyncMD->GetMethodInstantiation(); + if (numGenericArgs != inst.GetNumArgs()) + return false; + + for (uint32_t i = 0; i < numGenericArgs; i++) + { + PCCOR_SIGNATURE pSigArg; + uint32_t cbSigArg; + sig.GetSignature(&pSigArg, &cbSigArg); + if (!ZapSig::CompareSignatureToTypeHandle(pSigArg, pOrigModule, inst[i], pZapSigContext)) + return false; + + IfFailThrow(sig.SkipExactlyOne()); + } + } + + // The sig should have AsyncVariant set (resumption stubs are for async variants) + bool sigIsAsync = (methodFlags & ENCODE_METHOD_SIG_AsyncVariant) != 0; + if (sigIsAsync != pAsyncMD->IsAsyncVariantMethod()) + return false; return true; } +// Looks up the R2R entry point for the resumption stub corresponding to the given async variant method. +// Returns NULL if not found. If found, also returns the runtime function index via pRuntimeFunctionIndex. +PCODE ReadyToRunInfo::LookupResumptionStubEntryPoint(MethodDesc* pAsyncVariantMD, PrepareCodeConfig* pConfig, BOOL fFixups, uint* pRuntimeFunctionIndex) +{ + STANDARD_VM_CONTRACT; + + _ASSERTE(pAsyncVariantMD->IsAsyncVariantMethod()); + + if (m_instMethodEntryPoints.IsNull()) + return (PCODE)NULL; + + // The resumption stub is stored with the same hash as the async variant method + NativeHashtable::Enumerator lookup = m_instMethodEntryPoints.Lookup(GetVersionResilientMethodHashCode(pAsyncVariantMD)); + NativeParser entryParser; + uint offset = (uint)-1; + + while (lookup.GetNext(entryParser)) + { + PCCOR_SIGNATURE pBlob = (PCCOR_SIGNATURE)entryParser.GetBlob(); + SigPointer sig(pBlob); + if (SigMatchesResumptionStubForMethod(pAsyncVariantMD, sig, m_pModule)) + { + offset = entryParser.GetOffset() + (uint)(sig.GetPtr() - pBlob); + break; + } + } + + if (offset == (uint)-1) + return (PCODE)NULL; + + uint id; + offset = m_nativeReader.DecodeUnsigned(offset, &id); + + if (id & 1) + { + if (id & 2) + { + uint val; + m_nativeReader.DecodeUnsigned(offset, &val); + offset -= val; + } + + if (fFixups) + { + BOOL mayUsePrecompiledPInvokeMethods = TRUE; + mayUsePrecompiledPInvokeMethods = !pConfig->IsForMulticoreJit(); + + if (!m_pModule->FixupDelayList(dac_cast(GetImage()->GetBase()) + offset, mayUsePrecompiledPInvokeMethods)) + { + pConfig->SetReadyToRunRejectedPrecompiledCode(); + return (PCODE)NULL; + } + } + + id >>= 2; + } + else + { + id >>= 1; + } + + _ASSERTE(id < m_nRuntimeFunctions); + if (pRuntimeFunctionIndex != nullptr) + *pRuntimeFunctionIndex = id; + + return dac_cast(GetImage()->GetBase()) + m_pRuntimeFunctions[id].BeginAddress; +} + bool ReadyToRunInfo::GetPgoInstrumentationData(MethodDesc * pMD, BYTE** pAllocatedMemory, ICorJitInfo::PgoInstrumentationSchema**ppSchema, UINT *pcSchema, BYTE** pInstrumentationData) { STANDARD_VM_CONTRACT; @@ -1196,14 +1377,14 @@ PCODE ReadyToRunInfo::GetEntryPoint(MethodDesc * pMD, PrepareCodeConfig* pConfig if (ReadyToRunCodeDisabled()) goto done; - // TODO: (async) R2R support for async variants (https://github.com/dotnet/runtime/issues/121559) - if (pMD->IsAsyncVariantMethod()) - goto done; - ETW::MethodLog::GetR2RGetEntryPointStart(pMD); uint offset; - if (pMD->HasClassOrMethodInstantiation()) + // Async variants and resumption stubs are stored in the instance methods table + if (pMD->HasClassOrMethodInstantiation() + || pMD->IsAsyncVariantMethod() + || (pMD->IsDynamicMethod() && ((DynamicMethodDesc*)pMD)->IsILStub() + && ((DynamicMethodDesc*)pMD)->IsAsyncResumptionStub())) { if (m_instMethodEntryPoints.IsNull()) goto done; @@ -1287,6 +1468,35 @@ PCODE ReadyToRunInfo::GetEntryPoint(MethodDesc * pMD, PrepareCodeConfig* pConfig pEntryPoint = dac_cast(GetImage()->GetBase()) + m_pRuntimeFunctions[id].BeginAddress; m_pCompositeInfo->SetMethodDescForEntryPointInNativeImage(pEntryPoint, pMD); + // For async variant methods, we also need to look up the corresponding resumption stub + // and create a DynamicMethodDesc wrapper for it so that GC stack walks work correctly. + if (pMD->IsAsyncVariantMethod()) + { + uint stubRuntimeFunctionIndex = 0; + PCODE stubEntryPoint = LookupResumptionStubEntryPoint(pMD, pConfig, fFixups, &stubRuntimeFunctionIndex); + if (stubEntryPoint == (PCODE)NULL) + { + // Resumption stub not found - cannot use R2R code for this async variant + pEntryPoint = (PCODE)NULL; + goto done; + } + + // Create a DynamicMethodDesc wrapper for the R2R resumption stub + AllocMemTracker amTracker; + MethodTable* pStubMT = m_pModule->GetILStubCache()->GetOrCreateStubMethodTable(m_pModule); + + MethodDesc* pStubMD = ILStubCache::CreateR2RBackedAsyncResumptionStub( + pMD->GetLoaderAllocator(), + pStubMT, + stubEntryPoint, + &amTracker); + + amTracker.SuppressRelease(); + + // Register the stub's entry point so GC can find it during stack walks + m_pCompositeInfo->SetMethodDescForEntryPointInNativeImage(stubEntryPoint, pStubMD); + } + #ifdef PROFILING_SUPPORTED { BEGIN_PROFILER_CALLBACK(CORProfilerTrackCacheSearches()); diff --git a/src/coreclr/vm/readytoruninfo.h b/src/coreclr/vm/readytoruninfo.h index 9f58e81c736c93..57f2fea7d4613f 100644 --- a/src/coreclr/vm/readytoruninfo.h +++ b/src/coreclr/vm/readytoruninfo.h @@ -339,6 +339,10 @@ class ReadyToRunInfo PTR_MethodDesc GetMethodDescForEntryPointInNativeImage(PCODE entryPoint); void SetMethodDescForEntryPointInNativeImage(PCODE entryPoint, PTR_MethodDesc methodDesc); + // Looks up the R2R entry point for the resumption stub corresponding to the given async variant method. + // Returns NULL if not found. + PCODE LookupResumptionStubEntryPoint(MethodDesc* pAsyncVariantMD, PrepareCodeConfig* pConfig, BOOL fFixups, uint* pRuntimeFunctionIndex); + PTR_ReadyToRunCoreInfo GetComponentInfo() { return dac_cast(&m_component); } friend struct ::cdac_data; diff --git a/src/coreclr/vm/stackwalk.cpp b/src/coreclr/vm/stackwalk.cpp index ed5de14ac839f5..9d5ea10b91ced7 100644 --- a/src/coreclr/vm/stackwalk.cpp +++ b/src/coreclr/vm/stackwalk.cpp @@ -505,9 +505,11 @@ PCODE Thread::VirtualUnwindCallFrame(T_CONTEXT* pContext, ARM_ONLY((DWORD*))(&uImageBaseFromOS), NULL); - // Note that he address returned from the OS is different from the one we have computed + // Note that the address returned from the OS is different from the one we have computed // when unwind info is registered using RtlAddGrowableFunctionTable. Compare RUNTIME_FUNCTION content. - _ASSERTE( (uImageBase == uImageBaseFromOS) && (memcmp(pFunctionEntry, pFunctionEntryFromOS, sizeof(RUNTIME_FUNCTION)) == 0) ); + // pFunctionEntryFromOS can be NULL for async methods in R2R images where unwind info may not be + // registered with the OS. + _ASSERTE( (pFunctionEntryFromOS == NULL) || ((uImageBase == uImageBaseFromOS) && (memcmp(pFunctionEntry, pFunctionEntryFromOS, sizeof(RUNTIME_FUNCTION)) == 0)) ); #endif // _DEBUG && !TARGET_UNIX } diff --git a/src/coreclr/vm/zapsig.cpp b/src/coreclr/vm/zapsig.cpp index 09d9800df3e143..9d252c223343e5 100644 --- a/src/coreclr/vm/zapsig.cpp +++ b/src/coreclr/vm/zapsig.cpp @@ -922,6 +922,7 @@ MethodDesc *ZapSig::DecodeMethod(ModuleBase *pInfoModule, BOOL isInstantiatingStub = (methodFlags & ENCODE_METHOD_SIG_InstantiatingStub); BOOL isUnboxingStub = (methodFlags & ENCODE_METHOD_SIG_UnboxingStub); bool isAsyncVariant = (methodFlags & ENCODE_METHOD_SIG_AsyncVariant) != 0; + bool isResumptionStub = (methodFlags & ENCODE_METHOD_SIG_ResumptionStub) != 0; pMethod = MethodDesc::FindOrCreateAssociatedMethodDesc(pMethod, thOwner.GetMethodTable(), isUnboxingStub, diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/CorElementType.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/CorElementType.cs index cb2b6d3da2df94..250acfff6cb4e8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/CorElementType.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/CorElementType.cs @@ -41,5 +41,6 @@ internal enum CorElementType : byte ELEMENT_TYPE_MODIFIER = 0x40, ELEMENT_TYPE_SENTINEL = 0x41, ELEMENT_TYPE_PINNED = 0x45, + ELEMENT_TYPE_CONTINUATION = 0x46, } } diff --git a/src/tests/async/awaitingnotasync/awaitingnotasync.cs b/src/tests/async/awaitingnotasync/awaitingnotasync.cs index bf8ad8c9cc12a0..a87553b1ef6fbc 100644 --- a/src/tests/async/awaitingnotasync/awaitingnotasync.cs +++ b/src/tests/async/awaitingnotasync/awaitingnotasync.cs @@ -48,6 +48,5 @@ private static async Task AsyncEntryPoint() // await(await ...)) Assert.Equal(7, await await await GetTask(GetTask(GetTask(7)))); - } } diff --git a/src/tests/profiler/native/rejitprofiler/sigparse.h b/src/tests/profiler/native/rejitprofiler/sigparse.h index 249afa2a80062d..fb892335c8ed6c 100644 --- a/src/tests/profiler/native/rejitprofiler/sigparse.h +++ b/src/tests/profiler/native/rejitprofiler/sigparse.h @@ -65,59 +65,60 @@ Number ::= 29-bit-encoded-integer */ -#define ELEMENT_TYPE_END 0x00 //Marks end of a list -#define ELEMENT_TYPE_VOID 0x01 -#define ELEMENT_TYPE_BOOLEAN 0x02 -#define ELEMENT_TYPE_CHAR 0x03 -#define ELEMENT_TYPE_I1 0x04 -#define ELEMENT_TYPE_U1 0x05 -#define ELEMENT_TYPE_I2 0x06 -#define ELEMENT_TYPE_U2 0x07 -#define ELEMENT_TYPE_I4 0x08 -#define ELEMENT_TYPE_U4 0x09 -#define ELEMENT_TYPE_I8 0x0a -#define ELEMENT_TYPE_U8 0x0b -#define ELEMENT_TYPE_R4 0x0c -#define ELEMENT_TYPE_R8 0x0d -#define ELEMENT_TYPE_STRING 0x0e -#define ELEMENT_TYPE_PTR 0x0f // Followed by type -#define ELEMENT_TYPE_BYREF 0x10 // Followed by type -#define ELEMENT_TYPE_VALUETYPE 0x11 // Followed by TypeDef or TypeRef token -#define ELEMENT_TYPE_CLASS 0x12 // Followed by TypeDef or TypeRef token -#define ELEMENT_TYPE_VAR 0x13 // Generic parameter in a generic type definition, represented as number -#define ELEMENT_TYPE_ARRAY 0x14 // type rank boundsCount bound1 ... loCount lo1 ... -#define ELEMENT_TYPE_GENERICINST 0x15 // Generic type instantiation. Followed by type type-arg-count type-1 ... type-n -#define ELEMENT_TYPE_TYPEDBYREF 0x16 -#define ELEMENT_TYPE_I 0x18 // System.IntPtr -#define ELEMENT_TYPE_U 0x19 // System.UIntPtr -#define ELEMENT_TYPE_FNPTR 0x1b // Followed by full method signature -#define ELEMENT_TYPE_OBJECT 0x1c // System.Object -#define ELEMENT_TYPE_SZARRAY 0x1d // Single-dim array with 0 lower bound -#define ELEMENT_TYPE_MVAR 0x1e // Generic parameter in a generic method definition,represented as number -#define ELEMENT_TYPE_CMOD_REQD 0x1f // Required modifier : followed by a TypeDef or TypeRef token -#define ELEMENT_TYPE_CMOD_OPT 0x20 // Optional modifier : followed by a TypeDef or TypeRef token -#define ELEMENT_TYPE_INTERNAL 0x21 // Implemented within the CLI -#define ELEMENT_TYPE_MODIFIER 0x40 // Or'd with following element types -#define ELEMENT_TYPE_SENTINEL 0x41 // Sentinel for vararg method signature -#define ELEMENT_TYPE_PINNED 0x45 // Denotes a local variable that points at a pinned object - -#define SIG_METHOD_DEFAULT 0x00 // default calling convention -#define SIG_METHOD_C 0x01 // C calling convention -#define SIG_METHOD_STDCALL 0x02 // Stdcall calling convention -#define SIG_METHOD_THISCALL 0x03 // thiscall calling convention -#define SIG_METHOD_FASTCALL 0x04 // fastcall calling convention -#define SIG_METHOD_VARARG 0x05 // vararg calling convention -#define SIG_FIELD 0x06 // encodes a field -#define SIG_LOCAL_SIG 0x07 // used for the .locals directive -#define SIG_PROPERTY 0x08 // used to encode a property - -#define SIG_GENERIC 0x10 // used to indicate that the method has one or more generic parameters. -#define SIG_HASTHIS 0x20 // used to encode the keyword instance in the calling convention -#define SIG_EXPLICITTHIS 0x40 // used to encode the keyword explicit in the calling convention - -#define SIG_INDEX_TYPE_TYPEDEF 0x00 // ParseTypeDefOrRefEncoded returns this as the out index type for typedefs -#define SIG_INDEX_TYPE_TYPEREF 0x01 // ParseTypeDefOrRefEncoded returns this as the out index type for typerefs -#define SIG_INDEX_TYPE_TYPESPEC 0x02 // ParseTypeDefOrRefEncoded returns this as the out index type for typespecs +#define ELEMENT_TYPE_END 0x00 //Marks end of a list +#define ELEMENT_TYPE_VOID 0x01 +#define ELEMENT_TYPE_BOOLEAN 0x02 +#define ELEMENT_TYPE_CHAR 0x03 +#define ELEMENT_TYPE_I1 0x04 +#define ELEMENT_TYPE_U1 0x05 +#define ELEMENT_TYPE_I2 0x06 +#define ELEMENT_TYPE_U2 0x07 +#define ELEMENT_TYPE_I4 0x08 +#define ELEMENT_TYPE_U4 0x09 +#define ELEMENT_TYPE_I8 0x0a +#define ELEMENT_TYPE_U8 0x0b +#define ELEMENT_TYPE_R4 0x0c +#define ELEMENT_TYPE_R8 0x0d +#define ELEMENT_TYPE_STRING 0x0e +#define ELEMENT_TYPE_PTR 0x0f // Followed by type +#define ELEMENT_TYPE_BYREF 0x10 // Followed by type +#define ELEMENT_TYPE_VALUETYPE 0x11 // Followed by TypeDef or TypeRef token +#define ELEMENT_TYPE_CLASS 0x12 // Followed by TypeDef or TypeRef token +#define ELEMENT_TYPE_VAR 0x13 // Generic parameter in a generic type definition, represented as number +#define ELEMENT_TYPE_ARRAY 0x14 // type rank boundsCount bound1 ... loCount lo1 ... +#define ELEMENT_TYPE_GENERICINST 0x15 // Generic type instantiation. Followed by type type-arg-count type-1 ... type-n +#define ELEMENT_TYPE_TYPEDBYREF 0x16 +#define ELEMENT_TYPE_I 0x18 // System.IntPtr +#define ELEMENT_TYPE_U 0x19 // System.UIntPtr +#define ELEMENT_TYPE_FNPTR 0x1b // Followed by full method signature +#define ELEMENT_TYPE_OBJECT 0x1c // System.Object +#define ELEMENT_TYPE_SZARRAY 0x1d // Single-dim array with 0 lower bound +#define ELEMENT_TYPE_MVAR 0x1e // Generic parameter in a generic method definition,represented as number +#define ELEMENT_TYPE_CMOD_REQD 0x1f // Required modifier : followed by a TypeDef or TypeRef token +#define ELEMENT_TYPE_CMOD_OPT 0x20 // Optional modifier : followed by a TypeDef or TypeRef token +#define ELEMENT_TYPE_INTERNAL 0x21 // Implemented within the CLI +#define ELEMENT_TYPE_MODIFIER 0x40 // Or'd with following element types +#define ELEMENT_TYPE_SENTINEL 0x41 // Sentinel for vararg method signature +#define ELEMENT_TYPE_PINNED 0x45 // Denotes a local variable that points at a pinned object +#define ELEMENT_TYPE_CONTINUATION 0x46 // A specific continuation type. Followed by size, and boolean array + +#define SIG_METHOD_DEFAULT 0x00 // default calling convention +#define SIG_METHOD_C 0x01 // C calling convention +#define SIG_METHOD_STDCALL 0x02 // Stdcall calling convention +#define SIG_METHOD_THISCALL 0x03 // thiscall calling convention +#define SIG_METHOD_FASTCALL 0x04 // fastcall calling convention +#define SIG_METHOD_VARARG 0x05 // vararg calling convention +#define SIG_FIELD 0x06 // encodes a field +#define SIG_LOCAL_SIG 0x07 // used for the .locals directive +#define SIG_PROPERTY 0x08 // used to encode a property + +#define SIG_GENERIC 0x10 // used to indicate that the method has one or more generic parameters. +#define SIG_HASTHIS 0x20 // used to encode the keyword instance in the calling convention +#define SIG_EXPLICITTHIS 0x40 // used to encode the keyword explicit in the calling convention + +#define SIG_INDEX_TYPE_TYPEDEF 0x00 // ParseTypeDefOrRefEncoded returns this as the out index type for typedefs +#define SIG_INDEX_TYPE_TYPEREF 0x01 // ParseTypeDefOrRefEncoded returns this as the out index type for typerefs +#define SIG_INDEX_TYPE_TYPESPEC 0x02 // ParseTypeDefOrRefEncoded returns this as the out index type for typespecs typedef unsigned char sig_byte; From fcf110fa2cc2ad55ea5a5faeb048a86d72c07f85 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 5 Feb 2026 12:56:16 -0800 Subject: [PATCH 02/22] Undo extra changes --- src/coreclr/tools/.editorconfig | 2 +- .../tools/Common/JitInterface/CorInfoImpl.cs | 28 ++--- .../ReadyToRun/PrecodeHelperImport.cs | 29 ----- .../ReadyToRunCodegenNodeFactory.cs | 14 --- .../Compiler/ReadyToRunCodegenCompilation.cs | 2 +- .../ReadyToRunCompilationModuleGroupBase.cs | 80 ++++++------- .../src/System/Reflection/CorElementType.cs | 1 - .../awaitingnotasync/awaitingnotasync.cs | 1 + .../profiler/native/rejitprofiler/sigparse.h | 107 +++++++++--------- 9 files changed, 111 insertions(+), 153 deletions(-) diff --git a/src/coreclr/tools/.editorconfig b/src/coreclr/tools/.editorconfig index e097bb1bda5fc9..2604c0c3f48d8a 100644 --- a/src/coreclr/tools/.editorconfig +++ b/src/coreclr/tools/.editorconfig @@ -3,4 +3,4 @@ [*.cs] # TODO: Update tools to use LibraryImport instead of DllImport -dotnet_diagnostic.SYSLIB1054.severity = suggestion \ No newline at end of file +dotnet_diagnostic.SYSLIB1054.severity = suggestion diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 31b02783262741..5bf91b31d20832 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -116,7 +116,7 @@ public LikelyClassMethodRecord(IntPtr handle, uint likelihood) } [DllImport(JitLibrary)] - private static extern uint getLikelyClasses(LikelyClassMethodRecord* pLikelyClasses, uint maxLikelyClasses, PgoInstrumentationSchema* schema, uint countSchemaItems, byte* pInstrumentationData, int ilOffset); + private static extern uint getLikelyClasses(LikelyClassMethodRecord* pLikelyClasses, uint maxLikelyClasses, PgoInstrumentationSchema* schema, uint countSchemaItems, byte*pInstrumentationData, int ilOffset); [DllImport(JitLibrary)] private static extern uint getLikelyMethods(LikelyClassMethodRecord* pLikelyMethods, uint maxLikelyMethods, PgoInstrumentationSchema* schema, uint countSchemaItems, byte* pInstrumentationData, int ilOffset); @@ -140,7 +140,7 @@ private static extern CorJitResult JitCompileMethod(out IntPtr exception, ref CORINFO_METHOD_INFO info, uint flags, out IntPtr nativeEntry, out uint codeSize); [DllImport(JitSupportLibrary)] - private static extern IntPtr AllocException([MarshalAs(UnmanagedType.LPWStr)] string message, int messageLength); + private static extern IntPtr AllocException([MarshalAs(UnmanagedType.LPWStr)]string message, int messageLength); [DllImport(JitSupportLibrary)] private static extern void JitSetOs(IntPtr jit, CORINFO_OS os); @@ -839,12 +839,12 @@ private bool Get_CORINFO_METHOD_INFO(MethodDesc method, MethodIL methodIL, CORIN private Dictionary _instantiationToJitVisibleInstantiation; private CORINFO_CLASS_STRUCT_** GetJitInstantiation(Instantiation inst) { - IntPtr[] jitVisibleInstantiation; + IntPtr [] jitVisibleInstantiation; _instantiationToJitVisibleInstantiation ??= new Dictionary(); if (!_instantiationToJitVisibleInstantiation.TryGetValue(inst, out jitVisibleInstantiation)) { - jitVisibleInstantiation = new IntPtr[inst.Length]; + jitVisibleInstantiation = new IntPtr[inst.Length]; for (int i = 0; i < inst.Length; i++) jitVisibleInstantiation[i] = (IntPtr)ObjectToHandle(inst[i]); _instantiationToJitVisibleInstantiation.Add(inst, jitVisibleInstantiation); @@ -1067,7 +1067,7 @@ private TypeSystemEntity entityFromContext(CORINFO_CONTEXT_STRUCT* contextStruct { if (contextStruct == contextFromMethodBeingCompiled()) { - return MethodBeingCompiled.HasInstantiation ? (TypeSystemEntity)MethodBeingCompiled : (TypeSystemEntity)MethodBeingCompiled.OwningType; + return MethodBeingCompiled.HasInstantiation ? (TypeSystemEntity)MethodBeingCompiled: (TypeSystemEntity)MethodBeingCompiled.OwningType; } return (TypeSystemEntity)HandleToObject((void*)((nuint)contextStruct & ~(nuint)CorInfoContextFlags.CORINFO_CONTEXTFLAGS_MASK)); @@ -1838,14 +1838,14 @@ private void resolveToken(ref CORINFO_RESOLVED_TOKEN pResolvedToken) #if READYTORUN TypeDesc owningType = methodIL.OwningMethod.GetTypicalMethodDefinition().OwningType; - bool recordToken = owningType is EcmaType; + bool recordToken; if (!_compilation.CompilationModuleGroup.VersionsWithMethodBody(methodIL.OwningMethod.GetTypicalMethodDefinition())) { - recordToken &= (methodIL.GetMethodILScopeDefinition() is IMethodTokensAreUseableInCompilation); + recordToken = (methodIL.GetMethodILScopeDefinition() is IMethodTokensAreUseableInCompilation) && owningType is EcmaType; } else { - recordToken &= _compilation.CompilationModuleGroup.VersionsWithType(owningType) || _compilation.CompilationModuleGroup.CrossModuleInlineableType(owningType); + recordToken = (_compilation.CompilationModuleGroup.VersionsWithType(owningType) || _compilation.CompilationModuleGroup.CrossModuleInlineableType(owningType)) && owningType is EcmaType; } #endif @@ -1893,7 +1893,8 @@ private void resolveToken(ref CORINFO_RESOLVED_TOKEN pResolvedToken) pResolvedToken.hClass = null; } } - else if (result is FieldDesc) + else + if (result is FieldDesc) { FieldDesc field = result as FieldDesc; @@ -1980,7 +1981,8 @@ private void findCallSiteSig(CORINFO_MODULE_STRUCT_* module, uint methTOK, CORIN { result = WellKnownType.RuntimeMethodHandle; } - else if (pResolvedToken.hField != null) + else + if (pResolvedToken.hField != null) { result = WellKnownType.RuntimeFieldHandle; } @@ -2309,7 +2311,7 @@ public static int GetClassAlignmentRequirementStatic(DefType type) // private static bool ShouldAlign8(int dwR8Fields, int dwTotalFields) { - return dwR8Fields * 2 > dwTotalFields && dwR8Fields >= 2; + return dwR8Fields*2>dwTotalFields && dwR8Fields>=2; } private static bool ShouldAlign8(DefType type) @@ -3641,7 +3643,7 @@ private uint getThreadTLSIndex(ref void* ppIndirection) { throw new NotImplementedException("getThreadTLSIndex"); } private Dictionary _helperCache = new Dictionary(); - private void getHelperFtn(CorInfoHelpFunc ftnNum, CORINFO_CONST_LOOKUP* pNativeEntrypoint, CORINFO_METHOD_STRUCT_** pMethod) + private void getHelperFtn(CorInfoHelpFunc ftnNum, CORINFO_CONST_LOOKUP *pNativeEntrypoint, CORINFO_METHOD_STRUCT_** pMethod) { // We never return a method handle from the managed implementation of this method today if (pMethod != null) @@ -3807,7 +3809,6 @@ private bool getTailCallHelpers(ref CORINFO_RESOLVED_TOKEN callToken, CORINFO_SI // CompiledMethodNode instead of MethodEntrypoint for the pointer to the code instead of a fixup entryPoint = (void*)ObjectToHandle(_compilation.NodeFactory.CompiledMethodNode(resumptionStub)); return ObjectToHandle(resumptionStub); - #else _asyncResumptionStub ??= new AsyncResumptionStub(MethodBeingCompiled, _compilation.TypeSystemContext.GeneratedAssembly.GetGlobalModuleType()); @@ -3821,6 +3822,7 @@ private bool getTailCallHelpers(ref CORINFO_RESOLVED_TOKEN callToken, CORINFO_SI private int _codeAlignment; private byte[] _roData; + private MethodReadOnlyDataNode _roDataBlob; private int _roDataAlignment; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/PrecodeHelperImport.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/PrecodeHelperImport.cs index a026381b9e89d2..63fead7047f3a4 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/PrecodeHelperImport.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/PrecodeHelperImport.cs @@ -37,35 +37,6 @@ public override void EncodeData(ref ObjectDataBuilder dataBuilder, NodeFactory f } - // This is just here in case of future extension (there are no fields specific to this class) - public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) - { - return base.CompareToImpl(other, comparer); - } - } - - public class CodeDataImport : Import - { - public CodeDataImport(NodeFactory factory, Signature signature) - : base(factory.CodeDataImports, signature) - { - } - - protected override string GetName(NodeFactory factory) - { - return "CodeDataImport->" + ImportSignature.GetMangledName(factory.NameMangler); - } - - public override int ClassCode => 667823013; - - public override void EncodeData(ref ObjectDataBuilder dataBuilder, NodeFactory factory, bool relocsOnly) - { - // This needs to be an empty target pointer since it will be filled in with Module* - // when loaded by CoreCLR - dataBuilder.EmitZeroPointer(); - } - - // This is just here in case of future extension (there are no fields specific to this class) public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) { diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs index a54cfc5a3b9d8e..273df1e98e29ef 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs @@ -411,8 +411,6 @@ private void CreateNodeCaches() public ImportSectionNode PrecodeImports; - public ImportSectionNode CodeDataImports; - public ImportSectionNode ILBodyPrecodeImports; private NodeCache _constructedHelpers; @@ -479,8 +477,6 @@ public IEnumerable EnumerateCompiledMethods(EcmaModule moduleT { MethodDesc method = methodNode.Method; MethodWithGCInfo methodCodeNode = methodNode as MethodWithGCInfo; - //if (methodCodeNode.Method is AsyncResumptionStub) - // continue; #if DEBUG if ((!methodCodeNode.IsEmpty || CompilationModuleGroup.VersionsWithMethodBody(method)) && method.IsPrimaryMethodDesc()) { @@ -901,15 +897,6 @@ bool HasAnyProfileDataForInput() emitGCRefMap: false); ImportSectionsTable.AddEmbeddedObject(PrecodeImports); - CodeDataImports = new ImportSectionNode( - "CodeDataImports", - ReadyToRunImportSectionType.Unknown, - ReadyToRunImportSectionFlags.PCode, - (byte)Target.PointerSize, - emitPrecode: true, - emitGCRefMap: false); - ImportSectionsTable.AddEmbeddedObject(CodeDataImports); - StringImports = new ImportSectionNode( "StringImports", ReadyToRunImportSectionType.StringHandle, @@ -926,7 +913,6 @@ bool HasAnyProfileDataForInput() graph.AddRoot(DispatchImports, "Dispatch imports are always generated"); graph.AddRoot(HelperImports, "Helper imports are always generated"); graph.AddRoot(PrecodeImports, "Precode helper imports are always generated"); - graph.AddRoot(CodeDataImports, "Code data imports are always generated"); graph.AddRoot(ILBodyPrecodeImports, "IL body precode imports are always generated"); graph.AddRoot(StringImports, "String imports are always generated"); graph.AddRoot(Header, "ReadyToRunHeader is always generated"); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs index be9fdb41fe3009..bbc7077ab2f206 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs @@ -110,7 +110,7 @@ public bool CanInline(MethodDesc caller, MethodDesc callee) } } - if (callee.IsAsyncThunk() || callee.IsAsyncCall() || caller.IsAsyncThunk() || caller.IsAsyncCall()) + if (callee.IsAsyncThunk() || callee.IsAsyncCall()) { return false; } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs index 3b93fa3f53a6c8..16a53c2bc4bfd5 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs @@ -618,18 +618,18 @@ private bool IsNonVersionableWithILTokensThatDoNotNeedTranslationUncached(EcmaMe case ILOpcode.ldfld: case ILOpcode.ldflda: case ILOpcode.stfld: - { - int token = ilReader.ReadILToken(); - FieldDesc field = methodIL.GetObject(token) as FieldDesc; - if (field == null) - return false; - if (field.IsStatic) - return false; - MetadataType owningMetadataType = field.OwningType; - if (!owningMetadataType.IsNonVersionable()) - return false; - break; - } + { + int token = ilReader.ReadILToken(); + FieldDesc field = methodIL.GetObject(token) as FieldDesc; + if (field == null) + return false; + if (field.IsStatic) + return false; + MetadataType owningMetadataType = field.OwningType; + if (!owningMetadataType.IsNonVersionable()) + return false; + break; + } case ILOpcode.ldelem: case ILOpcode.ldelema: @@ -638,37 +638,37 @@ private bool IsNonVersionableWithILTokensThatDoNotNeedTranslationUncached(EcmaMe case ILOpcode.initobj: case ILOpcode.cpobj: case ILOpcode.sizeof_: - { - int token = ilReader.ReadILToken(); - TypeDesc type = methodIL.GetObject(token) as TypeDesc; - if (type == null) - return false; - - MetadataType metadataType = type as MetadataType; - if (metadataType == null) - continue; // Types which are not metadata types are all well defined in size + { + int token = ilReader.ReadILToken(); + TypeDesc type = methodIL.GetObject(token) as TypeDesc; + if (type == null) + return false; - if (!metadataType.IsValueType) - continue; // Reference types are all well defined in size for the sizeof instruction + MetadataType metadataType = type as MetadataType; + if (metadataType == null) + continue; // Types which are not metadata types are all well defined in size - if (metadataType.IsNonVersionable()) - continue; - return false; - } + if (!metadataType.IsValueType) + continue; // Reference types are all well defined in size for the sizeof instruction - case ILOpcode.stelem: - { - int token = ilReader.ReadILToken(); - MetadataType type = methodIL.GetObject(token) as MetadataType; - if (type == null) + if (metadataType.IsNonVersionable()) + continue; return false; + } - if (!type.IsValueType) - return false; - if (!type.IsNonVersionable()) - return false; - break; - } + case ILOpcode.stelem: + { + int token = ilReader.ReadILToken(); + MetadataType type = methodIL.GetObject(token) as MetadataType; + if (type == null) + return false; + + if (!type.IsValueType) + return false; + if (!type.IsNonVersionable()) + return false; + break; + } // IL instructions which refer to tokens which are not safe for NonVersionable methods case ILOpcode.box: @@ -727,7 +727,7 @@ public sealed override bool TryGetModuleTokenForExternalType(TypeDesc type, out if (_typeRefsInCompilationModuleSet == null) { - lock (_compilationModuleSet) + lock(_compilationModuleSet) { if (_typeRefsInCompilationModuleSet == null) { @@ -735,7 +735,7 @@ public sealed override bool TryGetModuleTokenForExternalType(TypeDesc type, out foreach (var module in _compilationModuleSet) { - EcmaModule ecmaModule = module; + EcmaModule ecmaModule = (EcmaModule)module; foreach (var typeRefHandle in ecmaModule.MetadataReader.TypeReferences) { try diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/CorElementType.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/CorElementType.cs index 250acfff6cb4e8..cb2b6d3da2df94 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/CorElementType.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/CorElementType.cs @@ -41,6 +41,5 @@ internal enum CorElementType : byte ELEMENT_TYPE_MODIFIER = 0x40, ELEMENT_TYPE_SENTINEL = 0x41, ELEMENT_TYPE_PINNED = 0x45, - ELEMENT_TYPE_CONTINUATION = 0x46, } } diff --git a/src/tests/async/awaitingnotasync/awaitingnotasync.cs b/src/tests/async/awaitingnotasync/awaitingnotasync.cs index a87553b1ef6fbc..bf8ad8c9cc12a0 100644 --- a/src/tests/async/awaitingnotasync/awaitingnotasync.cs +++ b/src/tests/async/awaitingnotasync/awaitingnotasync.cs @@ -48,5 +48,6 @@ private static async Task AsyncEntryPoint() // await(await ...)) Assert.Equal(7, await await await GetTask(GetTask(GetTask(7)))); + } } diff --git a/src/tests/profiler/native/rejitprofiler/sigparse.h b/src/tests/profiler/native/rejitprofiler/sigparse.h index fb892335c8ed6c..249afa2a80062d 100644 --- a/src/tests/profiler/native/rejitprofiler/sigparse.h +++ b/src/tests/profiler/native/rejitprofiler/sigparse.h @@ -65,60 +65,59 @@ Number ::= 29-bit-encoded-integer */ -#define ELEMENT_TYPE_END 0x00 //Marks end of a list -#define ELEMENT_TYPE_VOID 0x01 -#define ELEMENT_TYPE_BOOLEAN 0x02 -#define ELEMENT_TYPE_CHAR 0x03 -#define ELEMENT_TYPE_I1 0x04 -#define ELEMENT_TYPE_U1 0x05 -#define ELEMENT_TYPE_I2 0x06 -#define ELEMENT_TYPE_U2 0x07 -#define ELEMENT_TYPE_I4 0x08 -#define ELEMENT_TYPE_U4 0x09 -#define ELEMENT_TYPE_I8 0x0a -#define ELEMENT_TYPE_U8 0x0b -#define ELEMENT_TYPE_R4 0x0c -#define ELEMENT_TYPE_R8 0x0d -#define ELEMENT_TYPE_STRING 0x0e -#define ELEMENT_TYPE_PTR 0x0f // Followed by type -#define ELEMENT_TYPE_BYREF 0x10 // Followed by type -#define ELEMENT_TYPE_VALUETYPE 0x11 // Followed by TypeDef or TypeRef token -#define ELEMENT_TYPE_CLASS 0x12 // Followed by TypeDef or TypeRef token -#define ELEMENT_TYPE_VAR 0x13 // Generic parameter in a generic type definition, represented as number -#define ELEMENT_TYPE_ARRAY 0x14 // type rank boundsCount bound1 ... loCount lo1 ... -#define ELEMENT_TYPE_GENERICINST 0x15 // Generic type instantiation. Followed by type type-arg-count type-1 ... type-n -#define ELEMENT_TYPE_TYPEDBYREF 0x16 -#define ELEMENT_TYPE_I 0x18 // System.IntPtr -#define ELEMENT_TYPE_U 0x19 // System.UIntPtr -#define ELEMENT_TYPE_FNPTR 0x1b // Followed by full method signature -#define ELEMENT_TYPE_OBJECT 0x1c // System.Object -#define ELEMENT_TYPE_SZARRAY 0x1d // Single-dim array with 0 lower bound -#define ELEMENT_TYPE_MVAR 0x1e // Generic parameter in a generic method definition,represented as number -#define ELEMENT_TYPE_CMOD_REQD 0x1f // Required modifier : followed by a TypeDef or TypeRef token -#define ELEMENT_TYPE_CMOD_OPT 0x20 // Optional modifier : followed by a TypeDef or TypeRef token -#define ELEMENT_TYPE_INTERNAL 0x21 // Implemented within the CLI -#define ELEMENT_TYPE_MODIFIER 0x40 // Or'd with following element types -#define ELEMENT_TYPE_SENTINEL 0x41 // Sentinel for vararg method signature -#define ELEMENT_TYPE_PINNED 0x45 // Denotes a local variable that points at a pinned object -#define ELEMENT_TYPE_CONTINUATION 0x46 // A specific continuation type. Followed by size, and boolean array - -#define SIG_METHOD_DEFAULT 0x00 // default calling convention -#define SIG_METHOD_C 0x01 // C calling convention -#define SIG_METHOD_STDCALL 0x02 // Stdcall calling convention -#define SIG_METHOD_THISCALL 0x03 // thiscall calling convention -#define SIG_METHOD_FASTCALL 0x04 // fastcall calling convention -#define SIG_METHOD_VARARG 0x05 // vararg calling convention -#define SIG_FIELD 0x06 // encodes a field -#define SIG_LOCAL_SIG 0x07 // used for the .locals directive -#define SIG_PROPERTY 0x08 // used to encode a property - -#define SIG_GENERIC 0x10 // used to indicate that the method has one or more generic parameters. -#define SIG_HASTHIS 0x20 // used to encode the keyword instance in the calling convention -#define SIG_EXPLICITTHIS 0x40 // used to encode the keyword explicit in the calling convention - -#define SIG_INDEX_TYPE_TYPEDEF 0x00 // ParseTypeDefOrRefEncoded returns this as the out index type for typedefs -#define SIG_INDEX_TYPE_TYPEREF 0x01 // ParseTypeDefOrRefEncoded returns this as the out index type for typerefs -#define SIG_INDEX_TYPE_TYPESPEC 0x02 // ParseTypeDefOrRefEncoded returns this as the out index type for typespecs +#define ELEMENT_TYPE_END 0x00 //Marks end of a list +#define ELEMENT_TYPE_VOID 0x01 +#define ELEMENT_TYPE_BOOLEAN 0x02 +#define ELEMENT_TYPE_CHAR 0x03 +#define ELEMENT_TYPE_I1 0x04 +#define ELEMENT_TYPE_U1 0x05 +#define ELEMENT_TYPE_I2 0x06 +#define ELEMENT_TYPE_U2 0x07 +#define ELEMENT_TYPE_I4 0x08 +#define ELEMENT_TYPE_U4 0x09 +#define ELEMENT_TYPE_I8 0x0a +#define ELEMENT_TYPE_U8 0x0b +#define ELEMENT_TYPE_R4 0x0c +#define ELEMENT_TYPE_R8 0x0d +#define ELEMENT_TYPE_STRING 0x0e +#define ELEMENT_TYPE_PTR 0x0f // Followed by type +#define ELEMENT_TYPE_BYREF 0x10 // Followed by type +#define ELEMENT_TYPE_VALUETYPE 0x11 // Followed by TypeDef or TypeRef token +#define ELEMENT_TYPE_CLASS 0x12 // Followed by TypeDef or TypeRef token +#define ELEMENT_TYPE_VAR 0x13 // Generic parameter in a generic type definition, represented as number +#define ELEMENT_TYPE_ARRAY 0x14 // type rank boundsCount bound1 ... loCount lo1 ... +#define ELEMENT_TYPE_GENERICINST 0x15 // Generic type instantiation. Followed by type type-arg-count type-1 ... type-n +#define ELEMENT_TYPE_TYPEDBYREF 0x16 +#define ELEMENT_TYPE_I 0x18 // System.IntPtr +#define ELEMENT_TYPE_U 0x19 // System.UIntPtr +#define ELEMENT_TYPE_FNPTR 0x1b // Followed by full method signature +#define ELEMENT_TYPE_OBJECT 0x1c // System.Object +#define ELEMENT_TYPE_SZARRAY 0x1d // Single-dim array with 0 lower bound +#define ELEMENT_TYPE_MVAR 0x1e // Generic parameter in a generic method definition,represented as number +#define ELEMENT_TYPE_CMOD_REQD 0x1f // Required modifier : followed by a TypeDef or TypeRef token +#define ELEMENT_TYPE_CMOD_OPT 0x20 // Optional modifier : followed by a TypeDef or TypeRef token +#define ELEMENT_TYPE_INTERNAL 0x21 // Implemented within the CLI +#define ELEMENT_TYPE_MODIFIER 0x40 // Or'd with following element types +#define ELEMENT_TYPE_SENTINEL 0x41 // Sentinel for vararg method signature +#define ELEMENT_TYPE_PINNED 0x45 // Denotes a local variable that points at a pinned object + +#define SIG_METHOD_DEFAULT 0x00 // default calling convention +#define SIG_METHOD_C 0x01 // C calling convention +#define SIG_METHOD_STDCALL 0x02 // Stdcall calling convention +#define SIG_METHOD_THISCALL 0x03 // thiscall calling convention +#define SIG_METHOD_FASTCALL 0x04 // fastcall calling convention +#define SIG_METHOD_VARARG 0x05 // vararg calling convention +#define SIG_FIELD 0x06 // encodes a field +#define SIG_LOCAL_SIG 0x07 // used for the .locals directive +#define SIG_PROPERTY 0x08 // used to encode a property + +#define SIG_GENERIC 0x10 // used to indicate that the method has one or more generic parameters. +#define SIG_HASTHIS 0x20 // used to encode the keyword instance in the calling convention +#define SIG_EXPLICITTHIS 0x40 // used to encode the keyword explicit in the calling convention + +#define SIG_INDEX_TYPE_TYPEDEF 0x00 // ParseTypeDefOrRefEncoded returns this as the out index type for typedefs +#define SIG_INDEX_TYPE_TYPEREF 0x01 // ParseTypeDefOrRefEncoded returns this as the out index type for typerefs +#define SIG_INDEX_TYPE_TYPESPEC 0x02 // ParseTypeDefOrRefEncoded returns this as the out index type for typespecs typedef unsigned char sig_byte; From e1dec2b40f3e62f1e1984957cf28770fa93a3b81 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 5 Feb 2026 14:04:13 -0800 Subject: [PATCH 03/22] Fix merge issues --- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 3 --- .../ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs | 4 ---- 2 files changed, 7 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 3e5fc315e491a1..dd5af263346957 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -994,9 +994,6 @@ private ISymbolNode GetHelperFtnUncached(CorInfoHelpFunc ftnNum) case CorInfoHelpFunc.CORINFO_HELP_THROW: id = ReadyToRunHelper.Throw; break; - case CorInfoHelpFunc.CORINFO_HELP_THROWEXACT: - id = ReadyToRunHelper.ThrowExact; - break; case CorInfoHelpFunc.CORINFO_HELP_RETHROW: id = ReadyToRunHelper.Rethrow; break; diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs index e7c7527ee100af..0c1c91602ee175 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs @@ -1682,10 +1682,6 @@ private void ParseHelper(StringBuilder builder) builder.Append("THROW"); break; - case ReadyToRunHelper.ThrowExact: - builder.Append("THROW_EXACT"); - break; - case ReadyToRunHelper.Rethrow: builder.Append("RETHROW"); break; From 696b6b61bec5109b7a9375ba3e3578e59cf115bb Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 5 Feb 2026 16:00:30 -0800 Subject: [PATCH 04/22] Cleanup ManifestModuleWrappedIL creation --- .../Compiler/ReadyToRunCodegenCompilation.cs | 19 +++++++------------ .../ReadyToRunCompilationModuleGroupBase.cs | 2 -- .../IL/ReadyToRunILProvider.cs | 3 +-- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 4 ++-- 4 files changed, 10 insertions(+), 18 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs index bbc7077ab2f206..915f863afd1958 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs @@ -696,7 +696,7 @@ protected override void ComputeDependencyNodeDependencies(List Date: Thu, 5 Feb 2026 16:22:48 -0800 Subject: [PATCH 05/22] Use correct OwningMethod for ManifestMetadataModuleWrappedMethodIL.Initialize --- .../IL/ReadyToRunILProvider.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs index d715673be67f8b..eaff0275f002f5 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs @@ -129,6 +129,9 @@ private MethodIL TryGetPerInstantiationIntrinsicMethodIL(MethodDesc method) // This method is order dependent, and must be called during the single threaded portion of compilation public void CreateCrossModuleInlineableTokensForILBody(MethodDesc method) { + // This method accepts only method definitions, but accepts non-primary method definitions. + // That is, it must not be generic, but it may represent an AsyncVariant + Debug.Assert(method.IsTypicalMethodDefinition); Debug.Assert(_manifestMutableModule != null); var wrappedMethodIL = new ManifestModuleWrappedMethodIL(); @@ -146,7 +149,7 @@ public void CreateCrossModuleInlineableTokensForILBody(MethodDesc method) { if (!wrappedMethodIL.Initialize(_manifestMutableModule, AsyncThunkILEmitter.EmitAsyncMethodThunk(method, method.GetTargetOfAsyncVariant()), - (EcmaMethod)method.GetTargetOfAsyncVariant(), + method, false)) { // If we could not initialize the wrapped method IL, we should store a null. @@ -160,7 +163,7 @@ public void CreateCrossModuleInlineableTokensForILBody(MethodDesc method) if (!wrappedMethodIL.Initialize( _manifestMutableModule, ars.EmitIL(), - (EcmaMethod)ars.TargetMethod.GetPrimaryMethodDesc().GetTypicalMethodDefinition(), + ars, false)) { // If we could not initialize the wrapped method IL, we should store a null. @@ -304,7 +307,7 @@ class ManifestModuleWrappedMethodIL : MethodIL, IEcmaMethodIL, IMethodTokensAreU { int _maxStack; bool _isInitLocals; - EcmaMethod _owningMethod; + MethodDesc _owningMethod; ILExceptionRegion[] _exceptionRegions; byte[] _ilBytes; LocalVariableDefinition[] _locals; @@ -319,8 +322,9 @@ public bool Initialize(MutableModule mutableModule, EcmaMethodIL wrappedMethod) return Initialize(mutableModule, wrappedMethod, wrappedMethod.OwningMethod, true); } - public bool Initialize(MutableModule mutableModule, MethodIL wrappedMethod, EcmaMethod owningMethod, bool validateStandaloneMetadata) + public bool Initialize(MutableModule mutableModule, MethodIL wrappedMethod, MethodDesc owningMethod, bool validateStandaloneMetadata) { + Debug.Assert(owningMethod.IsTypicalMethodDefinition); HashSet methodsWhichCannotHaveAsyncVariants = null; _methodsWithAsyncVariants = null; @@ -331,7 +335,7 @@ public bool Initialize(MutableModule mutableModule, MethodIL wrappedMethod, Ecma try { Debug.Assert(mutableModule.ModuleThatIsCurrentlyTheSourceOfNewReferences == null); - mutableModule.ModuleThatIsCurrentlyTheSourceOfNewReferences = owningMethod.Module; + mutableModule.ModuleThatIsCurrentlyTheSourceOfNewReferences = ((EcmaMethod)owningMethod.GetPrimaryMethodDesc()).Module; var owningMethodHandle = mutableModule.TryGetEntityHandle(owningMethod); if (!owningMethodHandle.HasValue) return false; @@ -360,7 +364,7 @@ public bool Initialize(MutableModule mutableModule, MethodIL wrappedMethod, Ecma ILTokenReplacer.Replace(_ilBytes, GetMutableModuleToken); #if DEBUG if (validateStandaloneMetadata) - Debug.Assert(ReadyToRunStandaloneMethodMetadata.Compute(_owningMethod) != null); + Debug.Assert(ReadyToRunStandaloneMethodMetadata.Compute((EcmaMethod)_owningMethod.GetPrimaryMethodDesc()) != null); #endif // DEBUG } finally From d37bfc84b19ffcac18f399533b19d8f9d7cd70ae Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Fri, 6 Feb 2026 13:28:50 -0800 Subject: [PATCH 06/22] Undo formatting in CorInfoImpl.ReadyToRun.cs --- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 317 +++++++++--------- 1 file changed, 157 insertions(+), 160 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index a74ec35533b133..08353a1b3f01be 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -63,14 +63,14 @@ public FieldWithToken(FieldDesc field, ModuleToken token) var memberRef = token.MetadataReader.GetMemberReference((MemberReferenceHandle)token.Handle); switch (memberRef.Parent.Kind) { - case HandleKind.TypeDefinition: - case HandleKind.TypeReference: - case HandleKind.TypeSpecification: - OwningTypeNotDerivedFromToken = token.Module.GetType(memberRef.Parent) != field.OwningType; - break; + case HandleKind.TypeDefinition: + case HandleKind.TypeReference: + case HandleKind.TypeSpecification: + OwningTypeNotDerivedFromToken = token.Module.GetType(memberRef.Parent) != field.OwningType; + break; - default: - break; + default: + break; } } } @@ -179,10 +179,10 @@ private static TypeDesc GetMethodTokenOwningType(MethodWithToken methodToken, Ty case HandleKind.TypeDefinition: case HandleKind.TypeReference: case HandleKind.TypeSpecification: - { - Debug.Assert(devirtualizedMethodOwner == null); // Devirtualization is expected to always use a methoddef token - return HandleContext(moduleToken.Module, memberRef.Parent, methodToken.Method.OwningType, constrainedType, context, null, ref owningTypeNotDerivedFromToken); - } + { + Debug.Assert(devirtualizedMethodOwner == null); // Devirtualization is expected to always use a methoddef token + return HandleContext(moduleToken.Module, memberRef.Parent, methodToken.Method.OwningType, constrainedType, context, null, ref owningTypeNotDerivedFromToken); + } default: return methodToken.Method.OwningType; @@ -854,96 +854,96 @@ private bool getReadyToRunHelper(ref CORINFO_RESOLVED_TOKEN pResolvedToken, ref switch (id) { case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_NEW: - { - var type = HandleToObject(pResolvedToken.hClass); - Debug.Assert(type.IsDefType); - if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) - return false; + { + var type = HandleToObject(pResolvedToken.hClass); + Debug.Assert(type.IsDefType); + if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) + return false; - pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.NewHelper, type)); - } - break; + pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.NewHelper, type)); + } + break; case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_NEWARR_1: - { - var type = HandleToObject(pResolvedToken.hClass); - Debug.Assert(type.IsSzArray); - if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) - return false; + { + var type = HandleToObject(pResolvedToken.hClass); + Debug.Assert(type.IsSzArray); + if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) + return false; - pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.NewArr1, type)); - } - break; + pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.NewArr1, type)); + } + break; case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_ISINSTANCEOF: - { - var type = HandleToObject(pResolvedToken.hClass); - if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) - return false; + { + var type = HandleToObject(pResolvedToken.hClass); + if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) + return false; - // ECMA-335 III.4.3: If typeTok is a nullable type, Nullable, it is interpreted as "boxed" T - if (type.IsNullable) - type = type.Instantiation[0]; + // ECMA-335 III.4.3: If typeTok is a nullable type, Nullable, it is interpreted as "boxed" T + if (type.IsNullable) + type = type.Instantiation[0]; - pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.IsInstanceOf, type)); - } - break; + pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.IsInstanceOf, type)); + } + break; case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_CHKCAST: - { - var type = HandleToObject(pResolvedToken.hClass); - if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) - return false; + { + var type = HandleToObject(pResolvedToken.hClass); + if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) + return false; - // ECMA-335 III.4.3: If typeTok is a nullable type, Nullable, it is interpreted as "boxed" T - if (type.IsNullable) - type = type.Instantiation[0]; + // ECMA-335 III.4.3: If typeTok is a nullable type, Nullable, it is interpreted as "boxed" T + if (type.IsNullable) + type = type.Instantiation[0]; - pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.CastClass, type)); - } - break; + pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.CastClass, type)); + } + break; case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_GCSTATIC_BASE: case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_NONGCSTATIC_BASE: case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_THREADSTATIC_BASE: case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_NONGCTHREADSTATIC_BASE: - { - var type = HandleToObject(pResolvedToken.hClass); - if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) - return false; - var helperId = GetReadyToRunHelperFromStaticBaseHelper(id); - pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(helperId, type)); - } - break; - case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_GENERIC_HANDLE: - { - Debug.Assert(pGenericLookupKind.needsRuntimeLookup); - - ReadyToRunHelperId helperId = (ReadyToRunHelperId)pGenericLookupKind.runtimeLookupFlags; - TypeDesc constrainedType = null; - if (helperId == ReadyToRunHelperId.MethodEntry && pGenericLookupKind.runtimeLookupArgs != null) - { - constrainedType = (TypeDesc)GetRuntimeDeterminedObjectForToken(ref *(CORINFO_RESOLVED_TOKEN*)pGenericLookupKind.runtimeLookupArgs); - _compilation.NodeFactory.DetectGenericCycles(MethodBeingCompiled, constrainedType); - } - object helperArg = GetRuntimeDeterminedObjectForToken(ref pResolvedToken); - if (helperArg is MethodDesc methodDesc) { - var methodIL = HandleToObject(pResolvedToken.tokenScope); - MethodDesc sharedMethod = methodIL.OwningMethod.GetSharedRuntimeFormMethodTarget(); - _compilation.NodeFactory.DetectGenericCycles(MethodBeingCompiled, sharedMethod); - helperArg = new MethodWithToken(methodDesc, HandleToModuleToken(ref pResolvedToken), constrainedType, unboxing: false, context: sharedMethod); + var type = HandleToObject(pResolvedToken.hClass); + if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) + return false; + var helperId = GetReadyToRunHelperFromStaticBaseHelper(id); + pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(helperId, type)); } - else if (helperArg is FieldDesc fieldDesc) + break; + case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_GENERIC_HANDLE: { - helperArg = new FieldWithToken(fieldDesc, HandleToModuleToken(ref pResolvedToken)); - } + Debug.Assert(pGenericLookupKind.needsRuntimeLookup); - var methodContext = new GenericContext(HandleToObject(callerHandle)); - ISymbolNode helper = _compilation.SymbolNodeFactory.GenericLookupHelper( - pGenericLookupKind.runtimeLookupKind, - helperId, - helperArg, - methodContext); - pLookup = CreateConstLookupToSymbol(helper); - } - break; + ReadyToRunHelperId helperId = (ReadyToRunHelperId)pGenericLookupKind.runtimeLookupFlags; + TypeDesc constrainedType = null; + if (helperId == ReadyToRunHelperId.MethodEntry && pGenericLookupKind.runtimeLookupArgs != null) + { + constrainedType = (TypeDesc)GetRuntimeDeterminedObjectForToken(ref *(CORINFO_RESOLVED_TOKEN*)pGenericLookupKind.runtimeLookupArgs); + _compilation.NodeFactory.DetectGenericCycles(MethodBeingCompiled, constrainedType); + } + object helperArg = GetRuntimeDeterminedObjectForToken(ref pResolvedToken); + if (helperArg is MethodDesc methodDesc) + { + var methodIL = HandleToObject(pResolvedToken.tokenScope); + MethodDesc sharedMethod = methodIL.OwningMethod.GetSharedRuntimeFormMethodTarget(); + _compilation.NodeFactory.DetectGenericCycles(MethodBeingCompiled, sharedMethod); + helperArg = new MethodWithToken(methodDesc, HandleToModuleToken(ref pResolvedToken), constrainedType, unboxing: false, context: sharedMethod); + } + else if (helperArg is FieldDesc fieldDesc) + { + helperArg = new FieldWithToken(fieldDesc, HandleToModuleToken(ref pResolvedToken)); + } + + var methodContext = new GenericContext(HandleToObject(callerHandle)); + ISymbolNode helper = _compilation.SymbolNodeFactory.GenericLookupHelper( + pGenericLookupKind.runtimeLookupKind, + helperId, + helperArg, + methodContext); + pLookup = CreateConstLookupToSymbol(helper); + } + break; default: throw new NotImplementedException("ReadyToRun: " + id.ToString()); } @@ -2513,20 +2513,20 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO switch (pResult->kind) { case CORINFO_CALL_KIND.CORINFO_VIRTUALCALL_STUB: - { - if (pResult->codePointerOrStubLookup.lookupKind.needsRuntimeLookup) { - return; - } + if (pResult->codePointerOrStubLookup.lookupKind.needsRuntimeLookup) + { + return; + } - pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol( - _compilation.SymbolNodeFactory.InterfaceDispatchCell( - ComputeMethodWithToken(targetMethod, ref pResolvedToken, constrainedType: null, unboxing: false), - MethodBeingCompiled)); + pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol( + _compilation.SymbolNodeFactory.InterfaceDispatchCell( + ComputeMethodWithToken(targetMethod, ref pResolvedToken, constrainedType: null, unboxing: false), + MethodBeingCompiled)); - // If the abi of the method isn't stable, this will cause a usage of the RequiresRuntimeJitSymbol, which will trigger a RequiresRuntimeJitException - UpdateConstLookupWithRequiresRuntimeJitSymbolIfNeeded(ref pResult->codePointerOrStubLookup.constLookup, targetMethod); - } + // If the abi of the method isn't stable, this will cause a usage of the RequiresRuntimeJitSymbol, which will trigger a RequiresRuntimeJitException + UpdateConstLookupWithRequiresRuntimeJitSymbolIfNeeded(ref pResult->codePointerOrStubLookup.constLookup, targetMethod); + } break; @@ -2541,41 +2541,41 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO return; case CORINFO_CALL_KIND.CORINFO_CALL: - { - // Constrained token is not interesting with this transforms - if (pResult->thisTransform != CORINFO_THIS_TRANSFORM.CORINFO_NO_THIS_TRANSFORM) - constrainedType = null; - - MethodDesc nonUnboxingMethod = methodToCall; - bool isUnboxingStub = methodToCall.IsUnboxingThunk(); - if (isUnboxingStub) - { - nonUnboxingMethod = methodToCall.GetUnboxedMethod(); - } - if (nonUnboxingMethod is IL.Stubs.PInvokeTargetNativeMethod rawPinvoke) { - nonUnboxingMethod = rawPinvoke.Target; - } + // Constrained token is not interesting with this transforms + if (pResult->thisTransform != CORINFO_THIS_TRANSFORM.CORINFO_NO_THIS_TRANSFORM) + constrainedType = null; - if (methodToCall.OwningType.IsArray && methodToCall.IsConstructor) - { - pResult->codePointerOrStubLookup.constLookup = default; - } - else - { - // READYTORUN: FUTURE: Direct calls if possible - pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol( - _compilation.NodeFactory.MethodEntrypoint( - ComputeMethodWithToken(nonUnboxingMethod, ref pResolvedToken, constrainedType, unboxing: isUnboxingStub), - isInstantiatingStub: useInstantiatingStub, - isPrecodeImportRequired: (flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_LDFTN) != 0, - isJumpableImportRequired: false)); - } + MethodDesc nonUnboxingMethod = methodToCall; + bool isUnboxingStub = methodToCall.IsUnboxingThunk(); + if (isUnboxingStub) + { + nonUnboxingMethod = methodToCall.GetUnboxedMethod(); + } + if (nonUnboxingMethod is IL.Stubs.PInvokeTargetNativeMethod rawPinvoke) + { + nonUnboxingMethod = rawPinvoke.Target; + } - // If the abi of the method isn't stable, this will cause a usage of the RequiresRuntimeJitSymbol, which will trigger a RequiresRuntimeJitException - UpdateConstLookupWithRequiresRuntimeJitSymbolIfNeeded(ref pResult->codePointerOrStubLookup.constLookup, targetMethod); - } - break; + if (methodToCall.OwningType.IsArray && methodToCall.IsConstructor) + { + pResult->codePointerOrStubLookup.constLookup = default; + } + else + { + // READYTORUN: FUTURE: Direct calls if possible + pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol( + _compilation.NodeFactory.MethodEntrypoint( + ComputeMethodWithToken(nonUnboxingMethod, ref pResolvedToken, constrainedType, unboxing: isUnboxingStub), + isInstantiatingStub: useInstantiatingStub, + isPrecodeImportRequired: (flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_LDFTN) != 0, + isJumpableImportRequired: false)); + } + + // If the abi of the method isn't stable, this will cause a usage of the RequiresRuntimeJitSymbol, which will trigger a RequiresRuntimeJitException + UpdateConstLookupWithRequiresRuntimeJitSymbolIfNeeded(ref pResult->codePointerOrStubLookup.constLookup, targetMethod); + } + break; case CORINFO_CALL_KIND.CORINFO_VIRTUALCALL_VTABLE: // Only calls within the CoreLib version bubble support fragile NI codegen with vtable based calls, for better performance (because @@ -2615,10 +2615,6 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO MethodDesc canonMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); if (canonMethod.RequiresInstMethodDescArg()) { - //if (canonMethod.IsAsyncCall()) - //{ - // throw new RequiresRuntimeJitException(""); - //} pResult->instParamLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper( ReadyToRunHelperId.MethodDictionary, ComputeMethodWithToken(targetMethod, ref pResolvedToken, constrainedType: constrainedType, unboxing: false))); @@ -2694,17 +2690,17 @@ private void ComputeRuntimeLookupForSharedGenericToken( case DictionaryEntryKind.MethodEntrySlot: case DictionaryEntryKind.ConstrainedMethodEntrySlot: case DictionaryEntryKind.DispatchStubAddrSlot: - { - if (entryKind == DictionaryEntryKind.MethodDescSlot) - pResultLookup.lookupKind.runtimeLookupFlags = (ushort)ReadyToRunHelperId.MethodHandle; - else if (entryKind == DictionaryEntryKind.MethodEntrySlot || entryKind == DictionaryEntryKind.ConstrainedMethodEntrySlot) - pResultLookup.lookupKind.runtimeLookupFlags = (ushort)ReadyToRunHelperId.MethodEntry; - else - pResultLookup.lookupKind.runtimeLookupFlags = (ushort)ReadyToRunHelperId.VirtualDispatchCell; + { + if (entryKind == DictionaryEntryKind.MethodDescSlot) + pResultLookup.lookupKind.runtimeLookupFlags = (ushort)ReadyToRunHelperId.MethodHandle; + else if (entryKind == DictionaryEntryKind.MethodEntrySlot || entryKind == DictionaryEntryKind.ConstrainedMethodEntrySlot) + pResultLookup.lookupKind.runtimeLookupFlags = (ushort)ReadyToRunHelperId.MethodEntry; + else + pResultLookup.lookupKind.runtimeLookupFlags = (ushort)ReadyToRunHelperId.VirtualDispatchCell; - pResultLookup.lookupKind.runtimeLookupArgs = pConstrainedResolvedToken; - break; - } + pResultLookup.lookupKind.runtimeLookupArgs = pConstrainedResolvedToken; + break; + } case DictionaryEntryKind.FieldDescSlot: pResultLookup.lookupKind.runtimeLookupFlags = (ushort)ReadyToRunHelperId.FieldHandle; @@ -2866,26 +2862,26 @@ private void embedGenericHandle(ref CORINFO_RESOLVED_TOKEN pResolvedToken, bool break; case CorInfoGenericHandleType.CORINFO_HANDLETYPE_METHOD: - { - MethodDesc md = HandleToObject(pResolvedToken.hMethod); - TypeDesc td = HandleToObject(pResolvedToken.hClass); - - bool unboxingStub = false; - // - // This logic should be kept in sync with MethodTableBuilder::NeedsTightlyBoundUnboxingStub - // Essentially all ValueType virtual methods will require an Unboxing Stub - // - if ((td.IsValueType) && !md.Signature.IsStatic - && md.IsVirtual) { - unboxingStub = true; - } + MethodDesc md = HandleToObject(pResolvedToken.hMethod); + TypeDesc td = HandleToObject(pResolvedToken.hClass); - symbolNode = _compilation.SymbolNodeFactory.CreateReadyToRunHelper( - ReadyToRunHelperId.MethodHandle, - ComputeMethodWithToken(md, ref pResolvedToken, constrainedType: null, unboxing: unboxingStub)); - } - break; + bool unboxingStub = false; + // + // This logic should be kept in sync with MethodTableBuilder::NeedsTightlyBoundUnboxingStub + // Essentially all ValueType virtual methods will require an Unboxing Stub + // + if ((td.IsValueType) && !md.Signature.IsStatic + && md.IsVirtual) + { + unboxingStub = true; + } + + symbolNode = _compilation.SymbolNodeFactory.CreateReadyToRunHelper( + ReadyToRunHelperId.MethodHandle, + ComputeMethodWithToken(md, ref pResolvedToken, constrainedType: null, unboxing: unboxingStub)); + } + break; case CorInfoGenericHandleType.CORINFO_HANDLETYPE_FIELD: symbolNode = _compilation.SymbolNodeFactory.CreateReadyToRunHelper( @@ -3032,7 +3028,7 @@ private bool TypeCannotUseBasePlusOffsetEncoding(MetadataType type) private void getGSCookie(IntPtr* pCookieVal, IntPtr** ppCookieVal) { *pCookieVal = IntPtr.Zero; - *ppCookieVal = (IntPtr*)ObjectToHandle(_compilation.NodeFactory.GetReadyToRunHelperCell(ReadyToRunHelper.GSCookie)); + *ppCookieVal = (IntPtr *)ObjectToHandle(_compilation.NodeFactory.GetReadyToRunHelperCell(ReadyToRunHelper.GSCookie)); } private int* getAddrOfCaptureThreadGlobal(ref void* ppIndirection) @@ -3331,6 +3327,7 @@ private void reportInliningDecision(CORINFO_METHOD_STRUCT_* inlinerHnd, CORINFO_ // 2. If at any time, the set of methods that are inlined includes a method which has an IL body without // tokens that are useable in compilation, record that information, and once the multi-threaded portion // of the build finishes, it will then compute the IL bodies for those methods, then run the compilation again. + if (needsTokenTranslation && !(methodIL is IMethodTokensAreUseableInCompilation) && methodIL is EcmaMethodIL) { // We may have already acquired the right type of MethodIL here, or be working with a method that is an IL Intrinsic From af114a05dd61d957a70ff11a082fb89a37d60b0c Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Fri, 6 Feb 2026 13:56:45 -0800 Subject: [PATCH 07/22] Remove duplicated method from merge, remove unnecessary token manipulating --- .../ReadyToRun/TypeFixupSignature.cs | 47 ------------------- .../IL/ReadyToRunILProvider.cs | 4 +- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 12 +---- 3 files changed, 3 insertions(+), 60 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs index 1ceb33a5b9af02..69449f4d066499 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs @@ -136,53 +136,6 @@ private static void EncodeTypeLayout(ObjectDataSignatureBuilder dataBuilder, Typ } } - private static void EncodeContinuationTypeLayout(ObjectDataSignatureBuilder dataBuilder, AsyncContinuationType type) - { - int pointerSize = type.Context.Target.PointerSize; - int size = type.PointerMap.Size * pointerSize; - int alignment = pointerSize; - ReadyToRunTypeLayoutFlags flags = ReadyToRunTypeLayoutFlags.READYTORUN_LAYOUT_Alignment | ReadyToRunTypeLayoutFlags.READYTORUN_LAYOUT_GCLayout; - Debug.Assert(alignment == pointerSize); - flags |= ReadyToRunTypeLayoutFlags.READYTORUN_LAYOUT_Alignment_Native; - - bool gcLayoutEmpty = true; - foreach(bool hasPointer in type.PointerMap) - { - if (hasPointer) - { - gcLayoutEmpty = false; - break; - } - } - if (gcLayoutEmpty) - { - flags |= ReadyToRunTypeLayoutFlags.READYTORUN_LAYOUT_GCLayout_Empty; - } - - dataBuilder.EmitUInt((uint)flags); - dataBuilder.EmitUInt((uint)size); - - if (!gcLayoutEmpty) - { - // Encode the GC pointer map - GCPointerMap gcMap = type.PointerMap; - - byte[] encodedGCRefMap = new byte[(size / pointerSize + 7) / 8]; - int bitIndex = 0; - foreach (bool bit in gcMap) - { - if (bit) - { - encodedGCRefMap[bitIndex / 8] |= (byte)(1 << (bitIndex & 7)); - } - - ++bitIndex; - } - - dataBuilder.EmitBytes(encodedGCRefMap); - } - } - private static void EncodeContinuationTypeLayout(ObjectDataSignatureBuilder dataBuilder, AsyncContinuationType type) { int pointerSize = type.Context.Target.PointerSize; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs index eaff0275f002f5..8ea8fed5e19637 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs @@ -335,7 +335,7 @@ public bool Initialize(MutableModule mutableModule, MethodIL wrappedMethod, Meth try { Debug.Assert(mutableModule.ModuleThatIsCurrentlyTheSourceOfNewReferences == null); - mutableModule.ModuleThatIsCurrentlyTheSourceOfNewReferences = ((EcmaMethod)owningMethod.GetPrimaryMethodDesc()).Module; + mutableModule.ModuleThatIsCurrentlyTheSourceOfNewReferences = ((EcmaMethod)owningMethod.GetPrimaryMethodDesc().GetTypicalMethodDefinition()).Module; var owningMethodHandle = mutableModule.TryGetEntityHandle(owningMethod); if (!owningMethodHandle.HasValue) return false; @@ -364,7 +364,7 @@ public bool Initialize(MutableModule mutableModule, MethodIL wrappedMethod, Meth ILTokenReplacer.Replace(_ilBytes, GetMutableModuleToken); #if DEBUG if (validateStandaloneMetadata) - Debug.Assert(ReadyToRunStandaloneMethodMetadata.Compute((EcmaMethod)_owningMethod.GetPrimaryMethodDesc()) != null); + Debug.Assert(ReadyToRunStandaloneMethodMetadata.Compute((EcmaMethod)_owningMethod.GetPrimaryMethodDesc().GetTypicalMethodDefinition()) != null); #endif // DEBUG } finally diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 08353a1b3f01be..bc782d0c5af167 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -141,16 +141,6 @@ public MethodWithToken(MethodDesc method, ModuleToken token, TypeDesc constraine ConstrainedType = constrainedType; Unboxing = unboxing; OwningType = GetMethodTokenOwningType(this, constrainedType, context, devirtualizedMethodOwner, out OwningTypeNotDerivedFromToken); - if (method.IsAsync && method.IsAsyncVariant() && token.Module is MutableModule) - { - var ecmaMethod = (EcmaMethod)method.GetPrimaryMethodDesc().GetTypicalMethodDefinition(); - var newToken = new ModuleToken(ecmaMethod.Module, ecmaMethod.Handle); - Token = newToken; - if (Token.Module.GetObject((EntityHandle)Token.Handle) != newToken.Module.GetObject((EntityHandle)newToken.Handle)) - { - OwningTypeNotDerivedFromToken = true; - } - } } private static TypeDesc GetMethodTokenOwningType(MethodWithToken methodToken, TypeDesc constrainedType, TypeSystemEntity context, TypeDesc devirtualizedMethodOwner, out bool owningTypeNotDerivedFromToken) @@ -334,7 +324,7 @@ public bool Equals(MethodWithToken methodWithToken) && Unboxing == methodWithToken.Unboxing; if (equals) { - //Debug.Assert(OwningTypeNotDerivedFromToken == methodWithToken.OwningTypeNotDerivedFromToken); + Debug.Assert(OwningTypeNotDerivedFromToken == methodWithToken.OwningTypeNotDerivedFromToken); Debug.Assert(OwningType == methodWithToken.OwningType); } From 435c5d96b1d729c3680cebcb6c3c32d8b992fdf9 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Fri, 6 Feb 2026 14:14:32 -0800 Subject: [PATCH 08/22] Remove duplicates from merge --- .../aot/ILCompiler.Compiler/Compiler/JitHelper.cs | 10 ---------- .../ReadyToRunSignature.cs | 12 ------------ 2 files changed, 22 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs index b7ccd41fe88231..0204c7bf5cef0d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs @@ -334,16 +334,6 @@ public static void GetEntryPoint(TypeSystemContext context, ReadyToRunHelper id, methodDesc = context.GetCoreLibEntryPoint("System"u8, "RuntimeTypeHandle"u8, "GetRuntimeTypeHandleFromMethodTable"u8, null); break; - case ReadyToRunHelper.AllocContinuation: - methodDesc = context.GetCoreLibEntryPoint("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8, "AllocContinuation"u8, null); - break; - case ReadyToRunHelper.AllocContinuationMethod: - methodDesc = context.GetCoreLibEntryPoint("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8, "AllocContinuationMethod"u8, null); - break; - case ReadyToRunHelper.AllocContinuationClass: - methodDesc = context.GetCoreLibEntryPoint("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8, "AllocContinuationClass"u8, null); - break; - case ReadyToRunHelper.GetCurrentManagedThreadId: methodDesc = context.SystemModule.GetKnownType("System"u8, "Environment"u8).GetKnownMethod("get_CurrentManagedThreadId"u8, null); break; diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs index ad77da26d8a407..0931c018e7c04d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs @@ -1980,18 +1980,6 @@ private void ParseHelper(StringBuilder builder) builder.Append("PERSONALITY_ROUTINE_FILTER_FUNCLET"); break; - case ReadyToRunHelper.AllocContinuation: - builder.Append("ALLOC_CONTINUATION"); - break; - - case ReadyToRunHelper.AllocContinuationMethod: - builder.Append("ALLOC_CONTINUATION_METHOD"); - break; - - case ReadyToRunHelper.AllocContinuationClass: - builder.Append("ALLOC_CONTINUATION_CLASS"); - break; - // // Deprecated/legacy // From 8465ceb93b4d23bc6e5b5436dba0464576cd4b01 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Sun, 8 Feb 2026 14:59:33 -0800 Subject: [PATCH 09/22] Use the same version resilient hash code for resumption stub and async method. --- src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | 4 +++- .../Common/TypeSystem/IL/Stubs/AsyncResumptionStub.cs | 10 ++++++---- src/coreclr/vm/readytoruninfo.cpp | 5 ----- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 5bf91b31d20832..3ae8931de20d12 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1383,8 +1383,10 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) // a safe condition, and we could delete this assert. This assert exists in order to help identify // cases where the virtual function resolution algorithm either does not function, or is not used // correctly. + // TODO: Async variant devirtualization algorithm #if DEBUG - if (info->detail == CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_UNKNOWN) + if (info->detail == CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_UNKNOWN + && !decl.IsAsyncVariant()) { Console.Error.WriteLine($"Failed devirtualization with unexpected unknown failure while compiling {MethodBeingCompiled} with decl {decl} targeting type {objType}"); Debug.Assert(info->detail != CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_UNKNOWN); diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncResumptionStub.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncResumptionStub.cs index a3cd88b7511bf7..dbff48e25c7f44 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncResumptionStub.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncResumptionStub.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Immutable; using Internal.IL; using Internal.IL.Stubs; using Internal.TypeSystem; @@ -17,17 +16,15 @@ public partial class AsyncResumptionStub : ILStubMethod private readonly MethodDesc _targetMethod; private readonly TypeDesc _owningType; private MethodSignature _signature; - private ImmutableArray _name; public AsyncResumptionStub(MethodDesc targetMethod, TypeDesc owningType) { Debug.Assert(targetMethod.IsAsyncCall()); _targetMethod = targetMethod; _owningType = owningType; - _name = [.. "RESUME_"u8, .. _targetMethod.Name]; } - public override ReadOnlySpan Name => _name.AsSpan(); + public override ReadOnlySpan Name => _targetMethod.Name; public override string DiagnosticName => "RESUME_" + _targetMethod.DiagnosticName; public override TypeDesc OwningType => _owningType; @@ -38,6 +35,11 @@ public AsyncResumptionStub(MethodDesc targetMethod, TypeDesc owningType) public MethodDesc TargetMethod => _targetMethod; + /// + /// The hash of the async variant method is used at runtime to find the bucket of the resumption stub. + /// These should be identical for the async variant and the resumption stub. + /// + protected override int ComputeHashCode() => _targetMethod.GetHashCode(); private MethodSignature InitializeSignature() { diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index 7c369ce190a843..2ce5638831b4a2 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -1208,11 +1208,6 @@ static bool SigMatchesResumptionStubForMethod(MethodDesc* pAsyncMD, SigPointer & } } - // The sig should have AsyncVariant set (resumption stubs are for async variants) - bool sigIsAsync = (methodFlags & ENCODE_METHOD_SIG_AsyncVariant) != 0; - if (sigIsAsync != pAsyncMD->IsAsyncVariantMethod()) - return false; - return true; } From 49101ed2709e75a1e5a317292a1ddf9397f991d9 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Mon, 9 Feb 2026 11:01:11 -0800 Subject: [PATCH 10/22] Generalize CreateR2RBackedILStub --- src/coreclr/vm/ilstubcache.cpp | 88 +++++++++++++++---------------- src/coreclr/vm/ilstubcache.h | 8 ++- src/coreclr/vm/readytoruninfo.cpp | 26 +++++++-- 3 files changed, 69 insertions(+), 53 deletions(-) diff --git a/src/coreclr/vm/ilstubcache.cpp b/src/coreclr/vm/ilstubcache.cpp index 9ec8b3a1286333..595b4db0536705 100644 --- a/src/coreclr/vm/ilstubcache.cpp +++ b/src/coreclr/vm/ilstubcache.cpp @@ -137,7 +137,6 @@ namespace case DynamicMethodDesc::StubVirtualStaticMethodDispatch: return "IL_STUB_VirtualStaticMethodDispatch"; case DynamicMethodDesc::StubDelegateShuffleThunk: return "IL_STUB_DelegateShuffleThunk"; case DynamicMethodDesc::StubAsyncResume: return "IL_STUB_AsyncResume"; - case DynamicMethodDesc::StubAsyncResumeR2R: return "IL_STUB_AsyncResumeR2R"; default: UNREACHABLE_MSG("Unknown stub type"); } @@ -357,17 +356,17 @@ MethodDesc* ILStubCache::CreateNewMethodDesc(LoaderHeap* pCreationHeap, MethodTa RETURN pMD; } -// Creates a DynamicMethodDesc that wraps pre-compiled R2R async resumption stub code. +// Creates a DynamicMethodDesc that wraps pre-compiled R2R stub code. // Unlike regular IL stubs, this does not create a resolver or precode - it points // directly to the R2R native code. -// -// The stub signature is: object ResumptionStub(object continuation, ref byte resultLocation) -// -// static -MethodDesc* ILStubCache::CreateR2RBackedAsyncResumptionStub( +MethodDesc* ILStubCache::CreateR2RBackedILStub( LoaderAllocator* pAllocator, MethodTable* pMT, PCODE r2rEntryPoint, + DWORD stubType, + PCCOR_SIGNATURE pSig, + DWORD cbSig, + BOOL isAsync, AllocMemTracker* pamTracker) { CONTRACT(MethodDesc*) @@ -376,76 +375,73 @@ MethodDesc* ILStubCache::CreateR2RBackedAsyncResumptionStub( PRECONDITION(CheckPointer(pAllocator)); PRECONDITION(CheckPointer(pMT)); PRECONDITION(r2rEntryPoint != (PCODE)NULL); + PRECONDITION(stubType != DynamicMethodDesc::StubNotSet); POSTCONDITION(CheckPointer(RETVAL)); } CONTRACT_END; + DynamicMethodDesc::ILStubType ilStubType = (DynamicMethodDesc::ILStubType)stubType; + LoaderHeap* pCreationHeap = pAllocator->GetHighFrequencyHeap(); - // Allocate the MethodDescChunk - // - mcDynamic: This is a dynamic method - // - fNonVtableSlot: Dynamic methods don't have vtable slots - // - fNativeCodeSlot: We need a slot for the native code pointer - // - fHasAsyncMethodData: This is an async resumption stub MethodDescChunk* pChunk = MethodDescChunk::CreateChunk( pCreationHeap, 1, // count mcDynamic, // classification - TRUE, // fNonVtableSlot - TRUE, // fNativeCodeSlot - TRUE, // fHasAsyncMethodData + TRUE, // fNonVtableSlot - Dynamic methods don't have vtable slots + TRUE, // fNativeCodeSlot - we will set the native code pointer directly to the R2R entry point + isAsync, // HasAsyncMethodData pMT, pamTracker); DynamicMethodDesc* pMD = (DynamicMethodDesc*)pChunk->GetFirstMethodDesc(); - // Initialize basic MethodDesc state pMD->SetMemberDef(0); pMD->SetSlot(MethodTable::NO_SLOT); - pMD->SetStatic(); - - // Initialize DynamicMethodDesc flags - this is an IL stub that is public and static - pMD->InitializeFlags(DynamicMethodDesc::FlagPublic | - DynamicMethodDesc::FlagStatic | - DynamicMethodDesc::FlagIsILStub); - pMD->SetILStubType(DynamicMethodDesc::StubAsyncResumeR2R); - // No resolver needed - code already exists in R2R image - pMD->m_pResolver = nullptr; + if (isAsync) + { + pMD->SetHasAsyncMethodData(); + pMD->GetAddrOfAsyncMethodData()->flags = AsyncMethodFlags::AsyncCall; + } - // Set the method name - pMD->m_pszMethodName = GetStubMethodName(DynamicMethodDesc::StubAsyncResumeR2R); + // Determine static vs instance from the signature calling convention + SigPointer sigPtr(pSig, cbSig); + uint32_t callConvInfo; + IfFailThrow(sigPtr.GetCallingConvInfo(&callConvInfo)); - // Build and set the stub signature: object(object, ref byte) - // This matches BuildResumptionStubSignature in jitinterface.cpp - SigBuilder sigBuilder; - sigBuilder.AppendByte(IMAGE_CEE_CS_CALLCONV_DEFAULT); - sigBuilder.AppendData(2); // 2 arguments - sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT); // return type: object - sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT); // arg0: object (continuation) - sigBuilder.AppendElementType(ELEMENT_TYPE_BYREF); // arg1: ref byte (result location) - sigBuilder.AppendElementType(ELEMENT_TYPE_U1); + if (callConvInfo & CORINFO_CALLCONV_HASTHIS) + { + pMD->InitializeFlags(DynamicMethodDesc::FlagPublic | + DynamicMethodDesc::FlagIsILStub); + } + else + { + pMD->SetStatic(); + pMD->InitializeFlags(DynamicMethodDesc::FlagPublic | + DynamicMethodDesc::FlagStatic | + DynamicMethodDesc::FlagIsILStub); + } - DWORD cbSig; - PVOID pSigBytes = sigBuilder.GetSignature(&cbSig); + pMD->SetILStubType(ilStubType); - PVOID pSig = pamTracker->Track(pCreationHeap->AllocMem(S_SIZE_T(cbSig))); - memcpy(pSig, pSigBytes, cbSig); + // No resolver needed - code already exists in R2R image + pMD->m_pResolver = nullptr; - pMD->SetStoredMethodSig((PCCOR_SIGNATURE)pSig, cbSig); + pMD->m_pszMethodName = GetStubMethodName(ilStubType); - // Set async method data flags - pMD->SetHasAsyncMethodData(); - pMD->GetAddrOfAsyncMethodData()->flags = AsyncMethodFlags::AsyncCall; + // Copy the signature into the loader heap + PVOID pNewSig = pamTracker->Track(pCreationHeap->AllocMem(S_SIZE_T(cbSig))); + memcpy(pNewSig, pSig, cbSig); + pMD->SetStoredMethodSig((PCCOR_SIGNATURE)pNewSig, cbSig); // Set the native code directly - no precode needed since code already exists - // Use SetNativeCodeInterlocked to atomically set the entry point pMD->SetNativeCodeInterlocked(r2rEntryPoint); #ifdef _DEBUG pMD->m_pszDebugMethodName = pMD->m_pszMethodName; pMD->m_pszDebugClassName = "ILStubClass"; - pMD->m_pszDebugMethodSignature = "object(object, ref byte)"; + pMD->m_pszDebugMethodSignature = FormatSig(pMD, pCreationHeap, pamTracker); pMD->m_pDebugMethodTable = pMT; #endif // _DEBUG diff --git a/src/coreclr/vm/ilstubcache.h b/src/coreclr/vm/ilstubcache.h index 5a70ab0a36167b..0157530b54a7fb 100644 --- a/src/coreclr/vm/ilstubcache.h +++ b/src/coreclr/vm/ilstubcache.h @@ -76,13 +76,17 @@ class ILStubCache final ILStubLinker* pStubLinker, BOOL isAsync = FALSE); - // Creates a DynamicMethodDesc that wraps pre-compiled R2R async resumption stub code. + // Creates a DynamicMethodDesc that wraps pre-compiled R2R stub code. // Unlike regular IL stubs, this does not create a resolver or precode - it points // directly to the R2R native code. - static MethodDesc* CreateR2RBackedAsyncResumptionStub( + static MethodDesc* CreateR2RBackedILStub( LoaderAllocator* pAllocator, MethodTable* pMT, PCODE r2rEntryPoint, + DWORD stubType, // DynamicMethodDesc::ILStubType + PCCOR_SIGNATURE pSig, + DWORD cbSig, + BOOL isAsync, AllocMemTracker* pamTracker); MethodTable * GetStubMethodTable() diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index 2ce5638831b4a2..619fa533d58a42 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -18,6 +18,7 @@ #include "nativeimage.h" #include "dn-stdio.h" #include "ilstubcache.h" +#include "sigbuilder.h" #ifdef FEATURE_PERFMAP #include "perfmap.h" @@ -1377,9 +1378,7 @@ PCODE ReadyToRunInfo::GetEntryPoint(MethodDesc * pMD, PrepareCodeConfig* pConfig uint offset; // Async variants and resumption stubs are stored in the instance methods table if (pMD->HasClassOrMethodInstantiation() - || pMD->IsAsyncVariantMethod() - || (pMD->IsDynamicMethod() && ((DynamicMethodDesc*)pMD)->IsILStub() - && ((DynamicMethodDesc*)pMD)->IsAsyncResumptionStub())) + || pMD->IsAsyncVariantMethod()) { if (m_instMethodEntryPoints.IsNull()) goto done; @@ -1471,7 +1470,7 @@ PCODE ReadyToRunInfo::GetEntryPoint(MethodDesc * pMD, PrepareCodeConfig* pConfig PCODE stubEntryPoint = LookupResumptionStubEntryPoint(pMD, pConfig, fFixups, &stubRuntimeFunctionIndex); if (stubEntryPoint == (PCODE)NULL) { - // Resumption stub not found - cannot use R2R code for this async variant + // Resumption stub not found - do not use R2R code for this async variant pEntryPoint = (PCODE)NULL; goto done; } @@ -1480,10 +1479,27 @@ PCODE ReadyToRunInfo::GetEntryPoint(MethodDesc * pMD, PrepareCodeConfig* pConfig AllocMemTracker amTracker; MethodTable* pStubMT = m_pModule->GetILStubCache()->GetOrCreateStubMethodTable(m_pModule); - MethodDesc* pStubMD = ILStubCache::CreateR2RBackedAsyncResumptionStub( + // Build the resumption stub signature: object(object, ref byte) + // This matches BuildResumptionStubSignature in jitinterface.cpp + SigBuilder sigBuilder; + sigBuilder.AppendByte(IMAGE_CEE_CS_CALLCONV_DEFAULT); + sigBuilder.AppendData(2); // 2 arguments + sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT); // return type: object (continuation) + sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT); // arg0: object (continuation) + sigBuilder.AppendElementType(ELEMENT_TYPE_BYREF); // arg1: ref byte (result location) + sigBuilder.AppendElementType(ELEMENT_TYPE_U1); + + DWORD cbStubSig; + PVOID pStubSig = sigBuilder.GetSignature(&cbStubSig); + + MethodDesc* pStubMD = ILStubCache::CreateR2RBackedILStub( pMD->GetLoaderAllocator(), pStubMT, stubEntryPoint, + DynamicMethodDesc::StubAsyncResume, + (PCCOR_SIGNATURE)pStubSig, + cbStubSig, + FALSE, &amTracker); amTracker.SuppressRelease(); From 5bcb9b50b68e3028642e66871e51fd2002d53c5d Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Mon, 9 Feb 2026 12:50:45 -0800 Subject: [PATCH 11/22] Allow async methods without resumption stubs --- src/coreclr/vm/readytoruninfo.cpp | 68 +++++++++++++++---------------- 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index 619fa533d58a42..e104782bce8dfe 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -1468,44 +1468,40 @@ PCODE ReadyToRunInfo::GetEntryPoint(MethodDesc * pMD, PrepareCodeConfig* pConfig { uint stubRuntimeFunctionIndex = 0; PCODE stubEntryPoint = LookupResumptionStubEntryPoint(pMD, pConfig, fFixups, &stubRuntimeFunctionIndex); - if (stubEntryPoint == (PCODE)NULL) + if (stubEntryPoint != (PCODE)NULL) { - // Resumption stub not found - do not use R2R code for this async variant - pEntryPoint = (PCODE)NULL; - goto done; + // Create a DynamicMethodDesc wrapper for the R2R resumption stub + AllocMemTracker amTracker; + MethodTable* pStubMT = m_pModule->GetILStubCache()->GetOrCreateStubMethodTable(m_pModule); + + // Build the resumption stub signature: object(object, ref byte) + // This matches BuildResumptionStubSignature in jitinterface.cpp + SigBuilder sigBuilder; + sigBuilder.AppendByte(IMAGE_CEE_CS_CALLCONV_DEFAULT); + sigBuilder.AppendData(2); // 2 arguments + sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT); // return type: object (continuation) + sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT); // arg0: object (continuation) + sigBuilder.AppendElementType(ELEMENT_TYPE_BYREF); // arg1: ref byte (result location) + sigBuilder.AppendElementType(ELEMENT_TYPE_U1); + + DWORD cbStubSig; + PVOID pStubSig = sigBuilder.GetSignature(&cbStubSig); + + MethodDesc* pStubMD = ILStubCache::CreateR2RBackedILStub( + pMD->GetLoaderAllocator(), + pStubMT, + stubEntryPoint, + DynamicMethodDesc::StubAsyncResume, + (PCCOR_SIGNATURE)pStubSig, + cbStubSig, + FALSE, + &amTracker); + + amTracker.SuppressRelease(); + + // Register the stub's entry point so GC can find it during stack walks + m_pCompositeInfo->SetMethodDescForEntryPointInNativeImage(stubEntryPoint, pStubMD); } - - // Create a DynamicMethodDesc wrapper for the R2R resumption stub - AllocMemTracker amTracker; - MethodTable* pStubMT = m_pModule->GetILStubCache()->GetOrCreateStubMethodTable(m_pModule); - - // Build the resumption stub signature: object(object, ref byte) - // This matches BuildResumptionStubSignature in jitinterface.cpp - SigBuilder sigBuilder; - sigBuilder.AppendByte(IMAGE_CEE_CS_CALLCONV_DEFAULT); - sigBuilder.AppendData(2); // 2 arguments - sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT); // return type: object (continuation) - sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT); // arg0: object (continuation) - sigBuilder.AppendElementType(ELEMENT_TYPE_BYREF); // arg1: ref byte (result location) - sigBuilder.AppendElementType(ELEMENT_TYPE_U1); - - DWORD cbStubSig; - PVOID pStubSig = sigBuilder.GetSignature(&cbStubSig); - - MethodDesc* pStubMD = ILStubCache::CreateR2RBackedILStub( - pMD->GetLoaderAllocator(), - pStubMT, - stubEntryPoint, - DynamicMethodDesc::StubAsyncResume, - (PCCOR_SIGNATURE)pStubSig, - cbStubSig, - FALSE, - &amTracker); - - amTracker.SuppressRelease(); - - // Register the stub's entry point so GC can find it during stack walks - m_pCompositeInfo->SetMethodDescForEntryPointInNativeImage(stubEntryPoint, pStubMD); } #ifdef PROFILING_SUPPORTED From 17e3358ffbbac0f9598a1bfc9078c64c8a0b4486 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Mon, 9 Feb 2026 15:09:25 -0800 Subject: [PATCH 12/22] Allow using code for methods without resumption stubs in the r2r image --- .../DependencyAnalysis/ReadyToRun/SignatureBuilder.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs index 2224060906e119..8c5738a32018dc 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs @@ -448,9 +448,11 @@ public void EmitMethodSignature( if (method.Method is AsyncResumptionStub) { flags |= (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_ResumptionStub; + // For AsyncResumptionStubs, we want to encode the signature of the async variant method, not the resumption stub itself, + // so that they will share the same hash and be placed in the same bucket at runtime + method = new MethodWithToken(((AsyncResumptionStub)method.Method).TargetMethod, method.Token, method.ConstrainedType, method.Unboxing, method.Method); } - EmitMethodSpecificationSignature(method, flags, enforceDefEncoding, enforceOwningType, context); if (method.ConstrainedType != null) @@ -620,7 +622,7 @@ public SignatureContext EmitFixup(NodeFactory factory, ReadyToRunFixupKind fixup { throw new InternalCompilerErrorException("Attempt to use token from a module not within the version bubble"); } - + EmitUInt((uint)factory.ManifestMetadataTable.ModuleToIndex(targetModule)); return new SignatureContext(targetModule, outerContext.Resolver); } From c66218c77439480c7f68518263f2513e683183ad Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Mon, 9 Feb 2026 15:22:34 -0800 Subject: [PATCH 13/22] Enable RuntimeAsync library tests - Add UseRuntimeAsync property for source projects - Add TestRuntimeAsync property for test projects - Enable RuntimeAsync with crossgen2 - Add pipeline leg for runtimeasync tests with crossgen2 --- eng/pipelines/coreclr/crossgen2.yml | 24 ++++++++++++++++++++++++ eng/testing/tests.targets | 7 ++++++- src/libraries/Directory.Build.targets | 6 ++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/eng/pipelines/coreclr/crossgen2.yml b/eng/pipelines/coreclr/crossgen2.yml index 775928f7d3ffc2..4d755a1823a800 100644 --- a/eng/pipelines/coreclr/crossgen2.yml +++ b/eng/pipelines/coreclr/crossgen2.yml @@ -99,6 +99,30 @@ extends: creator: dotnet-bot testRunNamePrefixSuffix: TestReadyToRun_$(_BuildConfig) + - template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/global-build-job.yml + helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml + buildConfig: Release + platforms: + - linux_x64 + - windows_x64 + jobParameters: + testGroup: innerloop + buildArgs: -s clr+libs+libs.tests + -c $(_BuildConfig) + /p:TestReadyToRun=true + /p:TestRuntimeAsync=true + /p:UseRuntimeAsync=true + /p:ArchiveTests=true + nameSuffix: TestReadyToRun_RuntimeAsync_Libraries + timeoutInMinutes: 360 + postBuildSteps: + - template: /eng/pipelines/libraries/helix.yml + parameters: + creator: dotnet-bot + testRunNamePrefixSuffix: TestReadyToRun_RuntimeAsync_$(_BuildConfig) + # Run pri0 tests with hot/cold splitting enabled (only supported on x64 at the moment) # TODO: test on arm64 once supported - template: /eng/pipelines/common/platform-matrix.yml diff --git a/eng/testing/tests.targets b/eng/testing/tests.targets index 8c058abf2b7990..4c202294d0ac1a 100644 --- a/eng/testing/tests.targets +++ b/eng/testing/tests.targets @@ -2,7 +2,6 @@ + true + $(Features);runtime-async=on + true false diff --git a/src/libraries/Directory.Build.targets b/src/libraries/Directory.Build.targets index 4bcd8251decdab..947dbfc3e71a66 100644 --- a/src/libraries/Directory.Build.targets +++ b/src/libraries/Directory.Build.targets @@ -127,6 +127,12 @@ '$(IsGeneratorProject)' != 'true'">true + + + true + $(Features);runtime-async=on + + From 18754acfab47da3f038f9771998f255cb39e2c77 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Mon, 9 Feb 2026 15:41:39 -0800 Subject: [PATCH 14/22] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | 2 +- .../DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 3ae8931de20d12..d82a80cc730fc1 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3807,7 +3807,7 @@ private bool getTailCallHelpers(ref CORINFO_RESOLVED_TOKEN callToken, CORINFO_SI { #if READYTORUN var resumptionStub = new AsyncResumptionStub(MethodBeingCompiled, MethodBeingCompiled.OwningType); - var tokenSource = MethodBeingCompiled.GetPrimaryMethodDesc().GetTypicalMethodDefinition(); + // CompiledMethodNode instead of MethodEntrypoint for the pointer to the code instead of a fixup entryPoint = (void*)ObjectToHandle(_compilation.NodeFactory.CompiledMethodNode(resumptionStub)); return ObjectToHandle(resumptionStub); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs index 69449f4d066499..540a3c722ac036 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs @@ -120,7 +120,7 @@ private static void EncodeTypeLayout(ObjectDataSignatureBuilder dataBuilder, Typ // Encode the GC pointer map GCPointerMap gcMap = GCPointerMap.FromInstanceLayout(defType); - byte[] encodedGCRefMap = new byte[((size + 7) / pointerSize + 7) / 8]; + byte[] encodedGCRefMap = new byte[((size + (pointerSize - 1)) / pointerSize + 7) / 8]; int bitIndex = 0; foreach (bool bit in gcMap) { From 4dfdfe249c9f89f0344c79c147866a753e4c66ab Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Mon, 9 Feb 2026 15:48:26 -0800 Subject: [PATCH 15/22] Clean up extraneous changes and fix build --- .../ReadyToRunReader.cs | 36 ------------------- src/coreclr/vm/method.hpp | 6 ++-- src/coreclr/vm/methodtable.cpp | 27 +++----------- src/coreclr/vm/zapsig.cpp | 1 - 4 files changed, 6 insertions(+), 64 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs index f08516c277fdaa..a960fcfdee3a2a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs @@ -1041,24 +1041,6 @@ private void ParseInstanceMethodEntrypoints(bool[] isEntryPoint) var sig = DecodeMethodSignature(ref mdReader, ref decoder); - List modifiers = []; - if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_AsyncVariant) != 0) - { - modifiers.Add("[Async]"); - } - if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_ResumptionStub) != 0) - { - modifiers.Add("[Resume]"); - } - if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_InstantiatingStub) != 0) - { - throw new NotImplementedException("Crossgen2 should not emit code for Instantiating stubs."); - } - if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_UnboxingStub) != 0) - { - throw new NotImplementedException("Crossgen2 should not emit code for Unboxing stubs."); - } - int runtimeFunctionId; int? fixupOffset; GetRuntimeFunctionIndexFromOffset((int)decoder.Offset, out runtimeFunctionId, out fixupOffset); @@ -1138,24 +1120,6 @@ private void ParsePgoMethods() var sig = DecodeMethodSignature(ref mdReader, ref decoder); - List modifiers = []; - if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_AsyncVariant) != 0) - { - modifiers.Add("[Async]"); - } - if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_ResumptionStub) != 0) - { - modifiers.Add("[Resume]"); - } - if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_InstantiatingStub) != 0) - { - throw new NotImplementedException("Crossgen2 should not emit code for Instantiating stubs."); - } - if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_UnboxingStub) != 0) - { - throw new NotImplementedException("Crossgen2 should not emit code for Unboxing stubs."); - } - GetPgoOffsetAndVersion(decoder.Offset, out int pgoFormatVersion, out int pgoOffset); PgoInfoKey key = new PgoInfoKey(mdReader, sig.OwningType, sig.MethodHandle, sig.MethodTypeArgs, sig.SignaturePrefixes); diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index e84ca60c8c2b58..bb2cf9b7e7dd9d 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -262,8 +262,7 @@ using PTR_MethodDescCodeData = DPTR(MethodDescCodeData); enum class AsyncVariantLookup { MatchingAsyncVariant = 0, - AsyncOtherVariant, - AsyncResumptionStub + AsyncOtherVariant }; enum class MethodReturnKind @@ -2823,7 +2822,6 @@ class DynamicMethodDesc : public StoredSigMethodDesc StubDelegateInvokeMethod, StubAsyncResume, - StubAsyncResumeR2R, // R2R-backed async resumption stub (no IL, points directly to R2R code) StubLast }; @@ -3023,7 +3021,7 @@ class DynamicMethodDesc : public StoredSigMethodDesc LIMITED_METHOD_DAC_CONTRACT; _ASSERTE(IsILStub()); ILStubType type = GetILStubType(); - return type == DynamicMethodDesc::StubAsyncResume || type == DynamicMethodDesc::StubAsyncResumeR2R; + return type == DynamicMethodDesc::StubAsyncResume; } // Whether the stub takes a context argument that is an interop MethodDesc. diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 959a7ca2f57a82..470d35b23cbbfc 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -1762,7 +1762,7 @@ bool MethodTable::InterfaceMapIterator::CurrentInterfaceEquivalentTo(MethodTable if (pCurrentMethodTable == pMT) return true; - + if (pCurrentMethodTable->IsSpecialMarkerTypeForGenericCasting() && !pMTOwner->GetAuxiliaryData()->MayHaveOpenInterfacesInInterfaceMap() && pCurrentMethodTable->HasSameTypeDefAs(pMT)) { // Any matches need to use the special marker type logic @@ -1779,7 +1779,7 @@ bool MethodTable::InterfaceMapIterator::CurrentInterfaceEquivalentTo(MethodTable #ifndef DACCESS_COMPILE if (pMT->IsFullyLoaded()) SetInterface(pMT); -#endif +#endif return true; } else @@ -4354,7 +4354,7 @@ VOID DoAccessibilityCheckForConstraintSignature(Module *pModule, SigPointer *pSi case ELEMENT_TYPE_TYPEDBYREF: // Primitive types and such. Nothing to check break; - + case ELEMENT_TYPE_VAR: case ELEMENT_TYPE_MVAR: { @@ -4780,7 +4780,7 @@ void MethodTable::DoFullyLoad(Generics::RecursionGraph * const pVisited, const for (DWORD i = 0; i < formalParams.GetNumArgs(); i++) { - // This call to Bounded/DoAccessibilityCheckForConstraints will also cause constraint Variance rules to be checked + // This call to Bounded/DoAccessibilityCheckForConstraints will also cause constraint Variance rules to be checked // via the call to GetConstraints which will eventually call EEClass::CheckVarianceInSig BOOL Bounded(TypeVarTypeDesc *tyvar, DWORD depth); @@ -8007,25 +8007,6 @@ MethodDesc* MethodTable::GetParallelMethodDesc(MethodDesc* pDefMD, AsyncVariantL } CONTRACTL_END; - if (asyncVariantLookup == AsyncVariantLookup::AsyncResumptionStub) - { - mdMethodDef tkMethod = pDefMD->GetMemberDef(); - Module* mod = pDefMD->GetModule(); - bool isAsyncVariantMethod = pDefMD->IsAsyncVariantMethod(); - - MethodTable::IntroducedMethodIterator it(this); - for (; it.IsValid(); it.Next()) - { - MethodDesc* pMD = it.GetMethodDesc(); - if (pMD->GetMemberDef() == tkMethod - && pMD->GetModule() == mod - && pMD->IsAsyncVariantMethod() != isAsyncVariantMethod) - { - return pMD; - } - } - return NULL; - } if (asyncVariantLookup == AsyncVariantLookup::MatchingAsyncVariant) { #ifdef FEATURE_METADATA_UPDATER diff --git a/src/coreclr/vm/zapsig.cpp b/src/coreclr/vm/zapsig.cpp index 9d252c223343e5..09d9800df3e143 100644 --- a/src/coreclr/vm/zapsig.cpp +++ b/src/coreclr/vm/zapsig.cpp @@ -922,7 +922,6 @@ MethodDesc *ZapSig::DecodeMethod(ModuleBase *pInfoModule, BOOL isInstantiatingStub = (methodFlags & ENCODE_METHOD_SIG_InstantiatingStub); BOOL isUnboxingStub = (methodFlags & ENCODE_METHOD_SIG_UnboxingStub); bool isAsyncVariant = (methodFlags & ENCODE_METHOD_SIG_AsyncVariant) != 0; - bool isResumptionStub = (methodFlags & ENCODE_METHOD_SIG_ResumptionStub) != 0; pMethod = MethodDesc::FindOrCreateAssociatedMethodDesc(pMethod, thOwner.GetMethodTable(), isUnboxingStub, From 7f36d5aec0a11bf1236567f9d22d92808c9e4e02 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Tue, 10 Feb 2026 10:33:53 -0800 Subject: [PATCH 16/22] Don't use async method code if we can't find resumption stub This will throw out valid methods that don't await, but I don't think we have the resumption stub resolution perfect yet. --- src/coreclr/vm/readytoruninfo.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index e104782bce8dfe..d7b33699920eb5 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -1502,6 +1502,11 @@ PCODE ReadyToRunInfo::GetEntryPoint(MethodDesc * pMD, PrepareCodeConfig* pConfig // Register the stub's entry point so GC can find it during stack walks m_pCompositeInfo->SetMethodDescForEntryPointInNativeImage(stubEntryPoint, pStubMD); } + else + { + pEntryPoint = (PCODE)NULL; + goto done; + } } #ifdef PROFILING_SUPPORTED From 5c373960cc2707b2ae8cb10c15e23ff0cf7218b8 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 12 Feb 2026 12:33:32 -0800 Subject: [PATCH 17/22] Fix merge issues --- .../ReadyToRun/ModuleTokenResolver.cs | 33 ------------------- 1 file changed, 33 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs index 513603a1dbfc41..2a0e2a10c15c30 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs @@ -129,38 +129,6 @@ public ModuleToken GetModuleTokenForMethod(MethodDesc method, bool allowDynamica } } - public ModuleToken GetModuleTokenForField(FieldDesc field, bool allowDynamicallyCreatedReference, bool throwIfNotFound) - { - if (field.GetTypicalFieldDefinition() is EcmaField ecmaField) - { - if (_compilationModuleGroup.VersionsWithType(ecmaField.OwningType)) - { - return new ModuleToken(ecmaField.Module, ecmaField.Handle); - } - - // If that didn't work, it may be in the manifest module used for version resilient cross module inlining - if (allowDynamicallyCreatedReference) - { - var handle = _manifestMutableModule.TryGetExistingEntityHandle(ecmaField); - if (handle.HasValue) - { - return new ModuleToken(_manifestMutableModule, handle.Value); - } - } - } - - // Reverse lookup failed - if (throwIfNotFound) - { - throw new NotImplementedException(field.ToString()); - } - else - { - return default(ModuleToken); - } - } - - public void AddModuleTokenForMethod(MethodDesc method, ModuleToken token) { if (token.TokenType == CorTokenType.mdtMethodSpec) @@ -217,7 +185,6 @@ public ModuleToken GetModuleTokenForField(FieldDesc field, bool allowDynamically } } - private void DecodeMethodSpecificationSignatureToDiscoverUsedTypeTokens(BlobHandle signatureHandle, ModuleToken token) { MetadataReader metadataReader = token.MetadataReader; From d9af071f6b5dd5b90840a132afae1d97ddc9705b Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 12 Feb 2026 12:35:03 -0800 Subject: [PATCH 18/22] Add label for resume stub code in r2rdump --- .../aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs index a960fcfdee3a2a..e32a980b94e739 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs @@ -1015,6 +1015,10 @@ private DecodedMethodSignature DecodeMethodSignature(ref IAssemblyMetadata mdRea { signaturePrefixes.Add("[ASYNC]"); } + if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_ResumptionStub) != 0) + { + signaturePrefixes.Add("[RESUME]"); + } return new DecodedMethodSignature(owningType, methodHandle, methodTypeArgs, constrainedType, signaturePrefixes.ToArray()); } From d0ff7c164fce1ef259d90aa5d3fe1b97c1f02c30 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 12 Feb 2026 12:35:33 -0800 Subject: [PATCH 19/22] Don't emit async thunks - there are some issues to sort out --- .../tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs index 8ea8fed5e19637..aa5d8c413e036c 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs @@ -269,7 +269,7 @@ public override MethodIL GetMethodIL(MethodDesc method) return methodIL; return NeedsAsyncThunk(amv) ? - AsyncThunkILEmitter.EmitAsyncMethodThunk(amv, method.GetTargetOfAsyncVariant()) + null // Async thunks not supported yet : new AsyncEcmaMethodIL(amv, EcmaMethodIL.Create((EcmaMethod)method.GetTargetOfAsyncVariant())); } else if (method is MethodForInstantiatedType || method is InstantiatedMethod) From c1ecd59d1b48757313ed3cc29297126226272acd Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 12 Feb 2026 12:36:37 -0800 Subject: [PATCH 20/22] SetIsAsyncCall even if method is dispatch cell in ComputeCallRefMap --- src/coreclr/vm/frames.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/coreclr/vm/frames.cpp b/src/coreclr/vm/frames.cpp index 5c7464439ed873..dc03824e53936b 100644 --- a/src/coreclr/vm/frames.cpp +++ b/src/coreclr/vm/frames.cpp @@ -2229,11 +2229,14 @@ void ComputeCallRefMap(MethodDesc* pMD, { msig.SetHasParamTypeArg(); } + } - if (pMD->IsAsyncMethod()) - { - msig.SetIsAsyncCall(); - } + // The async continuation is a caller-side argument that is always passed + // regardless of whether the dispatch target has been resolved, unlike the + // instantiation argument which is an implementation detail of shared generics. + if (pMD->IsAsyncMethod()) + { + msig.SetIsAsyncCall(); } ArgIterator argit(&msig); From b7604a88ca7db5264c202e7c245658d86183aa8c Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 12 Feb 2026 15:33:21 -0800 Subject: [PATCH 21/22] Remove PInvokeTargetNativeMethod from GetPrimaryMethodDesc This appears to cause issues, as in HandleToMethodToken, we resolve the pinvoke method to the Target. This had a downstream effect of causing asserts to trip in DoPrestub(): _ASSERTE(pCode == (PCODE)NULL || GetNativeCode() == (PCODE)NULL || pCode == GetNativeCode()); This would trip when two methods were calling the same pinvoke method, and both would try to set the pCode on the same method desc. It's not yet clear to me why this is causing the failures, but I want to test the changes in CI. --- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 4 ++++ .../TypeSystem/MethodDescExtensions.cs | 8 +++----- src/coreclr/vm/jitinterface.cpp | 1 - 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 5d95dd6e623602..0eccb3f357c94f 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -1448,6 +1448,10 @@ private ModuleToken HandleToModuleToken(ref CORINFO_RESOLVED_TOKEN pResolvedToke // It's okay to strip the instantiation away because we don't need a MethodSpec // token - SignatureBuilder will generate the generic method signature // using instantiation parameters from the MethodDesc entity. + if (resultMethod is PInvokeTargetNativeMethod pInvoke) + { + resultMethod = pInvoke.Target; + } resultMethod = resultMethod.GetPrimaryMethodDesc().GetTypicalMethodDefinition(); if (!_compilation.NodeFactory.CompilationModuleGroup.VersionsWithType(resultMethod.OwningType)) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/MethodDescExtensions.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/MethodDescExtensions.cs index 935a160ee4f6ea..46e9cd1d144f2f 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/MethodDescExtensions.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/MethodDescExtensions.cs @@ -21,12 +21,10 @@ public static MethodDesc GetPrimaryMethodDesc(this MethodDesc method) { return method.GetUnboxedMethod().GetPrimaryMethodDesc(); } - return method switch + if (method is AsyncResumptionStub resumptionStub) { - PInvokeTargetNativeMethod pinvokeTarget => pinvokeTarget.Target, - AsyncResumptionStub resumptionStub => resumptionStub.TargetMethod.GetPrimaryMethodDesc(), - _ => method, - }; + return resumptionStub.TargetMethod.GetPrimaryMethodDesc(); + } } public static bool IsPrimaryMethodDesc(this MethodDesc method) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 5987f992f9b027..72ed6e806c614c 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -14156,7 +14156,6 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, default: STRESS_LOG1(LF_ZAP, LL_WARNING, "Unknown READYTORUN_HELPER %d\n", helperNum); - printf("Unknown READYTORUN_HELPER %d", helperNum); _ASSERTE(!"Unknown READYTORUN_HELPER"); return FALSE; } From 5c9cd91f40acbd90674fe35dc95118ffe8ca1f94 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 12 Feb 2026 15:56:13 -0800 Subject: [PATCH 22/22] Fix build error --- .../aot/ILCompiler.ReadyToRun/TypeSystem/MethodDescExtensions.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/MethodDescExtensions.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/MethodDescExtensions.cs index 46e9cd1d144f2f..a39db0fd7c9e80 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/MethodDescExtensions.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/MethodDescExtensions.cs @@ -25,6 +25,7 @@ public static MethodDesc GetPrimaryMethodDesc(this MethodDesc method) { return resumptionStub.TargetMethod.GetPrimaryMethodDesc(); } + return method; } public static bool IsPrimaryMethodDesc(this MethodDesc method)