-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move static helpers to managed (#108167)
Move the statics helpers from native code, occasionally using a HELPER_METHOD_FRAME, to managed code using QCalls for P/Invoke transition Particularly notable parts of the change: - Thread static lookup relies on the jit thread static optimization pathway. I needed to build a parallel helper call enum value for the case where we need to use the optimized helper path, but didn't actually have the ability to optimize in the JIT. - This change maintains the volatile read of the normal static values, through a custom JIT intrinsic used only in this codepath.
- Loading branch information
1 parent
5ca9043
commit 26612c8
Showing
39 changed files
with
590 additions
and
377 deletions.
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
10 changes: 10 additions & 0 deletions
10
src/coreclr/System.Private.CoreLib/src/System/Runtime/BypassReadyToRunAttribute.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,10 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
namespace System.Runtime | ||
{ | ||
// Use to disable ReadyToRun compilation for a method. | ||
internal sealed class BypassReadyToRunAttribute : Attribute | ||
{ | ||
} | ||
} |
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
272 changes: 272 additions & 0 deletions
272
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/StaticsHelpers.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,272 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Diagnostics; | ||
using System.Runtime.InteropServices; | ||
|
||
namespace System.Runtime.CompilerServices | ||
{ | ||
[StackTraceHidden] | ||
[DebuggerStepThrough] | ||
internal static unsafe partial class StaticsHelpers | ||
{ | ||
[LibraryImport(RuntimeHelpers.QCall)] | ||
private static partial void GetThreadStaticsByIndex(ByteRefOnStack result, int index, [MarshalAs(UnmanagedType.Bool)] bool gcStatics); | ||
|
||
[LibraryImport(RuntimeHelpers.QCall)] | ||
private static partial void GetThreadStaticsByMethodTable(ByteRefOnStack result, MethodTable* pMT, [MarshalAs(UnmanagedType.Bool)] bool gcStatics); | ||
|
||
[Intrinsic] | ||
private static ref byte VolatileReadAsByref(ref IntPtr address) => ref VolatileReadAsByref(ref address); | ||
|
||
[DebuggerHidden] | ||
[MethodImpl(MethodImplOptions.NoInlining)] | ||
private static ref byte GetNonGCStaticBaseSlow(MethodTable* mt) | ||
{ | ||
InitHelpers.InitClassSlow(mt); | ||
return ref DynamicStaticsInfo.MaskStaticsPointer(ref VolatileReadAsByref(ref mt->AuxiliaryData->GetDynamicStaticsInfo()._pNonGCStatics)); | ||
} | ||
|
||
[DebuggerHidden] | ||
private static ref byte GetNonGCStaticBase(MethodTable* mt) | ||
{ | ||
ref byte nonGCStaticBase = ref VolatileReadAsByref(ref mt->AuxiliaryData->GetDynamicStaticsInfo()._pNonGCStatics); | ||
|
||
if ((((nuint)Unsafe.AsPointer(ref nonGCStaticBase)) & DynamicStaticsInfo.ISCLASSNOTINITED) != 0) | ||
return ref GetNonGCStaticBaseSlow(mt); | ||
else | ||
return ref nonGCStaticBase; | ||
} | ||
|
||
[DebuggerHidden] | ||
private static ref byte GetDynamicNonGCStaticBase(DynamicStaticsInfo *dynamicStaticsInfo) | ||
{ | ||
ref byte nonGCStaticBase = ref VolatileReadAsByref(ref dynamicStaticsInfo->_pNonGCStatics); | ||
|
||
if ((((nuint)Unsafe.AsPointer(ref nonGCStaticBase)) & DynamicStaticsInfo.ISCLASSNOTINITED) != 0) | ||
return ref GetNonGCStaticBaseSlow(dynamicStaticsInfo->_methodTable); | ||
else | ||
return ref nonGCStaticBase; | ||
} | ||
|
||
[DebuggerHidden] | ||
[MethodImpl(MethodImplOptions.NoInlining)] | ||
private static ref byte GetGCStaticBaseSlow(MethodTable* mt) | ||
{ | ||
InitHelpers.InitClassSlow(mt); | ||
return ref DynamicStaticsInfo.MaskStaticsPointer(ref VolatileReadAsByref(ref mt->AuxiliaryData->GetDynamicStaticsInfo()._pGCStatics)); | ||
} | ||
|
||
[DebuggerHidden] | ||
private static ref byte GetGCStaticBase(MethodTable* mt) | ||
{ | ||
ref byte gcStaticBase = ref VolatileReadAsByref(ref mt->AuxiliaryData->GetDynamicStaticsInfo()._pGCStatics); | ||
|
||
if ((((nuint)Unsafe.AsPointer(ref gcStaticBase)) & DynamicStaticsInfo.ISCLASSNOTINITED) != 0) | ||
return ref GetGCStaticBaseSlow(mt); | ||
else | ||
return ref gcStaticBase; | ||
} | ||
|
||
[DebuggerHidden] | ||
private static ref byte GetDynamicGCStaticBase(DynamicStaticsInfo *dynamicStaticsInfo) | ||
{ | ||
ref byte gcStaticBase = ref VolatileReadAsByref(ref dynamicStaticsInfo->_pGCStatics); | ||
|
||
if ((((nuint)Unsafe.AsPointer(ref gcStaticBase)) & DynamicStaticsInfo.ISCLASSNOTINITED) != 0) | ||
return ref GetGCStaticBaseSlow(dynamicStaticsInfo->_methodTable); | ||
else | ||
return ref gcStaticBase; | ||
} | ||
|
||
// Thread static helpers | ||
|
||
/// <summary> | ||
/// Return beginning of the object as a reference to byte | ||
/// </summary> | ||
[DebuggerHidden] | ||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static ref byte GetObjectAsRefByte(object obj) | ||
{ | ||
return ref Unsafe.Subtract(ref RuntimeHelpers.GetRawData(obj), sizeof(MethodTable*)); | ||
} | ||
|
||
[StructLayout(LayoutKind.Sequential)] | ||
internal struct ThreadLocalData | ||
{ | ||
internal const int NUMBER_OF_TLSOFFSETS_NOT_USED_IN_NONCOLLECTIBLE_ARRAY = 2; | ||
internal int _cNonCollectibleTlsData; // Size of offset into the non-collectible TLS array which is valid, NOTE: this is relative to the start of the nonCollectibleTlsArrayData object, not the start of the data in the array | ||
internal int _cCollectibleTlsData; // Size of offset into the TLS array which is valid | ||
private IntPtr _nonCollectibleTlsArrayData_private; // This is object[], but using object[] directly causes the structure to be laid out via auto-layout, which is not what we want. | ||
internal IntPtr* _collectibleTlsArrayData; // Points at the Thread local array data. | ||
|
||
internal object[] NonCollectibleTlsArrayData | ||
{ | ||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
get | ||
{ | ||
return Unsafe.As<IntPtr, object[]>(ref _nonCollectibleTlsArrayData_private); | ||
} | ||
} | ||
} | ||
|
||
[DebuggerHidden] | ||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static int GetIndexOffset(int index) | ||
{ | ||
return index & 0xFFFFFF; | ||
} | ||
|
||
private const int NonCollectibleTLSIndexType = 0; | ||
private const int DirectOnThreadLocalDataTLSIndexType = 2; | ||
|
||
[DebuggerHidden] | ||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static int GetIndexType(int index) | ||
{ | ||
return index >> 24; | ||
} | ||
|
||
[DebuggerHidden] | ||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static bool IsIndexAllocated(int index) | ||
{ | ||
return index != -1; | ||
} | ||
|
||
[DebuggerHidden] | ||
[MethodImpl(MethodImplOptions.NoInlining)] | ||
private static ref byte GetNonGCThreadStaticsByIndexSlow(int index) | ||
{ | ||
ByteRef result = default; | ||
GetThreadStaticsByIndex(ByteRefOnStack.Create(ref result), index, false); | ||
return ref result.Get(); | ||
} | ||
|
||
[DebuggerHidden] | ||
[MethodImpl(MethodImplOptions.NoInlining)] | ||
private static ref byte GetGCThreadStaticsByIndexSlow(int index) | ||
{ | ||
ByteRef result = default; | ||
GetThreadStaticsByIndex(ByteRefOnStack.Create(ref result), index, true); | ||
return ref result.Get(); | ||
} | ||
|
||
[DebuggerHidden] | ||
[MethodImpl(MethodImplOptions.NoInlining)] | ||
private static ref byte GetNonGCThreadStaticBaseSlow(MethodTable* mt) | ||
{ | ||
ByteRef result = default; | ||
GetThreadStaticsByMethodTable(ByteRefOnStack.Create(ref result), mt, false); | ||
return ref result.Get(); | ||
} | ||
|
||
[DebuggerHidden] | ||
[MethodImpl(MethodImplOptions.NoInlining)] | ||
private static ref byte GetGCThreadStaticBaseSlow(MethodTable* mt) | ||
{ | ||
ByteRef result = default; | ||
GetThreadStaticsByMethodTable(ByteRefOnStack.Create(ref result), mt, true); | ||
return ref result.Get(); | ||
} | ||
|
||
[DebuggerHidden] | ||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static ref byte GetThreadLocalStaticBaseByIndex(int index, bool gcStatics) | ||
{ | ||
ThreadLocalData *t_ThreadStatics = System.Threading.Thread.GetThreadStaticsBase(); | ||
int indexOffset = GetIndexOffset(index); | ||
if (GetIndexType(index) == NonCollectibleTLSIndexType) | ||
{ | ||
if (t_ThreadStatics->_cNonCollectibleTlsData > GetIndexOffset(index)) | ||
{ | ||
object? threadStaticObjectNonCollectible = Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(t_ThreadStatics->NonCollectibleTlsArrayData), indexOffset - ThreadLocalData.NUMBER_OF_TLSOFFSETS_NOT_USED_IN_NONCOLLECTIBLE_ARRAY); | ||
if (threadStaticObjectNonCollectible != null) | ||
{ | ||
return ref GetObjectAsRefByte(threadStaticObjectNonCollectible); | ||
} | ||
} | ||
} | ||
else if (GetIndexType(index) == DirectOnThreadLocalDataTLSIndexType) | ||
{ | ||
return ref Unsafe.Add(ref Unsafe.AsRef<byte>(t_ThreadStatics), indexOffset); | ||
} | ||
else | ||
{ | ||
int cCollectibleTlsData = t_ThreadStatics->_cCollectibleTlsData; | ||
if (cCollectibleTlsData > indexOffset) | ||
{ | ||
IntPtr* pCollectibleTlsArrayData = t_ThreadStatics->_collectibleTlsArrayData; | ||
|
||
pCollectibleTlsArrayData += indexOffset; | ||
IntPtr objHandle = *pCollectibleTlsArrayData; | ||
if (objHandle != IntPtr.Zero) | ||
{ | ||
object? threadStaticObject = GCHandle.InternalGet(objHandle); | ||
if (threadStaticObject != null) | ||
{ | ||
return ref GetObjectAsRefByte(threadStaticObject); | ||
} | ||
} | ||
} | ||
} | ||
|
||
if (gcStatics) | ||
return ref GetGCThreadStaticsByIndexSlow(index); | ||
else | ||
return ref GetNonGCThreadStaticsByIndexSlow(index); | ||
} | ||
|
||
[DebuggerHidden] | ||
private static ref byte GetNonGCThreadStaticBase(MethodTable* mt) | ||
{ | ||
int index = mt->AuxiliaryData->GetThreadStaticsInfo()._nonGCTlsIndex; | ||
if (IsIndexAllocated(index)) | ||
return ref GetThreadLocalStaticBaseByIndex(index, false); | ||
else | ||
return ref GetNonGCThreadStaticBaseSlow(mt); | ||
} | ||
|
||
[DebuggerHidden] | ||
private static ref byte GetGCThreadStaticBase(MethodTable* mt) | ||
{ | ||
int index = mt->AuxiliaryData->GetThreadStaticsInfo()._gcTlsIndex; | ||
if (IsIndexAllocated(index)) | ||
return ref GetThreadLocalStaticBaseByIndex(index, true); | ||
else | ||
return ref GetGCThreadStaticBaseSlow(mt); | ||
} | ||
|
||
[DebuggerHidden] | ||
private static ref byte GetDynamicNonGCThreadStaticBase(ThreadStaticsInfo *threadStaticsInfo) | ||
{ | ||
int index = threadStaticsInfo->_nonGCTlsIndex; | ||
if (IsIndexAllocated(index)) | ||
return ref GetThreadLocalStaticBaseByIndex(index, false); | ||
else | ||
return ref GetNonGCThreadStaticBaseSlow(threadStaticsInfo->_genericStatics._dynamicStatics._methodTable); | ||
} | ||
|
||
[DebuggerHidden] | ||
private static ref byte GetDynamicGCThreadStaticBase(ThreadStaticsInfo *threadStaticsInfo) | ||
{ | ||
int index = threadStaticsInfo->_gcTlsIndex; | ||
if (IsIndexAllocated(index)) | ||
return ref GetThreadLocalStaticBaseByIndex(index, true); | ||
else | ||
return ref GetGCThreadStaticBaseSlow(threadStaticsInfo->_genericStatics._dynamicStatics._methodTable); | ||
} | ||
|
||
[DebuggerHidden] | ||
private static ref byte GetOptimizedNonGCThreadStaticBase(int index) | ||
{ | ||
return ref GetThreadLocalStaticBaseByIndex(index, false); | ||
} | ||
|
||
[DebuggerHidden] | ||
private static ref byte GetOptimizedGCThreadStaticBase(int index) | ||
{ | ||
return ref GetThreadLocalStaticBaseByIndex(index, true); | ||
} | ||
} | ||
} |
Oops, something went wrong.