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

using ILCompiler.DependencyAnalysis.Wasm;
using ILCompiler.DependencyAnalysisFramework;

using Internal.IL;
using Internal.NativeFormat;
using Internal.Runtime;
using Internal.Text;
using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;

namespace ILCompiler.DependencyAnalysis
{
    public abstract partial class NodeFactory
    {
        private TargetDetails _target;
        private CompilerTypeSystemContext _context;
        private CompilationModuleGroup _compilationModuleGroup;
        private VTableSliceProvider _vtableSliceProvider;
        private DictionaryLayoutProvider _dictionaryLayoutProvider;
        private InlinedThreadStatics _inlinedThreadStatics;
        protected readonly ImportedNodeProvider _importedNodeProvider;
        private bool _markingComplete;

        public NodeFactory(
            CompilerTypeSystemContext context,
            CompilationModuleGroup compilationModuleGroup,
            MetadataManager metadataManager,
            InteropStubManager interoptStubManager,
            NameMangler nameMangler,
            LazyGenericsPolicy lazyGenericsPolicy,
            VTableSliceProvider vtableSliceProvider,
            DictionaryLayoutProvider dictionaryLayoutProvider,
            InlinedThreadStatics inlinedThreadStatics,
            ImportedNodeProvider importedNodeProvider,
            PreinitializationManager preinitializationManager,
            DevirtualizationManager devirtualizationManager,
            ObjectDataInterner dataInterner,
            TypeMapManager typeMapManager)
        {
            _target = context.Target;

            InitialInterfaceDispatchStub = new AddressTakenExternFunctionSymbolNode(new Utf8String("RhpInitialDynamicInterfaceDispatch"u8));

            _context = context;
            _compilationModuleGroup = compilationModuleGroup;
            _vtableSliceProvider = vtableSliceProvider;
            _dictionaryLayoutProvider = dictionaryLayoutProvider;
            _inlinedThreadStatics = inlinedThreadStatics;
            NameMangler = nameMangler;
            InteropStubManager = interoptStubManager;
            CreateNodeCaches();
            MetadataManager = metadataManager;
            LazyGenericsPolicy = lazyGenericsPolicy;
            _importedNodeProvider = importedNodeProvider;
            PreinitializationManager = preinitializationManager;
            DevirtualizationManager = devirtualizationManager;
            ObjectInterner = dataInterner;
            TypeMapManager = typeMapManager;
        }

        public void SetMarkingComplete()
        {
            _markingComplete = true;
        }

        public bool MarkingComplete => _markingComplete;

        public TargetDetails Target
        {
            get
            {
                return _target;
            }
        }

        public LazyGenericsPolicy LazyGenericsPolicy { get; }
        public CompilationModuleGroup CompilationModuleGroup
        {
            get
            {
                return _compilationModuleGroup;
            }
        }

        public CompilerTypeSystemContext TypeSystemContext
        {
            get
            {
                return _context;
            }
        }

        public MetadataManager MetadataManager
        {
            get;
        }

        public NameMangler NameMangler
        {
            get;
        }

        public ISymbolNode InitialInterfaceDispatchStub
        {
            get;
        }

        public PreinitializationManager PreinitializationManager
        {
            get;
        }

        public DevirtualizationManager DevirtualizationManager
        {
            get;
        }

        public InteropStubManager InteropStubManager
        {
            get;
        }

        internal ObjectDataInterner ObjectInterner
        {
            get;
        }

        protected virtual bool CanFold(MethodDesc method) => false;

        public TypeMapManager TypeMapManager
        {
            get;
        }

        /// <summary>
        /// Return true if the type is not permitted by the rules of the runtime to have an MethodTable.
        /// The implementation here is not intended to be complete, but represents many conditions
        /// which make a type ineligible to be an MethodTable. (This function is intended for use in assertions only)
        /// </summary>
        private static bool TypeCannotHaveEEType(TypeDesc type)
        {
            if (type.GetTypeDefinition() is INonEmittableType)
                return true;

            if (type.IsRuntimeDeterminedSubtype)
                return true;

            if (type.IsSignatureVariable)
                return true;

            if (type.IsGenericParameter)
                return true;

            return false;
        }

        protected struct NodeCache<TKey, TValue>
        {
            private Func<TKey, TValue> _creator;
            private ConcurrentDictionary<TKey, TValue> _cache;

            public NodeCache(Func<TKey, TValue> creator, IEqualityComparer<TKey> comparer)
            {
                _creator = creator;
                _cache = new ConcurrentDictionary<TKey, TValue>(comparer);
            }

            public NodeCache(Func<TKey, TValue> creator)
            {
                _creator = creator;
                _cache = new ConcurrentDictionary<TKey, TValue>();
            }

            public TValue GetOrAdd(TKey key)
            {
                return _cache.GetOrAdd(key, _creator);
            }

            public TValue GetOrAdd(TKey key, Func<TKey, TValue> creator)
            {
                return _cache.GetOrAdd(key, creator);
            }
        }

