File: Compiler\CompilerTypeSystemContext.Aot.cs
Web Access
Project: src\src\runtime\src\coreclr\tools\aot\ILCompiler.Compiler\ILCompiler.Compiler.csproj (ILCompiler.Compiler)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Runtime.CompilerServices;

using Internal.TypeSystem;
using Internal.IL;

using Interlocked = System.Threading.Interlocked;

namespace ILCompiler
{
    public partial class CompilerTypeSystemContext
    {
        // Chosen rather arbitrarily. For the app that I was looking at, cutoff point of 7 compiled
        // more than 10 minutes on a release build of the compiler, and I lost patience.
        // Cutoff point of 5 produced an 1.7 GB object file.
        // Cutoff point of 4 produced an 830 MB object file.
        // Cutoff point of 3 produced an 470 MB object file.
        // We want this to be high enough so that it doesn't cut off too early. But also not too
        // high because things that are recursive often end up expanding laterally as well
        // through various other generic code the deep code calls into.
        public const int DefaultGenericCycleDepthCutoff = 4;

        public const int DefaultGenericCycleBreadthCutoff = 10;

        public SharedGenericsConfiguration GenericsConfig
        {
            get;
        }

        private readonly MetadataFieldLayoutAlgorithm _metadataFieldLayoutAlgorithm = new CompilerMetadataFieldLayoutAlgorithm();
        private readonly RuntimeDeterminedFieldLayoutAlgorithm _runtimeDeterminedFieldLayoutAlgorithm = new RuntimeDeterminedFieldLayoutAlgorithm();
        private readonly VectorOfTFieldLayoutAlgorithm _vectorOfTFieldLayoutAlgorithm;
        private readonly VectorFieldLayoutAlgorithm _vectorFieldLayoutAlgorithm;
        private readonly Int128FieldLayoutAlgorithm _int128FieldLayoutAlgorithm;
        private readonly TypeWithRepeatedFieldsFieldLayoutAlgorithm _typeWithRepeatedFieldsFieldLayoutAlgorithm;

        private TypeDesc[] _arrayOfTInterfaces;
        private TypeDesc[] _arrayEnumeratorOfTInterfaces;
        private ArrayOfTRuntimeInterfacesAlgorithm _arrayOfTRuntimeInterfacesAlgorithm;
        private MetadataType _arrayOfTType;
        private MetadataType _arrayOfTEnumeratorType;
        private MetadataType _attributeType;

        public CompilerTypeSystemContext(TargetDetails details, SharedGenericsMode genericsMode, DelegateFeature delegateFeatures,
            int genericCycleDepthCutoff = DefaultGenericCycleDepthCutoff,
            int genericCycleBreadthCutoff = DefaultGenericCycleBreadthCutoff)
            : base(details)
        {
            _genericsMode = genericsMode;

            _virtualMethodAlgorithm = new AsyncAwareVirtualMethodResolutionAlgorithm(this);

            _vectorOfTFieldLayoutAlgorithm = new VectorOfTFieldLayoutAlgorithm(_metadataFieldLayoutAlgorithm);
            _vectorFieldLayoutAlgorithm = new VectorFieldLayoutAlgorithm(_metadataFieldLayoutAlgorithm);
            _int128FieldLayoutAlgorithm = new Int128FieldLayoutAlgorithm(_metadataFieldLayoutAlgorithm);
            _typeWithRepeatedFieldsFieldLayoutAlgorithm = new TypeWithRepeatedFieldsFieldLayoutAlgorithm(_metadataFieldLayoutAlgorithm);

            _delegateInfoHashtable = new DelegateInfoHashtable(delegateFeatures);
            _continuationTypeHashtable = new ContinuationTypeHashtable(this);

            _genericCycleDetector = new LazyGenericsSupport.GenericCycleDetector(genericCycleDepthCutoff, genericCycleBreadthCutoff);

            GenericsConfig = new SharedGenericsConfiguration();
        }

        public MetadataType ArrayOfTEnumeratorType
        {
            get
            {
                // This type is optional, but it's fine for this cache to be ineffective if that happens.
                // Those scenarios are rare and typically deal with small compilations.
                return _arrayOfTEnumeratorType ??= SystemModule.GetType("System"u8, "SZGenericArrayEnumerator`1"u8, throwIfNotFound: false);
            }
        }

