Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,22 @@ namespace ILCompiler.ObjectWriter
/// </remarks>
internal partial class CoffObjectWriter : ObjectWriter
{
protected sealed record SectionDefinition(CoffSectionHeader Header, Stream Stream, List<CoffRelocation> Relocations, Utf8String ComdatName, Utf8String SymbolName);
protected sealed class SectionDefinition
{
public SectionDefinition(CoffSectionHeader header, Stream stream, Utf8String comdatName, Utf8String symbolName)
{
Header = header;
Stream = stream;
ComdatName = comdatName;
SymbolName = symbolName;
}

public CoffSectionHeader Header { get; }
public Stream Stream { get; }
public List<CoffRelocation> Relocations { get; set; }
public Utf8String ComdatName { get; }
public Utf8String SymbolName { get; }
}

protected readonly Machine _machine;
protected readonly List<SectionDefinition> _sections = new();
Expand Down Expand Up @@ -156,7 +171,7 @@ private protected override void CreateSection(ObjectNodeSection section, Utf8Str
}
}

_sections.Add(new SectionDefinition(sectionHeader, sectionStream, new List<CoffRelocation>(), comdatName, symbolName));
_sections.Add(new SectionDefinition(sectionHeader, sectionStream, comdatName, symbolName));
}

protected internal override void UpdateSectionAlignment(int sectionIndex, int alignment)
Expand Down Expand Up @@ -278,10 +293,17 @@ private protected override void EmitSymbolTable(

private protected override void EmitRelocations(int sectionIndex, List<SymbolicRelocation> relocationList)
{
CoffSectionHeader sectionHeader = _sections[sectionIndex].Header;
List<CoffRelocation> coffRelocations = _sections[sectionIndex].Relocations;
SectionDefinition section = _sections[sectionIndex];
CoffSectionHeader sectionHeader = section.Header;
if (relocationList.Count > 0)
{
List<CoffRelocation> coffRelocations = section.Relocations;
if (coffRelocations is null)
{
coffRelocations = new List<CoffRelocation>();
section.Relocations = coffRelocations;
}

if (relocationList.Count <= ushort.MaxValue)
{
sectionHeader.NumberOfRelocations = (ushort)relocationList.Count;
Expand Down Expand Up @@ -405,8 +427,9 @@ private protected override void EmitObjectFile(Stream outputFileStream)
}

// Section relocations
section.Header.PointerToRelocations = section.Relocations.Count > 0 ? dataOffset : 0;
dataOffset += (uint)(section.Relocations.Count * CoffRelocation.Size);
int relocationCount = section.Relocations?.Count ?? 0;
section.Header.PointerToRelocations = relocationCount > 0 ? dataOffset : 0;
dataOffset += (uint)(relocationCount * CoffRelocation.Size);

// Record the section layout
_outputSectionLayout.Add(new OutputSection(section.Header.Name, section.Header.PointerToRawData, section.Header.VirtualAddress, section.Header.SizeOfRawData));
Expand Down Expand Up @@ -452,9 +475,9 @@ private protected override void EmitObjectFile(Stream outputFileStream)
section.Stream.CopyTo(outputFileStream);
}

if (section.Relocations.Count > 0)
if (section.Relocations is List<CoffRelocation> relocations)
{
foreach (var relocation in section.Relocations)
foreach (CoffRelocation relocation in relocations)
{
relocation.Write(outputFileStream);
}
Expand Down
24 changes: 20 additions & 4 deletions src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ private protected SectionWriter GetOrCreateSection(ObjectNodeSection section, Ut
sectionIndex = _sectionIndexToData.Count;
CreateSection(section, comdatName, symbolName, sectionIndex, sectionData.GetReadStream());
_sectionIndexToData.Add(sectionData);
_sectionIndexToRelocations.Add(new());
_sectionIndexToRelocations.Add(null);
if (comdatName.IsNull)
{
_sectionNameToSectionIndex.Add(section.Name, sectionIndex);
Expand Down Expand Up @@ -234,12 +234,19 @@ protected internal virtual void EmitRelocation(
Utf8String symbolName,
long addend)
{
_sectionIndexToRelocations[sectionIndex].Add(new SymbolicRelocation(offset, relocType, symbolName, addend));
List<SymbolicRelocation> relocations = _sectionIndexToRelocations[sectionIndex];
if (relocations is null)
{
relocations = new List<SymbolicRelocation>();
_sectionIndexToRelocations[sectionIndex] = relocations;
}

relocations.Add(new SymbolicRelocation(offset, relocType, symbolName, addend));
}

private protected bool SectionHasRelocations(int sectionIndex)
{
return _sectionIndexToRelocations[sectionIndex].Count > 0;
return _sectionIndexToRelocations[sectionIndex]?.Count > 0;
}

private protected virtual void EmitReferencedMethod(Utf8String symbolName) { }
Expand Down Expand Up @@ -310,6 +317,11 @@ private SortedSet<Utf8String> GetUndefinedSymbols()
SortedSet<Utf8String> undefinedSymbolSet = new SortedSet<Utf8String>();
foreach (var relocationList in _sectionIndexToRelocations)
{
if (relocationList is null)
{
continue;
}

foreach (var symbolicRelocation in relocationList)
{
if (!_definedSymbols.ContainsKey(symbolicRelocation.SymbolName))
Expand Down Expand Up @@ -597,7 +609,11 @@ public virtual void EmitObject(Stream outputFileStream, IReadOnlyCollection<Depe
int relocSectionIndex = 0;
foreach (List<SymbolicRelocation> relocationList in _sectionIndexToRelocations)
{
EmitRelocations(relocSectionIndex, relocationList);
if (relocationList is not null)
{
EmitRelocations(relocSectionIndex, relocationList);
}

relocSectionIndex++;
}

Expand Down
100 changes: 78 additions & 22 deletions src/coreclr/tools/Common/Compiler/ObjectWriter/SectionData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,47 +16,69 @@ namespace ILCompiler.ObjectWriter
/// Optimized append-only structure for writing sections.
/// </summary>
/// <remarks>
/// The section data are kept in memory as a list of buffers. It supports
/// appending existing read-only buffers without copying (such as buffer
/// from ObjectNode.ObjectData).
/// The section data are kept in memory as a sequence of buffers, with the
/// first buffer stored directly and a list allocated only when needed. It
/// supports appending existing read-only buffers without copying (such as
/// buffer from ObjectNode.ObjectData).
/// </remarks>
internal sealed class SectionData
{
private readonly ArrayBufferWriter<byte> _appendBuffer = new();
private readonly List<ReadOnlyMemory<byte>> _buffers = new();
private const int PaddingBufferSize = 16;
private const byte NopPaddingByte = 0x90;

private static readonly byte[] s_zeroPadding = new byte[PaddingBufferSize];
private static readonly byte[] s_nopPadding = CreatePadding(NopPaddingByte);

private ArrayBufferWriter<byte> _appendBuffer;
private List<ReadOnlyMemory<byte>> _buffers;
private ReadOnlyMemory<byte> _firstBuffer;
private int _bufferCount;
private long _length;
private readonly byte[] _padding = new byte[16];
private readonly byte[] _padding;

public SectionData(byte paddingByte = 0)
{
_padding.AsSpan().Fill(paddingByte);
_padding = paddingByte switch
{
0 => s_zeroPadding,
NopPaddingByte => s_nopPadding,
_ => CreatePadding(paddingByte),
};
}

private static byte[] CreatePadding(byte value)
{
byte[] result = new byte[PaddingBufferSize];
result.AsSpan().Fill(value);
return result;
}

private void FlushAppendBuffer()
{
if (_appendBuffer.WrittenCount > 0)
if (_appendBuffer is { WrittenCount: > 0 } appendBuffer)
{
_buffers.Add(_appendBuffer.WrittenSpan.ToArray());
_length += _appendBuffer.WrittenCount;
_appendBuffer.Clear();
AddBuffer(appendBuffer.WrittenSpan.ToArray());
_length += appendBuffer.WrittenCount;
appendBuffer.Clear();
}
}

public void AppendData(ReadOnlyMemory<byte> data)
{
FlushAppendBuffer();
_buffers.Add(data);
AddBuffer(data);
_length += data.Length;
}

public void AppendPadding(int paddingLength)
{
if (paddingLength > 0)
{
if (_appendBuffer.WrittenCount > 0 || paddingLength > _padding.Length)
if ((_appendBuffer?.WrittenCount ?? 0) > 0 || paddingLength > _padding.Length)
{
_appendBuffer.GetSpan(paddingLength).Slice(0, paddingLength).Fill(_padding[0]);
_appendBuffer.Advance(paddingLength);
ArrayBufferWriter<byte> appendBuffer = GetAppendBuffer();
appendBuffer.GetSpan(paddingLength).Slice(0, paddingLength).Fill(_padding[0]);
appendBuffer.Advance(paddingLength);
}
else
{
Expand All @@ -65,15 +87,48 @@ public void AppendPadding(int paddingLength)
}
}

public IBufferWriter<byte> BufferWriter => _appendBuffer;
public IBufferWriter<byte> BufferWriter => GetAppendBuffer();

public long Length => _length + _appendBuffer.WrittenCount;
public long Length => _length + (_appendBuffer?.WrittenCount ?? 0);

/// <summary>
/// Gets a read-only stream accessing the section data.
/// </summary>
public Stream GetReadStream() => new ReadStream(this);

private ArrayBufferWriter<byte> GetAppendBuffer()
{
return _appendBuffer ??= new ArrayBufferWriter<byte>();
}

private void AddBuffer(ReadOnlyMemory<byte> data)
{
if (_bufferCount == 0)
{
_firstBuffer = data;
}
else
{
if (_buffers is null)
{
_buffers = new List<ReadOnlyMemory<byte>>();
_buffers.Add(_firstBuffer);
}

_buffers.Add(data);
}

_bufferCount++;
}

private int BufferCount => _bufferCount;

private ReadOnlyMemory<byte> GetBuffer(int index)
{
Debug.Assert((uint)index < (uint)_bufferCount);
return _buffers is null ? _firstBuffer : _buffers[index];
}

private sealed class ReadStream : Stream
{
private readonly SectionData _sectionData;
Expand All @@ -98,11 +153,12 @@ public override long Position
_position = 0;
_bufferIndex = 0;
_bufferPosition = 0;
while (_position < value && _bufferIndex < _sectionData._buffers.Count)
while (_position < value && _bufferIndex < _sectionData.BufferCount)
{
if (_sectionData._buffers[_bufferIndex].Length < value - _position)
ReadOnlyMemory<byte> currentBuffer = _sectionData.GetBuffer(_bufferIndex);
if (currentBuffer.Length < value - _position)
{
_position += _sectionData._buffers[_bufferIndex].Length;
_position += currentBuffer.Length;
_bufferIndex++;
}
else
Expand Down Expand Up @@ -136,9 +192,9 @@ public override int Read(Span<byte> buffer)

// _bufferIndex and _bufferPosition is only valid after seeking when
// _position < _length
while (_position < _sectionData._length && _bufferIndex < _sectionData._buffers.Count)
while (_position < _sectionData._length && _bufferIndex < _sectionData.BufferCount)
{
ReadOnlySpan<byte> currentBuffer = _sectionData._buffers[_bufferIndex].Span.Slice(_bufferPosition);
ReadOnlySpan<byte> currentBuffer = _sectionData.GetBuffer(_bufferIndex).Span.Slice(_bufferPosition);

if (currentBuffer.Length >= buffer.Length)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
<ItemGroup>
<Compile Include="DependencyGraphTests.cs" />
<Compile Include="DevirtualizationTests.cs" />
<Compile Include="ObjectWriterTests.cs" />
<Compile Include="SwiftLoweringTests.cs" />
</ItemGroup>
</Project>
Loading
Loading