        private void CreateNodeCaches()
        {
            _typeSymbols = new NecessaryTypeSymbolHashtable(this);

            _metadataTypeSymbols = new MetadataTypeSymbolHashtable(this);

            _constructedTypeSymbols = new ConstructedTypeSymbolHashtable(this);

            _importedTypeSymbols = new NodeCache<TypeDesc, IEETypeNode>((TypeDesc type) =>
            {
                Debug.Assert(_compilationModuleGroup.ShouldReferenceThroughImportTable(type));
                return _importedNodeProvider.ImportedEETypeNode(this, type);
            });

            _nonGCStatics = new NodeCache<MetadataType, ISortableSymbolNode>((MetadataType type) =>
            {
                if (_compilationModuleGroup.ContainsType(type) && !_compilationModuleGroup.ShouldReferenceThroughImportTable(type))
                {
                    return new NonGCStaticsNode(type, PreinitializationManager);
                }
                else
                {
                    return _importedNodeProvider.ImportedNonGCStaticNode(this, type);
                }
            });

            _GCStatics = new NodeCache<MetadataType, ISortableSymbolNode>((MetadataType type) =>
            {
                if (_compilationModuleGroup.ContainsType(type) && !_compilationModuleGroup.ShouldReferenceThroughImportTable(type))
                {
                    return new GCStaticsNode(type, PreinitializationManager);
                }
                else
                {
                    return _importedNodeProvider.ImportedGCStaticNode(this, type);
                }
            });

            _GCStaticIndirectionNodes = new NodeCache<MetadataType, EmbeddedObjectNode>((MetadataType type) =>
            {
                ISymbolNode gcStaticsNode = TypeGCStaticsSymbol(type);
                Debug.Assert(gcStaticsNode is GCStaticsNode);
                return GCStaticsRegion.NewNode((GCStaticsNode)gcStaticsNode);
            });

            _threadStatics = new NodeCache<MetadataType, ISymbolDefinitionNode>(CreateThreadStaticsNode);

            if (_inlinedThreadStatics.IsComputed())
            {
                _inlinedThreadStatiscNode = new ThreadStaticsNode(_inlinedThreadStatics, this);
            }

            _typeThreadStaticIndices = new NodeCache<MetadataType, TypeThreadStaticIndexNode>(type =>
            {
                if (_inlinedThreadStatics.IsComputed() &&
                    _inlinedThreadStatics.GetOffsets().ContainsKey(type))
                {
                    return new TypeThreadStaticIndexNode(type, _inlinedThreadStatiscNode);
                }

                return new TypeThreadStaticIndexNode(type, null);
            });

            _GCStaticEETypes = new NodeCache<(GCPointerMap, bool), DataOnlyEETypeNode>(((GCPointerMap gcMap, bool requiresAlign8) key) =>
            {
                // Base type: System.Object. This allows storing an instance of this type in an array of objects,
                // or finding associated module from BulkType event source events.
                DefType baseType = _context.GetWellKnownType(WellKnownType.Object);
                return new DataOnlyEETypeNode("GCStaticEEType", key.gcMap, baseType, key.requiresAlign8);
            });

            _asyncContinuationEETypes = new NodeCache<AsyncContinuationType, AsyncContinuationEETypeNode>((AsyncContinuationType key) =>
            {
                return new AsyncContinuationEETypeNode(key);
            });

            _readOnlyDataBlobs = new NodeCache<ReadOnlyDataBlobKey, BlobNode>(key =>
            {
                return new BlobNode(key.Name, ObjectNodeSection.ReadOnlyDataSection, key.Data, key.Alignment);
            });

            _fieldRvaDataBlobs = new NodeCache<Internal.TypeSystem.Ecma.EcmaField, FieldRvaDataNode>(key =>
            {
                return new FieldRvaDataNode(key);
            });

            _externFunctionSymbols = new NodeCache<Utf8String, ExternFunctionSymbolNode>((Utf8String name) =>
            {
                return new ExternFunctionSymbolNode(name);
            });
            _externIndirectFunctionSymbols = new NodeCache<Utf8String, ExternFunctionSymbolNode>((Utf8String name) =>
            {
                return new ExternFunctionSymbolNode(name, isIndirection: true);
            });
            _externDataSymbols = new NodeCache<Utf8String, ExternDataSymbolNode>((Utf8String name) =>
            {
                return new ExternDataSymbolNode(name);
            });

            _pInvokeModuleFixups = new NodeCache<PInvokeModuleData, PInvokeModuleFixupNode>((PInvokeModuleData moduleData) =>
            {
                return new PInvokeModuleFixupNode(moduleData);
            });

            _pInvokeMethodFixups = new NodeCache<PInvokeMethodData, PInvokeMethodFixupNode>((PInvokeMethodData methodData) =>
            {
                return new PInvokeMethodFixupNode(methodData);
            });

            _methodEntrypoints = new MethodEntrypointHashtable(this);

            _tentativeMethodEntrypoints = new NodeCache<MethodDesc, IMethodNode>((MethodDesc method) =>
            {
                IMethodNode entrypoint = MethodEntrypoint(method, unboxingStub: false);
                return new TentativeMethodNode(entrypoint is TentativeMethodNode tentative ?
                    tentative.RealBody : (IMethodBodyNode)entrypoint);
            });

            _tentativeMethods = new NodeCache<IMethodBodyNode, TentativeInstanceMethodNode>(method =>
            {
                return new TentativeInstanceMethodNode(method);
            });

            _unboxingStubs = new NodeCache<MethodDesc, IMethodNode>(CreateUnboxingStubNode);

            _methodAssociatedData = new NodeCache<IMethodNode, MethodAssociatedDataNode>(methodNode =>
            {
                return new MethodAssociatedDataNode(methodNode);
            });

            _fatFunctionPointers = new NodeCache<MethodKey, FatFunctionPointerNode>(method =>
            {
                return new FatFunctionPointerNode(method.Method, method.IsUnboxingStub, addressTaken: false);
            });

            _fatAddressTakenFunctionPointers = new NodeCache<MethodKey, FatFunctionPointerNode>(method =>
            {
                return new FatFunctionPointerNode(method.Method, method.IsUnboxingStub, addressTaken: true);
            });

            _gvmDependenciesNode = new NodeCache<MethodDesc, GVMDependenciesNode>(method =>
            {
                return new GVMDependenciesNode(method);
            });

            _gvmImpls = new NodeCache<MethodDesc, GenericVirtualMethodImplNode>(method =>
            {
                return new GenericVirtualMethodImplNode(method);
            });

            _genericMethodEntries = new NodeCache<MethodDesc, GenericMethodsHashtableEntryNode>(method =>
            {
                return new GenericMethodsHashtableEntryNode(method);
            });

            _exactMethodEntries = new NodeCache<MethodDesc, ExactMethodInstantiationsEntryNode>(method =>
            {
                return new ExactMethodInstantiationsEntryNode(method);
            });

            _gvmTableEntries = new NodeCache<TypeDesc, TypeGVMEntriesNode>(type =>
            {
                return new TypeGVMEntriesNode(type);
            });

            _addressTakenMethods = new NodeCache<MethodDesc, AddressTakenMethodNode>(method =>
            {
                return new AddressTakenMethodNode(MethodEntrypoint(method, unboxingStub: false));
            });

            _reflectedDelegateTargetMethods = new NodeCache<MethodDesc, DelegateTargetVirtualMethodNode>(method =>
            {
                return new DelegateTargetVirtualMethodNode(method, reflected: true);
            });

            _delegateTargetMethods = new NodeCache<MethodDesc, DelegateTargetVirtualMethodNode>(method =>
            {
                return new DelegateTargetVirtualMethodNode(method, reflected: false);
            });

            _reflectedDelegates = new NodeCache<TypeDesc, ReflectedDelegateNode>(type =>
            {
                return new ReflectedDelegateNode(type);
            });

            _reflectedMethods = new NodeCache<MethodDesc, ReflectedMethodNode>(method =>
            {
                return new ReflectedMethodNode(method);
            });

            _reflectedFields = new NodeCache<FieldDesc, ReflectedFieldNode>(field =>
            {
                return new ReflectedFieldNode(field);
            });

            _reflectedTypes = new NodeCache<TypeDesc, ReflectedTypeNode>(type =>
            {
                TypeSystemContext.EnsureLoadableType(type);
                return new ReflectedTypeNode(type);
            });

            _notReadOnlyFields = new NodeCache<FieldDesc, NotReadOnlyFieldNode>(field =>
            {
                return new NotReadOnlyFieldNode(field);
            });

            _genericStaticBaseInfos = new NodeCache<MetadataType, GenericStaticBaseInfoNode>(type =>
            {
                return new GenericStaticBaseInfoNode(type);
            });

            _objectGetTypeCalled = new NodeCache<MetadataType, ObjectGetTypeCalledNode>(type =>
            {
                return new ObjectGetTypeCalledNode(type);
            });

            _objectGetTypeFlowDependencies = new NodeCache<MetadataType, ObjectGetTypeFlowDependenciesNode>(type =>
            {
                return new ObjectGetTypeFlowDependenciesNode(type);
            });

            _shadowConcreteMethods = new ShadowConcreteMethodHashtable(this);

            _shadowNonConcreteMethods = new ShadowNonConcreteMethodHashtable(this);

            _virtMethods = new VirtualMethodUseHashtable(this);

            _variantMethods = new NodeCache<MethodDesc, VariantInterfaceMethodUseNode>((MethodDesc method) =>
            {
                // We don't need to track virtual method uses for types that have a vtable with a known layout.
                // It's a waste of CPU time and memory.
                Debug.Assert(method.OwningType.IsGenericDefinition || !VTable(method.OwningType).HasKnownVirtualMethodUse);

                return new VariantInterfaceMethodUseNode(method);
            });

            _interfaceUses = new NodeCache<TypeDesc, InterfaceUseNode>((TypeDesc type) =>
            {
                return new InterfaceUseNode(type);
            });

            _readyToRunHelpers = new NodeCache<ReadyToRunHelperKey, ISymbolNode>(CreateReadyToRunHelperNode);

            _genericReadyToRunHelpersFromDict = new NodeCache<ReadyToRunGenericHelperKey, ISymbolNode>(CreateGenericLookupFromDictionaryNode);
            _genericReadyToRunHelpersFromType = new NodeCache<ReadyToRunGenericHelperKey, ISymbolNode>(CreateGenericLookupFromTypeNode);

            _frozenStringNodes = new NodeCache<string, FrozenStringNode>((string data) =>
            {
                return new FrozenStringNode(data, TypeSystemContext);
            });

            _frozenObjectNodes = new NodeCache<SerializedFrozenObjectKey, SerializedFrozenObjectNode>(key =>
            {
                return new SerializedFrozenObjectNode(key.OwnerType, key.AllocationSiteId, key.SerializableObject);
            });

            _frozenMetadataRuntimeTypeNodes = new NodeCache<TypeDesc, FrozenRuntimeTypeNode>(key =>
            {
                return new FrozenRuntimeTypeNode(key, withMetadata: true);
            });

            _frozenNecessaryRuntimeTypeNodes = new NodeCache<TypeDesc, FrozenRuntimeTypeNode>(key =>
            {
                return new FrozenRuntimeTypeNode(key, withMetadata: false);
            });

            _interfaceDispatchCells = new NodeCache<DispatchCellKey, InterfaceDispatchCellNode>(callSiteCell =>
            {
                return new InterfaceDispatchCellNode(callSiteCell.Target, callSiteCell.CallsiteId);
            });

            _interfaceDispatchMaps = new NodeCache<TypeDesc, InterfaceDispatchMapNode>((TypeDesc type) =>
            {
                return new InterfaceDispatchMapNode(this, type);
            });

            _sealedVtableNodes = new NodeCache<TypeDesc, SealedVTableNode>((TypeDesc type) =>
            {
                return new SealedVTableNode(type);
            });

            _runtimeMethodHandles = new NodeCache<MethodDesc, RuntimeMethodHandleNode>((MethodDesc method) =>
            {
                return new RuntimeMethodHandleNode(method);
            });

            _runtimeFieldHandles = new NodeCache<FieldDesc, RuntimeFieldHandleNode>((FieldDesc field) =>
            {
                return new RuntimeFieldHandleNode(field);
            });

            _dataflowAnalyzedMethods = new NodeCache<MethodILKey, DataflowAnalyzedMethodNode>((MethodILKey il) =>
            {
                return new DataflowAnalyzedMethodNode(il.MethodIL);
            });

            _dataflowAnalyzedTypeDefinitions = new NodeCache<TypeDesc, DataflowAnalyzedTypeDefinitionNode>((TypeDesc type) =>
            {
                return new DataflowAnalyzedTypeDefinitionNode(type);
            });

            _dynamicDependencyAttributesOnEntities = new NodeCache<TypeSystemEntity, DynamicDependencyAttributesOnEntityNode>((TypeSystemEntity entity) =>
            {
                return new DynamicDependencyAttributesOnEntityNode(entity);
            });

            _embeddedTrimmingDescriptors = new NodeCache<EcmaModule, EmbeddedTrimmingDescriptorNode>((module) =>
            {
                return new EmbeddedTrimmingDescriptorNode(module);
            });

            _genericCompositions = new NodeCache<Instantiation, GenericCompositionNode>((Instantiation details) =>
            {
                return new GenericCompositionNode(details, constructed: false);
            });

            _constructedGenericCompositions = new NodeCache<Instantiation, GenericCompositionNode>((Instantiation details) =>
            {
                return new GenericCompositionNode(details, constructed: true);
            });

            _genericVariances = new NodeCache<GenericVarianceDetails, GenericVarianceNode>((GenericVarianceDetails details) =>
            {
                return new GenericVarianceNode(details);
            });

            _eagerCctorIndirectionNodes = new NodeCache<MethodDesc, EmbeddedObjectNode>((MethodDesc method) =>
            {
                Debug.Assert(method.IsStaticConstructor);
                Debug.Assert(PreinitializationManager.HasEagerStaticConstructor((MetadataType)method.OwningType));
                return EagerCctorTable.NewNode(MethodEntrypoint(method));
            });

            _delegateMarshalingDataNodes = new NodeCache<DefType, DelegateMarshallingDataNode>(type =>
            {
                return new DelegateMarshallingDataNode(type);
            });

            _structMarshalingDataNodes = new NodeCache<DefType, StructMarshallingDataNode>(type =>
            {
                return new StructMarshallingDataNode(type);
            });

            _vTableNodes = new VTableSliceHashtable(this);

            _methodGenericDictionaries = new NodeCache<MethodDesc, ISortableSymbolNode>(method =>
            {
                if (CompilationModuleGroup.ContainsMethodDictionary(method))
                {
                    return new MethodGenericDictionaryNode(method, this);
                }
                else
                {
                    return _importedNodeProvider.ImportedMethodDictionaryNode(this, method);
                }
            });

            _typeGenericDictionaries = new NodeCache<TypeDesc, TypeGenericDictionaryNode>(type =>
            {
                Debug.Assert(CompilationModuleGroup.ContainsTypeDictionary(type));
                Debug.Assert(!this.LazyGenericsPolicy.UsesLazyGenerics(type));
                return new TypeGenericDictionaryNode(type, this);
            });

            _typesWithMetadata = new NodeCache<MetadataType, TypeMetadataNode>(type =>
            {
                return new TypeMetadataNode(type);
            });

            _methodsWithMetadata = new NodeCache<MethodDesc, MethodMetadataNode>(method =>
            {
                return new MethodMetadataNode(method, isMinimal: false);
            });

            _methodsWithLimitedMetadata = new NodeCache<MethodDesc, MethodMetadataNode>(method =>
            {
                return new MethodMetadataNode(method, isMinimal: true);
            });

            _fieldsWithMetadata = new NodeCache<FieldDesc, FieldMetadataNode>(field =>
            {
                return new FieldMetadataNode(field);
            });

            _propertiesWithMetadata = new NodeCache<PropertyPseudoDesc, PropertyMetadataNode>(property =>
            {
                return new PropertyMetadataNode(property);
            });

            _eventsWithMetadata = new NodeCache<EventPseudoDesc, EventMetadataNode>(@event =>
            {
                return new EventMetadataNode(@event);
            });

            _modulesWithMetadata = new NodeCache<ModuleDesc, ModuleMetadataNode>(module =>
            {
                return new ModuleMetadataNode(module);
            });

            _inlineableStringResources = new NodeCache<EcmaModule, InlineableStringsResourceNode>(module =>
            {
                return new InlineableStringsResourceNode(module);
            });

            _customAttributesWithMetadata = new NodeCache<ReflectableCustomAttribute, CustomAttributeMetadataNode>(ca =>
            {
                return new CustomAttributeMetadataNode(ca);
            });

            _parametersWithMetadata = new NodeCache<ReflectableParameter, MethodParameterMetadataNode>(p =>
            {
                return new MethodParameterMetadataNode(p);
            });

            _genericDictionaryLayouts = new NodeCache<TypeSystemEntity, DictionaryLayoutNode>(_dictionaryLayoutProvider.GetLayout);

            _stringAllocators = new NodeCache<MethodDesc, IMethodNode>(constructor =>
            {
                return new StringAllocatorMethodNode(constructor);
            });

            _externalTypeMapRequests = new NodeCache<TypeDesc, ExternalTypeMapRequestNode>(type =>
            {
                return new ExternalTypeMapRequestNode(type);
            });

            _proxyTypeMapRequests = new NodeCache<TypeDesc, ProxyTypeMapRequestNode>(type =>
            {
                return new ProxyTypeMapRequestNode(type);
            });

            _analysisCharacteristics = new NodeCache<string, AnalysisCharacteristicNode>(c =>
            {
                return new AnalysisCharacteristicNode(c);
            });

            _wasmTypeNodes = new NodeCache<WasmFuncType, WasmTypeNode>(key =>
            {
                return new WasmTypeNode(key);
            });

            NativeLayout = new NativeLayoutHelper(this);
        }

