File: Compiler\DependencyAnalysis\ReadyToRun\TypeFixupSignature.cs
Web Access
Project: src\src\runtime\src\coreclr\tools\aot\ILCompiler.ReadyToRun\ILCompiler.ReadyToRun.csproj (ILCompiler.ReadyToRun)
// 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 Internal.Text;
using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;
using Internal.TypeSystem.Interop;
using Internal.ReadyToRunConstants;
using Internal.CorConstants;
using Internal.JitInterface;

namespace ILCompiler.DependencyAnalysis.ReadyToRun
{
    public class TypeFixupSignature : Signature
    {
        private readonly ReadyToRunFixupKind _fixupKind;

        private readonly TypeDesc _typeDesc;

        public TypeFixupSignature(ReadyToRunFixupKind fixupKind, TypeDesc typeDesc)
        {
            _fixupKind = fixupKind;
            _typeDesc = typeDesc;

            // Ensure types in signature are loadable and resolvable, otherwise we'll fail later while emitting the signature
            ((CompilerTypeSystemContext)typeDesc.Context).EnsureLoadableType(typeDesc);
        }

        public override int ClassCode => 255607008;

        public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
        {
            ObjectDataSignatureBuilder dataBuilder = new ObjectDataSignatureBuilder(factory, relocsOnly);

            if (!relocsOnly)
            {
                ReadyToRunFixupKind fixupKind = _fixupKind;
                dataBuilder.AddSymbol(this);

                if ((fixupKind == ReadyToRunFixupKind.Verify_TypeLayout) && ((MetadataType)_typeDesc).IsVectorTOrHasVectorTFields)
                {
                    fixupKind = ReadyToRunFixupKind.Check_TypeLayout;
                }

                IEcmaModule targetModule = factory.SignatureContext.GetTargetModule(_typeDesc);
                SignatureContext innerContext = dataBuilder.EmitFixup(factory, fixupKind, targetModule, factory.SignatureContext);
                if ((fixupKind == ReadyToRunFixupKind.Check_TypeLayout) ||
                    (fixupKind == ReadyToRunFixupKind.Verify_TypeLayout))
                {
                    dataBuilder.EmitTypeSignature(_typeDesc, innerContext);
                    Debug.Assert(_typeDesc.IsValueType);
                    EncodeTypeLayout(dataBuilder, _typeDesc);
                }
                else if (fixupKind == ReadyToRunFixupKind.ContinuationLayout)
                {
                    var act = _typeDesc as AsyncContinuationType;
                    // Emit EcmaType Continuation type
                    dataBuilder.EmitTypeSignature(act.BaseType, innerContext);
                    EncodeContinuationTypeLayout(dataBuilder, act);
                }
                else
                {
                    dataBuilder.EmitTypeSignature(_typeDesc, innerContext);
                }
            }

            return dataBuilder.ToObjectData();
        }

        private static void EncodeTypeLayout(ObjectDataSignatureBuilder dataBuilder, TypeDesc type)
        {
            Debug.Assert(type.IsValueType);
            MetadataType defType = (MetadataType)type;

            int pointerSize = type.Context.Target.PointerSize;
            int size = defType.InstanceFieldSize.AsInt;
            int alignment = Internal.JitInterface.CorInfoImpl.GetClassAlignmentRequirementStatic(defType);
            ReadyToRunTypeLayoutFlags flags = ReadyToRunTypeLayoutFlags.READYTORUN_LAYOUT_Alignment | ReadyToRunTypeLayoutFlags.READYTORUN_LAYOUT_GCLayout;
            if (alignment == pointerSize)
            {
                flags |= ReadyToRunTypeLayoutFlags.READYTORUN_LAYOUT_Alignment_Native;
            }

            if (!defType.ContainsGCPointers)
            {
                flags |= ReadyToRunTypeLayoutFlags.READYTORUN_LAYOUT_GCLayout_Empty;
            }

            if (defType.IsHomogeneousAggregate)
            {
                flags |= ReadyToRunTypeLayoutFlags.READYTORUN_LAYOUT_HFA;
            }

            dataBuilder.EmitUInt((uint)flags);
            dataBuilder.EmitUInt((uint)size);

            if (defType.IsHomogeneousAggregate)
            {
                ReadyToRunHFAElemType hfaElementType = (defType.ValueTypeShapeCharacteristics & ValueTypeShapeCharacteristics.AggregateMask) switch
                {
                    ValueTypeShapeCharacteristics.Float32Aggregate => ReadyToRunHFAElemType.Float32,
                    ValueTypeShapeCharacteristics.Float64Aggregate => ReadyToRunHFAElemType.Float64,
                    ValueTypeShapeCharacteristics.Vector64Aggregate => ReadyToRunHFAElemType.Vector64,
                    // See MethodTable::GetHFAType
                    ValueTypeShapeCharacteristics.Vector128Aggregate => ReadyToRunHFAElemType.Vector128,
                    _ => throw new NotSupportedException()
                };
                dataBuilder.EmitUInt((uint)hfaElementType);
            }

            if (alignment != pointerSize)
            {
                dataBuilder.EmitUInt((uint)alignment);
            }

            if (defType.ContainsGCPointers)
            {
                // Encode the GC pointer map
                GCPointerMap gcMap = GCPointerMap.FromInstanceLayout(defType);

                byte[] encodedGCRefMap = new byte[((size + (pointerSize - 1)) / pointerSize + 7) / 8];
                int bitIndex = 0;
                foreach (bool bit in gcMap)
                {
                    if (bit)
                    {
                        encodedGCRefMap[bitIndex / 8] |= (byte)(1 << (bitIndex & 7));
                    }

                    ++bitIndex;
                }

                dataBuilder.EmitBytes(encodedGCRefMap);
            }
        }

