File: Compiler\DependencyAnalysis\ReadyToRun\WasmAsyncResumeInfoFixupsNode.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 Internal.Text;

namespace ILCompiler.DependencyAnalysis.ReadyToRun
{
    internal sealed class WasmAsyncResumeInfoFixupsNode : HeaderTableNode
    {
        private static readonly ObjectNodeSection FixupSection =
            new ObjectNodeSection("wasm.asyncresumeinfo", SectionType.ReadOnly);

        private readonly List<Fixup> _fixups = new List<Fixup>();

        public override ObjectNodeSection GetSection(NodeFactory factory) => FixupSection;

        public void AddFixup(ISymbolNode location, int offset)
        {
            lock (_fixups)
            {
                _fixups.Add(new Fixup(location, offset));
            }
        }

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

        public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
        {
            Fixup[] fixups;
            lock (_fixups)
            {
                fixups = _fixups.ToArray();
            }

            Array.Sort(fixups, CompareFixups);

            int stride = factory.Target.PointerSize * 2;
            List<FixupChunk> chunks = new List<FixupChunk>();
            for (int i = 0; i < fixups.Length;)
            {
                Fixup first = fixups[i];
                int count = 1;
                while ((i + count) < fixups.Length &&
                       fixups[i + count].Location == first.Location &&
                       fixups[i + count].Offset == first.Offset + count * stride)
                {
                    count++;
                }

                chunks.Add(new FixupChunk(first.Location, first.Offset, count, stride));
                i += count;
            }

            ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly);
            builder.AddSymbol(this);
            EmitULEB128(ref builder, checked((uint)chunks.Count));

            foreach (FixupChunk chunk in chunks)
            {
                builder.EmitReloc(chunk.Location, RelocType.WASM_ASYNC_RESUME_INFO_DELTA_ULEB, chunk.Offset);
                EmitULEB128(ref builder, checked((uint)chunk.Count));
                EmitULEB128(ref builder, checked((uint)chunk.Stride));
            }

            return builder.ToObjectData();
        }

        public override int ClassCode => 1794382631;

        private static int CompareFixups(Fixup left, Fixup right)
        {
            int result = CompilerComparer.Instance.Compare((ISortableNode)left.Location, (ISortableNode)right.Location);
            return result != 0 ? result : left.Offset.CompareTo(right.Offset);
        }

        private static void EmitULEB128(ref ObjectDataBuilder builder, uint value)
        {
            do
            {
                byte current = (byte)(value & 0x7F);
                value >>= 7;
                if (value != 0)
                {
                    current |= 0x80;
                }
                builder.EmitByte(current);
            } while (value != 0);
        }

        private readonly struct Fixup
        {
            public Fixup(ISymbolNode location, int offset)
            {
                Location = location;
                Offset = offset;
            }

            public ISymbolNode Location { get; }
            public int Offset { get; }
        }

        private readonly struct FixupChunk
        {
            public FixupChunk(ISymbolNode location, int offset, int count, int stride)
            {
                Location = location;
                Offset = offset;
                Count = count;
                Stride = stride;
            }

            public ISymbolNode Location { get; }
            public int Offset { get; }
            public int Count { get; }
            public int Stride { get; }
        }
    }
}