File: Compiler\Compilation.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.Collections.Generic;
using System.Collections.Immutable;
using System.IO;

using ILCompiler.DependencyAnalysis;
using ILCompiler.DependencyAnalysisFramework;

using Internal.IL;
using Internal.IL.Stubs;
using Internal.Text;
using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;

using CORINFO_DEVIRTUALIZATION_DETAIL = Internal.JitInterface.CORINFO_DEVIRTUALIZATION_DETAIL;
using Debug = System.Diagnostics.Debug;

namespace ILCompiler
{
    public abstract class Compilation : ICompilation
    {
        protected readonly DependencyAnalyzerBase<NodeFactory> _dependencyGraph;
        protected readonly NodeFactory _nodeFactory;
        protected readonly Logger _logger;
        protected readonly DebugInformationProvider _debugInformationProvider;
        private readonly IInliningPolicy _inliningPolicy;

        public NameMangler NameMangler => _nodeFactory.NameMangler;
        public NodeFactory NodeFactory => _nodeFactory;
        public CompilerTypeSystemContext TypeSystemContext => NodeFactory.TypeSystemContext;
        public Logger Logger => _logger;
        public PInvokeILProvider PInvokeILProvider { get; }

        private readonly TypeGetTypeMethodThunkCache _typeGetTypeMethodThunks;
        private readonly AssemblyGetExecutingAssemblyMethodThunkCache _assemblyGetExecutingAssemblyMethodThunks;
        private readonly MethodBaseGetCurrentMethodThunkCache _methodBaseGetCurrentMethodThunks;

        protected Compilation(
            DependencyAnalyzerBase<NodeFactory> dependencyGraph,
            NodeFactory nodeFactory,
            IEnumerable<ICompilationRootProvider> compilationRoots,
            ILProvider ilProvider,
            DebugInformationProvider debugInformationProvider,
            IInliningPolicy inliningPolicy,
            Logger logger)
        {
            _dependencyGraph = dependencyGraph;
            _nodeFactory = nodeFactory;
            _logger = logger;
            _debugInformationProvider = debugInformationProvider;
            _inliningPolicy = inliningPolicy;

            _dependencyGraph.ComputeDependencyRoutine += ComputeDependencyNodeDependencies;
            NodeFactory.AttachToDependencyGraph(_dependencyGraph);

            var rootingService = new RootingServiceProvider(nodeFactory, _dependencyGraph.AddRoot);
            foreach (var rootProvider in compilationRoots)
                rootProvider.AddCompilationRoots(rootingService);

            MetadataType globalModuleGeneratedType = nodeFactory.TypeSystemContext.GeneratedAssembly.GetGlobalModuleType();
            _typeGetTypeMethodThunks = new TypeGetTypeMethodThunkCache(globalModuleGeneratedType);
            _assemblyGetExecutingAssemblyMethodThunks = new AssemblyGetExecutingAssemblyMethodThunkCache(globalModuleGeneratedType);
            _methodBaseGetCurrentMethodThunks = new MethodBaseGetCurrentMethodThunkCache();

            PInvokeILProvider = _nodeFactory.InteropStubManager.CreatePInvokeILProvider();
            if (PInvokeILProvider != null)
            {
                ilProvider = new CombinedILProvider(ilProvider, PInvokeILProvider);
            }

            _methodILCache = new ILCache(ilProvider);
        }

        private ILCache _methodILCache;

        public virtual MethodIL GetMethodIL(MethodDesc method)
        {
            // Flush the cache when it grows too big
            if (_methodILCache.Count > 1000)
                _methodILCache = new ILCache(_methodILCache.ILProvider);

            return _methodILCache.GetOrCreateValue(method).MethodIL;
        }

        protected abstract void ComputeDependencyNodeDependencies(List<DependencyNodeCore<NodeFactory>> obj);

        protected abstract void CompileInternal(string outputFile, ObjectDumper dumper);

        public void DetectGenericCycles(MethodDesc caller, MethodDesc callee)
        {
            _nodeFactory.TypeSystemContext.DetectGenericCycles(caller, callee);
        }