        protected virtual ISymbolNode CreateGenericLookupFromDictionaryNode(ReadyToRunGenericHelperKey helperKey)
        {
            return new ReadyToRunGenericLookupFromDictionaryNode(this, helperKey.HelperId, helperKey.Target, helperKey.DictionaryOwner);
        }

        protected virtual ISymbolNode CreateGenericLookupFromTypeNode(ReadyToRunGenericHelperKey helperKey)
        {
            return new ReadyToRunGenericLookupFromTypeNode(this, helperKey.HelperId, helperKey.Target, helperKey.DictionaryOwner);
        }

        private IEETypeNode CreateNecessaryTypeNode(TypeDesc type)
        {
            Debug.Assert(!_compilationModuleGroup.ShouldReferenceThroughImportTable(type));
            if (_compilationModuleGroup.ContainsType(type))
            {
                if (type.IsGenericDefinition)
                {
                    return new ReflectionInvisibleGenericDefinitionEETypeNode(this, type);
                }
                else if (type.IsCanonicalDefinitionType(CanonicalFormKind.Any))
                {
                    return new CanonicalDefinitionEETypeNode(this, type);
                }
                else
                {
                    return new EETypeNode(this, type);
                }
            }
            else
            {
                return new ExternEETypeSymbolNode(this, type);
            }
        }

        private IEETypeNode CreateMetadataTypeNode(TypeDesc type)
        {
            // Canonical definition types are *not* metadata types (call NecessaryTypeSymbol to get them)
            Debug.Assert(!type.IsCanonicalDefinitionType(CanonicalFormKind.Any));
            Debug.Assert(!_compilationModuleGroup.ShouldReferenceThroughImportTable(type));

            if (_compilationModuleGroup.ContainsType(type))
            {
                if (type.IsGenericDefinition)
                {
                    return new ReflectionVisibleGenericDefinitionEETypeNode(this, type);
                }
                else
                {
                    return new MetadataEETypeNode(this, type);
                }
            }
            else
            {
                return new ExternEETypeSymbolNode(this, type);
            }
        }

        private IEETypeNode CreateConstructedTypeNode(TypeDesc type)
        {
            // Canonical definition types are *not* constructed types (call NecessaryTypeSymbol to get them)
            Debug.Assert(!type.IsCanonicalDefinitionType(CanonicalFormKind.Any));
            Debug.Assert(!_compilationModuleGroup.ShouldReferenceThroughImportTable(type));
            Debug.Assert(!type.IsGenericDefinition);

            if (_compilationModuleGroup.ContainsType(type))
            {
                return new ConstructedEETypeNode(this, type);
            }
            else
            {
                return new ExternEETypeSymbolNode(this, type);
            }
        }

        protected abstract IMethodNode CreateMethodEntrypointNode(MethodDesc method);

        protected abstract IMethodNode CreateUnboxingStubNode(MethodDesc method);

        protected abstract ISymbolNode CreateReadyToRunHelperNode(ReadyToRunHelperKey helperCall);

        protected virtual ISymbolDefinitionNode CreateThreadStaticsNode(MetadataType type)
        {
            return new ThreadStaticsNode(type, this);
        }

        private abstract class TypeSymbolHashtable : LockFreeReaderHashtable<TypeDesc, IEETypeNode>
        {
            protected readonly NodeFactory _factory;
            public TypeSymbolHashtable(NodeFactory factory) => _factory = factory;
            protected override bool CompareKeyToValue(TypeDesc key, IEETypeNode value) => key == value.Type;
            protected override bool CompareValueToValue(IEETypeNode value1, IEETypeNode value2) => value1.Type == value2.Type;
            protected override int GetKeyHashCode(TypeDesc key) => key.GetHashCode();
            protected override int GetValueHashCode(IEETypeNode value) => value.Type.GetHashCode();
        }

        private sealed class NecessaryTypeSymbolHashtable : TypeSymbolHashtable
        {
            public NecessaryTypeSymbolHashtable(NodeFactory factory) : base(factory) { }
            protected override IEETypeNode CreateValueFromKey(TypeDesc key) => _factory.CreateNecessaryTypeNode(key);
        }

        private NodeCache<AsyncContinuationType, AsyncContinuationEETypeNode> _asyncContinuationEETypes;
        private NecessaryTypeSymbolHashtable _typeSymbols;

        public IEETypeNode NecessaryTypeSymbol(TypeDesc type)
        {
            if (_compilationModuleGroup.ShouldReferenceThroughImportTable(type))
            {
                return ImportedEETypeSymbol(type);
            }

            if (_compilationModuleGroup.ShouldPromoteToFullType(type))
            {
                return ConstructedTypeSymbol(type);
            }

            if (type is AsyncContinuationType continuation)
            {
                return _asyncContinuationEETypes.GetOrAdd(continuation);
            }

            Debug.Assert(!TypeCannotHaveEEType(type));

            return _typeSymbols.GetOrCreateValue(type);
        }

        private sealed class MetadataTypeSymbolHashtable : TypeSymbolHashtable
        {
            public MetadataTypeSymbolHashtable(NodeFactory factory) : base(factory) { }
            protected override IEETypeNode CreateValueFromKey(TypeDesc key) => _factory.CreateMetadataTypeNode(key);
        }

        private MetadataTypeSymbolHashtable _metadataTypeSymbols;

        public IEETypeNode MetadataTypeSymbol(TypeDesc type)
        {
            if (_compilationModuleGroup.ShouldReferenceThroughImportTable(type))
            {
                return ImportedEETypeSymbol(type);
            }

            if (_compilationModuleGroup.ShouldPromoteToFullType(type))
            {
                return ConstructedTypeSymbol(type);
            }

            // Generating typeof of a continuation type is not supported; we'd need a full MethodTable.
            Debug.Assert(type is not AsyncContinuationType);

            Debug.Assert(!TypeCannotHaveEEType(type));

            return _metadataTypeSymbols.GetOrCreateValue(type);
        }