        private static void EncodeContinuationTypeLayout(ObjectDataSignatureBuilder dataBuilder, AsyncContinuationType type)
        {
            int pointerSize = type.Context.Target.PointerSize;
            int size = type.PointerMap.Size * pointerSize;
            int alignment = pointerSize;
            ReadyToRunTypeLayoutFlags flags = ReadyToRunTypeLayoutFlags.READYTORUN_LAYOUT_Alignment | ReadyToRunTypeLayoutFlags.READYTORUN_LAYOUT_GCLayout;
            Debug.Assert(alignment == pointerSize);
            flags |= ReadyToRunTypeLayoutFlags.READYTORUN_LAYOUT_Alignment_Native;

            bool gcLayoutEmpty = true;
            foreach (bool hasPointer in type.PointerMap)
            {
                if (hasPointer)
                {
                    gcLayoutEmpty = false;
                    break;
                }
            }
            if (gcLayoutEmpty)
            {
                flags |= ReadyToRunTypeLayoutFlags.READYTORUN_LAYOUT_GCLayout_Empty;
            }

            dataBuilder.EmitUInt((uint)flags);
            dataBuilder.EmitUInt((uint)size);

            if (!gcLayoutEmpty)
            {
                // Encode the GC pointer map
                GCPointerMap gcMap = type.PointerMap;

                byte[] encodedGCRefMap = new byte[(size / pointerSize + 7) / 8];
                int bitIndex = 0;
                foreach (bool bit in gcMap)
                {
                    if (bit)
                    {
                        encodedGCRefMap[bitIndex / 8] |= (byte)(1 << (bitIndex & 7));
                    }

                    ++bitIndex;
                }

                dataBuilder.EmitBytes(encodedGCRefMap);
            }
        }

        public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
        {
            sb.Append(nameMangler.CompilationUnitPrefix);
            sb.Append($@"TypeFixupSignature({_fixupKind.ToString()}): ");
            sb.Append(nameMangler.GetMangledTypeName(_typeDesc));
        }

        public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
        {
            TypeFixupSignature otherNode = (TypeFixupSignature)other;
            int result = ((int)_fixupKind).CompareTo((int)otherNode._fixupKind);
            if (result != 0)
                return result;

            return comparer.Compare(_typeDesc, otherNode._typeDesc);
        }

        protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory)
        {
            DependencyList dependencies = new DependencyList();

            if (_typeDesc.HasInstantiation &&
                !_typeDesc.IsGenericDefinition &&
                (factory.CompilationCurrentPhase == 0) &&
                factory.CompilationModuleGroup.VersionsWithType(_typeDesc))
            {
                dependencies.Add(factory.AllMethodsOnType(_typeDesc), "Methods on generic type instantiation");
            }

            if (_fixupKind == ReadyToRunFixupKind.TypeHandle)
            {
                AddDependenciesForAsyncStateMachineBox(ref dependencies, factory, _typeDesc);
            }
            return dependencies;
        }

        public static void AddDependenciesForAsyncStateMachineBox(ref DependencyList dependencies, NodeFactory factory, TypeDesc type)
        {
            ReadyToRunCompilerContext context = (ReadyToRunCompilerContext)type.Context;
            // If adding a typehandle to the AsyncStateMachineBox, pre-compile the most commonly used methods.
            // As long as we haven't already reached compilation phase 7, which is an arbitrary number of phases of compilation chosen so that
            // simple examples of async will get compiled
            if (factory.OptimizationFlags.OptimizeAsyncMethods && type.GetTypeDefinition() == context.AsyncStateMachineBoxType && !type.IsGenericDefinition && factory.CompilationCurrentPhase <= 7)
            {
                if (dependencies == null)
                    dependencies = new DependencyList();

                // This is the async state machine box, compile the cctor, and the MoveNext method.
                foreach (MethodDesc method in type.ConvertToCanonForm(CanonicalFormKind.Specific).GetAllMethods())
                {
                    if (!method.IsGenericMethodDefinition &&
                        factory.CompilationModuleGroup.ContainsMethodBody(method, false))
                    {
                        switch (method.GetName())
                        {
                            case "MoveNext":
                            case ".cctor":
                                try
                                {
                                    factory.DetectGenericCycles(type, method);
                                    dependencies.Add(factory.CompiledMethodNode(method), $"AsyncStateMachineBox Method on type {type.ToString()}");
                                }
                                catch (TypeSystemException)
                                {
                                }
                                break;
                        }
                    }
                }
            }
        }
    }
}