        public virtual IEETypeNode NecessaryTypeSymbolIfPossible(TypeDesc type)
        {
            return _nodeFactory.NecessaryTypeSymbol(type);
        }

        public bool CanInline(MethodDesc caller, MethodDesc callee)
        {
            return _inliningPolicy.CanInline(caller, callee);
        }

        public bool CanReferenceConstructedMethodTable(TypeDesc type)
        {
            return NodeFactory.DevirtualizationManager.CanReferenceConstructedMethodTable(type.NormalizeInstantiation());
        }

        public bool CanReferenceConstructedTypeOrCanonicalFormOfType(TypeDesc type)
        {
            return NodeFactory.DevirtualizationManager.CanReferenceConstructedTypeOrCanonicalFormOfType(type.NormalizeInstantiation());
        }

        public DelegateCreationInfo GetDelegateCtor(TypeDesc delegateType, MethodDesc target, TypeDesc constrainedType, bool followVirtualDispatch)
        {
            // If we're creating a delegate to a virtual method that cannot be overridden, devirtualize.
            // This is not just an optimization - it's required for correctness in the presence of sealed
            // vtable slots.
            if (followVirtualDispatch && NodeFactory.DevirtualizationManager.IsEffectivelySealed(target))
                followVirtualDispatch = false;

            if (followVirtualDispatch)
                target = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(target);

            return DelegateCreationInfo.Create(delegateType, target, constrainedType, NodeFactory, followVirtualDispatch);
        }

        /// <summary>
        /// Gets an object representing the static data for RVA mapped fields from the PE image.
        /// </summary>
        public virtual ISymbolNode GetFieldRvaData(FieldDesc field)
        {
            if (field.GetType() == typeof(PInvokeLazyFixupField))
            {
                return NodeFactory.PInvokeMethodFixup(new PInvokeMethodData((PInvokeLazyFixupField)field));
            }
            else if (field is ExternSymbolMappedField externField)
            {
                return NodeFactory.ExternVariable(new Utf8String(externField.SymbolName));
            }
            else
            {
                // Use the typical field definition in case this is an instantiated generic type
                return NodeFactory.FieldRvaData((EcmaField)field.GetTypicalFieldDefinition());
            }
        }

        public bool HasLazyStaticConstructor(TypeDesc type)
        {
            return NodeFactory.PreinitializationManager.HasLazyStaticConstructor(type);
        }

        public MethodDebugInformation GetDebugInfo(MethodIL methodIL)
        {
            return _debugInformationProvider.GetDebugInfo(methodIL);
        }

        /// <summary>
        /// Resolves a reference to an intrinsic method to a new method that takes it's place in the compilation.
        /// This is used for intrinsics where the intrinsic expansion depends on the callsite.
        /// </summary>
        /// <param name="intrinsicMethod">The intrinsic method called.</param>
        /// <param name="callsiteMethod">The callsite that calls the intrinsic.</param>
        /// <returns>The intrinsic implementation to be called for this specific callsite.</returns>
        public MethodDesc ExpandIntrinsicForCallsite(MethodDesc intrinsicMethod, MethodDesc callsiteMethod)
        {
            Debug.Assert(intrinsicMethod.IsIntrinsic);

            var intrinsicOwningType = intrinsicMethod.OwningType as MetadataType;
            if (intrinsicOwningType == null)
                return intrinsicMethod;

            if (intrinsicOwningType.Module != TypeSystemContext.SystemModule)
                return intrinsicMethod;

            if (intrinsicOwningType.Name.SequenceEqual("Type"u8) && intrinsicOwningType.Namespace.SequenceEqual("System"u8))
            {
                if (intrinsicMethod.Signature.IsStatic && intrinsicMethod.Name.SequenceEqual("GetType"u8))
                {
                    ModuleDesc callsiteModule = (callsiteMethod.OwningType as MetadataType)?.Module;
                    if (callsiteModule != null)
                    {
                        Debug.Assert(callsiteModule is IAssemblyDesc, "Multi-module assemblies");
                        return _typeGetTypeMethodThunks.GetHelper(intrinsicMethod, ((IAssemblyDesc)callsiteModule).GetName().Name);
                    }
                }
            }
            else if (intrinsicOwningType.Name.SequenceEqual("Assembly"u8) && intrinsicOwningType.Namespace.SequenceEqual("System.Reflection"u8))
            {
                if (intrinsicMethod.Signature.IsStatic && intrinsicMethod.Name.SequenceEqual("GetExecutingAssembly"u8))
                {
                    ModuleDesc callsiteModule = (callsiteMethod.OwningType as MetadataType)?.Module;
                    if (callsiteModule != null)
                    {
                        Debug.Assert(callsiteModule is IAssemblyDesc, "Multi-module assemblies");
                        return _assemblyGetExecutingAssemblyMethodThunks.GetHelper((IAssemblyDesc)callsiteModule);
                    }
                }
            }
            else if (intrinsicOwningType.Name.SequenceEqual("MethodBase"u8) && intrinsicOwningType.Namespace.SequenceEqual("System.Reflection"u8))
            {
                if (intrinsicMethod.Signature.IsStatic && intrinsicMethod.Name.SequenceEqual("GetCurrentMethod"u8))
                {
                    if (callsiteMethod.IsAsyncVariant())
                    {
                        // For async methods, we need to get the MethodBase for the thunk variant.
                        callsiteMethod = TypeSystemContext.GetTargetOfAsyncVariantMethod(callsiteMethod);
                    }
                    return _methodBaseGetCurrentMethodThunks.GetHelper(callsiteMethod).InstantiateAsOpen();
                }
            }

            return intrinsicMethod;
        }

