From 502c0bb430bfd7be202bd257f4ecde93d7e6bdea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 2 Jun 2023 14:49:11 +0900 Subject: [PATCH 1/2] Enable identical code folding on Linux This instructs the linker to deduplicate identical sections on Linux. We were previously not doing this for two reasons: 1. We weren't passing the command line switch. 2. We had trouble getting linker to actually fold things. Linux linkers are particularly picky on what they're willing to fold. We've been historically putting these things to RW sections (dotnet/corert#686) and that doesn't help. Looking at C++, it looks like it places vtables into `.text` section, so let's just do the same. I also found out why `--foldmethodbodies` doesn't work outside Windows - we place managed code into a `__managedcode` section and Linux linkers won't fold sections that have names that are valid C identifiers (would need to name this `.managedcode` like on Windows) - https://github.com/dotnet/llvm-project/blob/c01ca3bc8a420b3796d816c43bdd06e337c72ea6/lld/ELF/ICF.cpp#L192. Not addressing that part because the whole thing looks like a hairy yak and this option is not supported anyway. --- .../Microsoft.NETCore.Native.Unix.targets | 1 + .../DependencyAnalysis/ObjectNodeSection.cs | 9 +++++++- .../DictionaryLayoutNode.cs | 23 +++++++++---------- .../EETypeOptionalFieldsNode.cs | 2 +- .../GenericCompositionNode.cs | 2 +- .../DependencyAnalysis/GenericVarianceNode.cs | 2 +- .../InterfaceDispatchMapNode.cs | 2 +- .../MethodExceptionHandlingInfoNode.cs | 2 +- .../DependencyAnalysis/ObjectWriter.cs | 14 ++++------- .../DependencyAnalysis/SealedVTableNode.cs | 2 +- 10 files changed, 30 insertions(+), 29 deletions(-) diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets index ad2c022f4be7fc..07dad14830a6a7 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets @@ -151,6 +151,7 @@ The .NET Foundation licenses this file to you under the MIT license. + diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNodeSection.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNodeSection.cs index 6f0c0bec5aba19..1c06558a0d2486 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNodeSection.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNodeSection.cs @@ -37,7 +37,13 @@ public bool IsStandardSection { get { - return this == DataSection || this == ReadOnlyDataSection || this == FoldableReadOnlyDataSection || this == TextSection || this == XDataSection || this == BssSection; + return this == DataSection + || this == ReadOnlyDataSection + || this == FoldableReadOnlyDataSection + || this == TextSection + || this == FoldableTextSection + || this == XDataSection + || this == BssSection; } } @@ -46,6 +52,7 @@ public bool IsStandardSection public static readonly ObjectNodeSection ReadOnlyDataSection = new ObjectNodeSection("rdata", SectionType.ReadOnly); public static readonly ObjectNodeSection FoldableReadOnlyDataSection = new ObjectNodeSection("rdata", SectionType.ReadOnly); public static readonly ObjectNodeSection TextSection = new ObjectNodeSection("text", SectionType.Executable); + public static readonly ObjectNodeSection FoldableTextSection = new ObjectNodeSection("text", SectionType.Executable); public static readonly ObjectNodeSection TLSSection = new ObjectNodeSection("TLS", SectionType.Writeable); public static readonly ObjectNodeSection BssSection = new ObjectNodeSection("bss", SectionType.Uninitialized); public static readonly ObjectNodeSection HydrationTargetSection = new ObjectNodeSection("hydrated", SectionType.Uninitialized); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DictionaryLayoutNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DictionaryLayoutNode.cs index c3b7828e6beb93..6412e0ed340c34 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DictionaryLayoutNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DictionaryLayoutNode.cs @@ -49,22 +49,21 @@ private void Validate() public virtual ObjectNodeSection DictionarySection(NodeFactory factory) { - if (factory.Target.IsWindows) + (ObjectNodeSection foldableSection, ObjectNodeSection unfoldableSection) = factory.Target.OperatingSystem switch { - if (_owningMethodOrType is TypeDesc) - { - return ObjectNodeSection.FoldableReadOnlyDataSection; - } - else - { - // Method dictionary serves as an identity at runtime which means they are not foldable. - Debug.Assert(_owningMethodOrType is MethodDesc); - return ObjectNodeSection.ReadOnlyDataSection; - } + TargetOS.Windows => (ObjectNodeSection.FoldableReadOnlyDataSection, ObjectNodeSection.ReadOnlyDataSection), + _ => (ObjectNodeSection.FoldableTextSection, ObjectNodeSection.TextSection), + }; + + if (_owningMethodOrType is TypeDesc) + { + return foldableSection; } else { - return ObjectNodeSection.DataSection; + // Method dictionary serves as an identity at runtime which means they are not foldable. + Debug.Assert(_owningMethodOrType is MethodDesc); + return unfoldableSection; } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeOptionalFieldsNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeOptionalFieldsNode.cs index cb10e790d4176a..5d35af6c5bf001 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeOptionalFieldsNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeOptionalFieldsNode.cs @@ -19,7 +19,7 @@ public override ObjectNodeSection GetSection(NodeFactory factory) if (factory.Target.IsWindows) return ObjectNodeSection.FoldableReadOnlyDataSection; else - return ObjectNodeSection.DataSection; + return ObjectNodeSection.FoldableTextSection; } public override bool StaticDependenciesAreComputed => true; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericCompositionNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericCompositionNode.cs index 447bbfa7b392a6..e3e2d8be9f7d3d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericCompositionNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericCompositionNode.cs @@ -42,7 +42,7 @@ public override ObjectNodeSection GetSection(NodeFactory factory) if (factory.Target.IsWindows) return ObjectNodeSection.FoldableReadOnlyDataSection; else - return ObjectNodeSection.DataSection; + return ObjectNodeSection.FoldableTextSection; } public override bool IsShareable => true; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericVarianceNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericVarianceNode.cs index 7fedd00acc87ef..25b81119a6b803 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericVarianceNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericVarianceNode.cs @@ -41,7 +41,7 @@ public override ObjectNodeSection GetSection(NodeFactory factory) if (factory.Target.IsWindows) return ObjectNodeSection.FoldableReadOnlyDataSection; else - return ObjectNodeSection.DataSection; + return ObjectNodeSection.FoldableTextSection; } public override bool IsShareable => true; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs index 38104d7ab015c9..f7cf632ad50e28 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs @@ -44,7 +44,7 @@ public override ObjectNodeSection GetSection(NodeFactory factory) if (factory.Target.IsWindows) return ObjectNodeSection.FoldableReadOnlyDataSection; else - return ObjectNodeSection.DataSection; + return ObjectNodeSection.FoldableTextSection; } protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodExceptionHandlingInfoNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodExceptionHandlingInfoNode.cs index 1b1bd06d72de2d..c4aa96c1ed9a04 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodExceptionHandlingInfoNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodExceptionHandlingInfoNode.cs @@ -24,7 +24,7 @@ public MethodExceptionHandlingInfoNode(MethodDesc owningMethod, ObjectData data) public override ObjectNodeSection GetSection(NodeFactory factory) => _owningMethod.Context.Target.IsWindows ? ObjectNodeSection.FoldableReadOnlyDataSection - : ObjectNodeSection.DataSection; + : ObjectNodeSection.FoldableTextSection; public override bool StaticDependenciesAreComputed => true; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectWriter.cs index 7b491fad1dc274..d067443c4ca817 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectWriter.cs @@ -70,9 +70,6 @@ public class ObjectWriter : IDisposable, ITypesDebugInfoWriter private NodeFactory _nodeFactory; private readonly bool _isSingleFileCompilation; - // Unix section containing LSDA data, like EH Info and GC Info - public static readonly ObjectNodeSection LsdaSection = new ObjectNodeSection(".dotnet_eh_table", SectionType.ReadOnly); - private UserDefinedTypeDescriptor _userDefinedTypeDescriptor; #if DEBUG @@ -610,11 +607,7 @@ public void BuildCFIMap(NodeFactory factory, ObjectNode node) int len = frameInfo.BlobData.Length; byte[] blob = frameInfo.BlobData; - ObjectNodeSection lsdaSection = LsdaSection; - if (ShouldShareSymbol(node)) - { - lsdaSection = GetSharedSection(lsdaSection, ((ISymbolNode)node).GetMangledName(_nodeFactory.NameMangler)); - } + ObjectNodeSection lsdaSection = GetSharedSection(ObjectNodeSection.TextSection, ((ISymbolNode)node).GetMangledName(_nodeFactory.NameMangler)); SwitchSection(_nativeObjectWriter, lsdaSection.Name, GetCustomSectionAttributes(lsdaSection), lsdaSection.ComdatName); _sb.Clear().Append("_lsda").Append(i.ToStringInvariant()).Append(_currentNodeZeroTerminatedName); @@ -900,7 +893,8 @@ private bool ShouldShareSymbol(ObjectNode node) ObjectNodeSection section = node.GetSection(_nodeFactory); if (section == ObjectNodeSection.FoldableManagedCodeUnixContentSection || section == ObjectNodeSection.FoldableManagedCodeWindowsContentSection || - section == ObjectNodeSection.FoldableReadOnlyDataSection) + section == ObjectNodeSection.FoldableReadOnlyDataSection || + section == ObjectNodeSection.FoldableTextSection) return true; if (_isSingleFileCompilation) @@ -959,7 +953,7 @@ public static void EmitObject(string objectFilePath, IReadOnlyCollection this.GetMangledName(factory.NameMangler); - public override ObjectNodeSection GetSection(NodeFactory factory) => _type.Context.Target.IsWindows ? ObjectNodeSection.FoldableReadOnlyDataSection : ObjectNodeSection.DataSection; + public override ObjectNodeSection GetSection(NodeFactory factory) => _type.Context.Target.IsWindows ? ObjectNodeSection.FoldableReadOnlyDataSection : ObjectNodeSection.FoldableTextSection; public virtual void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { From b3af9ff8c913a20c40ff29843f11a207d3e3dc5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 2 Jun 2023 15:36:03 +0900 Subject: [PATCH 2/2] Update ObjectWriter.cs --- .../Compiler/DependencyAnalysis/ObjectWriter.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectWriter.cs index d067443c4ca817..2ecc3b5e71c3df 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectWriter.cs @@ -607,7 +607,11 @@ public void BuildCFIMap(NodeFactory factory, ObjectNode node) int len = frameInfo.BlobData.Length; byte[] blob = frameInfo.BlobData; - ObjectNodeSection lsdaSection = GetSharedSection(ObjectNodeSection.TextSection, ((ISymbolNode)node).GetMangledName(_nodeFactory.NameMangler)); + ObjectNodeSection lsdaSection = ObjectNodeSection.TextSection; + if (ShouldShareSymbol(node)) + { + lsdaSection = GetSharedSection(lsdaSection, ((ISymbolNode)node).GetMangledName(_nodeFactory.NameMangler)); + } SwitchSection(_nativeObjectWriter, lsdaSection.Name, GetCustomSectionAttributes(lsdaSection), lsdaSection.ComdatName); _sb.Clear().Append("_lsda").Append(i.ToStringInvariant()).Append(_currentNodeZeroTerminatedName);