Skip to content

Commit

Permalink
Deduplicate LSDA data
Browse files Browse the repository at this point in the history
Extracting a piece of dotnet#87045 that I had to revert in that PR. Native linkers don't like when LSDA is in a COMDAT so fold these in the object writer instead. Seems to save about 1.2% in the Stage1 app. Obviously Unix only.
  • Loading branch information
MichalStrehovsky committed Feb 5, 2024
1 parent 7b3e409 commit dc74c82
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public enum FrameInfoFlags
HasAssociatedData = 0x10,
}

public struct FrameInfo
public struct FrameInfo : IEquatable<FrameInfo>
{
public readonly FrameInfoFlags Flags;
public readonly int StartOffset;
Expand All @@ -30,6 +30,24 @@ public FrameInfo(FrameInfoFlags flags, int startOffset, int endOffset, byte[] bl
EndOffset = endOffset;
BlobData = blobData;
}

public bool Equals(FrameInfo other)
=> Flags == other.Flags
&& StartOffset == other.StartOffset
&& EndOffset == other.EndOffset
&& ((ReadOnlySpan<byte>)BlobData).SequenceEqual(other.BlobData);

public override bool Equals(object obj) => obj is FrameInfo other && Equals(other);

public override int GetHashCode()
{
HashCode hash = default;
hash.Add(Flags);
hash.Add(StartOffset);
hash.Add(EndOffset);
hash.AddBytes(BlobData);
return hash.ToHashCode();
}
}

public struct DebugEHClauseInfo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public virtual void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder
sb.Append("_associatedData_").Append(nameMangler.GetMangledMethodName(_methodNode.Method));
}