        private sealed class ConstructedTypeSymbolHashtable : TypeSymbolHashtable
        {
            public ConstructedTypeSymbolHashtable(NodeFactory factory) : base(factory) { }
            protected override IEETypeNode CreateValueFromKey(TypeDesc key) => _factory.CreateConstructedTypeNode(key);
        }

        private ConstructedTypeSymbolHashtable _constructedTypeSymbols;

        public IEETypeNode ConstructedTypeSymbol(TypeDesc type)
        {
            if (_compilationModuleGroup.ShouldReferenceThroughImportTable(type))
            {
                return ImportedEETypeSymbol(type);
            }

            if (type is AsyncContinuationType continuation)
            {
                return _asyncContinuationEETypes.GetOrAdd(continuation);
            }

            Debug.Assert(!TypeCannotHaveEEType(type));

            return _constructedTypeSymbols.GetOrCreateValue(type);
        }

        public IEETypeNode MaximallyConstructableType(TypeDesc type)
        {
            if (ConstructedEETypeNode.CreationAllowed(type))
                return ConstructedTypeSymbol(type);
            else if (type.IsGenericDefinition)
                return MetadataTypeSymbol(type);
            else
                return NecessaryTypeSymbol(type);
        }

        private NodeCache<TypeDesc, IEETypeNode> _importedTypeSymbols;

        private IEETypeNode ImportedEETypeSymbol(TypeDesc type)
        {
            Debug.Assert(_compilationModuleGroup.ShouldReferenceThroughImportTable(type));
            return _importedTypeSymbols.GetOrAdd(type);
        }

        private NodeCache<MetadataType, ISortableSymbolNode> _nonGCStatics;

        public ISortableSymbolNode TypeNonGCStaticsSymbol(MetadataType type)
        {
            Debug.Assert(!TypeCannotHaveEEType(type));
            return _nonGCStatics.GetOrAdd(type);
        }

        private NodeCache<MetadataType, ISortableSymbolNode> _GCStatics;

        public ISortableSymbolNode TypeGCStaticsSymbol(MetadataType type)
        {
            Debug.Assert(!TypeCannotHaveEEType(type));
            return _GCStatics.GetOrAdd(type);
        }

        private NodeCache<MetadataType, EmbeddedObjectNode> _GCStaticIndirectionNodes;

        public EmbeddedObjectNode GCStaticIndirection(MetadataType type)
        {
            return _GCStaticIndirectionNodes.GetOrAdd(type);
        }

        private NodeCache<MetadataType, ISymbolDefinitionNode> _threadStatics;
        private ThreadStaticsNode _inlinedThreadStatiscNode;

        public ISymbolDefinitionNode TypeThreadStaticsSymbol(MetadataType type)
        {
            // This node is always used in the context of its index within the region.
            // We should never ask for this if the current compilation doesn't contain the
            // associated type.
            Debug.Assert(_compilationModuleGroup.ContainsType(type));
            return _threadStatics.GetOrAdd(type);
        }

        private NodeCache<MetadataType, TypeThreadStaticIndexNode> _typeThreadStaticIndices;

        public ISortableSymbolNode TypeThreadStaticIndex(MetadataType type)
        {
            if (_compilationModuleGroup.ContainsType(type))
            {
                return _typeThreadStaticIndices.GetOrAdd(type);
            }
            else
            {
                return ExternDataSymbol(NameMangler.NodeMangler.ThreadStaticsIndex(type));
            }
        }

        private NodeCache<DispatchCellKey, InterfaceDispatchCellNode> _interfaceDispatchCells;

        public InterfaceDispatchCellNode InterfaceDispatchCell(MethodDesc method, ISortableSymbolNode callSite = null)
        {
            return _interfaceDispatchCells.GetOrAdd(new DispatchCellKey(method, callSite));
        }

        private NodeCache<MethodDesc, RuntimeMethodHandleNode> _runtimeMethodHandles;

        public RuntimeMethodHandleNode RuntimeMethodHandle(MethodDesc method)
        {
            return _runtimeMethodHandles.GetOrAdd(method);
        }

        private NodeCache<FieldDesc, RuntimeFieldHandleNode> _runtimeFieldHandles;

        public RuntimeFieldHandleNode RuntimeFieldHandle(FieldDesc field)
        {
            return _runtimeFieldHandles.GetOrAdd(field);
        }

        private NodeCache<MethodILKey, DataflowAnalyzedMethodNode> _dataflowAnalyzedMethods;

        public DataflowAnalyzedMethodNode DataflowAnalyzedMethod(MethodIL methodIL)
        {
            return _dataflowAnalyzedMethods.GetOrAdd(new MethodILKey(methodIL));
        }

        private NodeCache<TypeDesc, DataflowAnalyzedTypeDefinitionNode> _dataflowAnalyzedTypeDefinitions;

        public DataflowAnalyzedTypeDefinitionNode DataflowAnalyzedTypeDefinition(TypeDesc type)
        {
            return _dataflowAnalyzedTypeDefinitions.GetOrAdd(type);
        }

        private NodeCache<TypeSystemEntity, DynamicDependencyAttributesOnEntityNode> _dynamicDependencyAttributesOnEntities;

        public DynamicDependencyAttributesOnEntityNode DynamicDependencyAttributesOnEntity(TypeSystemEntity entity)
        {
            return _dynamicDependencyAttributesOnEntities.GetOrAdd(entity);
        }

        private NodeCache<EcmaModule, EmbeddedTrimmingDescriptorNode> _embeddedTrimmingDescriptors;

        public EmbeddedTrimmingDescriptorNode EmbeddedTrimmingDescriptor(EcmaModule module)
        {
            return _embeddedTrimmingDescriptors.GetOrAdd(module);
        }

        private NodeCache<(GCPointerMap, bool), DataOnlyEETypeNode> _GCStaticEETypes;

        public ISymbolNode GCStaticEEType(GCPointerMap gcMap, bool requiredAlign8)
        {
            requiredAlign8 &= Target.SupportsAlign8;
            return _GCStaticEETypes.GetOrAdd((gcMap, requiredAlign8));
        }

        private NodeCache<ReadOnlyDataBlobKey, BlobNode> _readOnlyDataBlobs;

        public BlobNode ReadOnlyDataBlob(Utf8String name, byte[] blobData, int alignment)
        {
            return _readOnlyDataBlobs.GetOrAdd(new ReadOnlyDataBlobKey(name, blobData, alignment));
        }

        private NodeCache<Internal.TypeSystem.Ecma.EcmaField, FieldRvaDataNode> _fieldRvaDataBlobs;

        public ISymbolNode FieldRvaData(Internal.TypeSystem.Ecma.EcmaField field)
        {
            return _fieldRvaDataBlobs.GetOrAdd(field);
        }

        private NodeCache<TypeDesc, SealedVTableNode> _sealedVtableNodes;

        internal SealedVTableNode SealedVTable(TypeDesc type)
        {
            return _sealedVtableNodes.GetOrAdd(type);
        }

        private NodeCache<TypeDesc, InterfaceDispatchMapNode> _interfaceDispatchMaps;

        internal InterfaceDispatchMapNode InterfaceDispatchMap(TypeDesc type)
        {
            return _interfaceDispatchMaps.GetOrAdd(type);
        }

        private NodeCache<Instantiation, GenericCompositionNode> _genericCompositions;

        internal ISymbolNode GenericComposition(Instantiation details)
        {
            return _genericCompositions.GetOrAdd(details);
        }

        private NodeCache<Instantiation, GenericCompositionNode> _constructedGenericCompositions;

        internal ISymbolNode ConstructedGenericComposition(Instantiation details)
        {
            return _constructedGenericCompositions.GetOrAdd(details);
        }

        private NodeCache<GenericVarianceDetails, GenericVarianceNode> _genericVariances;

        internal ISymbolNode GenericVariance(GenericVarianceDetails details)
        {
            return _genericVariances.GetOrAdd(details);
        }

        private NodeCache<Utf8String, ExternFunctionSymbolNode> _externFunctionSymbols;

        public ISortableSymbolNode ExternFunctionSymbol(Utf8String name)
        {
            return _externFunctionSymbols.GetOrAdd(name);
        }

        private NodeCache<Utf8String, ExternFunctionSymbolNode> _externIndirectFunctionSymbols;

        public ISortableSymbolNode ExternIndirectFunctionSymbol(Utf8String name)
        {
            return _externIndirectFunctionSymbols.GetOrAdd(name);
        }

        private NodeCache<Utf8String, ExternDataSymbolNode> _externDataSymbols;

        public ISortableSymbolNode ExternDataSymbol(Utf8String name)
        {
            return _externDataSymbols.GetOrAdd(name);
        }

        public ISortableSymbolNode ExternVariable(Utf8String name)
        {
            Utf8String mangledName = NameMangler.NodeMangler.ExternVariable(name);
            return _externDataSymbols.GetOrAdd(mangledName);
        }

        private NodeCache<PInvokeModuleData, PInvokeModuleFixupNode> _pInvokeModuleFixups;

        public ISymbolNode PInvokeModuleFixup(PInvokeModuleData moduleData)
        {
            return _pInvokeModuleFixups.GetOrAdd(moduleData);
        }

        private NodeCache<PInvokeMethodData, PInvokeMethodFixupNode> _pInvokeMethodFixups;

