File: Compiler\DependencyAnalysis\ReadyToRun\CopiedMethodILDeduplicator.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 ILCompiler.DependencyAnalysis;

namespace ILCompiler.DependencyAnalysis.ReadyToRun
{
    public sealed class CopiedMethodILDeduplicator : IObjectDataDeduplicator
    {
        private readonly Func<IEnumerable<CopiedMethodILNode>> _nodesProvider;
        private Dictionary<ISymbolNode, ISymbolNode> _cachedMapping;

        public CopiedMethodILDeduplicator(Func<IEnumerable<CopiedMethodILNode>> nodesProvider)
        {
            _nodesProvider = nodesProvider;
        }

        public void DeduplicatePass(NodeFactory factory, Dictionary<ISymbolNode, ISymbolNode> previousSymbolRemapping, Dictionary<ISymbolNode, ISymbolNode> symbolRemapping)
        {
            if (_cachedMapping is null)
            {
                _cachedMapping = new Dictionary<ISymbolNode, ISymbolNode>();

                var sortedNodes = new List<CopiedMethodILNode>(_nodesProvider());
                sortedNodes.Sort(CompilerComparer.Instance);

                var hashSet = new HashSet<InternKey>(new InternComparer());

                foreach (CopiedMethodILNode node in sortedNodes)
                {
                    // No need to deduplicate unmarked nodes.
                    // They won't be emitted anyway.
                    if (!node.Marked)
                    {
                        continue;
                    }
                    var key = new InternKey(node, factory);
                    if (hashSet.TryGetValue(key, out InternKey existing))
                    {
                        _cachedMapping[node] = existing.Node;
                    }
                    else
                    {
                        hashSet.Add(key);
                    }
                }
            }

            foreach (KeyValuePair<ISymbolNode, ISymbolNode> entry in _cachedMapping)
            {
                symbolRemapping.TryAdd(entry.Key, entry.Value);
            }
        }

        private sealed class InternKey
        {
            public CopiedMethodILNode Node { get; }
            public int HashCode { get; }
            public byte[] Data { get; }

            public InternKey(CopiedMethodILNode node, NodeFactory factory)
            {
                Node = node;

                Data = node.GetData(factory, relocsOnly: false).Data;
                var hashCode = new HashCode();
                hashCode.AddBytes(Data);
                HashCode = hashCode.ToHashCode();
            }
        }

        private sealed class InternComparer : IEqualityComparer<InternKey>
        {
            public int GetHashCode(InternKey key) => key.HashCode;

            public bool Equals(InternKey a, InternKey b)
            {
                if (a.HashCode != b.HashCode)
                    return false;

                return a.Data.AsSpan().SequenceEqual(b.Data);
            }
        }
    }
}