Skip to content

Commit

Permalink
Remove JitGenericHandleCache (dotnet#106843)
Browse files Browse the repository at this point in the history
- It was only used for overflow scenarios from the generic dictionary (which don't happen), virtual resolution scenarios for creating delegates, and a few other rare R2R scenarios
- Replace the virtual resolution scenarios with a cache of the affected data in managed code, and move the helpers to managed
  - Use the GenericCache type which was previously only used on NativeAOT for Generic Virtual method resolution paths.
- Just remove the pointless checks from within the various normal generic lookup paths, and since they no longer do anything interesting in their hot paths except erect a helper method frame and call a worker routine, move those jit helpers to managed as well.
  • Loading branch information
davidwrighton authored Sep 18, 2024
1 parent 8d2b266 commit 128fdfd
Show file tree
Hide file tree
Showing 29 changed files with 582 additions and 502 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,9 @@
<Compile Include="$(BclSourcesRoot)\System\Reflection\TypeNameResolver.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Metadata\RuntimeTypeMetadataUpdateHandler.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\CastHelpers.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\GenericsHelpers.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\RuntimeHelpers.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\VirtualDispatchHelpers.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\ControlledExecution.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\DependentHandle.cs" />
<Compile Include="$(MSBuildThisFileDirectory)..\nativeaot\Common\src\System\Runtime\RhFailFastReason.cs" />
Expand Down
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);
}
}
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);
}
}
7 changes: 2 additions & 5 deletions src/coreclr/inc/CrstTypes.def
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ End

Crst Interop
AcquiredBefore PinnedHeapHandleTable AvailableParamTypes ClassInit DeadlockDetection GenericDictionaryExpansion
HandleTable InstMethodHashTable InteropData JitGenericHandleCache LoaderHeap SigConvert
HandleTable InstMethodHashTable InteropData LoaderHeap SigConvert
StubDispatchCache StubUnwindInfoHeapSegments SyncBlockCache TypeIDMap UnresolvedClassLock
PendingTypeLoadEntry
End
Expand All @@ -276,9 +276,6 @@ Crst Jit
SameLevelAs ClassInit
End

Crst JitGenericHandleCache
End

