From 9a651097fddbae42fd2aa68faee9b990b160d596 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Fri, 9 Dec 2022 11:45:37 -0500 Subject: [PATCH] [mono] Add a managed cache for flags in RuntimeType. (#78840) --- .../src/System/RuntimeType.Mono.cs | 134 ++++++++++++++++-- .../src/System/RuntimeTypeHandle.cs | 4 +- 2 files changed, 124 insertions(+), 14 deletions(-) diff --git a/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs b/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs index f8cadc996373..d9c35cbf4fe2 100644 --- a/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs @@ -1257,31 +1257,103 @@ public override bool IsEquivalentTo(Type? other) #region Attributes + internal CorElementType GetCorElementType() + { + TypeCache cache = Cache; + if ((cache.Cached & (int)TypeCacheEntries.CorElementType) != 0) + return cache.CorElementType; + + var type = this; + cache.CorElementType = RuntimeTypeHandle.GetCorElementType (new QCallTypeHandle(ref type)); + Interlocked.MemoryBarrier (); + UpdateCached(TypeCacheEntries.CorElementType); + return cache.CorElementType; + } + + internal TypeAttributes GetAttributes() + { + TypeCache cache = Cache; + if ((cache.Cached & (int)TypeCacheEntries.TypeAttributes) != 0) + return cache.TypeAttributes; + + var type = this; + cache.TypeAttributes = RuntimeTypeHandle.GetAttributes(new QCallTypeHandle(ref type)); + Interlocked.MemoryBarrier (); + UpdateCached(TypeCacheEntries.TypeAttributes); + return cache.TypeAttributes; + } + internal bool IsDelegate() { - return GetBaseType() == typeof(System.MulticastDelegate); + TypeCache cache = Cache; + if ((cache.Cached & (int)TypeCacheEntries.IsDelegate) != 0) + return (cache.Flags & (int)TypeCacheEntries.IsDelegate) != 0; + + bool res = GetBaseType() == typeof(System.MulticastDelegate); + CacheFlag(TypeCacheEntries.IsDelegate, res); + return res; } protected override bool IsValueTypeImpl() { + TypeCache cache = Cache; + if ((cache.Cached & (int)TypeCacheEntries.IsValueType) != 0) + return (cache.Flags & (int)TypeCacheEntries.IsValueType) != 0; + // We need to return true for generic parameters with the ValueType constraint. // So we cannot use the faster RuntimeTypeHandle.IsValueType because it returns // false for all generic parameters. - if (this == typeof(ValueType) || this == typeof(Enum)) - return false; - - return IsSubclassOf(typeof(ValueType)); + bool res = false; + if (!(this == typeof(ValueType) || this == typeof(Enum))) + res = IsSubclassOf(typeof(ValueType)); + CacheFlag(TypeCacheEntries.IsValueType, res); + return res; } // Returns true for generic parameters with the Enum constraint. public override bool IsEnum => GetBaseType() == EnumType; // Returns true for actual enum types only. - internal bool IsActualEnum => !IsGenericParameter && RuntimeTypeHandle.GetBaseType(this) == EnumType; + internal bool IsActualEnum + { + get + { + TypeCache cache = Cache; + if ((cache.Cached & (int)TypeCacheEntries.IsActualEnum) != 0) + return (cache.Flags & (int)TypeCacheEntries.IsActualEnum) != 0; + bool res = !IsGenericParameter && RuntimeTypeHandle.GetBaseType(this) == EnumType; + CacheFlag(TypeCacheEntries.IsActualEnum, res); + return res; + } + } public override bool IsConstructedGenericType => IsGenericType && !IsGenericTypeDefinition; - public override bool IsGenericType => RuntimeTypeHandle.HasInstantiation(this); - public override bool IsGenericTypeDefinition => RuntimeTypeHandle.IsGenericTypeDefinition(this); + + public override bool IsGenericType + { + get + { + TypeCache cache = Cache; + if ((cache.Cached & (int)TypeCacheEntries.IsGenericType) != 0) + return (cache.Flags & (int)TypeCacheEntries.IsGenericType) != 0; + bool res = RuntimeTypeHandle.HasInstantiation(this); + CacheFlag(TypeCacheEntries.IsGenericType, res); + return res; + } + } + + public override bool IsGenericTypeDefinition + { + get + { + TypeCache cache = Cache; + if ((cache.Cached & (int)TypeCacheEntries.IsGenericTypeDef) != 0) + return (cache.Flags & (int)TypeCacheEntries.IsGenericTypeDef) != 0; + bool res = RuntimeTypeHandle.IsGenericTypeDefinition(this); + CacheFlag(TypeCacheEntries.IsGenericTypeDef, res); + return res; + } + } public override Type GetGenericTypeDefinition() { @@ -1549,6 +1621,19 @@ private void CreateInstanceCheckThis() Interlocked.CompareExchange(ref cache, new TypeCache(), null) ?? cache; + [Flags] + // Types of entries cached in TypeCache + private enum TypeCacheEntries { + IsGenericTypeDef = 1, + IsDelegate = 2, + IsValueType = 4, + IsActualEnum = 8, + IsGenericType = 16, + CorElementType = 32, + TypeAttributes = 64, + DefaultCtor = 128 + } + internal sealed class TypeCache { public object? EnumInfo; @@ -1557,10 +1642,34 @@ internal sealed class TypeCache // ,+*&*[]\ in the identifier portions of the names // have been escaped with a leading backslash (\) public string? full_name; - public bool default_ctor_cached; public RuntimeConstructorInfo? default_ctor; + public CorElementType CorElementType; + public TypeAttributes TypeAttributes; + // TypeCacheEntries, accessed using CAS + public int Flags; + // TypeCacheEntries, accessed using CAS + public int Cached; } + private void UpdateCached(TypeCacheEntries entry) + { + TypeCache cache = Cache; + int oldCached = cache.Cached; + int newCached = oldCached | (int)entry; + // This CAS will ensure ordering with the the store into the cache + // If this fails, we will just take the slowpath again + Interlocked.CompareExchange(ref cache.Cached, newCached, oldCached); + } + + private void CacheFlag(TypeCacheEntries flag, bool value) + { + TypeCache cache = Cache; + int oldFlags = cache.Flags; + int newFlags = value ? (oldFlags | (int)flag) : oldFlags; + // If this fails, we will just take the slowpath again + if (Interlocked.CompareExchange(ref cache.Flags, newFlags, oldFlags) == oldFlags) + UpdateCached(flag); + } internal RuntimeType(object obj) { @@ -1569,10 +1678,10 @@ internal RuntimeType(object obj) internal RuntimeConstructorInfo? GetDefaultConstructor() { - TypeCache? cache = Cache; + TypeCache cache = Cache; RuntimeConstructorInfo? ctor = null; - if (Volatile.Read(ref cache.default_ctor_cached)) + if ((cache.Cached & (int)TypeCacheEntries.DefaultCtor) != 0) return cache.default_ctor; ListBuilder ctors = GetConstructorCandidates( @@ -1582,9 +1691,10 @@ internal RuntimeType(object obj) if (ctors.Count == 1) cache.default_ctor = ctor = (RuntimeConstructorInfo)ctors[0]; + Interlocked.MemoryBarrier (); // Note down even if we found no constructors - Volatile.Write(ref cache.default_ctor_cached, true); + UpdateCached(TypeCacheEntries.DefaultCtor); return ctor; } diff --git a/src/mono/System.Private.CoreLib/src/System/RuntimeTypeHandle.cs b/src/mono/System.Private.CoreLib/src/System/RuntimeTypeHandle.cs index 4678097b3f8f..62245b8fd5aa 100644 --- a/src/mono/System.Private.CoreLib/src/System/RuntimeTypeHandle.cs +++ b/src/mono/System.Private.CoreLib/src/System/RuntimeTypeHandle.cs @@ -119,7 +119,7 @@ public override int GetHashCode() internal static TypeAttributes GetAttributes(RuntimeType type) { - return GetAttributes(new QCallTypeHandle(ref type)); + return type.GetAttributes(); } public ModuleHandle GetModuleHandle() @@ -223,7 +223,7 @@ internal static bool HasReferences(RuntimeType type) internal static CorElementType GetCorElementType(RuntimeType type) { - return GetCorElementType (new QCallTypeHandle(ref type)); + return type.GetCorElementType(); } internal static bool HasInstantiation(RuntimeType type)