        public PInvokeMethodFixupNode PInvokeMethodFixup(PInvokeMethodData methodData)
        {
            return _pInvokeMethodFixups.GetOrAdd(methodData);
        }

        private sealed class VTableSliceHashtable : LockFreeReaderHashtable<TypeDesc, VTableSliceNode>
        {
            private readonly NodeFactory _factory;
            public VTableSliceHashtable(NodeFactory factory) => _factory = factory;
            protected override bool CompareKeyToValue(TypeDesc key, VTableSliceNode value) => key == value.Type;
            protected override bool CompareValueToValue(VTableSliceNode value1, VTableSliceNode value2) => value1.Type == value2.Type;
            protected override VTableSliceNode CreateValueFromKey(TypeDesc key)
            {
                if (_factory.CompilationModuleGroup.ShouldProduceFullVTable(key))
                    return new EagerlyBuiltVTableSliceNode(key);
                else
                    return _factory._vtableSliceProvider.GetSlice(key);
            }
            protected override int GetKeyHashCode(TypeDesc key) => key.GetHashCode();
            protected override int GetValueHashCode(VTableSliceNode value) => value.Type.GetHashCode();
        }

        private VTableSliceHashtable _vTableNodes;

        public VTableSliceNode VTable(TypeDesc type)
        {
            return _vTableNodes.GetOrCreateValue(type);
        }

        private NodeCache<MethodDesc, ISortableSymbolNode> _methodGenericDictionaries;
        public ISortableSymbolNode MethodGenericDictionary(MethodDesc method)
        {
            return _methodGenericDictionaries.GetOrAdd(method);
        }

        private NodeCache<TypeDesc, TypeGenericDictionaryNode> _typeGenericDictionaries;
        public TypeGenericDictionaryNode TypeGenericDictionary(TypeDesc type)
        {
            return _typeGenericDictionaries.GetOrAdd(type);
        }

        private NodeCache<TypeSystemEntity, DictionaryLayoutNode> _genericDictionaryLayouts;
        public DictionaryLayoutNode GenericDictionaryLayout(TypeSystemEntity methodOrType)
        {
            return _genericDictionaryLayouts.GetOrAdd(methodOrType);
        }

        private NodeCache<MethodDesc, IMethodNode> _stringAllocators;
        public IMethodNode StringAllocator(MethodDesc stringConstructor)
        {
            return _stringAllocators.GetOrAdd(stringConstructor);
        }

        public uint ThreadStaticBaseOffset(MetadataType type)
        {
            if (_inlinedThreadStatics.IsComputed() &&
                _inlinedThreadStatics.GetOffsets().TryGetValue(type, out var offset))
            {
                return (uint)offset;
            }

            return 0;
        }

        private sealed class MethodEntrypointHashtable : LockFreeReaderHashtable<MethodDesc, IMethodNode>
        {
            private readonly NodeFactory _factory;
            public MethodEntrypointHashtable(NodeFactory factory) => _factory = factory;
            protected override bool CompareKeyToValue(MethodDesc key, IMethodNode value) => key == value.Method;
            protected override bool CompareValueToValue(IMethodNode value1, IMethodNode value2) => value1.Method == value2.Method;
            protected override IMethodNode CreateValueFromKey(MethodDesc key) => _factory.CreateMethodEntrypointNode(key);
            protected override int GetKeyHashCode(MethodDesc key) => key.GetHashCode();
            protected override int GetValueHashCode(IMethodNode value) => value.Method.GetHashCode();
        }

        private MethodEntrypointHashtable _methodEntrypoints;
        private NodeCache<MethodDesc, IMethodNode> _unboxingStubs;
        private NodeCache<IMethodNode, MethodAssociatedDataNode> _methodAssociatedData;

        public IMethodNode MethodEntrypoint(MethodDesc method, bool unboxingStub = false)
        {
            if (unboxingStub)
            {
                return _unboxingStubs.GetOrAdd(method);
            }

            return _methodEntrypoints.GetOrCreateValue(method);
        }

        protected NodeCache<MethodDesc, IMethodNode> _tentativeMethodEntrypoints;

        public IMethodNode TentativeMethodEntrypoint(MethodDesc method, bool unboxingStub = false)
        {
            // Didn't implement unboxing stubs for now. Would need to pass down the flag.
            Debug.Assert(!unboxingStub);
            return _tentativeMethodEntrypoints.GetOrAdd(method);
        }

        private NodeCache<IMethodBodyNode, TentativeInstanceMethodNode> _tentativeMethods;
        public IMethodNode MethodEntrypointOrTentativeMethod(MethodDesc method, bool unboxingStub = false)
        {
            // We might be able to optimize the method body away if the owning type was never seen as allocated.
            if (method.NotCallableWithoutOwningEEType() && CompilationModuleGroup.AllowInstanceMethodOptimization(method))
            {
                Debug.Assert(!unboxingStub);
                return _tentativeMethods.GetOrAdd((IMethodBodyNode)MethodEntrypoint(method, unboxingStub));
            }

            return MethodEntrypoint(method, unboxingStub);
        }

        public MethodAssociatedDataNode MethodAssociatedData(IMethodNode methodNode)
        {
            return _methodAssociatedData.GetOrAdd(methodNode);
        }

        private NodeCache<MethodKey, FatFunctionPointerNode> _fatFunctionPointers;

        public IMethodNode FatFunctionPointer(MethodDesc method, bool isUnboxingStub = false)
        {
            return _fatFunctionPointers.GetOrAdd(new MethodKey(method, isUnboxingStub));
        }

        private NodeCache<MethodKey, FatFunctionPointerNode> _fatAddressTakenFunctionPointers;

        public IMethodNode FatAddressTakenFunctionPointer(MethodDesc method, bool isUnboxingStub = false)
        {
            if (!CanFold(method))
                return FatFunctionPointer(method, isUnboxingStub);

            return _fatAddressTakenFunctionPointers.GetOrAdd(new MethodKey(method, isUnboxingStub));
        }

        public IMethodNode ExactCallableAddress(MethodDesc method, bool isUnboxingStub = false)
        {
            MethodDesc canonMethod = method.GetCanonMethodTarget(CanonicalFormKind.Specific);
            if (method != canonMethod)
                return FatFunctionPointer(method, isUnboxingStub);
            else
                return MethodEntrypoint(method, isUnboxingStub);
        }

        public IMethodNode ExactCallableAddressTakenAddress(MethodDesc method, bool isUnboxingStub = false)
        {
            MethodDesc canonMethod = method.GetCanonMethodTarget(CanonicalFormKind.Specific);
            if (method != canonMethod)
                return FatAddressTakenFunctionPointer(method, isUnboxingStub);
            else
                return AddressTakenMethodEntrypoint(method, isUnboxingStub);
        }

        public IMethodNode CanonicalEntrypoint(MethodDesc method)
        {
            MethodDesc canonMethod = method.GetCanonMethodTarget(CanonicalFormKind.Specific);
            if (method != canonMethod)
                return ShadowConcreteMethod(method);
            else
                return MethodEntrypoint(method);
        }

        private NodeCache<MethodDesc, GVMDependenciesNode> _gvmDependenciesNode;
        public GVMDependenciesNode GVMDependencies(MethodDesc method)
        {
            return _gvmDependenciesNode.GetOrAdd(method);
        }

        private NodeCache<MethodDesc, GenericVirtualMethodImplNode> _gvmImpls;
        public GenericVirtualMethodImplNode GenericVirtualMethodImpl(MethodDesc method)
        {
            return _gvmImpls.GetOrAdd(method);
        }

        private NodeCache<MethodDesc, GenericMethodsHashtableEntryNode> _genericMethodEntries;
        public GenericMethodsHashtableEntryNode GenericMethodsHashtableEntry(MethodDesc method)
        {
            return _genericMethodEntries.GetOrAdd(method);
        }

        private NodeCache<MethodDesc, ExactMethodInstantiationsEntryNode> _exactMethodEntries;
        public ExactMethodInstantiationsEntryNode ExactMethodInstantiationsHashtableEntry(MethodDesc method)
        {
            return _exactMethodEntries.GetOrAdd(method);
        }

        private NodeCache<TypeDesc, TypeGVMEntriesNode> _gvmTableEntries;
        internal TypeGVMEntriesNode TypeGVMEntries(TypeDesc type)
        {
            return _gvmTableEntries.GetOrAdd(type);
        }

        private NodeCache<MethodDesc, AddressTakenMethodNode> _addressTakenMethods;
        public IMethodNode AddressTakenMethodEntrypoint(MethodDesc method, bool unboxingStub = false)
        {
            if (unboxingStub || !CanFold(method))
                return MethodEntrypoint(method, unboxingStub);

            return _addressTakenMethods.GetOrAdd(method);
        }

        private NodeCache<MethodDesc, DelegateTargetVirtualMethodNode> _reflectedDelegateTargetMethods;
        public DelegateTargetVirtualMethodNode ReflectedDelegateTargetVirtualMethod(MethodDesc method)
        {
            return _reflectedDelegateTargetMethods.GetOrAdd(method);
        }

        private NodeCache<MethodDesc, DelegateTargetVirtualMethodNode> _delegateTargetMethods;
        public DelegateTargetVirtualMethodNode DelegateTargetVirtualMethod(MethodDesc method)
        {
            return _delegateTargetMethods.GetOrAdd(method);
        }

        private ReflectedDelegateNode _unknownReflectedDelegate = new ReflectedDelegateNode(null);
        private NodeCache<TypeDesc, ReflectedDelegateNode> _reflectedDelegates;
        public ReflectedDelegateNode ReflectedDelegate(TypeDesc type)
        {
            if (type == null)
                return _unknownReflectedDelegate;

            return _reflectedDelegates.GetOrAdd(type);
        }

