-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remove JitGenericHandleCache #106843
Merged
davidwrighton
merged 12 commits into
dotnet:main
from
davidwrighton:remove_jitgenericcache
Sep 18, 2024
Merged
Remove JitGenericHandleCache #106843
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
8ffb3c3
Remove JitGenericHandleCache
davidwrighton a752b1b
Tweak details
davidwrighton ac776e7
Add a VirtualFuncPtr2 function to cover scenarios which don't need 3 …
davidwrighton 3dd827a
Merge branch 'main' of github.com:dotnet/runtime into remove_jitgener…
davidwrighton 1a2f4dc
Move generic handle helpers to managed as well
davidwrighton f2dba1d
Remove unnecessary work to have a slightly more efficient implementat…
davidwrighton a25473a
Merge branch 'main' of github.com:dotnet/runtime into remove_jitgener…
davidwrighton 41342c4
Add support for nested types in the DEFINE_CLASS_U pathway
davidwrighton e40b2d6
Merge branch 'main' into remove_jitgenericcache
davidwrighton 2301226
Fix MUSL build error
davidwrighton 2515ac3
Merge branch 'remove_jitgenericcache' of github.com:davidwrighton/run…
davidwrighton 3ee7d3d
Code review feedback
davidwrighton File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
47 changes: 47 additions & 0 deletions
47
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/GenericsHelpers.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
using System.Diagnostics; | ||
using System.Runtime.InteropServices; | ||
|
||
namespace System.Runtime.CompilerServices; | ||
|
||
[StackTraceHidden] | ||
[DebuggerStepThrough] | ||
internal static unsafe partial class GenericsHelpers | ||
{ | ||
[LibraryImport(RuntimeHelpers.QCall)] | ||
private static partial IntPtr GenericHandleWorker(IntPtr pMD, IntPtr pMT, IntPtr signature, uint dictionaryIndexAndSlot, IntPtr pModule); | ||
|
||
public struct GenericHandleArgs | ||
{ | ||
public IntPtr signature; | ||
public IntPtr module; | ||
public uint dictionaryIndexAndSlot; | ||
}; | ||
|
||
[DebuggerHidden] | ||
public static IntPtr Method(IntPtr methodHnd, IntPtr signature) | ||
{ | ||
return GenericHandleWorker(methodHnd, IntPtr.Zero, signature, 0xFFFFFFFF, IntPtr.Zero); | ||
} | ||
|
||
[DebuggerHidden] | ||
public static unsafe IntPtr MethodWithSlotAndModule(IntPtr methodHnd, GenericHandleArgs * pArgs) | ||
{ | ||
return GenericHandleWorker(methodHnd, IntPtr.Zero, pArgs->signature, pArgs->dictionaryIndexAndSlot, pArgs->module); | ||
} | ||
|
||
[DebuggerHidden] | ||
public static IntPtr Class(IntPtr classHnd, IntPtr signature) | ||
{ | ||
return GenericHandleWorker(IntPtr.Zero, classHnd, signature, 0xFFFFFFFF, IntPtr.Zero); | ||
} | ||
|
||
[DebuggerHidden] | ||
public static unsafe IntPtr ClassWithSlotAndModule(IntPtr classHnd, GenericHandleArgs * pArgs) | ||
{ | ||
return GenericHandleWorker(IntPtr.Zero, classHnd, pArgs->signature, pArgs->dictionaryIndexAndSlot, pArgs->module); | ||
} | ||
} |
96 changes: 96 additions & 0 deletions
96
...eclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/VirtualDispatchHelpers.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.Numerics; | ||
using System.Runtime.InteropServices; | ||
|
||
namespace System.Runtime.CompilerServices; | ||
|
||
[StackTraceHidden] | ||
[DebuggerStepThrough] | ||
internal static unsafe partial class VirtualDispatchHelpers | ||
{ | ||
private struct VirtualResolutionData : IEquatable<VirtualResolutionData> | ||
{ | ||
public int _hashCode; | ||
public MethodTable* _objectMethodTable; | ||
public IntPtr _classHandle; | ||
public IntPtr _methodHandle; | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
public VirtualResolutionData(MethodTable* objectMethodTable, IntPtr classHandle, IntPtr methodHandle) | ||
{ | ||
_hashCode = (int) ((uint)objectMethodTable + (BitOperations.RotateLeft((uint)classHandle, 5)) + (BitOperations.RotateRight((uint)methodHandle, 5))); | ||
_objectMethodTable = objectMethodTable; | ||
_classHandle = classHandle; | ||
_methodHandle = methodHandle; | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
public bool Equals(VirtualResolutionData other) => | ||
_hashCode == other._hashCode && | ||
(((nint)_objectMethodTable - (nint)other._objectMethodTable) | | ||
(_classHandle - other._classHandle) | | ||
(_methodHandle - other._methodHandle)) == 0; | ||
|
||
public override bool Equals(object? obj) => obj is VirtualResolutionData other && Equals(other); | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
public override int GetHashCode() => _hashCode; | ||
} | ||
|
||
private struct VirtualFunctionPointerArgs | ||
{ | ||
public IntPtr classHnd; | ||
public IntPtr methodHnd; | ||
}; | ||
|
||
#if DEBUG | ||
// use smaller numbers to hit resizing/preempting logic in debug | ||
private const int InitialCacheSize = 8; // MUST BE A POWER OF TWO | ||
private const int MaximumCacheSize = 512; | ||
#else | ||
private const int InitialCacheSize = 128; // MUST BE A POWER OF TWO | ||
private const int MaximumCacheSize = 8 * 1024; | ||
#endif // DEBUG | ||
|
||
private static GenericCache<VirtualResolutionData, IntPtr> s_virtualFunctionPointerCache = new GenericCache<VirtualResolutionData, IntPtr>(InitialCacheSize, MaximumCacheSize); | ||
|
||
[LibraryImport(RuntimeHelpers.QCall)] | ||
private static unsafe partial IntPtr ResolveVirtualFunctionPointer(ObjectHandleOnStack obj, IntPtr classHandle, IntPtr methodHandle); | ||
|
||
[MethodImpl(MethodImplOptions.NoInlining)] | ||
[DebuggerHidden] | ||
private static unsafe IntPtr VirtualFunctionPointerSlow(object obj, IntPtr classHandle, IntPtr methodHandle) | ||
{ | ||
IntPtr result = ResolveVirtualFunctionPointer(ObjectHandleOnStack.Create(ref obj), classHandle, methodHandle); | ||
s_virtualFunctionPointerCache.TrySet(new VirtualResolutionData(RuntimeHelpers.GetMethodTable(obj), classHandle, methodHandle), result); | ||
GC.KeepAlive(obj); | ||
return result; | ||
} | ||
|
||
[DebuggerHidden] | ||
private static unsafe IntPtr VirtualFunctionPointer(object obj, IntPtr classHandle, IntPtr methodHandle) | ||
{ | ||
if (s_virtualFunctionPointerCache.TryGet(new VirtualResolutionData(RuntimeHelpers.GetMethodTable(obj), classHandle, methodHandle), out IntPtr result)) | ||
{ | ||
return result; | ||
} | ||
return VirtualFunctionPointerSlow(obj, classHandle, methodHandle); | ||
} | ||
|
||
[DebuggerHidden] | ||
private static unsafe IntPtr VirtualFunctionPointer_Dynamic(object obj, ref VirtualFunctionPointerArgs virtualFunctionPointerArgs) | ||
{ | ||
IntPtr classHandle = virtualFunctionPointerArgs.classHnd; | ||
IntPtr methodHandle = virtualFunctionPointerArgs.methodHnd; | ||
|
||
if (s_virtualFunctionPointerCache.TryGet(new VirtualResolutionData(RuntimeHelpers.GetMethodTable(obj), classHandle, methodHandle), out IntPtr result)) | ||
{ | ||
return result; | ||
} | ||
return VirtualFunctionPointerSlow(obj, classHandle, methodHandle); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
/*============================================================ | ||
** | ||
** Header: JitQCallHelpers.h | ||
** | ||
** | ||
===========================================================*/ | ||
|
||
#ifndef _JITQCALLHELPERS_H | ||
#define _JITQCALLHELPERS_H | ||
|
||
#include "qcall.h" | ||
#include "corinfo.h" | ||
|
||
class Module; | ||
class MethodTable; | ||
class MethodDesc; | ||
|
||
extern "C" void * QCALLTYPE ResolveVirtualFunctionPointer(QCall::ObjectHandleOnStack obj, CORINFO_CLASS_HANDLE classHnd, CORINFO_METHOD_HANDLE methodHnd); | ||
extern "C" CORINFO_GENERIC_HANDLE QCALLTYPE GenericHandleWorker(MethodDesc * pMD, MethodTable * pMT, LPVOID signature, DWORD dictionaryIndexAndSlot, Module* pModule); | ||
|
||
#endif //_JITQCALLHELPERS_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks a bit suspect that we are passing
obj
into the resolve worker, but then only useRuntimeHelpers.GetMethodTable(obj)
as the cache key. Do we have correct behavior for dynamic castable here?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NativeAOT follows this rule around not caching results (for interface dispatch, but appears to have the same problem for virtual function resolution) CoreCLR does not appear to me to have logic in its dispatch behavior which avoids caching. Possibly we intended to build it, but I'm pretty sure we didn't. I can build a test case to validate my expectation if you'd like. The performance of the existing IDynamicInterfaceCastable implementation would likely be highly impacted if we change the rules here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The result of this is a useable model, although it's not well defined in behavior. The result is that if any type uses
IDynamicInterfaceCastable
... the implementor ofIDynamicInterfaceCastable.GetInterfaceImplementaiton
really should provide an idempotent implementation for any case where it will successfully return an implementation, or they may have surprising behavior. However, we DO have the correct cache avoidance at cast time. This means that users of types which useIDynamicInterfaceCastable
experience the expected behavior.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may want to fix NativeAOT to match CoreCLR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. I've opened #107999 to track this issue