-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fold identical method bodies in the compiler (#101969)
Saves up to 5.2% in file size per rt-sz measurements. Adds a phase before object writing that looks for identical method bodies and deduplicates those that are same. To keep delegate equivalence working, the compiler also distinguishes between references to symbols and references to symbols that have an observable address identity. When a method is folded that has an observable address identity, the references that require observable address identity go through a unique jump thunk. This means that delegates point to jump thunks and reflection mapping tables point to jump thunks (whenever a method body got folded into a different method body). We do not need the jump thunks for references that are not address exposed (so a `call` in a method body will no go through a jump thunk). Since method body folding is still observable with stack trace APIs or debuggers, this is opt in. The user gets opted in by setting `StackTraceSupport=false` (or using an undocumented switch). I took a shortcut in a couple places where references that may or may not be address exposed get treated as address exposed. There are TODO comments around those. We may want to fix tracking within the compiler to tighten this. It may not matter much. I also took a shortcut in deduplication - we currently only look at leaf identical method bodies. The method bodies that become identical after first level of folding currently don't get folded. This leaves a bit size on the table still. There's a TODO comment as well. We also don't consider function pointers address exposed since there's no API to compare these. That's also a TODO for whenever we add such API.
- Loading branch information
1 parent
5508f79
commit ae80f73
Showing
32 changed files
with
942 additions
and
73 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
85 changes: 85 additions & 0 deletions
85
...reclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AddressTakenMethodNode.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
// 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.Diagnostics; | ||
|
||
using ILCompiler.DependencyAnalysisFramework; | ||
|
||
using Internal.Text; | ||
using Internal.TypeSystem; | ||
|
||
namespace ILCompiler.DependencyAnalysis | ||
{ | ||
/// <summary> | ||
/// Represents a method with address taken. Under normal circumstances, this node is not emitted | ||
/// into the object file and instead references to it are replaced to refer to the underlying method body. | ||
/// This is achieved through <see cref="ShouldSkipEmittingObjectNode(NodeFactory)"/> and <see cref="NodeForLinkage(NodeFactory)"/>. | ||
/// However, if the underlying method body got folded together with another method due to identical method body folding | ||
/// optimization, this node is not skipped and instead emits a jump stub. The purpose of the jump stub is to provide a | ||
/// unique code address for the address taken method. | ||
/// </summary> | ||
internal sealed class AddressTakenMethodNode : JumpStubNode, IMethodNode, ISymbolNodeWithLinkage | ||
{ | ||
private readonly IMethodNode _methodNode; | ||
|
||
public IMethodNode RealBody => _methodNode; | ||
|
||
public AddressTakenMethodNode(IMethodNode methodNode) | ||
: base(methodNode) | ||
{ | ||
_methodNode = methodNode; | ||
} | ||
|
||
public MethodDesc Method => _methodNode.Method; | ||
|
||
protected override string GetName(NodeFactory factory) | ||
{ | ||
return "Address taken method: " + _methodNode.GetMangledName(factory.NameMangler); | ||
} | ||
|
||
public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) | ||
{ | ||
return factory.ObjectInterner.GetDeduplicatedSymbol(factory, RealBody) == RealBody; | ||
} | ||
|
||
public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory) => null; | ||
public override IEnumerable<CombinedDependencyListEntry> SearchDynamicDependencies(List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory context) => null; | ||
public override bool InterestingForDynamicDependencyAnalysis => false; | ||
public override bool HasDynamicDependencies => false; | ||
public override bool HasConditionalStaticDependencies => false; | ||
|
||
public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) | ||
{ | ||
// We use the same mangled name as the underlying real method body. | ||
// This is okay since this node will go out of the way if the real body is marked | ||
// and part of the graph. | ||
_methodNode.AppendMangledName(nameMangler, sb); | ||
} | ||
|
||
public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) | ||
{ | ||
return _methodNode.CompareToImpl(((AddressTakenMethodNode)other)._methodNode, comparer); | ||
} | ||
|
||
public ISymbolNode NodeForLinkage(NodeFactory factory) | ||
{ | ||
// If someone refers to this node but the target method still has a unique body, | ||
// refer to the target method. | ||
return factory.ObjectInterner.GetDeduplicatedSymbol(factory, RealBody) == RealBody ? RealBody : this; | ||
} | ||
|
||
public override bool RepresentsIndirectionCell | ||
{ | ||
get | ||
{ | ||
Debug.Assert(!_methodNode.RepresentsIndirectionCell); | ||
return false; | ||
} | ||
} | ||
|
||
public override int ClassCode => 0xfab0355; | ||
|
||
public override bool IsShareable => ((ObjectNode)_methodNode).IsShareable; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.