File: Compiler\DependencyAnalysis\ReadyToRun\StoreMultiCallableAddrOfCodeSignature.cs
Web Access
Project: src\runtime\src\coreclr\tools\aot\ILCompiler.ReadyToRun\ILCompiler.ReadyToRun.csproj (ILCompiler.ReadyToRun)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Internal.ReadyToRunConstants;
using Internal.Text;
using Internal.TypeSystem;

namespace ILCompiler.DependencyAnalysis.ReadyToRun
{
    /// <summary>
    /// Signature node for the READYTORUN_FIXUP_StoreMultiCallableAddrOfCode fixup.
    ///
    /// This method-load-time fixup stores the runtime MultiCallableAddrOfCode of a target method
    /// into a location embedded in the R2R image (a slot in the compiled method's read-only data
    /// blob). It is used on WebAssembly where a callable code pointer cannot be materialized at
    /// compile time (the pointer representation is only known at runtime).
    ///
    /// The signature following the fixup kind byte is:
    ///   - target code RVA (4 bytes): points at the target method's code. On WebAssembly this is a
    ///     function-table index relative to the image (WASM_TABLE_INDEX_REL_I32); elsewhere an
    ///     imageBase RVA (IMAGE_REL_BASED_ADDR32NB). This matches the encoding used by
    ///     <see cref="ResumptionStubEntryPointSignature"/> so that the resulting entry point value
    ///     matches the one registered by the ResumptionStubEntryPoint fixup.
    ///   - location RVA (4 bytes): an IMAGE_REL_BASED_ADDR32NB RVA pointing at the location that the
    ///     runtime must overwrite with the MultiCallableAddrOfCode value.
    /// </summary>
    internal class StoreMultiCallableAddrOfCodeSignature : Signature
    {
        private readonly MethodWithGCInfo _target;
        private readonly ISymbolNode _location;
        private readonly int _locationOffset;

        public StoreMultiCallableAddrOfCodeSignature(MethodWithGCInfo target, ISymbolNode location, int locationOffset)
        {
            _target = target;
            _location = location;
            _locationOffset = locationOffset;
        }

        // The ClassCode must be greater than ResumptionStubEntryPointSignature.ClassCode (1927438562)
        // so that this fixup sorts (and therefore is processed) AFTER the ResumptionStubEntryPoint
        // fixup for the same method. That fixup registers the target (resumption stub) entry point so
        // that this fixup can resolve it to a MethodDesc at runtime. Imports within an import section
        // are ordered by CompilerComparer, which orders distinct node classes by ClassCode, and the
        // fixup blob is emitted in ascending import-offset order.
        public override int ClassCode => 1976543219;

        public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
        {
            ObjectDataSignatureBuilder builder = new ObjectDataSignatureBuilder(factory, relocsOnly);
            builder.AddSymbol(this);
            builder.EmitByte((byte)ReadyToRunFixupKind.StoreMultiCallableAddrOfCode);

            // On wasm the target code is a function-table index (WASM_TABLE_INDEX_REL_I32); elsewhere
            // an imageBase RVA. Keep this in sync with ResumptionStubEntryPointSignature.
            RelocType codeRelocType = factory.Target.Architecture == TargetArchitecture.Wasm32
                ? RelocType.WASM_TABLE_INDEX_REL_I32
                : RelocType.IMAGE_REL_BASED_ADDR32NB;
            builder.EmitReloc(_target, codeRelocType);

            // The location to update is always a data address expressed as an imageBase RVA.
            builder.EmitReloc(_location, RelocType.IMAGE_REL_BASED_ADDR32NB, delta: _locationOffset);

            return builder.ToObjectData();
        }

        public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
        {
            sb.Append("StoreMultiCallableAddrOfCode_"u8);
            sb.Append(nameMangler.GetMangledMethodName(_target.Method));
            sb.Append("_"u8);
            _location.AppendMangledName(nameMangler, sb);
            sb.Append("+"u8);
            sb.Append(_locationOffset);
        }

        public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
        {
            StoreMultiCallableAddrOfCodeSignature otherNode = (StoreMultiCallableAddrOfCodeSignature)other;
            int result = comparer.Compare(_target.Method, otherNode._target.Method);
            if (result != 0)
                return result;

            result = comparer.Compare((ISortableNode)_location, (ISortableNode)otherNode._location);
            if (result != 0)
                return result;

            return _locationOffset.CompareTo(otherNode._locationOffset);
        }
    }
}