File: Compiler\DependencyAnalysis\ReadyToRun\ReadyToRunHeaderNode.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.Collections.Generic;
using System.Diagnostics;

using Internal.Runtime;
using Internal.Text;
using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;
using Internal.ReadyToRunConstants;
using ILCompiler.DependencyAnalysisFramework;
using System.Threading.Tasks;

namespace ILCompiler.DependencyAnalysis.ReadyToRun
{
    public abstract class HeaderTableNode : ObjectNode, ISymbolDefinitionNode
    {
        public abstract void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb);

        public int Offset => 0;

        public override bool IsShareable => false;

        protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);

        public override bool StaticDependenciesAreComputed => true;

        public override ObjectNodeSection GetSection(NodeFactory factory)
        {
            if (factory.Target.IsWindows)
                return ObjectNodeSection.ReadOnlyDataSection;
            else
                return ObjectNodeSection.DataSection;
        }
    }

    public abstract class ModuleSpecificHeaderTableNode : HeaderTableNode
    {
        protected readonly EcmaModule _module;

        public ModuleSpecificHeaderTableNode(EcmaModule module)
        {
            _module = module;
        }

        public sealed override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
        {
            ModuleSpecificHeaderTableNode otherModuleSpecificHeaderTableNode = (ModuleSpecificHeaderTableNode)other;

            if (_module == null)
            {
                Debug.Assert(otherModuleSpecificHeaderTableNode._module != null);
                return -1;
            }
            else if (otherModuleSpecificHeaderTableNode._module == null)
            {
                return 1;
            }

            return _module.Assembly.GetName().Name.CompareTo(otherModuleSpecificHeaderTableNode._module.Assembly.GetName().Name);
        }

        protected abstract string ModuleSpecificName { get; }

        public sealed override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
        {
            sb.Append(nameMangler.CompilationUnitPrefix);
            if (_module != null)
            {
                sb.Append(ModuleSpecificName);
                sb.Append(_module.Assembly.GetName().Name);
            }
            else
            {
                sb.Append(ModuleSpecificName);
            }

        }
    }

    public abstract class ReadyToRunHeaderNode : ObjectNode, ISymbolDefinitionNode
    {
        struct HeaderItem
        {
            public HeaderItem(ReadyToRunSectionType id, DependencyNodeCore<NodeFactory> node, ISymbolNode startSymbol)
            {
                Id = id;
                Node = node;
                StartSymbol = startSymbol;
            }

            public readonly ReadyToRunSectionType Id;
            public readonly DependencyNodeCore<NodeFactory> Node;
            public readonly ISymbolNode StartSymbol;
        }

        private readonly List<HeaderItem> _items = new List<HeaderItem>();
        private readonly ReadyToRunFlags _flags;
        private readonly Task<(bool canSkipValidation, string[] reasons)> _shouldAddSkipTypeValidationFlag;

        public ReadyToRunHeaderNode(ReadyToRunFlags flags, EcmaModule moduleToCheckForSkipTypeValidation)
        {

            if (moduleToCheckForSkipTypeValidation != null)
            {
                _shouldAddSkipTypeValidationFlag = TypeValidationChecker.CanSkipValidation(moduleToCheckForSkipTypeValidation);
            }
            else
            {
                _shouldAddSkipTypeValidationFlag = Task.FromResult((false, new string[0]));
            }
            _flags = flags;
        }

        public void Add(ReadyToRunSectionType id, DependencyNodeCore<NodeFactory> node, ISymbolNode startSymbol)
        {
            _items.Add(new HeaderItem(id, node, startSymbol));
        }

        public void Add<T>(ReadyToRunSectionType id, T node)
            where T : DependencyNodeCore<NodeFactory>, ISymbolNode
        {
            Add(id, node, node);
        }

        public int Offset => 0;
        public override bool IsShareable => false;

        protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);

        protected abstract void AppendMangledHeaderName(NameMangler nameMangler, Utf8StringBuilder sb);

        public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) => AppendMangledHeaderName(nameMangler, sb);

        public override bool StaticDependenciesAreComputed => true;

        // For R2R, we can put the header in the read-only section on non-Windows as well. Since we emit a PE image
        // and do our own mapping, we don't need it to be writeable for the OS loader to handle absolute pointer relocs.
        // Our R2R PE images group read-only data into the .text section, so this doesn't result in more work to map.
        public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.ReadOnlyDataSection;

        public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
        {
            ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly);
            builder.RequireInitialPointerAlignment();
            builder.AddSymbol(this);

            EmitHeaderPrefix(ref builder);

            // Don't bother sorting if we're not emitting the contents
            if (!relocsOnly)
                _items.MergeSort((x, y) => Comparer<int>.Default.Compare((int)x.Id, (int)y.Id));

            // ReadyToRunHeader.Flags
            int flagsInt = (int)_flags;
            if (!relocsOnly)
            {
                if (_shouldAddSkipTypeValidationFlag.Result.canSkipValidation)
                {
                    flagsInt |= (int)ReadyToRunFlags.READYTORUN_FLAG_SkipTypeValidation;
                }
                else
                {
                    if (factory.OptimizationFlags.TypeValidation == TypeValidationRule.AutomaticWithLogging)
                    {
                        // If we are in automatic with logging mode, we reach here when we are unable to enable
                        // skip validation. When logging is enabled, write out the reasons we found for
                        // not doing so.
                        foreach (string reason in _shouldAddSkipTypeValidationFlag.Result.reasons)
                            System.Console.WriteLine(reason);
                    }
                }
            }
            builder.EmitInt(flagsInt);

            // ReadyToRunHeader.NumberOfSections
            ObjectDataBuilder.Reservation sectionCountReservation = builder.ReserveInt();

            int count = 0;
            foreach (var item in _items)
            {
                // Skip empty entries
                if (!relocsOnly && item.Node is ObjectNode on && on.ShouldSkipEmittingObjectNode(factory))
                    continue;

                builder.EmitInt((int)item.Id);

                builder.EmitReloc(item.StartSymbol, RelocType.IMAGE_REL_BASED_ADDR32NB);

                // The header entry for the runtime functions table should not include the 4 byte 0xffffffff sentinel
                // value in the covered range.
                int delta = item.Id == ReadyToRunSectionType.RuntimeFunctions ? RuntimeFunctionsTableNode.SentinelSizeAdjustment : 0;
                builder.EmitReloc(item.StartSymbol, RelocType.IMAGE_REL_SYMBOL_SIZE, delta);

                count++;
            }

            builder.EmitInt(sectionCountReservation, count);

            return builder.ToObjectData();
        }

        protected abstract void EmitHeaderPrefix(ref ObjectDataBuilder builder);

        protected internal override int Phase => (int)ObjectNodePhase.Ordered;
    }

    public class GlobalHeaderNode : ReadyToRunHeaderNode
    {
        public GlobalHeaderNode(ReadyToRunFlags flags, EcmaModule moduleToCheckForSkipValidation)
            : base(flags, moduleToCheckForSkipValidation)
        {
        }

        protected override void AppendMangledHeaderName(NameMangler nameMangler, Utf8StringBuilder sb)
        {
            sb.Append(nameMangler.CompilationUnitPrefix);
            sb.Append("__ReadyToRunHeader"u8);
        }

        protected override void EmitHeaderPrefix(ref ObjectDataBuilder builder)
        {
            // ReadyToRunHeader.Magic
            builder.EmitInt((int)(ReadyToRunHeaderConstants.Signature));

            // ReadyToRunHeader.MajorVersion
            builder.EmitShort((short)(ReadyToRunHeaderConstants.CurrentMajorVersion));
            builder.EmitShort((short)(ReadyToRunHeaderConstants.CurrentMinorVersion));
        }

        public override int ClassCode => (int)ObjectNodeOrder.GlobalHeaderNode;
    }

    public class AssemblyHeaderNode : ReadyToRunHeaderNode
    {
        private readonly int _index;

        public AssemblyHeaderNode(ReadyToRunFlags flags, int index)
            : base(flags, null)
        {
            _index = index;
        }

        protected override void EmitHeaderPrefix(ref ObjectDataBuilder builder)
        {
        }

        public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
        {
            return _index - ((AssemblyHeaderNode)other)._index;
        }

        protected override void AppendMangledHeaderName(NameMangler nameMangler, Utf8StringBuilder sb)
        {
            sb.Append(nameMangler.CompilationUnitPrefix);
            sb.Append("__ReadyToRunAssemblyHeader__"u8);
            sb.Append(_index.ToString());
        }

        public override int ClassCode => (int)ObjectNodeOrder.ReadyToRunAssemblyHeaderNode;
    }
}