diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/INodeWithCodeInfo.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/INodeWithCodeInfo.cs index 62e8c282fc334e..c216ff2c57d632 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/INodeWithCodeInfo.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/INodeWithCodeInfo.cs @@ -16,7 +16,7 @@ public enum FrameInfoFlags HasAssociatedData = 0x10, } - public struct FrameInfo + public struct FrameInfo : IEquatable { public readonly FrameInfoFlags Flags; public readonly int StartOffset; @@ -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)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 diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodAssociatedDataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodAssociatedDataNode.cs index 3f4562ce70e8a7..df661ea1ad642b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodAssociatedDataNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodAssociatedDataNode.cs @@ -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; @@ -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); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/UnixObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/UnixObjectWriter.cs index 0dc1ad9f964e4a..0ae31be325e186 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/UnixObjectWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/UnixObjectWriter.cs @@ -10,6 +10,8 @@ using Internal.TypeSystem; using Internal.TypeSystem.TypesDebugInfo; +using Debug = System.Diagnostics.Debug; + namespace ILCompiler.ObjectWriter { /// @@ -114,6 +116,57 @@ private protected void EmitLsda( } } + private sealed class LsdaCache + { + private sealed class LsdaComparer : IEqualityComparer + { + public static readonly LsdaComparer Instance = new LsdaComparer(); + + public bool Equals(INodeWithCodeInfo x, INodeWithCodeInfo y) + { + Debug.Assert(IsCacheable(x)); + Debug.Assert(IsCacheable(y)); + ReadOnlySpan xGc = x.GCInfo; + ReadOnlySpan yGc = y.GCInfo; + if (!xGc.SequenceEqual(yGc)) + return false; + + ReadOnlySpan xFrames = x.FrameInfos; + ReadOnlySpan 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 _lsdas = new Dictionary(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, @@ -125,6 +178,8 @@ 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}"); @@ -132,6 +187,12 @@ private protected override void EmitUnwindInfo( else { lsdaSectionWriter = _lsdaSectionWriter; + if (LsdaCache.IsCacheable(nodeWithCodeInfo) && !useFrameNames) + { + emittedLsdaSymbols = _lsdaCache.FindCachedLsda(nodeWithCodeInfo); + if (emittedLsdaSymbols == null) + newLsdaSymbols = new string[frameInfos.Length]; + } } long mainLsdaOffset = 0; @@ -143,10 +204,21 @@ 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); + EmitLsda(nodeWithCodeInfo, frameInfos, i, _lsdaSectionWriter, ref mainLsdaOffset); + } - lsdaSectionWriter.EmitSymbolDefinition(lsdaSymbolName); + string framSymbolName = $"_fram{i}{currentSymbolName}"; if (useFrameNames && start != 0) { sectionWriter.EmitSymbolDefinition(framSymbolName, start); @@ -166,9 +238,10 @@ private protected override void EmitUnwindInfo( personalitySymbolName: null); _dwarfEhFrame.AddFde(fde); } - - EmitLsda(nodeWithCodeInfo, frameInfos, i, _lsdaSectionWriter, ref mainLsdaOffset); } + + if (newLsdaSymbols != null) + _lsdaCache.AddLsdaToCache(nodeWithCodeInfo, newLsdaSymbols); } } diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/MethodCodeNode.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/MethodCodeNode.cs index a59888d2d291d2..d835084925619c 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/MethodCodeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/MethodCodeNode.cs @@ -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")); @@ -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;