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

namespace ILCompiler.DependencyAnalysis.ReadyToRun
{
    public class GCRefMapNode : ObjectNode, ISymbolDefinitionNode
    {
        /// <summary>
        /// Number of GC ref map records to represent with a single lookup pointer
        /// </summary>
        public const int GCREFMAP_LOOKUP_STRIDE = 1024;

        private readonly ImportSectionNode _importSection;
        private readonly List<IMethodNode> _methods;

        public GCRefMapNode(ImportSectionNode importSection)
        {
            _importSection = importSection;
            _methods = new List<IMethodNode>();
        }

        public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.ReadOnlyDataSection;

        public override bool IsShareable => false;

        public override int ClassCode => 555444333;

        public override bool StaticDependenciesAreComputed => true;

        public int Offset => 0;

        public bool IsEmpty => _methods.Count == 0;

        public void AddImport(Import import)
        {
            lock (_methods)
            {
                _methods.Add(import as IMethodNode);
            }
        }

        public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
        {
            sb.Append("GCRefMap->"u8);
            sb.Append(_importSection.Name);
        }

        public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
        {
            if (_methods.Count == 0 || relocsOnly)
            {
                return new ObjectData(
                    data: Array.Empty<byte>(),
                    relocs: Array.Empty<Relocation>(),
                    alignment: 1,
                    definedSymbols: new ISymbolDefinitionNode[] { this });
            }

            _methods.MergeSort(CompilerComparer.Instance);
            GCRefMapBuilder builder = new GCRefMapBuilder(factory.Target, relocsOnly);
            builder.Builder.RequireInitialAlignment(4);
            builder.Builder.AddSymbol(this);

            // First, emit the initial ref map offset and reserve the offset map entries
            int offsetCount = _methods.Count / GCREFMAP_LOOKUP_STRIDE;
            builder.Builder.EmitInt((offsetCount + 1) * sizeof(int));

            ObjectDataBuilder.Reservation[] offsets = new ObjectDataBuilder.Reservation[offsetCount];
            for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++)
            {
                offsets[offsetIndex] = builder.Builder.ReserveInt();
            }

            // Next, generate the actual method GC ref maps and update the offset map
            int nextOffsetIndex = 0;
            int nextMethodIndex = GCREFMAP_LOOKUP_STRIDE - 1;
            for (int methodIndex = 0; methodIndex < _methods.Count; methodIndex++)
            {
                IMethodNode methodNode = _methods[methodIndex];
                if (methodNode == null)
                {
                    // Flush an empty GC ref map block to prevent
                    // the indexed records from falling out of sync with methods
                    builder.Flush();
                }
                else
                {
                    bool isStub = false;
                    if (methodNode is DelayLoadHelperImport methodImport)
                    {
                        MethodFixupSignature signature = (MethodFixupSignature)methodImport.ImportSignature.Target;
                        isStub = signature.IsUnboxingStub || signature.IsInstantiatingStub;
                    }
                    builder.GetCallRefMap(methodNode.Method, isStub);
                }
                if (methodIndex >= nextMethodIndex)
                {
                    builder.Builder.EmitInt(offsets[nextOffsetIndex], builder.Builder.CountBytes);
                    nextOffsetIndex++;
                    nextMethodIndex += GCREFMAP_LOOKUP_STRIDE;
                }
            }
            Debug.Assert(nextOffsetIndex == offsets.Length);

            return builder.Builder.ToObjectData();
        }

        protected override string GetName(NodeFactory factory)
        {
            Utf8StringBuilder sb = new Utf8StringBuilder();
            AppendMangledName(factory.NameMangler, sb);
            return sb.ToString();
        }

        public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
        {
            return comparer.Compare(_importSection, ((GCRefMapNode)other)._importSection);
        }
    }
}