Crst JitPatchpoint
AcquiredBefore LoaderHeap
End
Expand Down Expand Up @@ -446,7 +443,7 @@ End
Crst ThreadStore
AcquiredBefore AvailableParamTypes DeadlockDetection DebuggerController
DebuggerHeapLock DebuggerJitInfo DynamicIL HandleTable IbcProfile
JitGenericHandleCache JumpStubCache LoaderHeap ModuleLookupTable ProfilerGCRefDataFreeList
JumpStubCache LoaderHeap ModuleLookupTable ProfilerGCRefDataFreeList
SingleUseLock SyncBlockCache SystemDomainDelayedUnloadList ThreadIdDispenser DebuggerMutex
JitInlineTrackingMap
End
Expand Down
115 changes: 56 additions & 59 deletions src/coreclr/inc/crsttypes_generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,63 +65,62 @@ enum CrstType
CrstIsJMCMethod = 47,
CrstISymUnmanagedReader = 48,
CrstJit = 49,
CrstJitGenericHandleCache = 50,
CrstJitInlineTrackingMap = 51,
CrstJitPatchpoint = 52,
CrstJumpStubCache = 53,
CrstLeafLock = 54,
CrstListLock = 55,
CrstLoaderAllocator = 56,
CrstLoaderAllocatorReferences = 57,
CrstLoaderHeap = 58,
CrstManagedObjectWrapperMap = 59,
CrstMethodDescBackpatchInfoTracker = 60,
CrstMethodTableExposedObject = 61,
CrstModule = 62,
CrstModuleLookupTable = 63,
CrstMulticoreJitHash = 64,
CrstMulticoreJitManager = 65,
CrstNativeImageEagerFixups = 66,
CrstNativeImageLoad = 67,
CrstNotifyGdb = 68,
CrstPEImage = 69,
CrstPendingTypeLoadEntry = 70,
CrstPerfMap = 71,
CrstPgoData = 72,
CrstPinnedByrefValidation = 73,
CrstPinnedHeapHandleTable = 74,
CrstProfilerGCRefDataFreeList = 75,
CrstProfilingAPIStatus = 76,
CrstRCWCache = 77,
CrstRCWCleanupList = 78,
CrstReadyToRunEntryPointToMethodDescMap = 79,
CrstReflection = 80,
CrstReJITGlobalRequest = 81,
CrstRetThunkCache = 82,
CrstSigConvert = 83,
CrstSingleUseLock = 84,
CrstStressLog = 85,
CrstStubCache = 86,
CrstStubDispatchCache = 87,
CrstStubUnwindInfoHeapSegments = 88,
CrstSyncBlockCache = 89,
CrstSyncHashLock = 90,
CrstSystemDomain = 91,
CrstSystemDomainDelayedUnloadList = 92,
CrstThreadIdDispenser = 93,
CrstThreadLocalStorageLock = 94,
CrstThreadStore = 95,
CrstTieredCompilation = 96,
CrstTypeEquivalenceMap = 97,
CrstTypeIDMap = 98,
CrstUMEntryThunkCache = 99,
CrstUMEntryThunkFreeListLock = 100,
CrstUniqueStack = 101,
CrstUnresolvedClassLock = 102,
CrstUnwindInfoTableLock = 103,
CrstVSDIndirectionCellLock = 104,
CrstWrapperTemplate = 105,
kNumberOfCrstTypes = 106
CrstJitInlineTrackingMap = 50,
CrstJitPatchpoint = 51,
CrstJumpStubCache = 52,
CrstLeafLock = 53,
CrstListLock = 54,
CrstLoaderAllocator = 55,
CrstLoaderAllocatorReferences = 56,
CrstLoaderHeap = 57,
CrstManagedObjectWrapperMap = 58,
CrstMethodDescBackpatchInfoTracker = 59,
CrstMethodTableExposedObject = 60,
CrstModule = 61,
CrstModuleLookupTable = 62,
CrstMulticoreJitHash = 63,
CrstMulticoreJitManager = 64,
CrstNativeImageEagerFixups = 65,
CrstNativeImageLoad = 66,
CrstNotifyGdb = 67,
CrstPEImage = 68,
CrstPendingTypeLoadEntry = 69,
CrstPerfMap = 70,
CrstPgoData = 71,
CrstPinnedByrefValidation = 72,
CrstPinnedHeapHandleTable = 73,
CrstProfilerGCRefDataFreeList = 74,
CrstProfilingAPIStatus = 75,
CrstRCWCache = 76,
CrstRCWCleanupList = 77,
CrstReadyToRunEntryPointToMethodDescMap = 78,
CrstReflection = 79,
CrstReJITGlobalRequest = 80,
CrstRetThunkCache = 81,
CrstSigConvert = 82,
CrstSingleUseLock = 83,
CrstStressLog = 84,
CrstStubCache = 85,
CrstStubDispatchCache = 86,
CrstStubUnwindInfoHeapSegments = 87,
CrstSyncBlockCache = 88,
CrstSyncHashLock = 89,
CrstSystemDomain = 90,
CrstSystemDomainDelayedUnloadList = 91,
CrstThreadIdDispenser = 92,
CrstThreadLocalStorageLock = 93,
CrstThreadStore = 94,
CrstTieredCompilation = 95,
CrstTypeEquivalenceMap = 96,
CrstTypeIDMap = 97,
CrstUMEntryThunkCache = 98,
CrstUMEntryThunkFreeListLock = 99,
CrstUniqueStack = 100,
CrstUnresolvedClassLock = 101,
CrstUnwindInfoTableLock = 102,
CrstVSDIndirectionCellLock = 103,
CrstWrapperTemplate = 104,
kNumberOfCrstTypes = 105
};

#endif // __CRST_TYPES_INCLUDED
Expand Down Expand Up @@ -182,7 +181,6 @@ int g_rgCrstLevelMap[] =
0, // CrstIsJMCMethod
6, // CrstISymUnmanagedReader
10, // CrstJit
0, // CrstJitGenericHandleCache
11, // CrstJitInlineTrackingMap
3, // CrstJitPatchpoint
5, // CrstJumpStubCache
Expand Down Expand Up @@ -293,7 +291,6 @@ LPCSTR g_rgCrstNameMap[] =
"CrstIsJMCMethod",
"CrstISymUnmanagedReader",
"CrstJit",
"CrstJitGenericHandleCache",
"CrstJitInlineTrackingMap",
"CrstJitPatchpoint",
"CrstJumpStubCache",
Expand Down
6 changes: 3 additions & 3 deletions src/coreclr/inc/jithelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,16 +221,16 @@
JITHELPER(CORINFO_HELP_NATIVE_MEMSET, Jit_NativeMemSet, METHOD__NIL)