        public bool IsArrayVariantCastable(TypeDesc type)
        {
            // Arrays and array enumerators have weird casting rules due to array covariance
            // (string[] castable to object[], or int[] castable to uint[], or int[] castable
            // to IList<SomeIntEnum>).

            if (type.IsSzArray
                || type.GetTypeDefinition() == ArrayOfTEnumeratorType
                || IsGenericArrayInterfaceType(type)
                || IsGenericArrayEnumeratorInterfaceType(type))
            {
                TypeDesc elementType = type.IsSzArray ? ((ArrayType)type).ElementType : type.Instantiation[0];
                if (CastingHelper.IsArrayElementTypeCastableBySize(elementType) ||
                    (elementType.IsDefType && !elementType.IsValueType))
                {
                    return true;
                }
            }

            return false;
        }

        protected override RuntimeInterfacesAlgorithm GetRuntimeInterfacesAlgorithmForNonPointerArrayType(ArrayType type)
        {
            _arrayOfTRuntimeInterfacesAlgorithm ??= new ArrayOfTRuntimeInterfacesAlgorithm(SystemModule.GetKnownType("System"u8, "Array`1"u8));
            return _arrayOfTRuntimeInterfacesAlgorithm;
        }

        public override FieldLayoutAlgorithm GetLayoutAlgorithmForType(DefType type)
        {
            if (type.IsRuntimeDeterminedType)
                return _runtimeDeterminedFieldLayoutAlgorithm;
            else if (VectorOfTFieldLayoutAlgorithm.IsVectorOfTType(type))
                return _vectorOfTFieldLayoutAlgorithm;
            else if (VectorFieldLayoutAlgorithm.IsVectorType(type))
                return _vectorFieldLayoutAlgorithm;
            else if (Int128FieldLayoutAlgorithm.IsIntegerType(type))
                return _int128FieldLayoutAlgorithm;
            else if (type is TypeWithRepeatedFields)
                return _typeWithRepeatedFieldsFieldLayoutAlgorithm;
            else
                return _metadataFieldLayoutAlgorithm;
        }

        protected override bool ComputeHasGCStaticBase(FieldDesc field)
        {
            Debug.Assert(field.IsStatic);

            if (field.IsThreadStatic)
                return true;

            TypeDesc fieldType = field.FieldType;
            if (fieldType.IsValueType)
                return ((DefType)fieldType).ContainsGCPointers;
            else
                return fieldType.IsGCPointer;
        }

        /// <summary>
        /// Returns true if <paramref name="type"/> is a generic interface type implemented by arrays.
        /// </summary>
        public bool IsGenericArrayInterfaceType(TypeDesc type)
        {
            // Hardcode the fact that all generic interfaces on array types have arity 1
            if (!type.IsInterface || type.Instantiation.Length != 1)
                return false;

            if (_arrayOfTInterfaces == null)
            {
                DefType[] implementedInterfaces = SystemModule.GetKnownType("System"u8, "Array`1"u8).ExplicitlyImplementedInterfaces;
                TypeDesc[] interfaceDefinitions = new TypeDesc[implementedInterfaces.Length];
                for (int i = 0; i < interfaceDefinitions.Length; i++)
                    interfaceDefinitions[i] = implementedInterfaces[i].GetTypeDefinition();
                Interlocked.CompareExchange(ref _arrayOfTInterfaces, interfaceDefinitions, null);
            }

            TypeDesc interfaceDefinition = type.GetTypeDefinition();
            foreach (var arrayInterfaceDefinition in _arrayOfTInterfaces)
            {
                if (interfaceDefinition == arrayInterfaceDefinition)
                    return true;
            }

            return false;
        }