public static bool MethodHasAssociatedData(NodeFactory factory, IMethodNode methodNode)
public static bool MethodHasAssociatedData(IMethodNode methodNode)
{
// Instantiating unboxing stubs. We need to store their non-unboxing target pointer (looked up by runtime)
ISpecialUnboxThunkNode unboxThunk = methodNode as ISpecialUnboxThunkNode;
Expand All @@ -63,7 +63,7 @@ public static bool MethodHasAssociatedData(NodeFactory factory, IMethodNode meth

public override ObjectData GetData(NodeFactory factory, bool relocsOnly)
{
Debug.Assert(MethodHasAssociatedData(factory, _methodNode));
Debug.Assert(MethodHasAssociatedData(_methodNode));

ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly);
objData.RequireInitialAlignment(1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
using Internal.TypeSystem;
using Internal.TypeSystem.TypesDebugInfo;

using Debug = System.Diagnostics.Debug;

namespace ILCompiler.ObjectWriter
{
/// <summary>
Expand Down Expand Up @@ -114,6 +116,57 @@ private protected void EmitLsda(
}
}

private sealed class LsdaCache
{
private sealed class LsdaComparer : IEqualityComparer<INodeWithCodeInfo>
{
public static readonly LsdaComparer Instance = new LsdaComparer();

public bool Equals(INodeWithCodeInfo x, INodeWithCodeInfo y)
{
Debug.Assert(IsCacheable(x));
Debug.Assert(IsCacheable(y));
ReadOnlySpan<byte> xGc = x.GCInfo;
ReadOnlySpan<byte> yGc = y.GCInfo;
if (!xGc.SequenceEqual(yGc))
return false;

ReadOnlySpan<FrameInfo> xFrames = x.FrameInfos;
ReadOnlySpan<FrameInfo> yFrames = y.FrameInfos;
return xFrames.SequenceEqual(yFrames);
}

public int GetHashCode(INodeWithCodeInfo obj)
{
Debug.Assert(IsCacheable(obj));
HashCode hash = default;
hash.AddBytes(obj.GCInfo);
foreach (FrameInfo f in obj.FrameInfos)
hash.Add(f.GetHashCode());
return hash.ToHashCode();
}
}

private Dictionary<INodeWithCodeInfo, string[]> _lsdas = new Dictionary<INodeWithCodeInfo, string[]>(LsdaComparer.Instance);

public static bool IsCacheable(INodeWithCodeInfo nodeWithCodeInfo)
=> nodeWithCodeInfo.EHInfo == null && !MethodAssociatedDataNode.MethodHasAssociatedData((IMethodNode)nodeWithCodeInfo);

public string[] FindCachedLsda(INodeWithCodeInfo nodeWithCodeInfo)
{
Debug.Assert(IsCacheable(nodeWithCodeInfo));
return _lsdas.GetValueOrDefault(nodeWithCodeInfo);
}

public void AddLsdaToCache(INodeWithCodeInfo nodeWithCodeInfo, string[] symbols)
{
Debug.Assert(IsCacheable(nodeWithCodeInfo));
_lsdas.Add(nodeWithCodeInfo, symbols);
}
}

private readonly LsdaCache _lsdaCache = new LsdaCache();

private protected override void EmitUnwindInfo(
SectionWriter sectionWriter,
INodeWithCodeInfo nodeWithCodeInfo,
Expand All @@ -125,13 +178,21 @@ private protected override void EmitUnwindInfo(
bool useFrameNames = UseFrameNames;
SectionWriter lsdaSectionWriter;

string[] newLsdaSymbols = null;
string[] emittedLsdaSymbols = null;
if (ShouldShareSymbol((ObjectNode)nodeWithCodeInfo))
{
lsdaSectionWriter = GetOrCreateSection(LsdaSection, currentSymbolName, $"_lsda0{currentSymbolName}");
}
else
{
lsdaSectionWriter = _lsdaSectionWriter;
if (LsdaCache.IsCacheable(nodeWithCodeInfo))
{
emittedLsdaSymbols = _lsdaCache.FindCachedLsda(nodeWithCodeInfo);
if (emittedLsdaSymbols == null)
newLsdaSymbols = new string[frameInfos.Length];
}
}

long mainLsdaOffset = 0;
Expand All @@ -143,10 +204,20 @@ private protected override void EmitUnwindInfo(
int end = frameInfo.EndOffset;
byte[] blob = frameInfo.BlobData;

string lsdaSymbolName = $"_lsda{i}{currentSymbolName}";
string framSymbolName = $"_fram{i}{currentSymbolName}";
string lsdaSymbolName;
if (emittedLsdaSymbols != null)
{
lsdaSymbolName = emittedLsdaSymbols[i];
}
else
{
lsdaSymbolName = $"_lsda{i}{currentSymbolName}";
if (newLsdaSymbols != null)
newLsdaSymbols[i] = lsdaSymbolName;
lsdaSectionWriter.EmitSymbolDefinition(lsdaSymbolName);
}

lsdaSectionWriter.EmitSymbolDefinition(lsdaSymbolName);
string framSymbolName = $"_fram{i}{currentSymbolName}";
if (useFrameNames && start != 0)
{
sectionWriter.EmitSymbolDefinition(framSymbolName, start);
Expand All @@ -167,8 +238,12 @@ private protected override void EmitUnwindInfo(
_dwarfEhFrame.AddFde(fde);
}

EmitLsda(nodeWithCodeInfo, frameInfos, i, _lsdaSectionWriter, ref mainLsdaOffset);
if (emittedLsdaSymbols == null)
EmitLsda(nodeWithCodeInfo, frameInfos, i, _lsdaSectionWriter, ref mainLsdaOffset);
}

if (newLsdaSymbols != null)
_lsdaCache.AddLsdaToCache(nodeWithCodeInfo, newLsdaSymbols);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact
dependencies.Add(_ehInfo, "Exception handling information");
}

if (MethodAssociatedDataNode.MethodHasAssociatedData(factory, this))
if (MethodAssociatedDataNode.MethodHasAssociatedData(this))
{
dependencies ??= new DependencyList();
dependencies.Add(new DependencyListEntry(factory.MethodAssociatedData(this), "Method associated data"));
Expand Down Expand Up @@ -121,7 +121,7 @@ public ISymbolNode GetUnboxingThunkTarget(NodeFactory factory)

public ISymbolNode GetAssociatedDataNode(NodeFactory factory)
{
if (MethodAssociatedDataNode.MethodHasAssociatedData(factory, this))
if (MethodAssociatedDataNode.MethodHasAssociatedData(this))
return factory.MethodAssociatedData(this);

return null;
Expand Down

0 comments on commit dc74c82

Please sign in to comment.