diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs index 65ed805b6b2bb..d8b41d831de6a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs @@ -1485,7 +1485,7 @@ private UseSiteInfo InitializeUseSiteDiagnostic(UseSiteInfo _lazyCustomAttributes; - private Tuple _lazyDocComment; - private CachedUseSiteInfo _lazyCachedUseSiteInfo = CachedUseSiteInfo.Uninitialized; - - private ObsoleteAttributeData _lazyObsoleteAttributeData = ObsoleteAttributeData.Uninitialized; +#nullable enable + private UncommonFields? _uncommonFields; +#nullable disable // CONSIDER: the parameters could be computed lazily (as in PEMethodSymbol). // CONSIDER: if the parameters were computed lazily, ParameterCount could be overridden to fall back on the signature (as in PEMethodSymbol). @@ -53,13 +51,16 @@ internal class PEPropertySymbol private struct PackedFlags { // Layout: - // |.........................|uu|rr|c|n|s| + // |....................c|o|d|uu|rr|c|n|s| // // s = special name flag. 1 bit // n = runtime special name flag. 1 bit // c = call methods directly flag. 1 bit // r = Required member. 2 bits (1 bit for value + 1 completion bit). // u = Unscoped ref. 2 bits (1 bit for value + 1 completion bit). + // d = Use site diagnostic flag. 1 bit + // o = Obsolete flag. 1 bit + // c = Custom attributes flag. 1 bit private const int IsSpecialNameFlag = 1 << 0; private const int IsRuntimeSpecialNameFlag = 1 << 1; private const int CallMethodsDirectlyFlag = 1 << 2; @@ -67,6 +68,9 @@ private struct PackedFlags private const int RequiredMemberCompletionBit = 1 << 5; private const int HasUnscopedRefAttribute = 1 << 6; private const int UnscopedRefCompletionBit = 1 << 7; + private const int IsUseSiteDiagnosticPopulatedBit = 1 << 8; + private const int IsObsoleteAttributePopulatedBit = 1 << 9; + private const int IsCustomAttributesPopulatedBit = 1 << 10; private int _bits; @@ -83,7 +87,7 @@ public void SetHasRequiredMemberAttribute(bool isRequired) ThreadSafeFlagOperations.Set(ref _bits, bitsToSet); } - public bool TryGetHasRequiredMemberAttribute(out bool hasRequiredMemberAttribute) + public readonly bool TryGetHasRequiredMemberAttribute(out bool hasRequiredMemberAttribute) { if ((_bits & RequiredMemberCompletionBit) != 0) { @@ -101,7 +105,7 @@ public void SetHasUnscopedRefAttribute(bool unscopedRef) ThreadSafeFlagOperations.Set(ref _bits, bitsToSet); } - public bool TryGetHasUnscopedRefAttribute(out bool hasUnscopedRefAttribute) + public readonly bool TryGetHasUnscopedRefAttribute(out bool hasUnscopedRefAttribute) { if ((_bits & UnscopedRefCompletionBit) != 0) { @@ -113,9 +117,38 @@ public bool TryGetHasUnscopedRefAttribute(out bool hasUnscopedRefAttribute) return false; } - public bool IsSpecialName => (_bits & IsSpecialNameFlag) != 0; - public bool IsRuntimeSpecialName => (_bits & IsRuntimeSpecialNameFlag) != 0; - public bool CallMethodsDirectly => (_bits & CallMethodsDirectlyFlag) != 0; + public readonly bool IsSpecialName => (_bits & IsSpecialNameFlag) != 0; + public readonly bool IsRuntimeSpecialName => (_bits & IsRuntimeSpecialNameFlag) != 0; + public readonly bool CallMethodsDirectly => (_bits & CallMethodsDirectlyFlag) != 0; + + public void SetUseSiteDiagnosticPopulated() + { + ThreadSafeFlagOperations.Set(ref _bits, IsUseSiteDiagnosticPopulatedBit); + } + + public readonly bool IsUseSiteDiagnosticPopulated => (_bits & IsUseSiteDiagnosticPopulatedBit) != 0; + + public void SetObsoleteAttributePopulated() + { + ThreadSafeFlagOperations.Set(ref _bits, IsObsoleteAttributePopulatedBit); + } + + public readonly bool IsObsoleteAttributePopulated => (_bits & IsObsoleteAttributePopulatedBit) != 0; + + public void SetCustomAttributesPopulated() + { + ThreadSafeFlagOperations.Set(ref _bits, IsCustomAttributesPopulatedBit); + } + + public readonly bool IsCustomAttributesPopulated => (_bits & IsCustomAttributesPopulatedBit) != 0; + } + + private sealed class UncommonFields + { + public ImmutableArray _lazyCustomAttributes; + public Tuple _lazyDocComment; + public CachedUseSiteInfo _lazyCachedUseSiteInfo = CachedUseSiteInfo.Uninitialized; + public ObsoleteAttributeData _lazyObsoleteAttributeData = ObsoleteAttributeData.Uninitialized; } internal static PEPropertySymbol Create( @@ -146,7 +179,8 @@ internal static PEPropertySymbol Create( if (propEx != null || isBad) { - result._lazyCachedUseSiteInfo.Initialize(new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, result)); + result.AccessUncommonFields()._lazyCachedUseSiteInfo.Initialize(new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, result)); + result._flags.SetUseSiteDiagnosticPopulated(); } return result; @@ -201,7 +235,8 @@ private PEPropertySymbol( if (getEx != null || setEx != null || mrEx != null || isBad) { - _lazyCachedUseSiteInfo.Initialize(new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this)); + AccessUncommonFields()._lazyCachedUseSiteInfo.Initialize(new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this)); + _flags.SetUseSiteDiagnosticPopulated(); } var returnInfo = propertyParams[0]; @@ -277,6 +312,33 @@ static bool anyUnexpectedRequiredModifiers(ParamInfo[] propertyParam } } + private UncommonFields CreateUncommonFields() + { + var retVal = new UncommonFields(); + if (!_flags.IsObsoleteAttributePopulated) + { + retVal._lazyObsoleteAttributeData = ObsoleteAttributeData.Uninitialized; + } + + if (!_flags.IsUseSiteDiagnosticPopulated) + { + retVal._lazyCachedUseSiteInfo = CachedUseSiteInfo.Uninitialized; + } + + if (_flags.IsCustomAttributesPopulated) + { + retVal._lazyCustomAttributes = ImmutableArray.Empty; + } + + return retVal; + } + + private UncommonFields AccessUncommonFields() + { + var retVal = _uncommonFields; + return retVal ?? InterlockedOperations.Initialize(ref _uncommonFields, CreateUncommonFields()); + } + private bool MustCallMethodsDirectlyCore() { if (this.RefKind != RefKind.None && _setMethod != null) @@ -635,7 +697,7 @@ public override ImmutableArray DeclaringSyntaxReferences public override ImmutableArray GetAttributes() { - if (_lazyCustomAttributes.IsDefault) + if (!_flags.IsCustomAttributesPopulated) { var containingPEModuleSymbol = (PEModuleSymbol)this.ContainingModule; @@ -646,10 +708,31 @@ public override ImmutableArray GetAttributes() out CustomAttributeHandle required, AttributeDescription.RequiredMemberAttribute); - ImmutableInterlocked.InterlockedInitialize(ref _lazyCustomAttributes, attributes); + if (!attributes.IsEmpty) + { + ImmutableInterlocked.InterlockedInitialize(ref AccessUncommonFields()._lazyCustomAttributes, attributes); + } + + _flags.SetCustomAttributesPopulated(); _flags.SetHasRequiredMemberAttribute(!required.IsNil); } - return _lazyCustomAttributes; + + var uncommonFields = _uncommonFields; + if (uncommonFields == null) + { + return ImmutableArray.Empty; + } + else + { + var result = uncommonFields._lazyCustomAttributes; + if (result.IsDefault) + { + result = ImmutableArray.Empty; + ImmutableInterlocked.InterlockedInitialize(ref uncommonFields._lazyCustomAttributes, result); + } + + return result; + } } internal override IEnumerable GetCustomAttributesToEmit(PEModuleBuilder moduleBuilder) @@ -814,24 +897,45 @@ private static ImmutableArray GetParameters( public override string GetDocumentationCommentXml(CultureInfo preferredCulture = null, bool expandIncludes = false, CancellationToken cancellationToken = default(CancellationToken)) { - return PEDocumentationCommentUtils.GetDocumentationComment(this, _containingType.ContainingPEModule, preferredCulture, cancellationToken, ref _lazyDocComment); + return PEDocumentationCommentUtils.GetDocumentationComment(this, _containingType.ContainingPEModule, preferredCulture, cancellationToken, ref AccessUncommonFields()._lazyDocComment); } internal override UseSiteInfo GetUseSiteInfo() { AssemblySymbol primaryDependency = PrimaryDependency; - if (!_lazyCachedUseSiteInfo.IsInitialized) + if (!_flags.IsUseSiteDiagnosticPopulated) { var result = new UseSiteInfo(primaryDependency); CalculateUseSiteDiagnostic(ref result); var diag = deriveCompilerFeatureRequiredUseSiteInfo(); MergeUseSiteDiagnostics(ref diag, result.DiagnosticInfo); result = result.AdjustDiagnosticInfo(diag); - _lazyCachedUseSiteInfo.Initialize(primaryDependency, result); + + if (result.DiagnosticInfo is not null || !result.SecondaryDependencies.IsNullOrEmpty()) + { + AccessUncommonFields()._lazyCachedUseSiteInfo.InterlockedInitializeFromSentinel(PrimaryDependency, result); + } + + _flags.SetUseSiteDiagnosticPopulated(); } - return _lazyCachedUseSiteInfo.ToUseSiteInfo(primaryDependency); + var uncommonFields = _uncommonFields; + if (uncommonFields == null) + { + return new UseSiteInfo(primaryDependency); + } + else + { + var result = uncommonFields._lazyCachedUseSiteInfo; + if (!result.IsInitialized) + { + uncommonFields._lazyCachedUseSiteInfo.InterlockedInitializeFromSentinel(primaryDependency, new UseSiteInfo(primaryDependency)); + result = uncommonFields._lazyCachedUseSiteInfo; + } + + return result.ToUseSiteInfo(primaryDependency); + } DiagnosticInfo deriveCompilerFeatureRequiredUseSiteInfo() { @@ -867,8 +971,30 @@ internal override ObsoleteAttributeData ObsoleteAttributeData { get { - ObsoleteAttributeHelpers.InitializeObsoleteDataFromMetadata(ref _lazyObsoleteAttributeData, _handle, (PEModuleSymbol)(this.ContainingModule), ignoreByRefLikeMarker: false, ignoreRequiredMemberMarker: false); - return _lazyObsoleteAttributeData; + if (!_flags.IsObsoleteAttributePopulated) + { + var result = ObsoleteAttributeHelpers.GetObsoleteDataFromMetadata(_handle, (PEModuleSymbol)(this.ContainingModule), ignoreByRefLikeMarker: false, ignoreRequiredMemberMarker: false); + if (result != null) + { + result = InterlockedOperations.Initialize(ref AccessUncommonFields()._lazyObsoleteAttributeData, result, ObsoleteAttributeData.Uninitialized); + } + + _flags.SetObsoleteAttributePopulated(); + return result; + } + + var uncommonFields = _uncommonFields; + if (uncommonFields == null) + { + return null; + } + else + { + var result = uncommonFields._lazyObsoleteAttributeData; + return ReferenceEquals(result, ObsoleteAttributeData.Uninitialized) + ? InterlockedOperations.Initialize(ref uncommonFields._lazyObsoleteAttributeData, initializedValue: null, ObsoleteAttributeData.Uninitialized) + : result; + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs index 0a0d718a4a27e..a584624035ce3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs @@ -236,7 +236,7 @@ private ImmutableArray GetDeclaredConstraintTypes(ConsList< { // we do not recognize these combinations as "unmanaged" hasUnmanagedModreqPattern = false; - _lazyCachedConstraintsUseSiteInfo.InterlockedCompareExchange(primaryDependency: null, new UseSiteInfo(new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this))); + _lazyCachedConstraintsUseSiteInfo.InterlockedInitializeFromSentinel(primaryDependency: null, new UseSiteInfo(new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this))); } _lazyHasIsUnmanagedConstraint = hasUnmanagedModreqPattern.ToThreeState(); @@ -416,7 +416,7 @@ private GenericParameterConstraintHandleCollection GetConstraintHandleCollection catch (BadImageFormatException) { constraints = default(GenericParameterConstraintHandleCollection); - _lazyCachedConstraintsUseSiteInfo.InterlockedCompareExchange(primaryDependency: null, new UseSiteInfo(new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this))); + _lazyCachedConstraintsUseSiteInfo.InterlockedInitializeFromSentinel(primaryDependency: null, new UseSiteInfo(new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this))); } return constraints; @@ -689,7 +689,7 @@ private TypeParameterBounds GetBounds(ConsList inProgress) diagnostics.Free(); - _lazyCachedConstraintsUseSiteInfo.InterlockedCompareExchange(primaryDependency, useSiteInfo); + _lazyCachedConstraintsUseSiteInfo.InterlockedInitializeFromSentinel(primaryDependency, useSiteInfo); Interlocked.CompareExchange(ref _lazyBounds, bounds, TypeParameterBounds.Unset); } diff --git a/src/Compilers/Core/Portable/Binding/UseSiteInfo.cs b/src/Compilers/Core/Portable/Binding/UseSiteInfo.cs index 44c85707f8ad1..5b4d4e0667415 100644 --- a/src/Compilers/Core/Portable/Binding/UseSiteInfo.cs +++ b/src/Compilers/Core/Portable/Binding/UseSiteInfo.cs @@ -517,7 +517,7 @@ private void Initialize(DiagnosticInfo? diagnosticInfo, ImmutableHashSet value) + public void InterlockedInitializeFromSentinel(TAssemblySymbol? primaryDependency, UseSiteInfo value) { if ((object?)_info == Sentinel) { @@ -526,7 +526,11 @@ public void InterlockedCompareExchange(TAssemblySymbol? primaryDependency, UseSi } } - public UseSiteInfo InterlockedInitialize(TAssemblySymbol? primaryDependency, UseSiteInfo value) + /// + /// Atomically initializes the cache with the given value if it is currently fully default. + /// This will not initialize . + /// + public UseSiteInfo InterlockedInitializeFromDefault(TAssemblySymbol? primaryDependency, UseSiteInfo value) { object? info = Compact(value.DiagnosticInfo, GetDependenciesToCache(primaryDependency, value)); Debug.Assert(info is object); diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEMethodSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEMethodSymbol.vb index 7c18149e0aeee..ab330becff378 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEMethodSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEMethodSymbol.vb @@ -1251,7 +1251,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE End If If useSiteInfo.DiagnosticInfo IsNot Nothing OrElse Not useSiteInfo.SecondaryDependencies.IsNullOrEmpty() Then - useSiteInfo = AccessUncommonFields()._lazyCachedUseSiteInfo.InterlockedInitialize(PrimaryDependency, useSiteInfo) + useSiteInfo = AccessUncommonFields()._lazyCachedUseSiteInfo.InterlockedInitializeFromDefault(PrimaryDependency, useSiteInfo) End If _packedFlags.SetIsUseSiteDiagnosticPopulated() diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.vb index fa89f737d2d97..6b033ecd2b07c 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.vb @@ -182,7 +182,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE constraints = metadataReader.GetGenericParameter(_handle).GetConstraints() Catch mrEx As BadImageFormatException constraints = Nothing - _lazyCachedBoundsUseSiteInfo.InterlockedCompareExchange(primaryDependency:=Nothing, New UseSiteInfo(Of AssemblySymbol)(ErrorFactory.ErrorInfo(ERRID.ERR_UnsupportedType1, Me))) + _lazyCachedBoundsUseSiteInfo.InterlockedInitializeFromSentinel(primaryDependency:=Nothing, New UseSiteInfo(Of AssemblySymbol)(ErrorFactory.ErrorInfo(ERRID.ERR_UnsupportedType1, Me))) End Try If constraints.Count > 0 Then @@ -298,7 +298,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE diagnosticsBuilder.Free() - _lazyCachedBoundsUseSiteInfo.InterlockedCompareExchange(primaryDependency, useSiteInfo) + _lazyCachedBoundsUseSiteInfo.InterlockedInitializeFromSentinel(primaryDependency, useSiteInfo) ImmutableInterlocked.InterlockedInitialize(_lazyConstraintTypes, GetConstraintTypesOnly(constraints)) End If