File: Compiler\ObjectWriter\Dwarf\DwarfLineProgramTableWriter.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;
using System.Buffers.Binary;
using System.Collections.Generic;
using ILCompiler.DependencyAnalysis;

namespace ILCompiler.ObjectWriter
{
    internal sealed class DwarfLineProgramTableWriter : IDisposable
    {
        private readonly SectionWriter _lineSectionWriter;
        private readonly byte _targetPointerSize;
        private readonly RelocType _codeRelocType;
        private readonly Dictionary<string, uint> _directoryNameToIndex = new();
        private readonly Dictionary<DwarfFileName, uint> _fileNameToIndex = new();
        private readonly byte[] _sizeBuffer;

        public const byte MaximumOperationsPerInstruction = 1;
        public const sbyte LineBase = -5;
        public const byte LineRange = 14;
        public const byte OpCodeBase = 13;

        private static ReadOnlySpan<byte> StandardOpCodeLengths =>
        [
            0, // DW_LNS_copy
            1, // DW_LNS_advance_pc
            1, // DW_LNS_advance_line
            1, // DW_LNS_set_file
            1, // DW_LNS_set_column
            0, // DW_LNS_negate_stmt
            0, // DW_LNS_set_basic_block
            0, // DW_LNS_const_add_pc
            1, // DW_LNS_fixed_advance_pc
            0, // DW_LNS_set_prologue_end
            0, // DW_LNS_set_epilogue_begin
            1, // DW_LNS_set_isa
        ];

        public DwarfLineProgramTableWriter(
            SectionWriter lineSectionWriter,
            IReadOnlyList<DwarfFileName> fileNames,
            byte targetPointerSize,
            byte minimumInstructionLength,
            RelocType codeRelocType)
        {
            _lineSectionWriter = lineSectionWriter;
            _targetPointerSize = targetPointerSize;
            _codeRelocType = codeRelocType;

            // Length
            _sizeBuffer = new byte[sizeof(uint)];
            lineSectionWriter.EmitData(_sizeBuffer);
            // Version
            lineSectionWriter.WriteLittleEndian<ushort>(4);
            // Header Length
            var headerSizeBuffer = new byte[sizeof(uint)];
            lineSectionWriter.EmitData(headerSizeBuffer);
            var headerStart = lineSectionWriter.Position;
            lineSectionWriter.WriteByte(minimumInstructionLength);
            lineSectionWriter.WriteByte(MaximumOperationsPerInstruction);
            // default_is_stmt
            lineSectionWriter.WriteByte(1);
            // line_base
            lineSectionWriter.WriteByte(unchecked((byte)LineBase));
            // line_range
            lineSectionWriter.WriteByte(LineRange);
            // opcode_base
            lineSectionWriter.WriteByte(OpCodeBase);
            // standard_opcode_lengths
            foreach (var opcodeLength in StandardOpCodeLengths)
            {
                lineSectionWriter.WriteULEB128(opcodeLength);
            }

            // Directory names
            uint directoryIndex = 1;
            foreach (var fileName in fileNames)
            {
                if (fileName.Directory is string directoryName &&
                    !string.IsNullOrEmpty(directoryName) &&
                    !_directoryNameToIndex.ContainsKey(directoryName))
                {
                    lineSectionWriter.WriteUtf8String(directoryName);
                    _directoryNameToIndex.Add(directoryName, directoryIndex);
                    directoryIndex++;
                }
            }
            // Terminate directory list (empty string)
            lineSectionWriter.WriteByte(0);

            // File names
            uint fileNameIndex = 1;
            foreach (var fileName in fileNames)
            {
                directoryIndex = fileName.Directory is string directoryName && !string.IsNullOrEmpty(directoryName) ? _directoryNameToIndex[directoryName] : 0;

                lineSectionWriter.WriteUtf8String(fileName.Name);
                lineSectionWriter.WriteULEB128(directoryIndex);
                lineSectionWriter.WriteULEB128(fileName.Time);
                lineSectionWriter.WriteULEB128(fileName.Size);

                _fileNameToIndex[fileName] = fileNameIndex;
                fileNameIndex++;
            }
            // Terminate file name list (empty string)
            lineSectionWriter.WriteByte(0);

            // Update header size
            BinaryPrimitives.WriteInt32LittleEndian(headerSizeBuffer, (int)(lineSectionWriter.Position - headerStart));
        }

        public void Dispose()
        {
            // Update size
            BinaryPrimitives.WriteInt32LittleEndian(_sizeBuffer, (int)(_lineSectionWriter.Position - sizeof(uint)));
        }

        public void WriteLineSequence(DwarfLineSequenceWriter lineSequenceWriter)
        {
            lineSequenceWriter.Write(_lineSectionWriter, _targetPointerSize, _codeRelocType);
        }
    }
}