        public bool NeedsSlotUseTracking(TypeDesc type)
        {
            return !NodeFactory.VTable(type).HasKnownVirtualMethodUse;
        }

        public bool IsEffectivelySealed(TypeDesc type)
        {
            return NodeFactory.DevirtualizationManager.IsEffectivelySealed(type);
        }

        public TypeDesc[] GetImplementingClasses(TypeDesc type)
        {
            return NodeFactory.DevirtualizationManager.GetImplementingClasses(type);
        }

        public bool IsEffectivelySealed(MethodDesc method)
        {
            return NodeFactory.DevirtualizationManager.IsEffectivelySealed(method);
        }

        public MethodDesc ResolveVirtualMethod(MethodDesc declMethod, TypeDesc implType, out CORINFO_DEVIRTUALIZATION_DETAIL devirtualizationDetail)
        {
            return NodeFactory.DevirtualizationManager.ResolveVirtualMethod(declMethod, implType, out devirtualizationDetail);
        }

        public bool NeedsRuntimeLookup(ReadyToRunHelperId lookupKind, object targetOfLookup)
        {
            switch (lookupKind)
            {
                case ReadyToRunHelperId.TypeHandle:
                case ReadyToRunHelperId.NecessaryTypeHandle:
                case ReadyToRunHelperId.MetadataTypeHandle:
                case ReadyToRunHelperId.DefaultConstructor:
                case ReadyToRunHelperId.TypeHandleForCasting:
                case ReadyToRunHelperId.ObjectAllocator:
                    return ((TypeDesc)targetOfLookup).IsRuntimeDeterminedSubtype;

                case ReadyToRunHelperId.MethodDictionary:
                case ReadyToRunHelperId.MethodEntry:
                case ReadyToRunHelperId.VirtualDispatchCell:
                case ReadyToRunHelperId.MethodHandle:
                    return ((MethodDesc)targetOfLookup).IsRuntimeDeterminedExactMethod;

                case ReadyToRunHelperId.FieldHandle:
                    return ((FieldDesc)targetOfLookup).OwningType.IsRuntimeDeterminedSubtype;

                case ReadyToRunHelperId.ConstrainedDirectCall:
                    return ((ConstrainedCallInfo)targetOfLookup).Method.IsRuntimeDeterminedExactMethod
                        || ((ConstrainedCallInfo)targetOfLookup).ConstrainedType.IsRuntimeDeterminedSubtype;

                default:
                    throw new NotImplementedException();
            }
        }

        public ReadyToRunHelperId GetLdTokenHelperForType(TypeDesc type)
        {
            // This will not correctly answer questions for canonical forms because the answer would
            // be about whether a type loader template for the type exists.
            // We need to make a small exception for canonical definitions because of RuntimeAugments.GetCanonType
            Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Any) || type.IsCanonicalDefinitionType(CanonicalFormKind.Any));

