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

Reduce closure and Lazy allocations inside CSharpCompilation.ctor #72371

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -309,7 +309,7 @@ private void BinaryOperatorEasyOut(BinaryOperatorKind kind, BoundExpression left
return;
}

BinaryOperatorSignature signature = this.Compilation.builtInOperators.GetSignature(easyOut);
BinaryOperatorSignature signature = this.Compilation.BuiltInOperators.GetSignature(easyOut);

Conversion leftConversion = Conversions.FastClassifyConversion(leftType, signature.LeftType);
Conversion rightConversion = Conversions.FastClassifyConversion(rightType, signature.RightType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -719,7 +719,7 @@ private void GetAllBuiltInOperators(BinaryOperatorKind kind, bool isChecked, Bou
}
else
{
this.Compilation.builtInOperators.GetSimpleBuiltInOperators(kind, operators, skipNativeIntegerOperators: !left.Type.IsNativeIntegerOrNullableThereof() && !right.Type.IsNativeIntegerOrNullableThereof());
this.Compilation.BuiltInOperators.GetSimpleBuiltInOperators(kind, operators, skipNativeIntegerOperators: !left.Type.IsNativeIntegerOrNullableThereof() && !right.Type.IsNativeIntegerOrNullableThereof());

// SPEC 7.3.4: For predefined enum and delegate operators, the only operators
// considered are those defined by an enum or delegate type that is the binding
Expand All @@ -734,7 +734,7 @@ private void GetAllBuiltInOperators(BinaryOperatorKind kind, bool isChecked, Bou
isUtf8ByteRepresentation(left) &&
isUtf8ByteRepresentation(right))
{
this.Compilation.builtInOperators.GetUtf8ConcatenationBuiltInOperator(left.Type, operators);
this.Compilation.BuiltInOperators.GetUtf8ConcatenationBuiltInOperator(left.Type, operators);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ private void UnaryOperatorEasyOut(UnaryOperatorKind kind, BoundExpression operan
return;
}

UnaryOperatorSignature signature = this.Compilation.builtInOperators.GetSignature(easyOut);
UnaryOperatorSignature signature = this.Compilation.BuiltInOperators.GetSignature(easyOut);

Conversion? conversion = Conversions.FastClassifyConversion(operandType, signature.OperandType);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ private void GetAllBuiltInOperators(UnaryOperatorKind kind, bool isChecked, Boun
// specification to match the previous implementation.

var operators = ArrayBuilder<UnaryOperatorSignature>.GetInstance();
this.Compilation.builtInOperators.GetSimpleBuiltInOperators(kind, operators, skipNativeIntegerOperators: !operand.Type.IsNativeIntegerOrNullableThereof());
this.Compilation.BuiltInOperators.GetSimpleBuiltInOperators(kind, operators, skipNativeIntegerOperators: !operand.Type.IsNativeIntegerOrNullableThereof());

GetEnumOperations(kind, operand, operators);

Expand Down
73 changes: 47 additions & 26 deletions src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,12 @@ public sealed partial class CSharpCompilation : Compilation
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

private readonly CSharpCompilationOptions _options;
private readonly Lazy<UsingsFromOptionsAndDiagnostics> _usingsFromOptions;
private readonly Lazy<ImmutableArray<NamespaceOrTypeAndUsingDirective>> _globalImports;
private readonly Lazy<Imports> _previousSubmissionImports;
private readonly Lazy<AliasSymbol> _globalNamespaceAlias; // alias symbol used to resolve "global::".
private readonly Lazy<ImplicitNamedTypeSymbol?> _scriptClass;
private UsingsFromOptionsAndDiagnostics? _lazyUsingsFromOptions;
private ImmutableArray<NamespaceOrTypeAndUsingDirective> _lazyGlobalImports;
private Imports? _lazyPreviousSubmissionImports;
private AliasSymbol? _lazyGlobalNamespaceAlias; // alias symbol used to resolve "global::".

private NamedTypeSymbol? _lazyScriptClass = ErrorTypeSymbol.UnknownResultType;

// The type of host object model if available.
private TypeSymbol? _lazyHostObjectTypeSymbol;
Expand Down Expand Up @@ -92,11 +93,11 @@ internal Conversions Conversions
/// <summary>
/// Manages anonymous types declared in this compilation. Unifies types that are structurally equivalent.
/// </summary>
private readonly AnonymousTypeManager _anonymousTypeManager;
private AnonymousTypeManager? _lazyAnonymousTypeManager;

private NamespaceSymbol? _lazyGlobalNamespace;

internal readonly BuiltInOperators builtInOperators;
private BuiltInOperators? _lazyBuiltInOperators;

/// <summary>
/// The <see cref="SourceAssemblySymbol"/> for this compilation. Do not access directly, use Assembly property
Expand Down Expand Up @@ -152,7 +153,7 @@ internal Conversions Conversions
/// <summary>
/// Cache of T to Nullable&lt;T&gt;.
/// </summary>
private readonly ConcurrentCache<TypeSymbol, NamedTypeSymbol> _typeToNullableVersion = new ConcurrentCache<TypeSymbol, NamedTypeSymbol>(size: 100);
private ConcurrentCache<TypeSymbol, NamedTypeSymbol>? _lazyTypeToNullableVersion;

/// <summary>Lazily caches SyntaxTrees by their mapped path. Used to look up the syntax tree referenced by an interceptor (temporary compat behavior).</summary>
/// <remarks>Must be removed prior to interceptors stable release.</remarks>
Expand Down Expand Up @@ -188,11 +189,19 @@ public override bool IsCaseSensitive
}
}

internal BuiltInOperators BuiltInOperators
{
get
{
return InterlockedOperations.Initialize(ref _lazyBuiltInOperators, static self => new BuiltInOperators(self), this);
}
}

internal AnonymousTypeManager AnonymousTypeManager
{
get
ToddGrun marked this conversation as resolved.
Show resolved Hide resolved
{
return _anonymousTypeManager;
return InterlockedOperations.Initialize(ref _lazyAnonymousTypeManager, static self => new AnonymousTypeManager(self), this);
}
}

Expand Down Expand Up @@ -469,16 +478,8 @@ private CSharpCompilation(
AsyncQueue<CompilationEvent>? eventQueue = null)
: base(assemblyName, references, features, isSubmission, semanticModelProvider, eventQueue)
{
WellKnownMemberSignatureComparer = new WellKnownMembersSignatureComparer(this);
_options = options;

this.builtInOperators = new BuiltInOperators(this);
_scriptClass = new Lazy<ImplicitNamedTypeSymbol?>(BindScriptClass);
_globalImports = new Lazy<ImmutableArray<NamespaceOrTypeAndUsingDirective>>(BindGlobalImports);
_usingsFromOptions = new Lazy<UsingsFromOptionsAndDiagnostics>(BindUsingsFromOptions);
_previousSubmissionImports = new Lazy<Imports>(ExpandPreviousSubmissionImports);
_globalNamespaceAlias = new Lazy<AliasSymbol>(CreateGlobalNamespaceAlias);
_anonymousTypeManager = new AnonymousTypeManager(this);
this.LanguageVersion = CommonLanguageVersion(syntaxAndDeclarations.ExternalSyntaxTrees);

if (isSubmission)
Expand Down Expand Up @@ -1503,7 +1504,15 @@ internal bool GetExternAliasTarget(string aliasName, out NamespaceSymbol @namesp
/// </summary>
internal new NamedTypeSymbol? ScriptClass
{
get { return _scriptClass.Value; }
ToddGrun marked this conversation as resolved.
Show resolved Hide resolved
get
{
if (ReferenceEquals(_lazyScriptClass, ErrorTypeSymbol.UnknownResultType))
{
Interlocked.CompareExchange(ref _lazyScriptClass, BindScriptClass()!, ErrorTypeSymbol.UnknownResultType);
}

return _lazyScriptClass;
}
}

/// <summary>
Expand All @@ -1526,7 +1535,8 @@ internal bool IsSubmissionSyntaxTree(SyntaxTree tree)
/// <summary>
/// Global imports (including those from previous submissions, if there are any).
/// </summary>
internal ImmutableArray<NamespaceOrTypeAndUsingDirective> GlobalImports => _globalImports.Value;
internal ImmutableArray<NamespaceOrTypeAndUsingDirective> GlobalImports
=> InterlockedOperations.Initialize(ref _lazyGlobalImports, static self => self.BindGlobalImports(), arg: this);

private ImmutableArray<NamespaceOrTypeAndUsingDirective> BindGlobalImports()
{
Expand Down Expand Up @@ -1565,7 +1575,8 @@ private ImmutableArray<NamespaceOrTypeAndUsingDirective> BindGlobalImports()
/// <summary>
/// Global imports not including those from previous submissions.
/// </summary>
private UsingsFromOptionsAndDiagnostics UsingsFromOptions => _usingsFromOptions.Value;
private UsingsFromOptionsAndDiagnostics UsingsFromOptions
=> InterlockedOperations.Initialize(ref _lazyUsingsFromOptions, static self => self.BindUsingsFromOptions(), this);

private UsingsFromOptionsAndDiagnostics BindUsingsFromOptions() => UsingsFromOptionsAndDiagnostics.FromOptions(this);

Expand All @@ -1590,7 +1601,8 @@ internal Imports GetSubmissionImports()
/// <summary>
/// Imports from all previous submissions.
/// </summary>
internal Imports GetPreviousSubmissionImports() => _previousSubmissionImports.Value;
internal Imports GetPreviousSubmissionImports()
=> InterlockedOperations.Initialize(ref _lazyPreviousSubmissionImports, static self => self.ExpandPreviousSubmissionImports(), this);

private Imports ExpandPreviousSubmissionImports()
{
Expand All @@ -1610,7 +1622,7 @@ internal AliasSymbol GlobalNamespaceAlias
{
get
{
return _globalNamespaceAlias.Value;
return InterlockedOperations.Initialize(ref _lazyGlobalNamespaceAlias, static self => self.CreateGlobalNamespaceAlias(), this);
}
}

Expand Down Expand Up @@ -1639,6 +1651,14 @@ internal AliasSymbol GlobalNamespaceAlias
return result;
}

private ConcurrentCache<TypeSymbol, NamedTypeSymbol> TypeToNullableVersion
{
get
{
return InterlockedOperations.Initialize(ref _lazyTypeToNullableVersion, static () => new ConcurrentCache<TypeSymbol, NamedTypeSymbol>(size: 100));
}
}

/// <summary>
/// Given a provided <paramref name="typeArgument"/>, gives back <see cref="Nullable{T}"/> constructed with that
/// argument. This function is only intended to be used for very common instantiations produced heavily during
Expand All @@ -1653,10 +1673,11 @@ internal NamedTypeSymbol GetOrCreateNullableType(TypeSymbol typeArgument)
Debug.Fail($"Unsupported type argument: {typeArgument.ToDisplayString()}");
#endif

if (!_typeToNullableVersion.TryGetValue(typeArgument, out var constructedNullableInstance))
var typeToNullableVersion = TypeToNullableVersion;
if (!typeToNullableVersion.TryGetValue(typeArgument, out var constructedNullableInstance))
{
constructedNullableInstance = this.GetSpecialType(SpecialType.System_Nullable_T).Construct(typeArgument);
_typeToNullableVersion.TryAdd(typeArgument, constructedNullableInstance);
typeToNullableVersion.TryAdd(typeArgument, constructedNullableInstance);
}

return constructedNullableInstance;
Expand Down Expand Up @@ -4198,7 +4219,7 @@ csharpLeftType.TypeKind is TypeKind.Dynamic ||

if (easyOutBinaryKind != BinaryOperatorKind.Error)
{
var signature = this.builtInOperators.GetSignature(easyOutBinaryKind);
var signature = this.BuiltInOperators.GetSignature(easyOutBinaryKind);
if (csharpReturnType.SpecialType == signature.ReturnType.SpecialType &&
csharpLeftType.SpecialType == signature.LeftType.SpecialType &&
csharpRightType.SpecialType == signature.RightType.SpecialType)
Expand Down Expand Up @@ -4421,7 +4442,7 @@ void validateSignature()

if (easyOutUnaryKind != UnaryOperatorKind.Error)
{
var signature = this.builtInOperators.GetSignature(easyOutUnaryKind);
var signature = this.BuiltInOperators.GetSignature(easyOutUnaryKind);
if (csharpReturnType.SpecialType == signature.ReturnType.SpecialType &&
csharpOperandType.SpecialType == signature.OperandType.SpecialType)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.CSharp
{
public partial class CSharpCompilation
{
internal readonly WellKnownMembersSignatureComparer WellKnownMemberSignatureComparer;
private WellKnownMembersSignatureComparer? _lazyWellKnownMemberSignatureComparer;

/// <summary>
/// An array of cached well known types available for use in this Compilation.
Expand All @@ -35,6 +35,9 @@ public partial class CSharpCompilation
private int _needsGeneratedAttributes;
private bool _needsGeneratedAttributes_IsFrozen;

internal WellKnownMembersSignatureComparer WellKnownMemberSignatureComparer
=> InterlockedOperations.Initialize(ref _lazyWellKnownMemberSignatureComparer, static self => new WellKnownMembersSignatureComparer(self), this);

/// <summary>
/// Returns a value indicating which embedded attributes should be generated during emit phase.
/// The value is set during binding the symbols that need those attributes, and is frozen on first trial to get it.
Expand Down
4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Test/Emit2/Emit/NumericIntPtrTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1537,7 +1537,7 @@ static void verifyOperators(CSharpCompilation comp)
static void verifyUnaryOperators(CSharpCompilation comp, UnaryOperatorKind operatorKind, bool skipNativeIntegerOperators)
{
var builder = ArrayBuilder<UnaryOperatorSignature>.GetInstance();
comp.builtInOperators.GetSimpleBuiltInOperators(operatorKind, builder, skipNativeIntegerOperators);
comp.BuiltInOperators.GetSimpleBuiltInOperators(operatorKind, builder, skipNativeIntegerOperators);
var operators = builder.ToImmutableAndFree();
int expectedSigned = skipNativeIntegerOperators ? 0 : 1;
int expectedUnsigned = skipNativeIntegerOperators ? 0 : (operatorKind == UnaryOperatorKind.UnaryMinus) ? 0 : 1;
Expand All @@ -1548,7 +1548,7 @@ static void verifyUnaryOperators(CSharpCompilation comp, UnaryOperatorKind opera
static void verifyBinaryOperators(CSharpCompilation comp, BinaryOperatorKind operatorKind, bool skipNativeIntegerOperators)
{
var builder = ArrayBuilder<BinaryOperatorSignature>.GetInstance();
comp.builtInOperators.GetSimpleBuiltInOperators(operatorKind, builder, skipNativeIntegerOperators);
comp.BuiltInOperators.GetSimpleBuiltInOperators(operatorKind, builder, skipNativeIntegerOperators);
var operators = builder.ToImmutableAndFree();
int expected = skipNativeIntegerOperators ? 0 : 1;
verifyOperators(operators, (op, signed) => isNativeInt(op.LeftType, signed), expected, expected);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4789,7 +4789,7 @@ static void verifyOperators(CSharpCompilation comp)
static void verifyUnaryOperators(CSharpCompilation comp, UnaryOperatorKind operatorKind, bool skipNativeIntegerOperators)
{
var builder = ArrayBuilder<UnaryOperatorSignature>.GetInstance();
comp.builtInOperators.GetSimpleBuiltInOperators(operatorKind, builder, skipNativeIntegerOperators);
comp.BuiltInOperators.GetSimpleBuiltInOperators(operatorKind, builder, skipNativeIntegerOperators);
var operators = builder.ToImmutableAndFree();
int expectedSigned = skipNativeIntegerOperators ? 0 : 1;
int expectedUnsigned = skipNativeIntegerOperators ? 0 : (operatorKind == UnaryOperatorKind.UnaryMinus) ? 0 : 1;
Expand All @@ -4800,7 +4800,7 @@ static void verifyUnaryOperators(CSharpCompilation comp, UnaryOperatorKind opera
static void verifyBinaryOperators(CSharpCompilation comp, BinaryOperatorKind operatorKind, bool skipNativeIntegerOperators)
{
var builder = ArrayBuilder<BinaryOperatorSignature>.GetInstance();
comp.builtInOperators.GetSimpleBuiltInOperators(operatorKind, builder, skipNativeIntegerOperators);
comp.BuiltInOperators.GetSimpleBuiltInOperators(operatorKind, builder, skipNativeIntegerOperators);
var operators = builder.ToImmutableAndFree();
int expected = skipNativeIntegerOperators ? 0 : 1;
verifyOperators(operators, (op, signed) => isNativeInt(op.LeftType, signed), expected, expected);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7593,7 +7593,7 @@ ExpressionSyntax node4
}
else
{
signature = compilation.builtInOperators.GetSignature(result);
signature = compilation.BuiltInOperators.GetSignature(result);

if ((object)underlying != (object)type)
{
Expand Down Expand Up @@ -8232,14 +8232,14 @@ ExpressionSyntax node8
else if ((op == BinaryOperatorKind.Addition || op == BinaryOperatorKind.Subtraction) &&
leftType.IsEnumType() && (rightType.IsIntegralType() || rightType.IsCharType()) &&
(result = OverloadResolution.BinopEasyOut.OpKind(op, leftType.EnumUnderlyingTypeOrSelf(), rightType)) != BinaryOperatorKind.Error &&
TypeSymbol.Equals((signature = compilation.builtInOperators.GetSignature(result)).RightType, leftType.EnumUnderlyingTypeOrSelf(), TypeCompareKind.ConsiderEverything2))
TypeSymbol.Equals((signature = compilation.BuiltInOperators.GetSignature(result)).RightType, leftType.EnumUnderlyingTypeOrSelf(), TypeCompareKind.ConsiderEverything2))
{
signature = new BinaryOperatorSignature(signature.Kind | BinaryOperatorKind.EnumAndUnderlying, leftType, signature.RightType, leftType);
}
else if ((op == BinaryOperatorKind.Addition || op == BinaryOperatorKind.Subtraction) &&
rightType.IsEnumType() && (leftType.IsIntegralType() || leftType.IsCharType()) &&
(result = OverloadResolution.BinopEasyOut.OpKind(op, leftType, rightType.EnumUnderlyingTypeOrSelf())) != BinaryOperatorKind.Error &&
TypeSymbol.Equals((signature = compilation.builtInOperators.GetSignature(result)).LeftType, rightType.EnumUnderlyingTypeOrSelf(), TypeCompareKind.ConsiderEverything2))
TypeSymbol.Equals((signature = compilation.BuiltInOperators.GetSignature(result)).LeftType, rightType.EnumUnderlyingTypeOrSelf(), TypeCompareKind.ConsiderEverything2))
{
signature = new BinaryOperatorSignature(signature.Kind | BinaryOperatorKind.EnumAndUnderlying, signature.LeftType, rightType, rightType);
}
Expand Down Expand Up @@ -8355,7 +8355,7 @@ ExpressionSyntax node8
}
else
{
signature = compilation.builtInOperators.GetSignature(result);
signature = compilation.BuiltInOperators.GetSignature(result);
}

Assert.NotNull(symbol1);
Expand Down