From 124470c5055ec30ebbfb94e367f709505d72a814 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Wed, 25 Mar 2026 15:43:02 -0700 Subject: [PATCH 1/6] Intern AsyncResumptionStub instances to fix sort crash with --opt-cross-module AsyncResumptionStub was created via 'new' per JIT compilation context. When a method was recompiled (e.g. via PrepareForCompilationRetry for cross-module inlining), a second CorInfoImpl created a second AsyncResumptionStub for the same target method. Since NodeCache uses reference equality, both entered the dependency graph as separate MethodWithGCInfo nodes. During sorting, their CompareToImpl returned 0 (they wrap the same target method), tripping the Debug.Assert(result != 0) in TypeSystemComparer.Compare(MethodDesc, MethodDesc). Fix: Add an AsyncResumptionStubHashtable (LockFreeReaderHashtable) to CompilerTypeSystemContext that interns AsyncResumptionStub instances by (targetMethod, owningType). Update all creation sites to use the new GetAsyncResumptionStub() method instead of 'new AsyncResumptionStub(...)'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../CompilerTypeSystemContext.Async.cs | 39 +++++++++++++++++++ .../IL/ILImporter.Scanner.cs | 2 +- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 2 +- .../JitInterface/CorInfoImpl.RyuJit.cs | 2 +- 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Async.cs b/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Async.cs index 53111252b0a235..4f243a53ab38c4 100644 --- a/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Async.cs +++ b/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Async.cs @@ -200,6 +200,45 @@ protected override bool CompareValueToValue(AsyncMethodVariant value1, AsyncMeth } private AsyncVariantHashtable _asyncVariantHashtable = new AsyncVariantHashtable(); + public AsyncResumptionStub GetAsyncResumptionStub(MethodDesc targetMethod, TypeDesc owningType) + { + return _asyncResumptionStubHashtable.GetOrCreateValue(new AsyncResumptionStubKey(targetMethod, owningType)); + } + + private readonly struct AsyncResumptionStubKey : System.IEquatable + { + public readonly MethodDesc TargetMethod; + public readonly TypeDesc OwningType; + + public AsyncResumptionStubKey(MethodDesc targetMethod, TypeDesc owningType) + { + TargetMethod = targetMethod; + OwningType = owningType; + } + + public bool Equals(AsyncResumptionStubKey other) + => TargetMethod == other.TargetMethod && OwningType == other.OwningType; + + public override bool Equals(object obj) + => obj is AsyncResumptionStubKey other && Equals(other); + + public override int GetHashCode() + => TargetMethod.GetHashCode(); + } + + private sealed class AsyncResumptionStubHashtable : LockFreeReaderHashtable + { + protected override int GetKeyHashCode(AsyncResumptionStubKey key) => key.TargetMethod.GetHashCode(); + protected override int GetValueHashCode(AsyncResumptionStub value) => value.TargetMethod.GetHashCode(); + protected override bool CompareKeyToValue(AsyncResumptionStubKey key, AsyncResumptionStub value) + => key.TargetMethod == value.TargetMethod && key.OwningType == value.OwningType; + protected override bool CompareValueToValue(AsyncResumptionStub value1, AsyncResumptionStub value2) + => value1.TargetMethod == value2.TargetMethod && value1.OwningType == value2.OwningType; + protected override AsyncResumptionStub CreateValueFromKey(AsyncResumptionStubKey key) + => new AsyncResumptionStub(key.TargetMethod, key.OwningType); + } + private AsyncResumptionStubHashtable _asyncResumptionStubHashtable = new AsyncResumptionStubHashtable(); + public MetadataType GetContinuationType(GCPointerMap pointerMap) { return _continuationTypeHashtable.GetOrCreateValue(pointerMap); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs index a67e3048ce7cdf..42266439e30e0a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs @@ -460,7 +460,7 @@ private void ImportCall(ILOpcode opcode, int token) const string asyncReason = "Async state machine"; - var resumptionStub = new AsyncResumptionStub(_canonMethod, _compilation.TypeSystemContext.GeneratedAssembly.GetGlobalModuleType()); + var resumptionStub = ((CompilerTypeSystemContext)_canonMethod.Context).GetAsyncResumptionStub(_canonMethod, _compilation.TypeSystemContext.GeneratedAssembly.GetGlobalModuleType()); _dependencies.Add(_compilation.NodeFactory.MethodEntrypoint(resumptionStub), asyncReason); _dependencies.Add(_factory.ConstructedTypeSymbol(_compilation.TypeSystemContext.ContinuationType), asyncReason); 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 58fd3d387ba99a..3ce96b1208ce75 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -502,7 +502,7 @@ private void AddResumptionStubFixup(MethodWithGCInfo compiledStubNode) { if (_asyncResumptionStub is null) { - _asyncResumptionStub = new AsyncResumptionStub(MethodBeingCompiled, MethodBeingCompiled.OwningType); + _asyncResumptionStub = ((CompilerTypeSystemContext)MethodBeingCompiled.Context).GetAsyncResumptionStub(MethodBeingCompiled, MethodBeingCompiled.OwningType); AddResumptionStubFixup(_compilation.NodeFactory.CompiledMethodNode(_asyncResumptionStub)); } diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index cb3964df10c304..6a5fcc614bf374 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -57,7 +57,7 @@ private UnboxingMethodDesc getUnboxingThunk(MethodDesc method) private CORINFO_METHOD_STRUCT_* getAsyncResumptionStub(ref void* entryPoint) { - _asyncResumptionStub ??= new AsyncResumptionStub(MethodBeingCompiled, _compilation.TypeSystemContext.GeneratedAssembly.GetGlobalModuleType()); + _asyncResumptionStub ??= ((CompilerTypeSystemContext)MethodBeingCompiled.Context).GetAsyncResumptionStub(MethodBeingCompiled, _compilation.TypeSystemContext.GeneratedAssembly.GetGlobalModuleType()); entryPoint = (void*)ObjectToHandle(_compilation.NodeFactory.MethodEntrypoint(_asyncResumptionStub)); return ObjectToHandle(_asyncResumptionStub); From b697bd6209aec9a9113686cc8d242142254f0b48 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Wed, 25 Mar 2026 17:01:10 -0700 Subject: [PATCH 2/6] Address PR feedback: remove casts, delete R2R field, improve hash - Use HashCode.Combine for AsyncResumptionStubKey.GetHashCode and update GetKeyHashCode/GetValueHashCode for consistency - Remove unnecessary (CompilerTypeSystemContext) casts since _compilation.TypeSystemContext already returns the right type - Delete _asyncResumptionStub field from ReadyToRun CorInfoImpl (no longer needed since stubs are cached in the type system context) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Compiler/CompilerTypeSystemContext.Async.cs | 6 +++--- .../tools/Common/JitInterface/CorInfoImpl.cs | 2 ++ .../ILCompiler.Compiler/IL/ILImporter.Scanner.cs | 2 +- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 13 ++++--------- .../JitInterface/CorInfoImpl.RyuJit.cs | 2 +- 5 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Async.cs b/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Async.cs index 4f243a53ab38c4..479eefabcd70b3 100644 --- a/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Async.cs +++ b/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Async.cs @@ -223,13 +223,13 @@ public override bool Equals(object obj) => obj is AsyncResumptionStubKey other && Equals(other); public override int GetHashCode() - => TargetMethod.GetHashCode(); + => System.HashCode.Combine(TargetMethod, OwningType); } private sealed class AsyncResumptionStubHashtable : LockFreeReaderHashtable { - protected override int GetKeyHashCode(AsyncResumptionStubKey key) => key.TargetMethod.GetHashCode(); - protected override int GetValueHashCode(AsyncResumptionStub value) => value.TargetMethod.GetHashCode(); + protected override int GetKeyHashCode(AsyncResumptionStubKey key) => key.GetHashCode(); + protected override int GetValueHashCode(AsyncResumptionStub value) => System.HashCode.Combine(value.TargetMethod, value.OwningType); protected override bool CompareKeyToValue(AsyncResumptionStubKey key, AsyncResumptionStub value) => key.TargetMethod == value.TargetMethod && key.OwningType == value.OwningType; protected override bool CompareValueToValue(AsyncResumptionStub value1, AsyncResumptionStub value2) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index a8d0f13e5cde23..43a286f7eb4c43 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -703,7 +703,9 @@ private void CompileMethodCleanup() #if !READYTORUN _debugInfo = null; #endif +#if !READYTORUN _asyncResumptionStub = null; +#endif _debugLocInfos = null; _debugVarInfos = null; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs index 42266439e30e0a..a4ac471ef55321 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs @@ -460,7 +460,7 @@ private void ImportCall(ILOpcode opcode, int token) const string asyncReason = "Async state machine"; - var resumptionStub = ((CompilerTypeSystemContext)_canonMethod.Context).GetAsyncResumptionStub(_canonMethod, _compilation.TypeSystemContext.GeneratedAssembly.GetGlobalModuleType()); + AsyncResumptionStub resumptionStub = _compilation.TypeSystemContext.GetAsyncResumptionStub(_canonMethod, _compilation.TypeSystemContext.GeneratedAssembly.GetGlobalModuleType()); _dependencies.Add(_compilation.NodeFactory.MethodEntrypoint(resumptionStub), asyncReason); _dependencies.Add(_factory.ConstructedTypeSymbol(_compilation.TypeSystemContext.ContinuationType), asyncReason); 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 3ce96b1208ce75..cb670ba2db0cbe 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -477,8 +477,6 @@ unsafe partial class CorInfoImpl private List _ilBodiesNeeded; private Dictionary _preInitedTypes = new Dictionary(); private HashSet _synthesizedPgoDependencies; - private MethodDesc _asyncResumptionStub; - public bool HasColdCode { get; private set; } public CorInfoImpl(ReadyToRunCodegenCompilation compilation) @@ -500,14 +498,11 @@ private void AddResumptionStubFixup(MethodWithGCInfo compiledStubNode) private CORINFO_METHOD_STRUCT_* getAsyncResumptionStub(ref void* entryPoint) { - if (_asyncResumptionStub is null) - { - _asyncResumptionStub = ((CompilerTypeSystemContext)MethodBeingCompiled.Context).GetAsyncResumptionStub(MethodBeingCompiled, MethodBeingCompiled.OwningType); - AddResumptionStubFixup(_compilation.NodeFactory.CompiledMethodNode(_asyncResumptionStub)); - } + MethodDesc asyncResumptionStub = _compilation.TypeSystemContext.GetAsyncResumptionStub(MethodBeingCompiled, MethodBeingCompiled.OwningType); + AddResumptionStubFixup(_compilation.NodeFactory.CompiledMethodNode(asyncResumptionStub)); - entryPoint = (void*)ObjectToHandle(_compilation.NodeFactory.CompiledMethodNode(_asyncResumptionStub)); - return ObjectToHandle(_asyncResumptionStub); + entryPoint = (void*)ObjectToHandle(_compilation.NodeFactory.CompiledMethodNode(asyncResumptionStub)); + return ObjectToHandle(asyncResumptionStub); } private static mdToken FindGenericMethodArgTypeSpec(EcmaModule module) diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index 6a5fcc614bf374..095aa45bfce27e 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -57,7 +57,7 @@ private UnboxingMethodDesc getUnboxingThunk(MethodDesc method) private CORINFO_METHOD_STRUCT_* getAsyncResumptionStub(ref void* entryPoint) { - _asyncResumptionStub ??= ((CompilerTypeSystemContext)MethodBeingCompiled.Context).GetAsyncResumptionStub(MethodBeingCompiled, _compilation.TypeSystemContext.GeneratedAssembly.GetGlobalModuleType()); + _asyncResumptionStub ??= _compilation.TypeSystemContext.GetAsyncResumptionStub(MethodBeingCompiled, _compilation.TypeSystemContext.GeneratedAssembly.GetGlobalModuleType()); entryPoint = (void*)ObjectToHandle(_compilation.NodeFactory.MethodEntrypoint(_asyncResumptionStub)); return ObjectToHandle(_asyncResumptionStub); From ebed8f82013353dd1453cad8cd09d035d5ce02ab Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Wed, 25 Mar 2026 17:17:59 -0700 Subject: [PATCH 3/6] Remove _asyncResumptionStub field from RyuJit CorInfoImpl Stubs are cached in the type system context hashtable, so the per-compilation field is no longer needed. Also removes the now-unnecessary cleanup in common CorInfoImpl.cs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | 4 +--- .../ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs | 7 +++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 43a286f7eb4c43..a7ed29a772cc73 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -703,9 +703,7 @@ private void CompileMethodCleanup() #if !READYTORUN _debugInfo = null; #endif -#if !READYTORUN - _asyncResumptionStub = null; -#endif + _debugLocInfos = null; _debugVarInfos = null; diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index 095aa45bfce27e..141ca0c60720b6 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -34,7 +34,6 @@ internal unsafe partial class CorInfoImpl private RyuJitCompilation _compilation; private MethodDebugInformation _debugInfo; - private MethodDesc _asyncResumptionStub; private MethodCodeNode _methodCodeNode; private DebugLocInfo[] _debugLocInfos; private DebugVarInfo[] _debugVarInfos; @@ -57,10 +56,10 @@ private UnboxingMethodDesc getUnboxingThunk(MethodDesc method) private CORINFO_METHOD_STRUCT_* getAsyncResumptionStub(ref void* entryPoint) { - _asyncResumptionStub ??= _compilation.TypeSystemContext.GetAsyncResumptionStub(MethodBeingCompiled, _compilation.TypeSystemContext.GeneratedAssembly.GetGlobalModuleType()); + MethodDesc asyncResumptionStub = _compilation.TypeSystemContext.GetAsyncResumptionStub(MethodBeingCompiled, _compilation.TypeSystemContext.GeneratedAssembly.GetGlobalModuleType()); - entryPoint = (void*)ObjectToHandle(_compilation.NodeFactory.MethodEntrypoint(_asyncResumptionStub)); - return ObjectToHandle(_asyncResumptionStub); + entryPoint = (void*)ObjectToHandle(_compilation.NodeFactory.MethodEntrypoint(asyncResumptionStub)); + return ObjectToHandle(asyncResumptionStub); } public void CompileMethod(MethodCodeNode methodCodeNodeNeedingCode, MethodIL methodIL = null) From f4462aa238d116c0705e8afc38d9a3c2539e4fb9 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 26 Mar 2026 09:21:56 -0700 Subject: [PATCH 4/6] Fix extra blank line and guard AddResumptionStubFixup against duplicate calls - Remove extra blank line left in CorInfoImpl.cs cleanup path - Add _resumptionStubFixupAdded bool to prevent duplicate fixups if getAsyncResumptionStub is called multiple times per method Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | 2 +- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index a7ed29a772cc73..956f19e5e159ad 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -704,7 +704,6 @@ private void CompileMethodCleanup() _debugInfo = null; #endif - _debugLocInfos = null; _debugVarInfos = null; _lastException = null; @@ -718,6 +717,7 @@ private void CompileMethodCleanup() _stashedInlinedMethods.Clear(); _ilBodiesNeeded = null; _synthesizedPgoDependencies = null; + _resumptionStubFixupAdded = false; #endif _instantiationToJitVisibleInstantiation = null; 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 cb670ba2db0cbe..9045396eeb5d53 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -496,10 +496,17 @@ private void AddResumptionStubFixup(MethodWithGCInfo compiledStubNode) _methodCodeNode.Fixups.Add(_compilation.SymbolNodeFactory.ResumptionStubEntryPoint(compiledStubNode)); } + private bool _resumptionStubFixupAdded; + private CORINFO_METHOD_STRUCT_* getAsyncResumptionStub(ref void* entryPoint) { MethodDesc asyncResumptionStub = _compilation.TypeSystemContext.GetAsyncResumptionStub(MethodBeingCompiled, MethodBeingCompiled.OwningType); - AddResumptionStubFixup(_compilation.NodeFactory.CompiledMethodNode(asyncResumptionStub)); + + if (!_resumptionStubFixupAdded) + { + AddResumptionStubFixup(_compilation.NodeFactory.CompiledMethodNode(asyncResumptionStub)); + _resumptionStubFixupAdded = true; + } entryPoint = (void*)ObjectToHandle(_compilation.NodeFactory.CompiledMethodNode(asyncResumptionStub)); return ObjectToHandle(asyncResumptionStub); From 437e5a71c371dcf14a3122e2f2f60a40ed9f50d8 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 26 Mar 2026 09:40:41 -0700 Subject: [PATCH 5/6] Remove _resumptionStubFixupAdded guard, rely on existing dedup ResumptionStubEntryPoint is a cached factory (NodeCache) that returns the same node instance for the same input, and GetFixupBlob() already deduplicates at emission time via merge-sort + compaction. This is consistent with how AddPrecodeFixup and other fixup methods work. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | 1 - .../JitInterface/CorInfoImpl.ReadyToRun.cs | 9 +-------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 956f19e5e159ad..b4fde174ed8f6d 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -717,7 +717,6 @@ private void CompileMethodCleanup() _stashedInlinedMethods.Clear(); _ilBodiesNeeded = null; _synthesizedPgoDependencies = null; - _resumptionStubFixupAdded = false; #endif _instantiationToJitVisibleInstantiation = null; 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 9045396eeb5d53..cb670ba2db0cbe 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -496,17 +496,10 @@ private void AddResumptionStubFixup(MethodWithGCInfo compiledStubNode) _methodCodeNode.Fixups.Add(_compilation.SymbolNodeFactory.ResumptionStubEntryPoint(compiledStubNode)); } - private bool _resumptionStubFixupAdded; - private CORINFO_METHOD_STRUCT_* getAsyncResumptionStub(ref void* entryPoint) { MethodDesc asyncResumptionStub = _compilation.TypeSystemContext.GetAsyncResumptionStub(MethodBeingCompiled, MethodBeingCompiled.OwningType); - - if (!_resumptionStubFixupAdded) - { - AddResumptionStubFixup(_compilation.NodeFactory.CompiledMethodNode(asyncResumptionStub)); - _resumptionStubFixupAdded = true; - } + AddResumptionStubFixup(_compilation.NodeFactory.CompiledMethodNode(asyncResumptionStub)); entryPoint = (void*)ObjectToHandle(_compilation.NodeFactory.CompiledMethodNode(asyncResumptionStub)); return ObjectToHandle(asyncResumptionStub); From 896bb227f792852613f854a7de07561627e5bdc9 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 26 Mar 2026 15:23:10 -0700 Subject: [PATCH 6/6] Treat OwningType as passenger data in AsyncResumptionStubKey OwningType is not part of the key's identity - only TargetMethod determines uniqueness. This matches the pattern used by other similar keys in the codebase (e.g. ReadyToRunGenericHelperKey). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Common/Compiler/CompilerTypeSystemContext.Async.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Async.cs b/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Async.cs index 479eefabcd70b3..9930fd679920b4 100644 --- a/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Async.cs +++ b/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Async.cs @@ -217,23 +217,23 @@ public AsyncResumptionStubKey(MethodDesc targetMethod, TypeDesc owningType) } public bool Equals(AsyncResumptionStubKey other) - => TargetMethod == other.TargetMethod && OwningType == other.OwningType; + => TargetMethod == other.TargetMethod; public override bool Equals(object obj) => obj is AsyncResumptionStubKey other && Equals(other); public override int GetHashCode() - => System.HashCode.Combine(TargetMethod, OwningType); + => TargetMethod.GetHashCode(); } private sealed class AsyncResumptionStubHashtable : LockFreeReaderHashtable { protected override int GetKeyHashCode(AsyncResumptionStubKey key) => key.GetHashCode(); - protected override int GetValueHashCode(AsyncResumptionStub value) => System.HashCode.Combine(value.TargetMethod, value.OwningType); + protected override int GetValueHashCode(AsyncResumptionStub value) => value.TargetMethod.GetHashCode(); protected override bool CompareKeyToValue(AsyncResumptionStubKey key, AsyncResumptionStub value) - => key.TargetMethod == value.TargetMethod && key.OwningType == value.OwningType; + => key.TargetMethod == value.TargetMethod; protected override bool CompareValueToValue(AsyncResumptionStub value1, AsyncResumptionStub value2) - => value1.TargetMethod == value2.TargetMethod && value1.OwningType == value2.OwningType; + => value1.TargetMethod == value2.TargetMethod; protected override AsyncResumptionStub CreateValueFromKey(AsyncResumptionStubKey key) => new AsyncResumptionStub(key.TargetMethod, key.OwningType); }