|
// 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 Internal.IL;
using Internal.TypeSystem;
using FlowAnnotations = ILLink.Shared.TrimAnalysis.FlowAnnotations;
namespace ILCompiler
{
/// <summary>
/// Manages policies around static constructors (.cctors) and static data initialization.
/// </summary>
public class PreinitializationManager
{
private readonly bool _supportsLazyCctors;
public ReadOnlyFieldPolicy ReadOnlyFieldPolicy => _preinitHashTable._readOnlyPolicy;
public PreinitializationManager(TypeSystemContext context, CompilationModuleGroup compilationGroup, ILProvider ilprovider, TypePreinit.TypePreinitializationPolicy policy, ReadOnlyFieldPolicy readOnlyPolicy, FlowAnnotations flowAnnotations)
{
_supportsLazyCctors = context.SystemModule.GetType("System.Runtime.CompilerServices"u8, "ClassConstructorRunner"u8, throwIfNotFound: false) != null;
_preinitHashTable = new PreinitializationInfoHashtable(compilationGroup, ilprovider, policy, readOnlyPolicy, flowAnnotations);
}
/// <summary>
/// Returns true if '<paramref name="type"/>' has a lazily executed static constructor.
/// A lazy static constructor gets executed on first access to type's members.
/// </summary>
public bool HasLazyStaticConstructor(TypeDesc type)
{
if (!type.HasStaticConstructor)
return false;
// If the cctor runs eagerly at startup, it's not lazy
if (HasEagerConstructorAttribute(type))
return false;
// If the class library doesn't support lazy cctors, everything is preinitialized before Main
// either by interpretting the cctor at compile time, or by running the cctor eagerly at startup.
if (!_supportsLazyCctors)
return false;
// Would be odd to see a type with a cctor that is not MetadataType
Debug.Assert(type is MetadataType);
var mdType = (MetadataType)type;
// The cctor on the Module type is the module constructor. That's special.
if (mdType.IsModuleType)
return false;
// If we can't interpret the cctor at compile time, the cctor is lazy.
return !IsPreinitialized(mdType);
}
/// <summary>
/// Returns true if '<paramref name="type"/>' has a static constructor that is eagerly
/// executed at process startup time.
/// </summary>
public bool HasEagerStaticConstructor(TypeDesc type)
{
if (!type.HasStaticConstructor)
return false;
// Would be odd to see a type with a cctor that is not MetadataType
Debug.Assert(type is MetadataType);
var mdType = (MetadataType)type;
// If the type is preinitialized at compile time, that's not eager.
if (IsPreinitialized(mdType))
return false;
// If the type is marked as eager or classlib doesn't have a cctor runner, it's eager.
return HasEagerConstructorAttribute(type) || !_supportsLazyCctors;
}
private static bool HasEagerConstructorAttribute(TypeDesc type)
{
MetadataType mdType = type as MetadataType;
return mdType != null &&
mdType.HasCustomAttribute("System.Runtime.CompilerServices", "EagerStaticClassConstructionAttribute");
}
public bool IsPreinitialized(MetadataType type)
{
if (!type.HasStaticConstructor)
return false;
// The cctor on the Module type is the module constructor. That's special.
if (type.IsModuleType)
return false;
// Generic definitions cannot be preinitialized
if (type.IsGenericDefinition)
return false;
return GetPreinitializationInfo(type).IsPreinitialized;
}
public void LogStatistics(Logger logger)
{
if (_preinitHashTable._policy is TypePreinit.DisabledPreinitializationPolicy)
return;
int totalEligibleTypes = 0;
int totalPreinitializedTypes = 0;
if (logger.IsVerbose)
{
foreach (var item in LockFreeReaderHashtable<MetadataType, TypePreinit.PreinitializationInfo>.Enumerator.Get(_preinitHashTable))
{
// Canonical types are not actual types. They represent the pessimized version of all types that share the form.
if (item.Type.IsCanonicalSubtype(CanonicalFormKind.Any))
continue;
totalEligibleTypes++;
if (item.IsPreinitialized)
{
logger.LogMessage($"Preinitialized type '{item.Type}'");
totalPreinitializedTypes++;
}
else
{
logger.LogMessage($"Could not preinitialize '{item.Type}': {item.FailureReason}");
}
}
logger.LogMessage($"Preinitialized {totalPreinitializedTypes} types out of {totalEligibleTypes}.");
}
}
public TypePreinit.PreinitializationInfo GetPreinitializationInfo(MetadataType type)
{
return _preinitHashTable.GetOrCreateValue(type);
}
private sealed class PreinitializationInfoHashtable : LockFreeReaderHashtable<MetadataType, TypePreinit.PreinitializationInfo>
{
private readonly CompilationModuleGroup _compilationGroup;
private readonly ILProvider _ilProvider;
internal readonly TypePreinit.TypePreinitializationPolicy _policy;
internal readonly ReadOnlyFieldPolicy _readOnlyPolicy;
private readonly FlowAnnotations _flowAnnotations;
public PreinitializationInfoHashtable(CompilationModuleGroup compilationGroup, ILProvider ilProvider, TypePreinit.TypePreinitializationPolicy policy, ReadOnlyFieldPolicy readOnlyPolicy, FlowAnnotations flowAnnotations)
{
_compilationGroup = compilationGroup;
_ilProvider = ilProvider;
_policy = policy;
_readOnlyPolicy = readOnlyPolicy;
_flowAnnotations = flowAnnotations;
}
protected override bool CompareKeyToValue(MetadataType key, TypePreinit.PreinitializationInfo value) => key == value.Type;
protected override bool CompareValueToValue(TypePreinit.PreinitializationInfo value1, TypePreinit.PreinitializationInfo value2) => value1.Type == value2.Type;
protected override int GetKeyHashCode(MetadataType key) => key.GetHashCode();
protected override int GetValueHashCode(TypePreinit.PreinitializationInfo value) => value.Type.GetHashCode();
protected override TypePreinit.PreinitializationInfo CreateValueFromKey(MetadataType key)
{
var info = TypePreinit.ScanType(_compilationGroup, _ilProvider, _policy, _readOnlyPolicy, _flowAnnotations, key);
// We either successfully preinitialized or
// the type doesn't have a canonical form or
// the policy doesn't allow treating canonical forms of this type as preinitialized
Debug.Assert(info.IsPreinitialized ||
(key.ConvertToCanonForm(CanonicalFormKind.Specific) is DefType canonType && (key == canonType || !_policy.CanPreinitializeAllConcreteFormsForCanonForm(canonType))));
return info;
}
}
private PreinitializationInfoHashtable _preinitHashTable;
}
}
|