Skip to content
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

Move static helpers to managed #108167

Merged
merged 30 commits into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0fbff89
Part1 Of implement InitClass helpers
davidwrighton Sep 19, 2024
7d6a205
Merge branch 'main' of github.com:dotnet/runtime into initclass_managed
davidwrighton Sep 19, 2024
51c69d5
It builds and works
davidwrighton Sep 19, 2024
d1fccda
Revert unnecessary changes
davidwrighton Sep 19, 2024
08fe963
Ooops we did need to keep the refactoring that put the debug only Met…
davidwrighton Sep 19, 2024
12c9de2
Merge branch 'initclass_managed' into thread_static_helpers
davidwrighton Sep 19, 2024
2c0cb78
Initial work which does static helpers
davidwrighton Sep 24, 2024
0fb7f4a
Fix build break
davidwrighton Sep 24, 2024
0cb8f78
Thread statics basically implemented...
davidwrighton Sep 24, 2024
aacdb2a
Merge branch 'main' of https://github.com/dotnet/runtime into thread_…
davidwrighton Oct 17, 2024
00b6df4
Finish merging in changes from main, and fix up some oopses... and it…
davidwrighton Oct 18, 2024
34ebcaf
Restructure static access so that we don't need a helper call to acce…
davidwrighton Oct 19, 2024
f740618
Tweaks for failures noted in PR testing
davidwrighton Oct 21, 2024
e2498bb
Fix comments on Unix builds for Arm64 and RISCV
davidwrighton Oct 21, 2024
0d03a51
Tail call the right helper
davidwrighton Oct 21, 2024
367e95b
Force volatile loads for the statics ref, to fix the arm64 statics ra…
davidwrighton Oct 22, 2024
ccfafe1
Get rid of extra null check
davidwrighton Oct 22, 2024
5ff66b4
Fix error around fcall class ordering
davidwrighton Oct 22, 2024
0586455
Attempt to fix Unix assembly code, and fix fcall registration
davidwrighton Oct 24, 2024
0f6290a
Properly thread through using the "optimized" helper 2 for thread sta…
davidwrighton Oct 24, 2024
8f5448f
Remove unnecessary changes from the PR
davidwrighton Oct 25, 2024
0d6edfc
Fixing naming
davidwrighton Oct 28, 2024
46fc106
Change GetDynamicStaticsInfo and GetThreadStaticsInfo to be instance …
davidwrighton Oct 28, 2024
a08dc02
Merge branch 'thread_static_helpers' of https://github.com/davidwrigh…
davidwrighton Oct 29, 2024
8fb5f22
More code review driven fixes
davidwrighton Oct 29, 2024
4c92dc1
Merge branch 'main' of https://github.com/dotnet/runtime into thread_…
davidwrighton Oct 29, 2024
f9d19fa
Fix build issues
davidwrighton Oct 29, 2024
2a89bce
More feedback
davidwrighton Oct 29, 2024
47fc1e5
Address more feedback
davidwrighton Oct 29, 2024
67bc96a
More updates
davidwrighton Oct 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -195,10 +195,12 @@
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimePropertyInfo.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\TypeNameResolver.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Metadata\RuntimeTypeMetadataUpdateHandler.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\BypassReadyToRunAttribute.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\CastHelpers.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\GenericsHelpers.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\InitHelpers.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\RuntimeHelpers.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\StaticsHelpers.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\VirtualDispatchHelpers.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\ControlledExecution.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\DependentHandle.cs" />
Expand Down
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
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ internal static unsafe partial class InitHelpers

[DebuggerHidden]
[MethodImpl(MethodImplOptions.NoInlining)]
private static void InitClassSlow(MethodTable* mt)
internal static void InitClassSlow(MethodTable* mt)
{
InitClassHelper(mt);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,45 @@ public TypeHandle GetArrayElementTypeHandle()
public extern MethodTable* GetMethodTableMatchingParentClass(MethodTable* parent);
}

[StructLayout(LayoutKind.Sequential)]
internal unsafe ref struct DynamicStaticsInfo
{
internal const int ISCLASSNOTINITED = 1;
internal IntPtr _pGCStatics; // The ISCLASSNOTINITED bit is set when the class is NOT initialized
internal IntPtr _pNonGCStatics; // The ISCLASSNOTINITED bit is set when the class is NOT initialized

/// <summary>
/// Given a statics pointer in the DynamicStaticsInfo, get the actual statics pointer.
/// If the class it initialized, this mask is not necessary
/// </summary>
internal static ref byte MaskStaticsPointer(ref byte staticsPtr)
{
fixed (byte* p = &staticsPtr)
{
return ref Unsafe.AsRef<byte>((byte*)((nuint)p & ~(nuint)DynamicStaticsInfo.ISCLASSNOTINITED));
}
}

internal unsafe MethodTable* _methodTable;
}

[StructLayout(LayoutKind.Sequential)]
internal unsafe ref struct GenericsStaticsInfo
{
// Pointer to field descs for statics
internal IntPtr _pFieldDescs;
internal DynamicStaticsInfo _dynamicStatics;
}

[StructLayout(LayoutKind.Sequential)]
internal unsafe ref struct ThreadStaticsInfo
{
internal int _nonGCTlsIndex;
internal int _gcTlsIndex;
internal GenericsStaticsInfo _genericStatics;
}


// Subset of src\vm\methodtable.h
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct MethodTableAuxiliaryData
Expand Down Expand Up @@ -939,6 +978,18 @@ public RuntimeType? ExposedClassObject
public bool IsClassInited => (Volatile.Read(ref Flags) & enum_flag_Initialized) != 0;

public bool IsClassInitedAndActive => (Volatile.Read(ref Flags) & (enum_flag_Initialized | enum_flag_EnsuredInstanceActive)) == (enum_flag_Initialized | enum_flag_EnsuredInstanceActive);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref DynamicStaticsInfo GetDynamicStaticsInfo()
{
return ref Unsafe.Subtract(ref Unsafe.As<MethodTableAuxiliaryData, DynamicStaticsInfo>(ref this), 1);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref ThreadStaticsInfo GetThreadStaticsInfo()
{
return ref Unsafe.Subtract(ref Unsafe.As<MethodTableAuxiliaryData, ThreadStaticsInfo>(ref this), 1);
}
}

/// <summary>
Expand Down
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
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
{
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);
}
}
}
Loading
Loading