File: Compiler\DependencyAnalysis\FatFunctionPointerNode.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 Internal.Text;
using Internal.TypeSystem;

using Debug = System.Diagnostics.Debug;

namespace ILCompiler.DependencyAnalysis
{
    /// <summary>
    /// Represents a fat function pointer - a data structure that captures a pointer to a canonical
    /// method body along with the instantiation context the canonical body requires.
    /// Pointers to these structures can be created by e.g. ldftn/ldvirtftn of a method with a canonical body.
    /// </summary>
    public class FatFunctionPointerNode : DehydratableObjectNode, IMethodNode, ISymbolDefinitionNode
    {
        private readonly bool _isUnboxingStub;
        private readonly bool _isAddressTaken;

        public bool IsUnboxingStub => _isUnboxingStub;

        public FatFunctionPointerNode(MethodDesc methodRepresented, bool isUnboxingStub, bool addressTaken)
        {
            // We should not create these for methods that don't have a canonical method body
            Debug.Assert(methodRepresented.GetCanonMethodTarget(CanonicalFormKind.Specific) != methodRepresented);

            Method = methodRepresented;
            _isUnboxingStub = isUnboxingStub;
            _isAddressTaken = addressTaken;
        }

        public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
        {
            string prefix = $"__fat{(_isUnboxingStub ? "unbox" : "")}{(_isAddressTaken ? "addresstaken" : "")}pointer_";
            sb.Append(prefix).Append(nameMangler.GetMangledMethodName(Method));
        }

        int ISymbolDefinitionNode.Offset => 0;
        int ISymbolNode.Offset => Method.Context.Target.Architecture == TargetArchitecture.Wasm32 ? 1 << 31 : 2;

        public override bool IsShareable => true;

        public MethodDesc Method { get; }

        protected override ObjectNodeSection GetDehydratedSection(NodeFactory factory)
        {
            if (factory.Target.IsWindows)
                return ObjectNodeSection.ReadOnlyDataSection;
            else
                return ObjectNodeSection.DataSection;
        }

        public override bool StaticDependenciesAreComputed => true;

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

        protected override ObjectData GetDehydratableData(NodeFactory factory, bool relocsOnly = false)
        {
            var builder = new ObjectDataBuilder(factory, relocsOnly);

            // These need to be aligned the same as method bodies because they show up in same contexts
            // (macOS ARM64 has even stricter alignment requirement for the linker, so round up to pointer size)
            Debug.Assert(factory.Target.MinimumFunctionAlignment <= factory.Target.PointerSize);
            builder.RequireInitialAlignment(factory.Target.PointerSize);

            builder.AddSymbol(this);

            MethodDesc canonMethod = Method.GetCanonMethodTarget(CanonicalFormKind.Specific);

            // Pointer to the canonical body of the method
            ISymbolNode target = _isAddressTaken
                ? factory.AddressTakenMethodEntrypoint(canonMethod, _isUnboxingStub)
                : factory.MethodEntrypoint(canonMethod, _isUnboxingStub);
            builder.EmitPointerReloc(target);

            // Find out what's the context to use
            ISortableSymbolNode contextParameter;
            if (canonMethod.RequiresInstMethodDescArg())
            {
                contextParameter = factory.MethodGenericDictionary(Method);
            }
            else
            {
                Debug.Assert(canonMethod.RequiresInstMethodTableArg());

                // Ask for a constructed type symbol because we need the vtable to get to the dictionary
                contextParameter = factory.ConstructedTypeSymbol(Method.OwningType);
            }

            // The next entry is a pointer to the context to be used for the canonical method
            builder.EmitPointerReloc(contextParameter);

            return builder.ToObjectData();
        }

        public override int ClassCode => 190463489;

        public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
        {
            var compare = _isUnboxingStub.CompareTo(((FatFunctionPointerNode)other)._isUnboxingStub);
            if (compare != 0)
                return compare;

            compare = _isAddressTaken.CompareTo(((FatFunctionPointerNode)other)._isAddressTaken);
            if (compare != 0)
                return compare;

            return comparer.Compare(Method, ((FatFunctionPointerNode)other).Method);
        }
    }
}