Skip to content

Commit 0621e64

Browse files
Fold generic method bodies by default (#117411)
Fixes #103951. Same method with different genericness are not distinguishable in the stack traces. They are still distinguishable in the debugger, but it might be acceptable, especially since we have an opt out (undocumented for now).
1 parent 1d6452a commit 0621e64

File tree

7 files changed

+57
-15
lines changed

7 files changed

+57
-15
lines changed

src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,12 @@ The .NET Foundation licenses this file to you under the MIT license.
237237
<_IlcNoSingleWarnAssemblies Include="@(_IlcNoSingleWarnAssembliesRaw)" Condition="!$([System.IO.File]::Exists('%(Identity)'))" />
238238
</ItemGroup>
239239

240+
<PropertyGroup>
241+
<_IlcMethodBodyFoldingValue Condition="$(IlcFoldIdenticalMethodBodies) == 'true' or $(StackTraceSupport) == 'false'">all</_IlcMethodBodyFoldingValue>
242+
<_IlcMethodBodyFoldingValue Condition="$(_IlcMethodBodyFoldingValue) == '' and $(IlcFoldIdenticalMethodBodies) != 'false' and $(IlcMultiModule) != 'true'">generic</_IlcMethodBodyFoldingValue>
243+
<_IlcMethodBodyFoldingValue Condition="$(_IlcMethodBodyFoldingValue) == '' or $(Optimize) != 'true'">none</_IlcMethodBodyFoldingValue>
244+
</PropertyGroup>
245+
240246
<ItemGroup>
241247
<IlcArg Include="@(IlcCompileInput)" />
242248
<IlcArg Include="-o:$(NativeIntermediateOutputPath)%(ManagedBinary.Filename)$(IlcOutputFileExt)" />
@@ -275,7 +281,7 @@ The .NET Foundation licenses this file to you under the MIT license.
275281
<IlcArg Condition="$(IlcGenerateCompleteTypeMetadata) == 'true'" Include="--completetypemetadata" />
276282
<IlcArg Condition="$(StackTraceSupport) != 'false'" Include="--stacktracedata" />
277283
<IlcArg Condition="$(IlcScanReflection) != 'false'" Include="--scanreflection" />
278-
<IlcArg Condition="$(IlcFoldIdenticalMethodBodies) == 'true' or $(StackTraceSupport) == 'false'" Include="--methodbodyfolding" />
284+
<IlcArg Include="--methodbodyfolding:$(_IlcMethodBodyFoldingValue)" />
279285
<IlcArg Condition="$(Optimize) == 'true' and $(OptimizationPreference) == 'Size'" Include="--Os" />
280286
<IlcArg Condition="$(Optimize) == 'true' and $(OptimizationPreference) == 'Speed'" Include="--Ot" />
281287
<IlcArg Condition="'$(_linuxLibcFlavor)' == 'bionic'" Include="--noinlinetls" />

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public partial class CompilationBuilder
2121
protected MethodImportationErrorProvider _methodImportationErrorProvider = new MethodImportationErrorProvider();
2222
protected ReadOnlyFieldPolicy _readOnlyFieldPolicy = new ReadOnlyFieldPolicy();
2323
protected IInliningPolicy _inliningPolicy;
24-
protected bool _methodBodyFolding;
24+
protected MethodBodyFoldingMode _methodBodyFolding;
2525
protected InstructionSetSupport _instructionSetSupport;
2626
protected SecurityMitigationOptions _mitigationOptions;
2727
protected bool _dehydrate;
@@ -94,9 +94,9 @@ public CompilationBuilder UseDehydration(bool dehydrate)
9494
return this;
9595
}
9696

97-
public CompilationBuilder UseMethodBodyFolding(bool enable)
97+
public CompilationBuilder UseMethodBodyFolding(MethodBodyFoldingMode mode)
9898
{
99-
_methodBodyFolding = enable;
99+
_methodBodyFolding = mode;
100100
return this;
101101
}
102102