// Generics
JITHELPER(CORINFO_HELP_RUNTIMEHANDLE_METHOD, JIT_GenericHandleMethod, METHOD__NIL)
JITHELPER(CORINFO_HELP_RUNTIMEHANDLE_CLASS, JIT_GenericHandleClass, METHOD__NIL)
DYNAMICJITHELPER(CORINFO_HELP_RUNTIMEHANDLE_METHOD, NULL, METHOD__GENERICSHELPERS__METHOD)
DYNAMICJITHELPER(CORINFO_HELP_RUNTIMEHANDLE_CLASS, NULL, METHOD__GENERICSHELPERS__CLASS)
JITHELPER(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE, JIT_GetRuntimeType, METHOD__NIL)
JITHELPER(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE_MAYBENULL, JIT_GetRuntimeType_MaybeNull, METHOD__NIL)
JITHELPER(CORINFO_HELP_METHODDESC_TO_STUBRUNTIMEMETHOD, JIT_GetRuntimeMethodStub,METHOD__NIL)
JITHELPER(CORINFO_HELP_FIELDDESC_TO_STUBRUNTIMEFIELD, JIT_GetRuntimeFieldStub, METHOD__NIL)
JITHELPER(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE, JIT_GetRuntimeType, METHOD__NIL)
JITHELPER(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE_MAYBENULL, JIT_GetRuntimeType_MaybeNull, METHOD__NIL)

JITHELPER(CORINFO_HELP_VIRTUAL_FUNC_PTR, JIT_VirtualFunctionPointer, METHOD__NIL)
DYNAMICJITHELPER(CORINFO_HELP_VIRTUAL_FUNC_PTR, NULL, METHOD__VIRTUALDISPATCHHELPERS__VIRTUALFUNCTIONPOINTER)

JITHELPER(CORINFO_HELP_READYTORUN_NEW, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_READYTORUN_NEWARR_1, NULL, METHOD__NIL)
Expand Down
24 changes: 24 additions & 0 deletions src/coreclr/vm/JitQCallHelpers.h
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
4 changes: 1 addition & 3 deletions src/coreclr/vm/amd64/cgenamd64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -992,9 +992,7 @@ PCODE DynamicHelpers::CreateDictionaryLookupHelper(LoaderAllocator * pAllocator,
{
STANDARD_VM_CONTRACT;

PCODE helperAddress = (pLookup->helper == CORINFO_HELP_RUNTIMEHANDLE_METHOD ?
GetEEFuncEntryPoint(JIT_GenericHandleMethodWithSlotAndModule) :
GetEEFuncEntryPoint(JIT_GenericHandleClassWithSlotAndModule));
PCODE helperAddress = GetDictionaryLookupHelper(pLookup->helper);

GenericHandleArgs * pArgs = (GenericHandleArgs *)(void *)pAllocator->GetDynamicHelpersHeap()->AllocAlignedMem(sizeof(GenericHandleArgs), DYNAMIC_HELPER_ALIGNMENT);
ExecutableWriterHolder<GenericHandleArgs> argsWriterHolder(pArgs, sizeof(GenericHandleArgs));
Expand Down
4 changes: 1 addition & 3 deletions src/coreclr/vm/arm/stubs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2018,9 +2018,7 @@ PCODE DynamicHelpers::CreateDictionaryLookupHelper(LoaderAllocator * pAllocator,
{
STANDARD_VM_CONTRACT;

PCODE helperAddress = (pLookup->helper == CORINFO_HELP_RUNTIMEHANDLE_METHOD ?
GetEEFuncEntryPoint(JIT_GenericHandleMethodWithSlotAndModule) :
GetEEFuncEntryPoint(JIT_GenericHandleClassWithSlotAndModule));
PCODE helperAddress = GetDictionaryLookupHelper(pLookup->helper);

GenericHandleArgs * pArgs = (GenericHandleArgs *)(void *)pAllocator->GetDynamicHelpersHeap()->AllocAlignedMem(sizeof(GenericHandleArgs), DYNAMIC_HELPER_ALIGNMENT);
ExecutableWriterHolder<GenericHandleArgs> argsWriterHolder(pArgs, sizeof(GenericHandleArgs));
Expand Down
4 changes: 1 addition & 3 deletions src/coreclr/vm/arm64/stubs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1946,9 +1946,7 @@ PCODE DynamicHelpers::CreateDictionaryLookupHelper(LoaderAllocator * pAllocator,
{
STANDARD_VM_CONTRACT;

PCODE helperAddress = (pLookup->helper == CORINFO_HELP_RUNTIMEHANDLE_METHOD ?
GetEEFuncEntryPoint(JIT_GenericHandleMethodWithSlotAndModule) :
GetEEFuncEntryPoint(JIT_GenericHandleClassWithSlotAndModule));
PCODE helperAddress = GetDictionaryLookupHelper(pLookup->helper);

GenericHandleArgs * pArgs = (GenericHandleArgs *)(void *)pAllocator->GetDynamicHelpersHeap()->AllocAlignedMem(sizeof(GenericHandleArgs), DYNAMIC_HELPER_ALIGNMENT);
ExecutableWriterHolder<GenericHandleArgs> argsWriterHolder(pArgs, sizeof(GenericHandleArgs));
Expand Down
Loading

0 comments on commit 128fdfd

Please sign in to comment.