        private NodeCache<MethodDesc, ReflectedMethodNode> _reflectedMethods;
        public ReflectedMethodNode ReflectedMethod(MethodDesc method)
        {
            return _reflectedMethods.GetOrAdd(method);
        }

        private NodeCache<FieldDesc, ReflectedFieldNode> _reflectedFields;
        public ReflectedFieldNode ReflectedField(FieldDesc field)
        {
            return _reflectedFields.GetOrAdd(field);
        }

        private NodeCache<TypeDesc, ReflectedTypeNode> _reflectedTypes;
        public ReflectedTypeNode ReflectedType(TypeDesc type)
        {
            return _reflectedTypes.GetOrAdd(type);
        }

        private NodeCache<FieldDesc, NotReadOnlyFieldNode> _notReadOnlyFields;
        public NotReadOnlyFieldNode NotReadOnlyField(FieldDesc field)
        {
            return _notReadOnlyFields.GetOrAdd(field);
        }

        private NodeCache<MetadataType, GenericStaticBaseInfoNode> _genericStaticBaseInfos;
        internal GenericStaticBaseInfoNode GenericStaticBaseInfo(MetadataType type)
        {
            return _genericStaticBaseInfos.GetOrAdd(type);
        }

        private NodeCache<MetadataType, ObjectGetTypeCalledNode> _objectGetTypeCalled;
        internal ObjectGetTypeCalledNode ObjectGetTypeCalled(MetadataType type)
        {
            return _objectGetTypeCalled.GetOrAdd(type);
        }

        private NodeCache<MetadataType, ObjectGetTypeFlowDependenciesNode> _objectGetTypeFlowDependencies;
        internal ObjectGetTypeFlowDependenciesNode ObjectGetTypeFlowDependencies(MetadataType type)
        {
            return _objectGetTypeFlowDependencies.GetOrAdd(type);
        }

        private sealed class ShadowConcreteMethodHashtable : LockFreeReaderHashtable<MethodDesc, ShadowConcreteMethodNode>
        {
            private readonly NodeFactory _factory;
            public ShadowConcreteMethodHashtable(NodeFactory factory) => _factory = factory;
            protected override bool CompareKeyToValue(MethodDesc key, ShadowConcreteMethodNode value) => key == value.Method;
            protected override bool CompareValueToValue(ShadowConcreteMethodNode value1, ShadowConcreteMethodNode value2) => value1.Method == value2.Method;
            protected override ShadowConcreteMethodNode CreateValueFromKey(MethodDesc key) =>
                new ShadowConcreteMethodNode(key, _factory.MethodEntrypoint(key.GetCanonMethodTarget(CanonicalFormKind.Specific)));
            protected override int GetKeyHashCode(MethodDesc key) => key.GetHashCode();
            protected override int GetValueHashCode(ShadowConcreteMethodNode value) => value.Method.GetHashCode();
        }

        private ShadowConcreteMethodHashtable _shadowConcreteMethods;
        public ShadowConcreteMethodNode ShadowConcreteMethod(MethodDesc method)
        {
            return _shadowConcreteMethods.GetOrCreateValue(method);
        }

        private sealed class ShadowNonConcreteMethodHashtable : LockFreeReaderHashtable<MethodDesc, ShadowNonConcreteMethodNode>
        {
            private readonly NodeFactory _factory;
            public ShadowNonConcreteMethodHashtable(NodeFactory factory) => _factory = factory;
            protected override bool CompareKeyToValue(MethodDesc key, ShadowNonConcreteMethodNode value) => key == value.Method;
            protected override bool CompareValueToValue(ShadowNonConcreteMethodNode value1, ShadowNonConcreteMethodNode value2) => value1.Method == value2.Method;
            protected override ShadowNonConcreteMethodNode CreateValueFromKey(MethodDesc key) =>
                new ShadowNonConcreteMethodNode(key, _factory.MethodEntrypoint(key.GetCanonMethodTarget(CanonicalFormKind.Specific)));
            protected override int GetKeyHashCode(MethodDesc key) => key.GetHashCode();
            protected override int GetValueHashCode(ShadowNonConcreteMethodNode value) => value.Method.GetHashCode();
        }

        private ShadowNonConcreteMethodHashtable _shadowNonConcreteMethods;
        public ShadowNonConcreteMethodNode ShadowNonConcreteMethod(MethodDesc method)
        {
            return _shadowNonConcreteMethods.GetOrCreateValue(method);
        }

        private static readonly string[][] s_helperEntrypointNames = new string[][] {
            new string[] { "System.Runtime.CompilerServices", "ClassConstructorRunner", "CheckStaticClassConstructionReturnGCStaticBase" },
            new string[] { "System.Runtime.CompilerServices", "ClassConstructorRunner", "CheckStaticClassConstructionReturnNonGCStaticBase" },
            new string[] { "System.Runtime.CompilerServices", "ClassConstructorRunner", "CheckStaticClassConstructionReturnThreadStaticBase" },
            new string[] { "Internal.Runtime", "ThreadStatics", "GetThreadStaticBaseForType" },
            new string[] { "Internal.Runtime", "ThreadStatics", "GetInlinedThreadStaticBaseSlow" },
        };

        private ISymbolNode[] _helperEntrypointSymbols;

        public ISymbolNode HelperEntrypoint(HelperEntrypoint entrypoint)
        {
            _helperEntrypointSymbols ??= new ISymbolNode[s_helperEntrypointNames.Length];

            int index = (int)entrypoint;

            ISymbolNode symbol = _helperEntrypointSymbols[index];
            if (symbol == null)
            {
                var entry = s_helperEntrypointNames[index];

                var type = _context.SystemModule.GetKnownType(Encoding.UTF8.GetBytes(entry[0]), Encoding.UTF8.GetBytes(entry[1]));
                var method = type.GetKnownMethod(Encoding.UTF8.GetBytes(entry[2]), null);

                symbol = MethodEntrypoint(method);

                _helperEntrypointSymbols[index] = symbol;
            }
            return symbol;
        }

        private MetadataType _systemArrayOfTClass;
        public MetadataType ArrayOfTClass
        {
            get
            {
                return _systemArrayOfTClass ??= _context.SystemModule.GetKnownType("System"u8, "Array`1"u8);
            }
        }

        private MethodDesc _instanceMethodRemovedHelper;
        public MethodDesc InstanceMethodRemovedHelper
        {
            get
            {
                // This helper is optional, but it's fine for this cache to be ineffective if that happens.
                // Those scenarios are rare and typically deal with small compilations.
                return _instanceMethodRemovedHelper ??= TypeSystemContext.GetOptionalHelperEntryPoint("ThrowHelpers"u8, "ThrowInstanceBodyRemoved"u8);
            }
        }

        private sealed class VirtualMethodUseHashtable : LockFreeReaderHashtable<MethodDesc, VirtualMethodUseNode>
        {
            private readonly NodeFactory _factory;
            public VirtualMethodUseHashtable(NodeFactory factory) => _factory = factory;
            protected override bool CompareKeyToValue(MethodDesc key, VirtualMethodUseNode value) => key == value.Method;
            protected override bool CompareValueToValue(VirtualMethodUseNode value1, VirtualMethodUseNode value2) => value1.Method == value2.Method;
            protected override VirtualMethodUseNode CreateValueFromKey(MethodDesc key)
            {
                // We don't need to track virtual method uses for types that have a vtable with a known layout.
                // It's a waste of CPU time and memory.
                Debug.Assert(!_factory.VTable(key.OwningType).HasKnownVirtualMethodUse);
                return new VirtualMethodUseNode(key);
            }
            protected override int GetKeyHashCode(MethodDesc key) => key.GetHashCode();
            protected override int GetValueHashCode(VirtualMethodUseNode value) => value.Method.GetHashCode();
        }

        private VirtualMethodUseHashtable _virtMethods;

        public DependencyNodeCore<NodeFactory> VirtualMethodUse(MethodDesc decl)
        {
            return _virtMethods.GetOrCreateValue(decl);
        }

        private NodeCache<MethodDesc, VariantInterfaceMethodUseNode> _variantMethods;

        public DependencyNodeCore<NodeFactory> VariantInterfaceMethodUse(MethodDesc decl)
        {
            return _variantMethods.GetOrAdd(decl);
        }

        private NodeCache<TypeDesc, InterfaceUseNode> _interfaceUses;

        public DependencyNodeCore<NodeFactory> InterfaceUse(TypeDesc type)
        {
            return _interfaceUses.GetOrAdd(type);
        }

        private NodeCache<ReadyToRunHelperKey, ISymbolNode> _readyToRunHelpers;

        public ISymbolNode ReadyToRunHelper(ReadyToRunHelperId id, object target)
        {
            return _readyToRunHelpers.GetOrAdd(new ReadyToRunHelperKey(id, target));
        }

        private NodeCache<ReadyToRunGenericHelperKey, ISymbolNode> _genericReadyToRunHelpersFromDict;

        public ISymbolNode ReadyToRunHelperFromDictionaryLookup(ReadyToRunHelperId id, object target, TypeSystemEntity dictionaryOwner)
        {
            return _genericReadyToRunHelpersFromDict.GetOrAdd(new ReadyToRunGenericHelperKey(id, target, dictionaryOwner));
        }

        private NodeCache<ReadyToRunGenericHelperKey, ISymbolNode> _genericReadyToRunHelpersFromType;

