From 7d706d0856ecd597f7bae44c22b5c364cc674c5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Tue, 22 Nov 2022 17:22:17 +0900 Subject: [PATCH 1/2] Place frozen objects into dehydrated section Saves 1% in size on a Hello World on Windows, more on Linux because we're getting rid of full pointers. * Instead of using `ArrayOfEmbeddedDataNode` as the base for the frozen object region, derive from `DehydratableObjectNode` directly. The base class wasn't really saving us much work over `ObjectNode` anyway. * Move the "align object to minimum size" code to the central location while I'm touching it. * Keep track of all frozen objects in the `MetadataManager`. This is the general pattern we follow elsewhere. The old pattern where the frozen object adds itself from the callback also works, but this matches the approach we use for other summarized node kinds. --- .../ArrayOfFrozenObjectsNode.cs | 47 ++++++++++++++++--- .../DependencyAnalysis/FrozenObjectNode.cs | 16 +------ .../DependencyAnalysis/FrozenStringNode.cs | 16 +------ .../DependencyAnalysis/NodeFactory.cs | 7 +-- .../Compiler/MetadataManager.cs | 16 +++++++ 5 files changed, 61 insertions(+), 41 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ArrayOfFrozenObjectsNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ArrayOfFrozenObjectsNode.cs index b6d8ba5147f4b..1b77259df3465 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ArrayOfFrozenObjectsNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ArrayOfFrozenObjectsNode.cs @@ -1,34 +1,57 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Generic; +using System; +using Internal.Text; using Internal.TypeSystem; namespace ILCompiler.DependencyAnalysis { - public class ArrayOfFrozenObjectsNode : ArrayOfEmbeddedDataNode - where TEmbedded : EmbeddedObjectNode + public class ArrayOfFrozenObjectsNode : DehydratableObjectNode, ISymbolDefinitionNode { - public ArrayOfFrozenObjectsNode(string startSymbolMangledName, string endSymbolMangledName, IComparer nodeSorter) : base(startSymbolMangledName, endSymbolMangledName, nodeSorter) + private readonly ObjectAndOffsetSymbolNode _endSymbol; + public ISymbolNode EndSymbol => _endSymbol; + + public ArrayOfFrozenObjectsNode() { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__FrozenSegmentEnd", true); } + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + => sb.Append(nameMangler.CompilationUnitPrefix).Append("__FrozenSegmentStart"); + + public int Offset => 0; + private static void AlignNextObject(ref ObjectDataBuilder builder, NodeFactory factory) { builder.EmitZeros(AlignmentHelper.AlignUp(builder.CountBytes, factory.Target.PointerSize) - builder.CountBytes); } - protected override void GetElementDataForNodes(ref ObjectDataBuilder builder, NodeFactory factory, bool relocsOnly) + protected override ObjectData GetDehydratableData(NodeFactory factory, bool relocsOnly) { - foreach (EmbeddedObjectNode node in NodesList) + // This is a summary node + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, Array.Empty()); + + var builder = new ObjectDataBuilder(factory, relocsOnly); + builder.AddSymbol(this); + foreach (EmbeddedObjectNode node in factory.MetadataManager.GetFrozenObjects()) { AlignNextObject(ref builder, factory); if (!relocsOnly) node.InitializeOffsetFromBeginningOfArray(builder.CountBytes); + int initialOffset = builder.CountBytes; node.EncodeData(ref builder, factory, relocsOnly); + int objectSize = builder.CountBytes - initialOffset; + int minimumObjectSize = EETypeNode.GetMinimumObjectSize(factory.TypeSystemContext); + if (objectSize < minimumObjectSize) + { + builder.EmitZeros(minimumObjectSize - objectSize); + } + if (node is ISymbolDefinitionNode) { builder.AddSymbol((ISymbolDefinitionNode)node); @@ -38,8 +61,20 @@ protected override void GetElementDataForNodes(ref ObjectDataBuilder builder, No // Terminate with a null pointer as expected by the GC AlignNextObject(ref builder, factory); builder.EmitZeroPointer(); + + _endSymbol.SetSymbolOffset(builder.CountBytes); + builder.AddSymbol(_endSymbol); + + return builder.ToObjectData(); } + protected override ObjectNodeSection GetDehydratedSection(NodeFactory factory) => ObjectNodeSection.DataSection; + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + public override int ClassCode => -1771336339; + + public override bool IsShareable => false; + + public override bool StaticDependenciesAreComputed => true; } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FrozenObjectNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FrozenObjectNode.cs index 21bc5dda0b908..764bffd9603c1 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FrozenObjectNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FrozenObjectNode.cs @@ -13,7 +13,7 @@ namespace ILCompiler.DependencyAnalysis /// Represents a frozen object that is statically preallocated within the data section /// of the executable instead of on the GC heap. /// - public class FrozenObjectNode : EmbeddedObjectNode, ISymbolDefinitionNode + public sealed class FrozenObjectNode : EmbeddedObjectNode, ISymbolDefinitionNode { private readonly MetadataType _owningType; private readonly TypePreinit.ISerializableReference _data; @@ -58,20 +58,11 @@ int ISymbolDefinitionNode.Offset public override void EncodeData(ref ObjectDataBuilder dataBuilder, NodeFactory factory, bool relocsOnly) { - int initialOffset = dataBuilder.CountBytes; - // Sync Block dataBuilder.EmitZeroPointer(); // byte contents _data.WriteContent(ref dataBuilder, this, factory); - - int objectSize = dataBuilder.CountBytes - initialOffset; - int minimumObjectSize = EETypeNode.GetMinimumObjectSize(factory.TypeSystemContext); - if (objectSize < minimumObjectSize) - { - dataBuilder.EmitZeros(minimumObjectSize - objectSize); - } } protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); @@ -95,11 +86,6 @@ public override IEnumerable GetStaticDependencies(NodeFacto return dependencies; } - protected override void OnMarked(NodeFactory factory) - { - factory.FrozenSegmentRegion.AddEmbeddedObject(this); - } - public override int ClassCode => 1789429316; public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FrozenStringNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FrozenStringNode.cs index 0f155245303e5..c537f07fd0051 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FrozenStringNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FrozenStringNode.cs @@ -8,7 +8,7 @@ namespace ILCompiler.DependencyAnalysis { - public class FrozenStringNode : EmbeddedObjectNode, ISymbolDefinitionNode + public sealed class FrozenStringNode : EmbeddedObjectNode, ISymbolDefinitionNode { private string _data; private int _syncBlockSize; @@ -59,8 +59,6 @@ private static IEETypeNode GetEETypeNode(NodeFactory factory) public override void EncodeData(ref ObjectDataBuilder dataBuilder, NodeFactory factory, bool relocsOnly) { - int initialOffset = dataBuilder.CountBytes; - dataBuilder.EmitZeroPointer(); // Sync block dataBuilder.EmitPointerReloc(GetEETypeNode(factory)); @@ -74,13 +72,6 @@ public override void EncodeData(ref ObjectDataBuilder dataBuilder, NodeFactory f // Null-terminate for friendliness with interop dataBuilder.EmitShort(0); - - int objectSize = dataBuilder.CountBytes - initialOffset; - int minimumObjectSize = EETypeNode.GetMinimumObjectSize(factory.TypeSystemContext); - if (objectSize < minimumObjectSize) - { - dataBuilder.EmitZeros(minimumObjectSize - objectSize); - } } protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); @@ -93,11 +84,6 @@ public override IEnumerable GetStaticDependencies(NodeFacto }; } - protected override void OnMarked(NodeFactory factory) - { - factory.FrozenSegmentRegion.AddEmbeddedObject(this); - } - public override int ClassCode => -1733946122; public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs index efd7f86102b1f..c94dd4166ac6e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -1122,10 +1122,7 @@ public string GetSymbolAlternateName(ISymbolNode node) "__DispatchMapTableEnd", new SortableDependencyNode.ObjectNodeComparer(CompilerComparer.Instance)); - public ArrayOfEmbeddedDataNode FrozenSegmentRegion = new ArrayOfFrozenObjectsNode( - "__FrozenSegmentRegionStart", - "__FrozenSegmentRegionEnd", - new SortableDependencyNode.EmbeddedObjectNodeComparer(CompilerComparer.Instance)); + public ArrayOfFrozenObjectsNode FrozenSegmentRegion = new ArrayOfFrozenObjectsNode(); internal ModuleInitializerListNode ModuleInitializerList = new ModuleInitializerListNode(); @@ -1158,7 +1155,7 @@ public virtual void AttachToDependencyGraph(DependencyAnalyzerBase ReadyToRunHeader.Add(ReadyToRunSectionType.EagerCctor, EagerCctorTable, EagerCctorTable.StartSymbol, EagerCctorTable.EndSymbol); ReadyToRunHeader.Add(ReadyToRunSectionType.TypeManagerIndirection, TypeManagerIndirection, TypeManagerIndirection); ReadyToRunHeader.Add(ReadyToRunSectionType.InterfaceDispatchTable, DispatchMapTable, DispatchMapTable.StartSymbol); - ReadyToRunHeader.Add(ReadyToRunSectionType.FrozenObjectRegion, FrozenSegmentRegion, FrozenSegmentRegion.StartSymbol, FrozenSegmentRegion.EndSymbol); + ReadyToRunHeader.Add(ReadyToRunSectionType.FrozenObjectRegion, FrozenSegmentRegion, FrozenSegmentRegion, FrozenSegmentRegion.EndSymbol); ReadyToRunHeader.Add(ReadyToRunSectionType.ModuleInitializerList, ModuleInitializerList, ModuleInitializerList, ModuleInitializerList.EndSymbol); var commonFixupsTableNode = new ExternalReferencesTableNode("CommonFixupsTable", this); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs index 87d51cd0eeca8..c2a98daba71ab 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs @@ -56,6 +56,7 @@ public abstract class MetadataManager : ICompilationRootProvider private readonly SortedSet _reflectableMethods = new SortedSet(TypeSystemComparer.Instance); private readonly SortedSet _genericDictionariesGenerated = new SortedSet(CompilerComparer.Instance); private readonly SortedSet _methodBodiesGenerated = new SortedSet(CompilerComparer.Instance); + private readonly SortedSet _frozenObjects = new SortedSet(CompilerComparer.Instance); private readonly SortedSet _typeGVMEntries = new SortedSet(Comparer.Create((a, b) => TypeSystemComparer.Instance.Compare(a.AssociatedType, b.AssociatedType))); private readonly SortedSet _typesWithDelegateMarshalling = new SortedSet(TypeSystemComparer.Instance); @@ -272,6 +273,16 @@ protected virtual void Graph_NewMarkedNode(DependencyNodeCore obj) { _templateMethodEntries.Add(templateMethodEntry); } + + if (obj is FrozenObjectNode frozenObj) + { + _frozenObjects.Add(frozenObj); + } + + if (obj is FrozenStringNode frozenStr) + { + _frozenObjects.Add(frozenStr); + } } protected virtual bool AllMethodsCanBeReflectable => false; @@ -689,6 +700,11 @@ public IEnumerable GetReflectableMethods() return _reflectableMethods; } + public IEnumerable GetFrozenObjects() + { + return _frozenObjects; + } + internal IEnumerable GetCompiledMethodBodies() { return _methodBodiesGenerated; From 3866699eb5d7229b63da49556557b193e8d87757 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Wed, 30 Nov 2022 12:05:12 +0900 Subject: [PATCH 2/2] Update ArrayOfFrozenObjectsNode.cs --- .../Compiler/DependencyAnalysis/ArrayOfFrozenObjectsNode.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ArrayOfFrozenObjectsNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ArrayOfFrozenObjectsNode.cs index 1b77259df3465..a83b93447fa67 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ArrayOfFrozenObjectsNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ArrayOfFrozenObjectsNode.cs @@ -40,8 +40,7 @@ protected override ObjectData GetDehydratableData(NodeFactory factory, bool relo { AlignNextObject(ref builder, factory); - if (!relocsOnly) - node.InitializeOffsetFromBeginningOfArray(builder.CountBytes); + node.InitializeOffsetFromBeginningOfArray(builder.CountBytes); int initialOffset = builder.CountBytes; node.EncodeData(ref builder, factory, relocsOnly);