File: Compiler\DependencyAnalysis\ReadyToRun\WasmClosedStaticRetBufThunkNode.cs
Web Access
Project: 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 System;
using System.Collections.Generic;
using System.Diagnostics;

using ILCompiler.DependencyAnalysis.Wasm;
using ILCompiler.DependencyAnalysisFramework;
using ILCompiler.ObjectWriter;
using ILCompiler.ObjectWriter.WasmInstructions;
using Internal.JitInterface;
using Internal.Text;
using Internal.TypeSystem;

namespace ILCompiler.DependencyAnalysis.ReadyToRun
{
    public sealed class WasmClosedStaticRetBufThunkNode : StringDiscoverableAssemblyStubNode, INodeWithTypeSignature, ISortableSymbolNode
    {
        private readonly TypeSystemContext _context;
        private readonly WasmSignature _signature;
        private readonly WasmTypeNode _typeNode;
        private readonly string _lookupString;

        public WasmClosedStaticRetBufThunkNode(NodeFactory factory, WasmSignature signature)
        {
            _context = factory.TypeSystemContext;
            _signature = signature;
            _typeNode = factory.WasmTypeNode(signature);
            _lookupString = GetLookupString("D", signature.FuncType);
        }

        MethodSignature INodeWithTypeSignature.Signature => WasmLowering.RaiseSignature(_signature, _context);
        bool INodeWithTypeSignature.IsUnmanagedCallersOnly => false;
        bool INodeWithTypeSignature.IsAsyncCall => false;
        bool INodeWithTypeSignature.HasGenericContextArg => false;

        public override string LookupString => _lookupString;

        public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
        {
            sb.Append("WasmClosedStaticRetBufThunk("u8);
            sb.Append(_lookupString);
            sb.Append(")"u8);
        }

        protected override string GetName(NodeFactory factory)
        {
            Utf8StringBuilder sb = new Utf8StringBuilder();
            AppendMangledName(factory.NameMangler, sb);
            return sb.ToString();
        }

        // Keep closed-static return-buffer thunks after the existing Wasm transition and virtual thunks.
        public override int ClassCode => 948271452;

        public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) =>
            _signature.FuncType.CompareTo(((WasmClosedStaticRetBufThunkNode)other)._signature.FuncType);

        protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory)
        {
            DependencyList dependencies = base.ComputeNonRelocationBasedDependencies(factory);
            dependencies.Add(_typeNode, "Wasm closed static return-buffer thunk requires type node");
            return dependencies;
        }

        protected override void EmitCode(NodeFactory factory, ref WasmEmitter instructionEncoder, bool relocsOnly)
        {
            Debug.Assert(!instructionEncoder.Is64Bit);
            Debug.Assert(_signature.SignatureString[0] == 'S');
            Debug.Assert(!_signature.SignatureString.Contains('a'));

            ReadOnlySpan<WasmValueType> parameters = _signature.FuncType.Params.Types;
            Debug.Assert(parameters.Length >= 4);

            int portableEntryPointIndex = parameters.Length - 1;
            int targetEntryPointLocalIndex = parameters.Length;
            List<WasmExpr> expressions = new List<WasmExpr>(parameters.Length + 8);

            // The target PEP is stored immediately before this stub's PEP.
            expressions.Add(Local.Get(portableEntryPointIndex));
            expressions.Add(I32.Const(factory.Target.PointerSize));
            expressions.Add(I32.Sub);
            expressions.Add(I32.Load(0));
            expressions.Add(Local.Set(targetEntryPointLocalIndex));

            expressions.Add(Local.Get(0)); // shadow stack pointer
            expressions.Add(Local.Get(2)); // return buffer
            expressions.Add(Local.Get(1)); // captured first argument

            for (int argumentIndex = 3; argumentIndex < portableEntryPointIndex; argumentIndex++)
            {
                expressions.Add(Local.Get(argumentIndex));
            }

            expressions.Add(Local.Get(targetEntryPointLocalIndex));
            expressions.Add(Local.Get(targetEntryPointLocalIndex));
            expressions.Add(I32.Load(0));
            expressions.Add(ControlFlow.CallIndirect(_typeNode, 0));

            instructionEncoder.FunctionBody = new WasmFunctionBody(
                _signature.FuncType,
                new[] { WasmValueType.I32 },
                expressions.ToArray());
        }

        protected override void EmitCode(NodeFactory factory, ref X64.X64Emitter instructionEncoder, bool relocsOnly) => throw new NotSupportedException();
        protected override void EmitCode(NodeFactory factory, ref X86.X86Emitter instructionEncoder, bool relocsOnly) => throw new NotSupportedException();
        protected override void EmitCode(NodeFactory factory, ref ARM.ARMEmitter instructionEncoder, bool relocsOnly) => throw new NotSupportedException();
        protected override void EmitCode(NodeFactory factory, ref ARM64.ARM64Emitter instructionEncoder, bool relocsOnly) => throw new NotSupportedException();
        protected override void EmitCode(NodeFactory factory, ref LoongArch64.LoongArch64Emitter instructionEncoder, bool relocsOnly) => throw new NotSupportedException();
        protected override void EmitCode(NodeFactory factory, ref RiscV64.RiscV64Emitter instructionEncoder, bool relocsOnly) => throw new NotSupportedException();
    }
}