@@ -154,4 +154,11 @@ public enum SecurityMitigationOptions
154154
{
155155
ControlFlowGuardAnnotations = 0x0001,
156156
}
157+
158+
public enum MethodBodyFoldingMode
159+
{
160+
None,
161+
Generic,
162+
All,
163+
}
157164
}

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,7 +1062,7 @@ public IMethodNode FatFunctionPointer(MethodDesc method, bool isUnboxingStub = f
10621062

10631063
public IMethodNode FatAddressTakenFunctionPointer(MethodDesc method, bool isUnboxingStub = false)
10641064
{
1065-
if (ObjectInterner.IsNull)
1065+
if (!ObjectInterner.CanFold(method))
10661066
return FatFunctionPointer(method, isUnboxingStub);
10671067

10681068
return _fatAddressTakenFunctionPointers.GetOrAdd(new MethodKey(method, isUnboxingStub));
@@ -1128,7 +1128,7 @@ internal TypeGVMEntriesNode TypeGVMEntries(TypeDesc type)
11281128
private NodeCache<MethodDesc, AddressTakenMethodNode> _addressTakenMethods;
11291129
public IMethodNode AddressTakenMethodEntrypoint(MethodDesc method, bool unboxingStub = false)
11301130
{
1131-
if (unboxingStub || ObjectInterner.IsNull)
1131+
if (unboxingStub || !ObjectInterner.CanFold(method))
11321132
return MethodEntrypoint(method, unboxingStub);
11331133

11341134
return _addressTakenMethods.GetOrAdd(method);

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectDataInterner.cs

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,29 @@
66

77
using ILCompiler.DependencyAnalysis;
88

9+
using Internal.TypeSystem;
10+
911
using Debug = System.Diagnostics.Debug;
1012

1113
namespace ILCompiler
1214
{
1315
public sealed class ObjectDataInterner
1416
{
17+
private readonly bool _genericsOnly;
1518
private Dictionary<ISymbolNode, ISymbolNode> _symbolRemapping;
1619

17-
public static ObjectDataInterner Null { get; } = new ObjectDataInterner() { _symbolRemapping = new() };
20+
public static ObjectDataInterner Null { get; } = new ObjectDataInterner(genericsOnly: false) { _symbolRemapping = new() };
1821

19-
public bool IsNull => _symbolRemapping != null && _symbolRemapping.Count == 0;
22+
public ObjectDataInterner(bool genericsOnly)
23+
{
24+
_genericsOnly = genericsOnly;
25+
}
26+
27+
public bool CanFold(MethodDesc method)
28+
{
29+
return this != Null
30+
&& (!_genericsOnly || method.HasInstantiation || method.OwningType.HasInstantiation);
31+
}
2032

2133
private void EnsureMap(NodeFactory factory)
2234
{
@@ -34,11 +46,14 @@ private void EnsureMap(NodeFactory factory)
3446
{
3547
previousMethodHash = methodHash;
3648
previousSymbolRemapping = symbolRemapping;
37-
methodHash = new HashSet<MethodInternKey>(previousMethodHash?.Count ?? 0, new MethodInternComparer(factory, previousSymbolRemapping));
49+
methodHash = new HashSet<MethodInternKey>(previousMethodHash?.Count ?? 0, new MethodInternComparer(factory, previousSymbolRemapping, _genericsOnly));
3850
symbolRemapping = new Dictionary<ISymbolNode, ISymbolNode>((int)(1.05 * (previousSymbolRemapping?.Count ?? 0)));
3951

4052
foreach (IMethodBodyNode body in factory.MetadataManager.GetCompiledMethodBodies())
4153
{
54+
if (!CanFold(body.Method))
55+
continue;
56+
4257
// We don't track special unboxing thunks as virtual method use related so ignore them
4358
if (body is ISpecialUnboxThunkNode unboxThunk && unboxThunk.IsSpecialUnboxingThunk)
4459
continue;
@@ -107,9 +122,10 @@ private sealed class MethodInternComparer : IEqualityComparer<MethodInternKey>
107122
{
108123
private readonly NodeFactory _factory;
109124
private readonly Dictionary<ISymbolNode, ISymbolNode> _interner;
125+
private readonly bool _genericsOnly;
110126

111-
public MethodInternComparer(NodeFactory factory, Dictionary<ISymbolNode, ISymbolNode> interner)
112-
=> (_factory, _interner) = (factory, interner);
127+
public MethodInternComparer(NodeFactory factory, Dictionary<ISymbolNode, ISymbolNode> interner, bool genericsOnly)
128+
=> (_factory, _interner, _genericsOnly) = (factory, interner, genericsOnly);
113129

114130
public int GetHashCode(MethodInternKey key) => key.HashCode;
115131

@@ -161,6 +177,10 @@ public bool Equals(MethodInternKey a, MethodInternKey b)
161177
if (a.HashCode != b.HashCode)
162178
return false;
163179

180+
if (_genericsOnly
181+
&& a.Method.Method.GetTypicalMethodDefinition() != b.Method.Method.GetTypicalMethodDefinition())
182+
return false;
183+
164184
ObjectNode.ObjectData o1data = ((ObjectNode)a.Method).GetData(_factory, relocsOnly: false);
165185
ObjectNode.ObjectData o2data = ((ObjectNode)b.Method).GetData(_factory, relocsOnly: false);
166186

src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilationBuilder.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,12 @@ public override ICompilation ToCompilation()
136136
if (_resilient)
137137
options |= RyuJitCompilationOptions.UseResilience;
138138

139-
ObjectDataInterner interner = _methodBodyFolding ? new ObjectDataInterner() : ObjectDataInterner.Null;
139+
ObjectDataInterner interner = _methodBodyFolding switch
140+
{
141+
MethodBodyFoldingMode.Generic => new ObjectDataInterner(genericsOnly: true),
142+
MethodBodyFoldingMode.All => new ObjectDataInterner(genericsOnly: false),
143+
_ => ObjectDataInterner.Null,
144+
};
140145

141146
var factory = new RyuJitNodeFactory(_context, _compilationGroup, _metadataManager, _interopStubManager, _nameMangler, _vtableSliceProvider, _dictionaryLayoutProvider, _inlinedThreadStatics, GetPreinitializationManager(), _devirtualizationManager, interner, _typeMapManager);
142147

src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ internal sealed class ILCompilerRootCommand : RootCommand
101101
new("--noinlinetls") { Description = "Do not generate inline thread local statics" };
102102
public Option<bool> EmitStackTraceData { get; } =
103103
new("--stacktracedata") { Description = "Emit data to support generating stack trace strings at runtime" };
104-
public Option<bool> MethodBodyFolding { get; } =
105-
new("--methodbodyfolding") { Description = "Fold identical method bodies" };
104+
public Option<string> MethodBodyFolding { get; } =
105+
new("--methodbodyfolding") { Description = "Fold identical method bodies (one of: none, generic, all" };
106106
public Option<string[]> InitAssemblies { get; } =
107107
new("--initassembly") { DefaultValueFactory = _ => Array.Empty<string>(), Description = "Assembly(ies) with a library initializer" };
108108
public Option<string[]> FeatureSwitches { get; } =

src/coreclr/tools/aot/ILCompiler/Program.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -604,10 +604,14 @@ void RunScanner()
604604
compilationRoots.Add(metadataManager);
605605
compilationRoots.Add(interopStubManager);
606606

607+
MethodBodyFoldingMode foldingMode = string.IsNullOrEmpty(Get(_command.MethodBodyFolding))
608+
? MethodBodyFoldingMode.None
609+
: Enum.Parse<MethodBodyFoldingMode>(Get(_command.MethodBodyFolding), ignoreCase: true);
610+
607611
builder
608612
.UseInstructionSetSupport(instructionSetSupport)
609613
.UseBackendOptions(Get(_command.CodegenOptions))
610-
.UseMethodBodyFolding(enable: Get(_command.MethodBodyFolding))
614+
.UseMethodBodyFolding(foldingMode)
611615
.UseParallelism(parallelism)
612616
.UseMetadataManager(metadataManager)
613617
.UseInteropStubManager(interopStubManager)

0 commit comments

Comments
 (0)