        public bool IsGenericArrayEnumeratorInterfaceType(TypeDesc type)
        {
            // Hardcode the fact that all generic interfaces on array enumerator have arity 1
            if (!type.IsInterface || type.Instantiation.Length != 1)
                return false;

            if (_arrayEnumeratorOfTInterfaces == null)
            {
                DefType[] implementedInterfaces = ArrayOfTEnumeratorType?.ExplicitlyImplementedInterfaces;

                ArrayBuilder<TypeDesc> definitions = default;
                if (implementedInterfaces != null)
                {
                    foreach (DefType interfaceType in implementedInterfaces)
                    {
                        if (interfaceType.HasInstantiation)
                            definitions.Add(interfaceType.GetTypeDefinition());
                    }
                }

                Interlocked.CompareExchange(ref _arrayEnumeratorOfTInterfaces, definitions.ToArray(), null);
            }

            TypeDesc interfaceDefinition = type.GetTypeDefinition();
            return Array.IndexOf(_arrayEnumeratorOfTInterfaces, interfaceDefinition) >= 0;
        }

        protected override IEnumerable<MethodDesc> GetAllMethods(TypeDesc type)
        {
            return GetAllMethods(type, virtualOnly: false);
        }

        protected override IEnumerable<MethodDesc> GetAllVirtualMethods(TypeDesc type)
        {
            return GetAllMethods(type, virtualOnly: true);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private IEnumerable<MethodDesc> GetAllMethods(TypeDesc type, bool virtualOnly)
        {
            MetadataType attributeType = _attributeType ??= SystemModule.GetType("System"u8, "Attribute"u8);

            if (type.IsDelegate)
            {
                return GetAllMethodsForDelegate(type, virtualOnly);
            }
            else if (type.IsValueType)
            {
                return GetAllMethodsForValueType(type, virtualOnly);
            }
            else if (type.CanCastTo(attributeType))
            {
                return GetAllMethodsForAttribute(type, virtualOnly);
            }

            return virtualOnly ? type.GetVirtualMethods() : type.GetMethods();
        }

        protected virtual IEnumerable<MethodDesc> GetAllMethodsForDelegate(TypeDesc type, bool virtualOnly)
        {
            // Inject the synthetic methods that support the implementation of the delegate.
            InstantiatedType instantiatedType = type as InstantiatedType;
            if (instantiatedType != null)
            {
                DelegateInfo info = GetDelegateInfo(type.GetTypeDefinition());
                foreach (MethodDesc syntheticMethod in info.Methods)
                {
                    if (!virtualOnly || syntheticMethod.IsVirtual)
                        yield return GetMethodForInstantiatedType(syntheticMethod, instantiatedType);
                }
            }
            else
            {
                DelegateInfo info = GetDelegateInfo(type);
                foreach (MethodDesc syntheticMethod in info.Methods)
                {
                    if (!virtualOnly || syntheticMethod.IsVirtual)
                        yield return syntheticMethod;
                }
            }

            // Append all the methods defined in metadata
            IEnumerable<MethodDesc> metadataMethods = virtualOnly ? type.GetVirtualMethods() : type.GetMethods();
            foreach (var m in metadataMethods)
                yield return m;
        }

        internal DefType GetClosestDefType(TypeDesc type)
        {
            if (type.IsArray)
            {
                if (!type.IsArrayTypeWithoutGenericInterfaces())
                {
                    MetadataType arrayShadowType = _arrayOfTType ??= SystemModule.GetType("System"u8, "Array`1"u8);
                    return arrayShadowType.MakeInstantiatedType(((ArrayType)type).ElementType);
                }

                return GetWellKnownType(WellKnownType.Array);
            }

            Debug.Assert(type is DefType);
            return (DefType)type;
        }

        private readonly LazyGenericsSupport.GenericCycleDetector _genericCycleDetector;

        public void DetectGenericCycles(TypeSystemEntity owner, TypeSystemEntity referent)
        {
            _genericCycleDetector.DetectCycle(owner, referent);
        }

        public void LogWarnings(Logger logger)
        {
            _genericCycleDetector.LogWarnings(logger);
        }
    }

    public class SharedGenericsConfiguration
    {
        // To avoid infinite generic recursion issues during debug type record generation, attempt to
        // use canonical form for types with high generic complexity.
        public long MaxGenericDepthOfDebugRecord { get; }

        public SharedGenericsConfiguration()
        {
            MaxGenericDepthOfDebugRecord = 15;
        }
    }
}