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

using Internal.Text;
using Internal.TypeSystem;

namespace ILCompiler.DependencyAnalysis
{
    /// <summary>
    /// Represents the thread static region of a given type. This is very similar to <see cref="GCStaticsNode"/>,
    /// since the actual storage will be allocated on the GC heap at runtime and is allowed to contain GC pointers.
    /// </summary>
    public class ThreadStaticsNode : EmbeddedObjectNode, ISymbolDefinitionNode
    {
        private MetadataType _type;
        private InlinedThreadStatics _inlined;

        public ThreadStaticsNode(MetadataType type, NodeFactory factory)
        {
            Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Specific));
            Debug.Assert(!type.IsGenericDefinition);
            _type = type;
        }

        public ThreadStaticsNode(InlinedThreadStatics inlined, NodeFactory factory)
        {
            _inlined = inlined;
        }

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

        protected override void OnMarked(NodeFactory factory)
        {
            factory.ThreadStaticsRegion.AddEmbeddedObject(this);
        }

        public static Utf8String GetMangledName(TypeDesc type, NameMangler nameMangler)
        {
            return nameMangler.NodeMangler.ThreadStatics(type);
        }

        int ISymbolNode.Offset => 0;

        int ISymbolDefinitionNode.Offset => OffsetFromBeginningOfArray;

        public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
        {
            Utf8String mangledName = _type == null ? new Utf8String("_inlinedThreadStatics"u8) : GetMangledName(_type, nameMangler);
            sb.Append(mangledName);
        }

        private ISymbolNode GetGCStaticEETypeNode(NodeFactory factory)
        {
            GCPointerMap map = _type != null ?
                GCPointerMap.FromThreadStaticLayout(_type) :
                GCPointerMap.FromInlinedThreadStatics(
                    _inlined.GetTypes(),
                    _inlined.GetOffsets(),
                    _inlined.GetSize(),
                    factory.Target.PointerSize);
            bool requiresAlign8 = _type is not null && _type.ThreadGcStaticFieldAlignment.AsInt > factory.Target.PointerSize;

            return factory.GCStaticEEType(map, requiresAlign8);
        }

        public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory)
        {
            DependencyList result = new DependencyList();

            result.Add(new DependencyListEntry(GetGCStaticEETypeNode(factory), "ThreadStatic MethodTable"));

            if (_type != null)
            {
                if (factory.PreinitializationManager.HasEagerStaticConstructor(_type))
                {
                    result.Add(new DependencyListEntry(factory.EagerCctorIndirection(_type.GetStaticConstructor()), "Eager .cctor"));
                }

                ModuleUseBasedDependencyAlgorithm.AddDependenciesDueToModuleUse(ref result, factory, _type.Module);
            }
            else
            {
                foreach (var type in _inlined.GetTypes())
                {
                    if (factory.PreinitializationManager.HasEagerStaticConstructor(type))
                    {
                        result.Add(new DependencyListEntry(factory.EagerCctorIndirection(type.GetStaticConstructor()), "Eager .cctor"));
                    }

                    // inlined threadstatics do not need the index for execution, but may need it for debug visualization.
                    result.Add(new DependencyListEntry(factory.TypeThreadStaticIndex(type), "ThreadStatic index for debug visualization"));

                    ModuleUseBasedDependencyAlgorithm.AddDependenciesDueToModuleUse(ref result, factory, type.Module);
                }
            }

            return result;
        }

        public override bool HasConditionalStaticDependencies =>
            _type != null ?
                _type.ConvertToCanonForm(CanonicalFormKind.Specific) != _type:
                false;

        public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory)
        {
            Debug.Assert(_type != null);

            // If we have a type loader template for this type, we need to keep track of the generated
            // bases in the type info hashtable. The type symbol node does such accounting.
            return new CombinedDependencyListEntry[]
            {
                new CombinedDependencyListEntry(factory.NecessaryTypeSymbol(_type),
                    factory.NativeLayout.TemplateTypeLayout(_type.ConvertToCanonForm(CanonicalFormKind.Specific)),
                    "Keeping track of template-constructable type static bases"),
            };
        }

        public override bool StaticDependenciesAreComputed => true;

        public override void EncodeData(ref ObjectDataBuilder builder, NodeFactory factory, bool relocsOnly)
        {
            // At runtime, an instance of the GCStaticEEType will be created and a GCHandle to it
            // will be written in this location.
            builder.RequireInitialPointerAlignment();
            builder.EmitPointerReloc(GetGCStaticEETypeNode(factory));
        }

        public MetadataType Type => _type;

        public override int ClassCode => 2091208431;

        public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
        {
            // force the type of the storage block for inlined threadstatics to be "less"
            // than other storage blocks, - to ensure it is serialized as the item #0
            if (_type == null)
            {
                // there should only be at most one inlined storage type.
                Debug.Assert(other != null);
                return -1;
            }

            return comparer.Compare(_type, ((ThreadStaticsNode)other)._type);
        }

        internal int GetTypeStorageOffset(MetadataType type) => _inlined.GetOffsets()[type];
    }
}