diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
index a37abf7a0c1167..f14f27121b6d35 100644
--- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
+++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
@@ -237,7 +237,8 @@
Common\System\Collections\Generic\ArrayBuilder.cs
-
+
+
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs
index 74e07398481681..8c48595d4e2d3c 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs
@@ -642,11 +642,7 @@ public unsafe void Initialize()
RuntimeType arrayType = (RuntimeType)GetType();
- if (arrayType.GenericCache is not ArrayInitializeCache cache)
- {
- cache = new ArrayInitializeCache(arrayType);
- arrayType.GenericCache = cache;
- }
+ ArrayInitializeCache cache = arrayType.GetOrCreateCacheEntry();
delegate*[ constructorFtn = cache.ConstructorEntrypoint;
ref byte arrayRef = ref MemoryMarshal.GetArrayDataReference(this);
@@ -659,17 +655,21 @@ public unsafe void Initialize()
}
}
- private sealed unsafe partial class ArrayInitializeCache
+ internal sealed unsafe partial class ArrayInitializeCache : RuntimeType.IGenericCacheEntry
{
internal readonly delegate*][ ConstructorEntrypoint;
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Array_GetElementConstructorEntrypoint")]
private static partial delegate*][ GetElementConstructorEntrypoint(QCallTypeHandle arrayType);
- public ArrayInitializeCache(RuntimeType arrayType)
+ private ArrayInitializeCache(delegate*][ constructorEntrypoint)
{
- ConstructorEntrypoint = GetElementConstructorEntrypoint(new QCallTypeHandle(ref arrayType));
+ ConstructorEntrypoint = constructorEntrypoint;
}
+
+ public static ArrayInitializeCache Create(RuntimeType arrayType) => new(GetElementConstructorEntrypoint(new QCallTypeHandle(ref arrayType)));
+ public void InitializeCompositeCache(RuntimeType.CompositeCacheEntry compositeEntry) => compositeEntry._arrayInitializeCache = this;
+ public static ref ArrayInitializeCache? GetStorageRef(RuntimeType.CompositeCacheEntry compositeEntry) => ref compositeEntry._arrayInitializeCache;
}
}
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs
index cc46e2a75d8b2c..9c92874f8ec279 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs
@@ -87,31 +87,49 @@ private static EnumInfo GetEnumInfo(RuntimeType enumType, bo
typeof(TStorage) == typeof(nuint) || typeof(TStorage) == typeof(float) || typeof(TStorage) == typeof(double) || typeof(TStorage) == typeof(char),
$"Unexpected {nameof(TStorage)} == {typeof(TStorage)}");
- return enumType.GenericCache is EnumInfo info && (!getNames || info.Names is not null) ?
+ return enumType.FindCacheEntry>() is {} info && (!getNames || info.Names is not null) ?
info :
InitializeEnumInfo(enumType, getNames);
[MethodImpl(MethodImplOptions.NoInlining)]
static EnumInfo InitializeEnumInfo(RuntimeType enumType, bool getNames)
+ {
+ // If we're asked to get the cache with names,
+ // force that copy into the cache even if we already have a cache entry without names
+ // so we don't have to recompute the names if asked again.
+ return getNames
+ ? enumType.ReplaceCacheEntry(EnumInfo.Create(enumType, getNames: true))
+ : enumType.GetOrCreateCacheEntry>();
+ }
+ }
+
+ internal sealed partial class EnumInfo : RuntimeType.IGenericCacheEntry>
+ {
+ public static EnumInfo Create(RuntimeType type, bool getNames)
{
TStorage[]? values = null;
string[]? names = null;
GetEnumValuesAndNames(
- new QCallTypeHandle(ref enumType),
+ new QCallTypeHandle(ref type),
ObjectHandleOnStack.Create(ref values),
ObjectHandleOnStack.Create(ref names),
getNames ? Interop.BOOL.TRUE : Interop.BOOL.FALSE);
Debug.Assert(values!.GetType() == typeof(TStorage[]));
- Debug.Assert(!getNames || names!.GetType() == typeof(string[]));
- bool hasFlagsAttribute = enumType.IsDefined(typeof(FlagsAttribute), inherit: false);
+ bool hasFlagsAttribute = type.IsDefined(typeof(FlagsAttribute), inherit: false);
- var entry = new EnumInfo(hasFlagsAttribute, values, names!);
- enumType.GenericCache = entry;
- return entry;
+ return new EnumInfo(hasFlagsAttribute, values, names!);
}
+
+ public static EnumInfo Create(RuntimeType type) => Create(type, getNames: false);
+
+ public void InitializeCompositeCache(RuntimeType.CompositeCacheEntry compositeEntry) => compositeEntry._enumInfo = this;
+
+ // This type is the only type that will be stored in the _enumInfo field, so we can use Unsafe.As here.
+ public static ref EnumInfo? GetStorageRef(RuntimeType.CompositeCacheEntry compositeEntry)
+ => ref Unsafe.As?>(ref compositeEntry._enumInfo);
}
}
}
diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.ActivatorCache.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.ActivatorCache.cs
index 4d73cfad391430..17dd0f74d6879a 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.ActivatorCache.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.ActivatorCache.cs
@@ -13,7 +13,7 @@ internal sealed partial class RuntimeType
/// A cache which allows optimizing ,
/// , and related APIs.
///
- private sealed unsafe class ActivatorCache
+ internal sealed unsafe class ActivatorCache : IGenericCacheEntry
{
// The managed calli to the newobj allocator, plus its first argument (MethodTable*).
// In the case of the COM allocator, first arg is ComClassFactory*, not MethodTable*.
@@ -24,13 +24,15 @@ private sealed unsafe class ActivatorCache
private readonly delegate*]