-
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.
Precisely track reflected on fields (#70546)
The AOT compiler used simple logic to decide what fields should be kept/generated: if a type is considered constructed, generate all fields. This works, but it's not very efficient. With this change I'm introducing precise tracking of each field that should be accessible from reflection at runtime. The fields are represented by new nodes within the dependency graph. We track fields on individual generic instantiations and ensure we end up in a consistent state where if e.g. we decided `Class<int>.Foo` should be reflection accessible and `Class<double>` is used elsewhere in the program, `Class<double>.Foo` is also reflection accessible. This matches how IL Linker thinks about reflectability where genericness doesn't matter. We could be more optimal here, but various suppressions in framework rely on this logic. Additional reflectable fields only cost tens of bytes. I had to update various places within the compiler that didn't bother specifying field dependencies because they didn't matter in the past. Added a couple new tests that test the various invariants. This saves 2% in size on a `dotnet new webapi` template project with IlcTrimMetadata on. It is a small regression with `IlcTrimMetadata` off because we actually now track more things for the reflectable field: previously we would not make sure the field is reflection-accessible at runtime. We need a `TypeHandle` for the field type for it to be usable. As a potential future optimization, we could look into allowing reflection to see "unconstructed" `TypeHandles` (and use that one). Reflection is currently not allowed to see unconstructed `TypeHandles` to prevent people falling into a `RuntimeHelpers.AllocateUninitializedObject(someField.FieldType.TypeHandle)` trap that has a bad failure mode right now. Once we fix the failure mode, we could potentially allow it.
- Loading branch information
1 parent
f1f940f
commit f54c15a
Showing
20 changed files
with
442 additions
and
184 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
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
105 changes: 105 additions & 0 deletions
105
...coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectableFieldNode.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,105 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
|
||
using ILCompiler.DependencyAnalysisFramework; | ||
|
||
using Internal.TypeSystem; | ||
|
||
using Debug = System.Diagnostics.Debug; | ||
|
||
namespace ILCompiler.DependencyAnalysis | ||
{ | ||
/// <summary> | ||
/// Represents a field that is gettable/settable from reflection. | ||
/// </summary> | ||
public class ReflectableFieldNode : DependencyNodeCore<NodeFactory> | ||
{ | ||
private readonly FieldDesc _field; | ||
|
||
public ReflectableFieldNode(FieldDesc field) | ||
{ | ||
Debug.Assert(!field.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any) | ||
|| field.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific) == field.OwningType); | ||
_field = field; | ||
} | ||
|
||
public FieldDesc Field => _field; | ||
|
||
public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory) | ||
{ | ||
Debug.Assert(!factory.MetadataManager.IsReflectionBlocked(_field.GetTypicalFieldDefinition())); | ||
|
||
DependencyList dependencies = new DependencyList(); | ||
factory.MetadataManager.GetDependenciesDueToReflectability(ref dependencies, factory, _field); | ||
|
||
// No runtime artifacts needed if this is a generic definition or literal field | ||
if (_field.OwningType.IsGenericDefinition || _field.IsLiteral) | ||
{ | ||
return dependencies; | ||
} | ||
|
||
FieldDesc typicalField = _field.GetTypicalFieldDefinition(); | ||
if (typicalField != _field) | ||
{ | ||
// Ensure we consistently apply reflectability to all fields sharing the same definition. | ||
// Bases for different instantiations of the field have a conditional dependency on the definition node that | ||
// brings a ReflectableField of the instantiated field if it's necessary for it to be reflectable. | ||
dependencies.Add(factory.ReflectableField(typicalField), "Definition of the reflectable field"); | ||
} | ||
|
||
// Runtime reflection stack needs to see the type handle of the owning type | ||
dependencies.Add(factory.MaximallyConstructableType(_field.OwningType), "Instance base of a reflectable field"); | ||
|
||
// Root the static base of the type | ||
if (_field.IsStatic && !_field.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any)) | ||
{ | ||
// Infrastructure around static constructors is stashed in the NonGC static base | ||
bool needsNonGcStaticBase = factory.PreinitializationManager.HasLazyStaticConstructor(Field.OwningType); | ||
|
||
if (_field.HasRva) | ||
{ | ||
// No reflection access right now | ||
} | ||
else if (_field.IsThreadStatic) | ||
{ | ||
dependencies.Add(factory.TypeThreadStaticIndex((MetadataType)_field.OwningType), "Threadstatic base of a reflectable field"); | ||
} | ||
else if (_field.HasGCStaticBase) | ||
{ | ||
dependencies.Add(factory.TypeGCStaticsSymbol((MetadataType)_field.OwningType), "GC static base of a reflectable field"); | ||
} | ||
else | ||
{ | ||
dependencies.Add(factory.TypeNonGCStaticsSymbol((MetadataType)_field.OwningType), "NonGC static base of a reflectable field"); | ||
needsNonGcStaticBase = false; | ||
} | ||
|
||
if (needsNonGcStaticBase) | ||
{ | ||
dependencies.Add(factory.TypeNonGCStaticsSymbol((MetadataType)_field.OwningType), "CCtor context"); | ||
} | ||
} | ||
|
||
// Runtime reflection stack needs to obtain the type handle of the field | ||
// (but there's no type handles for function pointers) | ||
if (!_field.FieldType.IsFunctionPointer) | ||
dependencies.Add(factory.MaximallyConstructableType(_field.FieldType.NormalizeInstantiation()), "Type of the field"); | ||
|
||
return dependencies; | ||
} | ||
protected override string GetName(NodeFactory factory) | ||
{ | ||
return "Reflectable field: " + _field.ToString(); | ||
} | ||
|
||
public override bool InterestingForDynamicDependencyAnalysis => false; | ||
public override bool HasDynamicDependencies => false; | ||
public override bool HasConditionalStaticDependencies => false; | ||
public override bool StaticDependenciesAreComputed => true; | ||
public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory) => null; | ||
public override IEnumerable<CombinedDependencyListEntry> SearchDynamicDependencies(List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory factory) => null; | ||
} | ||
} |
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.