            if (type.IsGenericDefinition && NodeFactory.DevirtualizationManager.IsGenericDefinitionMethodTableReflectionVisible(type))
                return ReadyToRunHelperId.MetadataTypeHandle;

            if (ConstructedEETypeNode.CreationAllowed(type))
            {
                if (NodeFactory.DevirtualizationManager.CanReferenceConstructedMethodTable(type.NormalizeInstantiation()))
                    return ReadyToRunHelperId.TypeHandle;

                if (NodeFactory.DevirtualizationManager.CanReferenceMetadataMethodTable(type.NormalizeInstantiation()))
                    return ReadyToRunHelperId.MetadataTypeHandle;
            }

            return ReadyToRunHelperId.NecessaryTypeHandle;
        }

        public static MethodDesc GetConstructorForCreateInstanceIntrinsic(TypeDesc type)
        {
            MethodDesc ctor = type.GetDefaultConstructor();
            if (ctor == null)
            {
                MetadataType activatorType = type.Context.SystemModule.GetKnownType("System"u8, "Activator"u8);
                if (type.IsValueType && type.GetParameterlessConstructor() == null)
                {
                    ctor = activatorType.GetKnownNestedType("StructWithNoConstructor"u8).GetKnownMethod(".ctor"u8, null);
                }
                else
                {
                    ctor = activatorType.GetKnownMethod("MissingConstructorMethod"u8, null);
                }
            }

            return ctor;
        }

        public ISymbolNode ComputeConstantLookup(ReadyToRunHelperId lookupKind, object targetOfLookup)
        {
            switch (lookupKind)
            {
                case ReadyToRunHelperId.TypeHandle:
                    return NodeFactory.ConstructedTypeSymbol((TypeDesc)targetOfLookup);
                case ReadyToRunHelperId.MetadataTypeHandle:
                    return NodeFactory.MetadataTypeSymbol((TypeDesc)targetOfLookup);
                case ReadyToRunHelperId.NecessaryTypeHandle:
                    return NecessaryTypeSymbolIfPossible((TypeDesc)targetOfLookup);
                case ReadyToRunHelperId.TypeHandleForCasting:
                    {
                        var type = (TypeDesc)targetOfLookup;

                        if (type.IsInterface)
                        {
                            // We counter-intuitively ask for a constructed type symbol. This is needed due to IDynamicInterfaceCastable.
                            // If this cast happens with an object that implements IDynamicInterfaceCastable, user code will
                            // see a RuntimeTypeHandle representing this interface.
                            if (NodeFactory.DevirtualizationManager.CanHaveDynamicInterfaceImplementations(type))
                            {
                                return NodeFactory.MaximallyConstructableType(type);
                            }
                            else
                            {
                                return NecessaryTypeSymbolIfPossible(type);
                            }
                        }

                        if (type.IsNullable)
                            type = type.Instantiation[0];
                        return NecessaryTypeSymbolIfPossible(type);
                    }
                case ReadyToRunHelperId.MethodDictionary:
                    return NodeFactory.MethodGenericDictionary((MethodDesc)targetOfLookup);
                case ReadyToRunHelperId.MethodEntry:
                    return NodeFactory.FatFunctionPointer((MethodDesc)targetOfLookup);
                case ReadyToRunHelperId.MethodHandle:
                    return NodeFactory.RuntimeMethodHandle((MethodDesc)targetOfLookup);
                case ReadyToRunHelperId.FieldHandle:
                    return NodeFactory.RuntimeFieldHandle((FieldDesc)targetOfLookup);
                case ReadyToRunHelperId.DefaultConstructor:
                    {
                        var type = (TypeDesc)targetOfLookup;
                        MethodDesc ctor = GetConstructorForCreateInstanceIntrinsic(type);
                        return type.IsValueType ? NodeFactory.ExactCallableAddress(ctor) : NodeFactory.CanonicalEntrypoint(ctor);
                    }
                case ReadyToRunHelperId.ObjectAllocator:
                    {
                        var type = (TypeDesc)targetOfLookup;
                        return NodeFactory.ExternFunctionSymbol(new Utf8String(JitHelper.GetNewObjectHelperForType(type)));
                    }

                default:
                    throw new NotImplementedException();
            }
        }

        public GenericDictionaryLookup ComputeGenericLookup(MethodDesc contextMethod, ReadyToRunHelperId lookupKind, object targetOfLookup)
        {
            if (targetOfLookup is TypeSystemEntity typeSystemEntity)
            {
                _nodeFactory.TypeSystemContext.DetectGenericCycles(contextMethod, typeSystemEntity);
            }

            GenericContextSource contextSource;

            if (contextMethod.RequiresInstMethodDescArg())
            {
                contextSource = GenericContextSource.MethodParameter;
            }
            else if (contextMethod.RequiresInstMethodTableArg())
            {
                contextSource = GenericContextSource.TypeParameter;
            }
            else
            {
                Debug.Assert(contextMethod.AcquiresInstMethodTableFromThis());
                contextSource = GenericContextSource.ThisObject;
            }

            //
            // Some helpers represent logical concepts that might not be something that can be looked up in a dictionary
            //

            // Downgrade type handle for casting to a normal type handle if possible
            if (lookupKind == ReadyToRunHelperId.TypeHandleForCasting)
            {
                var type = (TypeDesc)targetOfLookup;
                if (!type.IsRuntimeDeterminedType || !((RuntimeDeterminedType)type).CanonicalType.IsNullable)
                {
                    if (type.IsNullable)
                    {
                        targetOfLookup = type.Instantiation[0];
                    }
                    lookupKind = ReadyToRunHelperId.NecessaryTypeHandle;
                }
            }

            DictionaryLayoutNode dictionaryLayout;
            if (contextSource == GenericContextSource.MethodParameter)
                dictionaryLayout = _nodeFactory.GenericDictionaryLayout(contextMethod);
            else
                dictionaryLayout = _nodeFactory.GenericDictionaryLayout(contextMethod.OwningType);

            // If the dictionary layout has fixed slots, we can compute the lookup now. Otherwise defer to helper.
            if (dictionaryLayout.HasFixedSlots)
            {
                int pointerSize = _nodeFactory.Target.PointerSize;

                GenericLookupResult lookup = ReadyToRunGenericHelperNode.GetLookupSignature(_nodeFactory, lookupKind, targetOfLookup);
                if (dictionaryLayout.TryGetSlotForEntry(lookup, out int dictionarySlot))
                {
                    int dictionaryOffset = dictionarySlot * pointerSize;

                    if (contextSource == GenericContextSource.MethodParameter)
                    {
                        return GenericDictionaryLookup.CreateFixedLookup(contextSource, dictionaryOffset);
                    }
                    else
                    {
                        int vtableSlot = VirtualMethodSlotHelper.GetGenericDictionarySlot(_nodeFactory, contextMethod.OwningType);
                        int vtableOffset = EETypeNode.GetVTableOffset(pointerSize) + vtableSlot * pointerSize;
                        return GenericDictionaryLookup.CreateFixedLookup(contextSource, vtableOffset, dictionaryOffset);
                    }
                }
                else
                {
                    return GenericDictionaryLookup.CreateNullLookup(contextSource);
                }
            }

            // Fixed lookup not possible - use helper.
            return GenericDictionaryLookup.CreateHelperLookup(contextSource, lookupKind, targetOfLookup);
        }

        public bool IsFatPointerCandidate(MethodDesc containingMethod, MethodSignature signature)
        {
            // Unmanaged calls are never fat pointers
            if ((signature.Flags & MethodSignatureFlags.UnmanagedCallingConventionMask) != 0)
                return false;

            if (containingMethod.OwningType is MetadataType owningType)
            {
                // RawCalliHelper is a way for the class library to opt out of fat calls
                if (owningType.Name.SequenceEqual("RawCalliHelper"u8))
                    return false;

                // Delegate invocation never needs fat calls
                if (owningType.IsDelegate && containingMethod.Name.SequenceEqual("Invoke"u8))
                    return false;
            }

            return true;
        }

        /// <summary>
        /// Retrieves method whose runtime handle is suitable for use with GVMLookupForSlot.
        /// </summary>
        public MethodDesc GetTargetOfGenericVirtualMethodCall(MethodDesc calledMethod)
        {
            // Should be a generic virtual method
            Debug.Assert(calledMethod.HasInstantiation && calledMethod.IsVirtual);

            // Needs to be either a concrete method, or a runtime determined form.
            Debug.Assert(!calledMethod.IsCanonicalMethod(CanonicalFormKind.Specific));

            MethodDesc targetMethod = calledMethod.GetCanonMethodTarget(CanonicalFormKind.Specific);
            MethodDesc targetMethodDefinition = targetMethod.GetMethodDefinition();

            MethodDesc slotNormalizedMethodDefinition = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(targetMethodDefinition);

            // If the method defines the slot, we can use that.
            if (slotNormalizedMethodDefinition == targetMethodDefinition)
            {
                return calledMethod;
            }

            // Normalize to the slot defining method
            InstantiatedMethod slotNormalizedMethod = TypeSystemContext.GetInstantiatedMethod(
                slotNormalizedMethodDefinition,
                targetMethod.Instantiation);

            // Since the slot normalization logic modified what method we're looking at, we need to compute the new target of lookup.
            //
            // If we could use virtual method resolution logic with runtime determined methods, we wouldn't need what we're going
            // to do below.
            MethodDesc runtimeDeterminedSlotNormalizedMethod;
            if (!slotNormalizedMethod.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any))
            {
                // If the owning type is not generic, we can use it as-is, potentially only replacing the runtime-determined
                // method instantiation part.
                runtimeDeterminedSlotNormalizedMethod = slotNormalizedMethod.GetMethodDefinition();
            }
            else
            {
                // If we need a runtime lookup but a normalization to the slot defining method happened above, we need to compute
                // the runtime lookup in terms of the base type that introduced the slot.
                //
                // To do that, we walk the base hierarchy of the runtime determined thing, looking for a type definition that matches
                // the slot-normalized virtual method. We then find the method on that type.
                TypeDesc runtimeDeterminedOwningType = calledMethod.OwningType;

                Debug.Assert(!runtimeDeterminedOwningType.IsInterface);

                while (!slotNormalizedMethod.OwningType.HasSameTypeDefinition(runtimeDeterminedOwningType))
                {
                    DefType runtimeDeterminedBaseTypeDefinition = runtimeDeterminedOwningType.GetTypeDefinition().BaseType;
                    if (runtimeDeterminedBaseTypeDefinition.HasInstantiation)
                    {
                        runtimeDeterminedOwningType = runtimeDeterminedBaseTypeDefinition.InstantiateSignature(runtimeDeterminedOwningType.Instantiation, default);
                    }
                    else
                    {
                        runtimeDeterminedOwningType = runtimeDeterminedBaseTypeDefinition;
                    }
                }

                // Now get the method on the newly found type
                Debug.Assert(runtimeDeterminedOwningType.HasInstantiation);
                runtimeDeterminedSlotNormalizedMethod = TypeSystemContext.GetMethodForInstantiatedType(
                    slotNormalizedMethod.GetTypicalMethodDefinition(),
                    (InstantiatedType)runtimeDeterminedOwningType);
            }

            return TypeSystemContext.GetInstantiatedMethod(runtimeDeterminedSlotNormalizedMethod, calledMethod.Instantiation);
        }

        CompilationResults ICompilation.Compile(string outputFile, ObjectDumper dumper)
        {
            dumper?.Begin();

            CompileInternal(outputFile, dumper);

            dumper?.End();

            return new CompilationResults(_dependencyGraph, _nodeFactory);
        }

        private sealed class ILCache : LockFreeReaderHashtable<MethodDesc, ILCache.MethodILData>
        {
            public ILProvider ILProvider { get; }

            public ILCache(ILProvider provider)
            {
                ILProvider = provider;
            }

            protected override int GetKeyHashCode(MethodDesc key)
            {
                return key.GetHashCode();
            }
            protected override int GetValueHashCode(MethodILData value)
            {
                return value.Method.GetHashCode();
            }
            protected override bool CompareKeyToValue(MethodDesc key, MethodILData value)
            {
                return ReferenceEquals(key, value.Method);
            }
            protected override bool CompareValueToValue(MethodILData value1, MethodILData value2)
            {
                return ReferenceEquals(value1.Method, value2.Method);
            }
            protected override MethodILData CreateValueFromKey(MethodDesc key)
            {
                return new MethodILData() { Method = key, MethodIL = ILProvider.GetMethodIL(key) };
            }

            internal sealed class MethodILData
            {
                public MethodDesc Method;
                public MethodIL MethodIL;
            }
        }

        private sealed class CombinedILProvider : ILProvider
        {
            private readonly ILProvider _primaryILProvider;
            private readonly PInvokeILProvider _pinvokeProvider;

            public CombinedILProvider(ILProvider primaryILProvider, PInvokeILProvider pinvokeILProvider)
            {
                _primaryILProvider = primaryILProvider;
                _pinvokeProvider = pinvokeILProvider;
            }

            public override MethodIL GetMethodIL(MethodDesc method)
            {
                MethodIL result = _primaryILProvider.GetMethodIL(method);
                if (result == null && method.IsPInvoke)
                    result = _pinvokeProvider.GetMethodIL(method);

                return result;
            }
        }
    }

    // Interface under which Compilation is exposed externally.
    public interface ICompilation
    {
        CompilationResults Compile(string outputFileName, ObjectDumper dumper);
    }

    public class CompilationResults
    {
        private readonly DependencyAnalyzerBase<NodeFactory> _graph;
        protected readonly NodeFactory _factory;

        protected ImmutableArray<DependencyNodeCore<NodeFactory>> MarkedNodes
        {
            get
            {
                return _graph.MarkedNodeList;
            }
        }

        internal CompilationResults(DependencyAnalyzerBase<NodeFactory> graph, NodeFactory factory)
        {
            _graph = graph;
            _factory = factory;
        }

        public void WriteDependencyLog(string fileName)
        {
            using (FileStream dgmlOutput = new FileStream(fileName, FileMode.Create))
            {
                DgmlWriter.WriteDependencyGraphToStream(dgmlOutput, _graph, _factory);
                dgmlOutput.Flush();
            }
        }

        public IEnumerable<MethodDesc> CompiledMethodBodies
        {
            get
            {
                foreach (var node in MarkedNodes)
                {
                    if (node is IMethodBodyNode methodBodyNode)
                        yield return methodBodyNode.Method;
                }
            }
        }

        public IEnumerable<TypeDesc> ConstructedEETypes
        {
            get
            {
                foreach (var node in MarkedNodes)
                {
                    if (node is ConstructedEETypeNode)
                        yield return ((IEETypeNode)node).Type;
                }
            }
        }

        public IEnumerable<TypeDesc> AllEETypes
        {
            get
            {
                foreach (var node in MarkedNodes)
                {
                    if (node is IEETypeNode typeNode)
                        yield return typeNode.Type;
                }
            }
        }

        public IEnumerable<MethodDesc> ReflectedMethods
        {
            get
            {
                foreach (var node in MarkedNodes)
                {
                    if (node is ReflectedMethodNode reflectedMethod)
                        yield return reflectedMethod.Method;
                }
            }
        }
    }

    public sealed class ConstrainedCallInfo
    {
        public readonly TypeDesc ConstrainedType;
        public readonly MethodDesc Method;
        public ConstrainedCallInfo(TypeDesc constrainedType, MethodDesc method)
            => (ConstrainedType, Method) = (constrainedType, method);
        public int CompareTo(ConstrainedCallInfo other, TypeSystemComparer comparer)
        {
            int result = comparer.Compare(ConstrainedType, other.ConstrainedType);
            if (result == 0)
                result = comparer.Compare(Method, other.Method);
            return result;
        }
        public override bool Equals(object obj) =>
            obj is ConstrainedCallInfo other
            && ConstrainedType == other.ConstrainedType
            && Method == other.Method;

        public override int GetHashCode() => HashCode.Combine(ConstrainedType, Method);
    }
}