        public ISymbolNode ReadyToRunHelperFromTypeLookup(ReadyToRunHelperId id, object target, TypeSystemEntity dictionaryOwner)
        {
            return _genericReadyToRunHelpersFromType.GetOrAdd(new ReadyToRunGenericHelperKey(id, target, dictionaryOwner));
        }

        private NodeCache<MetadataType, TypeMetadataNode> _typesWithMetadata;

        internal TypeMetadataNode TypeMetadata(MetadataType type)
        {
            // These are only meaningful for UsageBasedMetadataManager. We should not have them
            // in the dependency graph otherwise.
            Debug.Assert(MetadataManager is UsageBasedMetadataManager);
            return _typesWithMetadata.GetOrAdd(type);
        }

        private NodeCache<MethodDesc, MethodMetadataNode> _methodsWithMetadata;

        internal MethodMetadataNode MethodMetadata(MethodDesc method)
        {
            // These are only meaningful for UsageBasedMetadataManager. We should not have them
            // in the dependency graph otherwise.
            Debug.Assert(MetadataManager is UsageBasedMetadataManager);
            return _methodsWithMetadata.GetOrAdd(method);
        }

        private NodeCache<MethodDesc, MethodMetadataNode> _methodsWithLimitedMetadata;

        internal MethodMetadataNode LimitedMethodMetadata(MethodDesc method)
        {
            // These are only meaningful for UsageBasedMetadataManager. We should not have them
            // in the dependency graph otherwise.
            Debug.Assert(MetadataManager is UsageBasedMetadataManager);
            return _methodsWithLimitedMetadata.GetOrAdd(method);
        }

        private NodeCache<FieldDesc, FieldMetadataNode> _fieldsWithMetadata;

        internal FieldMetadataNode FieldMetadata(FieldDesc field)
        {
            // These are only meaningful for UsageBasedMetadataManager. We should not have them
            // in the dependency graph otherwise.
            Debug.Assert(MetadataManager is UsageBasedMetadataManager);
            return _fieldsWithMetadata.GetOrAdd(field);
        }

        private NodeCache<PropertyPseudoDesc, PropertyMetadataNode> _propertiesWithMetadata;

        internal PropertyMetadataNode PropertyMetadata(PropertyPseudoDesc property)
        {
            // These are only meaningful for UsageBasedMetadataManager. We should not have them
            // in the dependency graph otherwise.
            Debug.Assert(MetadataManager is UsageBasedMetadataManager);
            return _propertiesWithMetadata.GetOrAdd(property);
        }

        private NodeCache<EventPseudoDesc, EventMetadataNode> _eventsWithMetadata;

        internal EventMetadataNode EventMetadata(EventPseudoDesc @event)
        {
            // These are only meaningful for UsageBasedMetadataManager. We should not have them
            // in the dependency graph otherwise.
            Debug.Assert(MetadataManager is UsageBasedMetadataManager);
            return _eventsWithMetadata.GetOrAdd(@event);
        }

        private NodeCache<ModuleDesc, ModuleMetadataNode> _modulesWithMetadata;

        internal ModuleMetadataNode ModuleMetadata(ModuleDesc module)
        {
            // These are only meaningful for UsageBasedMetadataManager. We should not have them
            // in the dependency graph otherwise.
            Debug.Assert(MetadataManager is UsageBasedMetadataManager);
            return _modulesWithMetadata.GetOrAdd(module);
        }

        private NodeCache<EcmaModule, InlineableStringsResourceNode> _inlineableStringResources;
        internal InlineableStringsResourceNode InlineableStringResource(EcmaModule module)
        {
            return _inlineableStringResources.GetOrAdd(module);
        }

        private NodeCache<ReflectableCustomAttribute, CustomAttributeMetadataNode> _customAttributesWithMetadata;

        internal CustomAttributeMetadataNode CustomAttributeMetadata(ReflectableCustomAttribute ca)
        {
            // These are only meaningful for UsageBasedMetadataManager. We should not have them
            // in the dependency graph otherwise.
            Debug.Assert(MetadataManager is UsageBasedMetadataManager);
            return _customAttributesWithMetadata.GetOrAdd(ca);
        }

        private NodeCache<ReflectableParameter, MethodParameterMetadataNode> _parametersWithMetadata;

        internal MethodParameterMetadataNode MethodParameterMetadata(ReflectableParameter ca)
        {
            // These are only meaningful for UsageBasedMetadataManager. We should not have them
            // in the dependency graph otherwise.
            Debug.Assert(MetadataManager is UsageBasedMetadataManager);
            return _parametersWithMetadata.GetOrAdd(ca);
        }

        private NodeCache<string, FrozenStringNode> _frozenStringNodes;

        public FrozenStringNode SerializedStringObject(string data)
        {
            return _frozenStringNodes.GetOrAdd(data);
        }

        private NodeCache<SerializedFrozenObjectKey, SerializedFrozenObjectNode> _frozenObjectNodes;

        public SerializedFrozenObjectNode SerializedFrozenObject(MetadataType owningType, int allocationSiteId, TypePreinit.ISerializableReference data)
        {
            return _frozenObjectNodes.GetOrAdd(new SerializedFrozenObjectKey(owningType, allocationSiteId, data));
        }

        private NodeCache<TypeDesc, FrozenRuntimeTypeNode> _frozenMetadataRuntimeTypeNodes;

        public FrozenRuntimeTypeNode SerializedMetadataRuntimeTypeObject(TypeDesc type)
        {
            return _frozenMetadataRuntimeTypeNodes.GetOrAdd(type);
        }

        private NodeCache<TypeDesc, FrozenRuntimeTypeNode> _frozenNecessaryRuntimeTypeNodes;

        public FrozenRuntimeTypeNode SerializedNecessaryRuntimeTypeObject(TypeDesc type)
        {
            return _frozenNecessaryRuntimeTypeNodes.GetOrAdd(type);
        }

        private NodeCache<MethodDesc, EmbeddedObjectNode> _eagerCctorIndirectionNodes;

        public EmbeddedObjectNode EagerCctorIndirection(MethodDesc cctorMethod)
        {
            return _eagerCctorIndirectionNodes.GetOrAdd(cctorMethod);
        }

        public ISymbolNode ConstantUtf8String(string str)
        {
            int stringBytesCount = Encoding.UTF8.GetByteCount(str);
            byte[] stringBytes = new byte[stringBytesCount + 1];
            Encoding.UTF8.GetBytes(str, 0, str.Length, stringBytes, 0);

            Utf8String symbolName = new Utf8String("__utf8str_" + NameMangler.GetMangledStringName(str));

            return ReadOnlyDataBlob(symbolName, stringBytes, 1);
        }

        private NodeCache<DefType, DelegateMarshallingDataNode> _delegateMarshalingDataNodes;

        public DelegateMarshallingDataNode DelegateMarshallingData(DefType type)
        {
            return _delegateMarshalingDataNodes.GetOrAdd(type);
        }

        private NodeCache<DefType, StructMarshallingDataNode> _structMarshalingDataNodes;

        public StructMarshallingDataNode StructMarshallingData(DefType type)
        {
            return _structMarshalingDataNodes.GetOrAdd(type);
        }

        private NodeCache<TypeDesc, ExternalTypeMapRequestNode> _externalTypeMapRequests;

        public ExternalTypeMapRequestNode ExternalTypeMapRequest(TypeDesc type)
        {
            return _externalTypeMapRequests.GetOrAdd(type);
        }

        private NodeCache<TypeDesc, ProxyTypeMapRequestNode> _proxyTypeMapRequests;

        public ProxyTypeMapRequestNode ProxyTypeMapRequest(TypeDesc type)
        {
            return _proxyTypeMapRequests.GetOrAdd(type);
        }

        private NodeCache<string, AnalysisCharacteristicNode> _analysisCharacteristics;
        public AnalysisCharacteristicNode AnalysisCharacteristic(string ch)
        {
            return _analysisCharacteristics.GetOrAdd(ch);
        }

        private NodeCache<WasmFuncType, WasmTypeNode> _wasmTypeNodes;

        // TODO-Wasm: Do not use WasmFuncType directly as the key for better
        // memory efficiency on lookup
        public WasmTypeNode WasmTypeNode(MethodDesc desc)
        {
            // TODO-Wasm: Construct proper function type based on the passed in MethodDesc
            // once we have defined lowering rules for signatures in NativeAOT.
            throw new NotImplementedException("NAOT wasm type signature lowering not yet implemented");
        }

        /// <summary>
        /// Returns alternative symbol name that object writer should produce for given symbols
        /// in addition to the regular one.
        /// </summary>
        public Utf8String GetSymbolAlternateName(ISymbolNode node, out bool isHidden)
        {
            if (!NodeAliases.TryGetValue(node, out var value))
            {
                isHidden = false;
                return default;
            }

            isHidden = value.Hidden;
            return value.Name;
        }

        public ArrayOfEmbeddedPointersNode<GCStaticsNode> GCStaticsRegion = new ArrayOfEmbeddedPointersNode<GCStaticsNode>(
            "__GCStaticRegion",
            new SortableDependencyNode.ObjectNodeComparer(CompilerComparer.Instance));

        public ArrayOfEmbeddedDataNode<ThreadStaticsNode> ThreadStaticsRegion = new ArrayOfEmbeddedDataNode<ThreadStaticsNode>(
            "__ThreadStaticRegion",
            new SortableDependencyNode.EmbeddedObjectNodeComparer(CompilerComparer.Instance));

        public ArrayOfEmbeddedPointersNode<IMethodNode> EagerCctorTable = new ArrayOfEmbeddedPointersNode<IMethodNode>(
            "__EagerCctor",
            null);

        public ArrayOfFrozenObjectsNode FrozenSegmentRegion = new ArrayOfFrozenObjectsNode();

        internal ModuleInitializerListNode ModuleInitializerList = new ModuleInitializerListNode();

        public InterfaceDispatchCellSectionNode InterfaceDispatchCellSection = new InterfaceDispatchCellSectionNode();

        public ReadyToRunHeaderNode ReadyToRunHeader;

        public Dictionary<ISymbolNode, (Utf8String Name, bool Hidden)> NodeAliases = new Dictionary<ISymbolNode, (Utf8String, bool)>();

        protected internal TypeManagerIndirectionNode TypeManagerIndirection = new TypeManagerIndirectionNode();

        public TlsRootNode TlsRoot = new TlsRootNode();

        public virtual void AttachToDependencyGraph(DependencyAnalyzerBase<NodeFactory> graph)
        {
            ReadyToRunHeader = new ReadyToRunHeaderNode();

            graph.AddRoot(ReadyToRunHeader, "ReadyToRunHeader is always generated");
            graph.AddRoot(new ModulesSectionNode(), "ModulesSection is always generated");

            graph.AddRoot(GCStaticsRegion, "GC StaticsRegion is always generated");
            graph.AddRoot(ThreadStaticsRegion, "ThreadStaticsRegion is always generated");
            graph.AddRoot(EagerCctorTable, "EagerCctorTable is always generated");
            graph.AddRoot(TypeManagerIndirection, "TypeManagerIndirection is always generated");
            graph.AddRoot(FrozenSegmentRegion, "FrozenSegmentRegion is always generated");
            graph.AddRoot(InterfaceDispatchCellSection, "Interface dispatch cell section is always generated");
            graph.AddRoot(ModuleInitializerList, "Module initializer list is always generated");

            if (_inlinedThreadStatics.IsComputed())
            {
                graph.AddRoot(_inlinedThreadStatiscNode, "Inlined threadstatics are used if present");
                graph.AddRoot(TlsRoot, "Inlined threadstatics are used if present");
            }

            ReadyToRunHeader.Add(ReadyToRunSectionType.GCStaticRegion, GCStaticsRegion);
            ReadyToRunHeader.Add(ReadyToRunSectionType.ThreadStaticRegion, ThreadStaticsRegion);
            ReadyToRunHeader.Add(ReadyToRunSectionType.EagerCctor, EagerCctorTable);
            ReadyToRunHeader.Add(ReadyToRunSectionType.TypeManagerIndirection, TypeManagerIndirection);
            ReadyToRunHeader.Add(ReadyToRunSectionType.FrozenObjectRegion, FrozenSegmentRegion);
            ReadyToRunHeader.Add(ReadyToRunSectionType.ModuleInitializerList, ModuleInitializerList);

            var commonFixupsTableNode = new ExternalReferencesTableNode("CommonFixupsTable", this);
            InteropStubManager.AddToReadyToRunHeader(ReadyToRunHeader, this, commonFixupsTableNode);
            TypeMapManager.AddToReadyToRunHeader(ReadyToRunHeader, this, new ExternalReferencesTableIndex(commonFixupsTableNode, this));
            MetadataManager.AddToReadyToRunHeader(ReadyToRunHeader, this, commonFixupsTableNode);
            MetadataManager.AttachToDependencyGraph(graph);
            TypeMapManager.AttachToDependencyGraph(graph);
            ReadyToRunHeader.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.CommonFixupsTable), commonFixupsTableNode);
        }

        private sealed class ExternalReferencesTableIndex(ExternalReferencesTableNode table, NodeFactory factory) : INativeFormatTypeReferenceProvider
        {
            public Vertex EncodeReferenceToMethod(NativeWriter writer, MethodDesc method)
                => writer.GetUnsignedConstant(table.GetIndex(factory.MethodEntrypoint(method)));
            public Vertex EncodeReferenceToType(NativeWriter writer, TypeDesc type)
                => writer.GetUnsignedConstant(table.GetIndex(factory.NecessaryTypeSymbol(type)));
        }

        protected struct MethodKey : IEquatable<MethodKey>
        {
            public readonly MethodDesc Method;
            public readonly bool IsUnboxingStub;

            public MethodKey(MethodDesc method, bool isUnboxingStub)
            {
                Method = method;
                IsUnboxingStub = isUnboxingStub;
            }

            public bool Equals(MethodKey other) => Method == other.Method && IsUnboxingStub == other.IsUnboxingStub;
            public override bool Equals(object obj) => obj is MethodKey && Equals((MethodKey)obj);
            public override int GetHashCode() => Method.GetHashCode();
        }

        protected struct ReadyToRunHelperKey : IEquatable<ReadyToRunHelperKey>
        {
            public readonly object Target;
            public readonly ReadyToRunHelperId HelperId;

            public ReadyToRunHelperKey(ReadyToRunHelperId helperId, object target)
            {
                HelperId = helperId;
                Target = target;
            }

            public bool Equals(ReadyToRunHelperKey other) => HelperId == other.HelperId && Target.Equals(other.Target);
            public override bool Equals(object obj) => obj is ReadyToRunHelperKey && Equals((ReadyToRunHelperKey)obj);
            public override int GetHashCode()
            {
                int hashCode = (int)HelperId * 0x5498341 + 0x832424;
                hashCode = hashCode * 23 + Target.GetHashCode();
                return hashCode;
            }
        }

        protected struct ReadyToRunGenericHelperKey : IEquatable<ReadyToRunGenericHelperKey>
        {
            public readonly object Target;
            public readonly TypeSystemEntity DictionaryOwner;
            public readonly ReadyToRunHelperId HelperId;

            public ReadyToRunGenericHelperKey(ReadyToRunHelperId helperId, object target, TypeSystemEntity dictionaryOwner)
            {
                HelperId = helperId;
                Target = target;
                DictionaryOwner = dictionaryOwner;
            }

            public bool Equals(ReadyToRunGenericHelperKey other)
                => HelperId == other.HelperId && DictionaryOwner == other.DictionaryOwner && Target.Equals(other.Target);
            public override bool Equals(object obj) => obj is ReadyToRunGenericHelperKey && Equals((ReadyToRunGenericHelperKey)obj);
            public override int GetHashCode()
            {
                int hashCode = (int)HelperId * 0x5498341 + 0x832424;
                hashCode = hashCode * 23 + Target.GetHashCode();
                hashCode = hashCode * 23 + DictionaryOwner.GetHashCode();
                return hashCode;
            }
        }

        protected struct DispatchCellKey : IEquatable<DispatchCellKey>
        {
            public readonly MethodDesc Target;
            public readonly ISortableSymbolNode CallsiteId;

            public DispatchCellKey(MethodDesc target, ISortableSymbolNode callsiteId)
            {
                Target = target;
                CallsiteId = callsiteId;
            }

            public bool Equals(DispatchCellKey other) => Target == other.Target && CallsiteId == other.CallsiteId;
            public override bool Equals(object obj) => obj is DispatchCellKey && Equals((DispatchCellKey)obj);
            public override int GetHashCode()
            {
                int hashCode = Target.GetHashCode();
                if (CallsiteId != null)
                    hashCode = hashCode * 23 + CallsiteId.GetHashCode();
                return hashCode;
            }
        }

        protected struct ReadOnlyDataBlobKey : IEquatable<ReadOnlyDataBlobKey>
        {
            public readonly Utf8String Name;
            public readonly byte[] Data;
            public readonly int Alignment;

            public ReadOnlyDataBlobKey(Utf8String name, byte[] data, int alignment)
            {
                Name = name;
                Data = data;
                Alignment = alignment;
            }

            // The assumption here is that the name of the blob is unique.
            // We can't emit two blobs with the same name and different contents.
            // The name is part of the symbolic name and we don't do any mangling on it.
            public bool Equals(ReadOnlyDataBlobKey other) => Name.Equals(other.Name);
            public override bool Equals(object obj) => obj is ReadOnlyDataBlobKey && Equals((ReadOnlyDataBlobKey)obj);
            public override int GetHashCode() => Name.GetHashCode();
        }

        protected struct SerializedFrozenObjectKey : IEquatable<SerializedFrozenObjectKey>
        {
            public readonly MetadataType OwnerType;
            public readonly int AllocationSiteId;
            public readonly TypePreinit.ISerializableReference SerializableObject;

            public SerializedFrozenObjectKey(MetadataType ownerType, int allocationSiteId, TypePreinit.ISerializableReference obj)
            {
                Debug.Assert(ownerType.HasStaticConstructor);
                OwnerType = ownerType;
                AllocationSiteId = allocationSiteId;
                SerializableObject = obj;
            }

            public override bool Equals(object obj) => obj is SerializedFrozenObjectKey && Equals((SerializedFrozenObjectKey)obj);
            public bool Equals(SerializedFrozenObjectKey other) => OwnerType == other.OwnerType && AllocationSiteId == other.AllocationSiteId;
            public override int GetHashCode() => HashCode.Combine(OwnerType.GetHashCode(), AllocationSiteId);
        }

        private struct MethodILKey : IEquatable<MethodILKey>
        {
            public readonly MethodIL MethodIL;

            public MethodILKey(MethodIL methodIL) => MethodIL = methodIL;
            public override bool Equals(object obj) => obj is MethodILKey other && Equals(other);
            public bool Equals(MethodILKey other) => other.MethodIL.OwningMethod == this.MethodIL.OwningMethod;
            public override int GetHashCode() => MethodIL.OwningMethod.GetHashCode();

        }
    }
}