Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public partial class CompilationBuilder
protected DevirtualizationManager _devirtualizationManager = new DevirtualizationManager();
protected InlinedThreadStatics _inlinedThreadStatics = new InlinedThreadStatics();
protected MethodImportationErrorProvider _methodImportationErrorProvider = new MethodImportationErrorProvider();
protected ReadOnlyFieldPolicy _readOnlyFieldPolicy = new ReadOnlyFieldPolicy();
protected FieldPolicy _fieldPolicy = new FieldPolicy();
protected IInliningPolicy _inliningPolicy;
protected MethodBodyFoldingMode _methodBodyFolding;
protected InstructionSetSupport _instructionSetSupport;
Expand Down Expand Up @@ -112,9 +112,9 @@ public CompilationBuilder UseMethodImportationErrorProvider(MethodImportationErr
return this;
}

public CompilationBuilder UseReadOnlyFieldPolicy(ReadOnlyFieldPolicy policy)
public CompilationBuilder UseFieldPolicy(FieldPolicy policy)
{
_readOnlyFieldPolicy = policy;
_fieldPolicy = policy;
return this;
}

Expand All @@ -139,7 +139,7 @@ public CompilationBuilder UseTypeMapManager(TypeMapManager typeMapManager)
protected PreinitializationManager GetPreinitializationManager()
{
if (_preinitializationManager == null)
return new PreinitializationManager(_context, _compilationGroup, GetILProvider(), new TypePreinit.DisabledPreinitializationPolicy(), new StaticReadOnlyFieldPolicy(), null);
return new PreinitializationManager(_context, _compilationGroup, GetILProvider(), new TypePreinit.DisabledPreinitializationPolicy(), new FieldPolicyWithStaticInitOnly(), null);
return _preinitializationManager;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,11 @@ private void CreateNodeCaches()
return new NotReadOnlyFieldNode(field);
});

_fieldReads = new NodeCache<FieldDesc, StaticFieldReadNode>(field =>
{
return new StaticFieldReadNode(field);
});

_genericStaticBaseInfos = new NodeCache<MetadataType, GenericStaticBaseInfoNode>(type =>
{
return new GenericStaticBaseInfoNode(type);
Expand Down Expand Up @@ -1231,6 +1236,12 @@ public NotReadOnlyFieldNode NotReadOnlyField(FieldDesc field)
return _notReadOnlyFields.GetOrAdd(field);
}

private NodeCache<FieldDesc, StaticFieldReadNode> _fieldReads;
public StaticFieldReadNode StaticFieldRead(FieldDesc field)
{
return _fieldReads.GetOrAdd(field);
}

private NodeCache<MetadataType, GenericStaticBaseInfoNode> _genericStaticBaseInfos;
internal GenericStaticBaseInfoNode GenericStaticBaseInfo(MetadataType type)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@ protected override ObjectNodeSection GetDehydratedSection(NodeFactory factory)
if (!factory.Target.IsWindows)
return ObjectNodeSection.DataSection;

ReadOnlyFieldPolicy readOnlyPolicy = _preinitializationManager.ReadOnlyFieldPolicy;
FieldPolicy fieldPolicy = _preinitializationManager.FieldPolicy;

bool allFieldsReadOnly = true;
foreach (FieldDesc field in _type.GetFields())
{
if (!IsNonGcStaticField(field))
continue;

allFieldsReadOnly = readOnlyPolicy.IsReadOnly(field);
allFieldsReadOnly = fieldPolicy.IsReadOnly(field);
if (!allFieldsReadOnly)
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFacto
return dependencies;
}

if (_field.IsStatic)
dependencies.Add(factory.StaticFieldRead(_field), "Reflection readable static field");

// readonly static fields are not reflection settable, the rest are
if (!_field.IsInitOnly || !_field.IsStatic)
dependencies.Add(factory.NotReadOnlyField(_field), "Reflection writable field");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// 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 ILCompiler.DependencyAnalysisFramework;

using Internal.TypeSystem;

using Debug = System.Diagnostics.Debug;

namespace ILCompiler.DependencyAnalysis
{
/// <summary>
/// Represents a field that that can be read programmatically at runtime.
/// </summary>
public class StaticFieldReadNode : DependencyNodeCore<NodeFactory>
{
private readonly FieldDesc _field;

public StaticFieldReadNode(FieldDesc field)
{
Debug.Assert(field.IsStatic);
Debug.Assert(!field.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any)
|| field.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific) == field.OwningType);
_field = field;
}

public FieldDesc Field => _field;

protected override string GetName(NodeFactory factory)
{
return "Static field that can be read at runtime: " + _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<DependencyListEntry> GetStaticDependencies(NodeFactory factory) => null;
public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory) => null;
public override IEnumerable<CombinedDependencyListEntry> SearchDynamicDependencies(List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory factory) => null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@

namespace ILCompiler
{
public class ReadOnlyFieldPolicy
public class FieldPolicy
{
public virtual bool IsReadOnly(FieldDesc field) => field.IsInitOnly;
public virtual bool IsStaticFieldRead(FieldDesc field) => true;
}

public sealed class StaticReadOnlyFieldPolicy : ReadOnlyFieldPolicy
public sealed class FieldPolicyWithStaticInitOnly : FieldPolicy
{
public override bool IsReadOnly(FieldDesc field) => field.IsStatic && field.IsInitOnly;
}
Expand Down
29 changes: 25 additions & 4 deletions src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -271,9 +271,9 @@ public InlinedThreadStatics GetInlinedThreadStatics()
return new ScannedInlinedThreadStatics(_factory, MarkedNodes);
}

public ReadOnlyFieldPolicy GetReadOnlyFieldPolicy()
public FieldPolicy GetFieldPolicy()
{
return new ScannedReadOnlyPolicy(MarkedNodes);
return new ScannedFieldPolicy(MarkedNodes);
}

public TypeMapManager GetTypeMapManager()
Expand Down Expand Up @@ -998,18 +998,23 @@ public override bool CanPreinitializeAllConcreteFormsForCanonForm(DefType type)
}
}

private sealed class ScannedReadOnlyPolicy : ReadOnlyFieldPolicy
private sealed class ScannedFieldPolicy : FieldPolicy
{
private HashSet<FieldDesc> _writtenFields = new();
private HashSet<FieldDesc> _staticReadFields = new();

public ScannedReadOnlyPolicy(ImmutableArray<DependencyNodeCore<NodeFactory>> markedNodes)
public ScannedFieldPolicy(ImmutableArray<DependencyNodeCore<NodeFactory>> markedNodes)
{
foreach (var node in markedNodes)
{
if (node is NotReadOnlyFieldNode writtenField)
{
_writtenFields.Add(writtenField.Field);
}
else if (node is StaticFieldReadNode readField)
{
_staticReadFields.Add(readField.Field);
}
}
}

Expand All @@ -1026,6 +1031,22 @@ public override bool IsReadOnly(FieldDesc field)

return !_writtenFields.Contains(field);
}

public override bool IsStaticFieldRead(FieldDesc field)
{
Debug.Assert(field.IsStatic);

FieldDesc typicalField = field.GetTypicalFieldDefinition();
if (field != typicalField)
{
DefType owningType = field.OwningType;
var canonOwningType = (InstantiatedType)owningType.ConvertToCanonForm(CanonicalFormKind.Specific);
if (owningType != canonOwningType)
field = field.Context.GetFieldForInstantiatedType(typicalField, canonOwningType);
}

return _staticReadFields.Contains(field);
}
}

private sealed class ScannedTypeMapManager : TypeMapManager
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ public class PreinitializationManager
{
private readonly bool _supportsLazyCctors;

public ReadOnlyFieldPolicy ReadOnlyFieldPolicy => _preinitHashTable._readOnlyPolicy;
public FieldPolicy FieldPolicy => _preinitHashTable._fieldPolicy;

public PreinitializationManager(TypeSystemContext context, CompilationModuleGroup compilationGroup, ILProvider ilprovider, TypePreinit.TypePreinitializationPolicy policy, ReadOnlyFieldPolicy readOnlyPolicy, FlowAnnotations flowAnnotations)
public PreinitializationManager(TypeSystemContext context, CompilationModuleGroup compilationGroup, ILProvider ilprovider, TypePreinit.TypePreinitializationPolicy policy, FieldPolicy fieldPolicy, FlowAnnotations flowAnnotations)
{
_supportsLazyCctors = context.SystemModule.GetType("System.Runtime.CompilerServices", "ClassConstructorRunner", throwIfNotFound: false) != null;
_preinitHashTable = new PreinitializationInfoHashtable(compilationGroup, ilprovider, policy, readOnlyPolicy, flowAnnotations);
_preinitHashTable = new PreinitializationInfoHashtable(compilationGroup, ilprovider, policy, fieldPolicy, flowAnnotations);
}

/// <summary>
Expand Down Expand Up @@ -141,15 +141,15 @@ private sealed class PreinitializationInfoHashtable : LockFreeReaderHashtable<Me
private readonly CompilationModuleGroup _compilationGroup;
private readonly ILProvider _ilProvider;
internal readonly TypePreinit.TypePreinitializationPolicy _policy;
internal readonly ReadOnlyFieldPolicy _readOnlyPolicy;
internal readonly FieldPolicy _fieldPolicy;
private readonly FlowAnnotations _flowAnnotations;

public PreinitializationInfoHashtable(CompilationModuleGroup compilationGroup, ILProvider ilProvider, TypePreinit.TypePreinitializationPolicy policy, ReadOnlyFieldPolicy readOnlyPolicy, FlowAnnotations flowAnnotations)
public PreinitializationInfoHashtable(CompilationModuleGroup compilationGroup, ILProvider ilProvider, TypePreinit.TypePreinitializationPolicy policy, FieldPolicy fieldPolicy, FlowAnnotations flowAnnotations)
{
_compilationGroup = compilationGroup;
_ilProvider = ilProvider;
_policy = policy;
_readOnlyPolicy = readOnlyPolicy;
_fieldPolicy = fieldPolicy;
_flowAnnotations = flowAnnotations;
}

Expand All @@ -160,7 +160,7 @@ public PreinitializationInfoHashtable(CompilationModuleGroup compilationGroup, I

protected override TypePreinit.PreinitializationInfo CreateValueFromKey(MetadataType key)
{
var info = TypePreinit.ScanType(_compilationGroup, _ilProvider, _policy, _readOnlyPolicy, _flowAnnotations, key);
var info = TypePreinit.ScanType(_compilationGroup, _ilProvider, _policy, _fieldPolicy, _flowAnnotations, key);

// We either successfully preinitialized or
// the type doesn't have a canonical form or
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,21 @@ public class TypePreinit
private readonly CompilationModuleGroup _compilationGroup;
private readonly ILProvider _ilProvider;
private readonly TypePreinitializationPolicy _policy;
private readonly ReadOnlyFieldPolicy _readOnlyPolicy;
private readonly FieldPolicy _fieldPolicy;
private readonly FlowAnnotations _flowAnnotations;
private readonly Dictionary<FieldDesc, Value> _fieldValues = new Dictionary<FieldDesc, Value>();
private readonly Dictionary<string, StringInstance> _internedStrings = new Dictionary<string, StringInstance>();
private readonly Dictionary<TypeDesc, RuntimeTypeValue> _internedTypes = new Dictionary<TypeDesc, RuntimeTypeValue>();
private readonly Dictionary<MetadataType, NestedPreinitResult> _nestedPreinitResults = new Dictionary<MetadataType, NestedPreinitResult>();
private readonly Dictionary<EcmaField, byte[]> _rvaFieldDatas = new Dictionary<EcmaField, byte[]>();

private TypePreinit(MetadataType owningType, CompilationModuleGroup compilationGroup, ILProvider ilProvider, TypePreinitializationPolicy policy, ReadOnlyFieldPolicy readOnlyPolicy, FlowAnnotations flowAnnotations)
private TypePreinit(MetadataType owningType, CompilationModuleGroup compilationGroup, ILProvider ilProvider, TypePreinitializationPolicy policy, FieldPolicy fieldPolicy, FlowAnnotations flowAnnotations)
{
_type = owningType;
_compilationGroup = compilationGroup;
_ilProvider = ilProvider;
_policy = policy;
_readOnlyPolicy = readOnlyPolicy;
_fieldPolicy = fieldPolicy;
_flowAnnotations = flowAnnotations;

// Zero initialize all fields we model.
Expand All @@ -64,7 +64,7 @@ private TypePreinit(MetadataType owningType, CompilationModuleGroup compilationG
}
}

public static PreinitializationInfo ScanType(CompilationModuleGroup compilationGroup, ILProvider ilProvider, TypePreinitializationPolicy policy, ReadOnlyFieldPolicy readOnlyPolicy, FlowAnnotations flowAnnotations, MetadataType type)
public static PreinitializationInfo ScanType(CompilationModuleGroup compilationGroup, ILProvider ilProvider, TypePreinitializationPolicy policy, FieldPolicy readOnlyPolicy, FlowAnnotations flowAnnotations, MetadataType type)
{
Debug.Assert(type.HasStaticConstructor);
Debug.Assert(!type.IsGenericDefinition);
Expand Down Expand Up @@ -104,7 +104,16 @@ public static PreinitializationInfo ScanType(CompilationModuleGroup compilationG
{
var values = new List<KeyValuePair<FieldDesc, ISerializableValue>>();
foreach (var kvp in preinit._fieldValues)
values.Add(new KeyValuePair<FieldDesc, ISerializableValue>(kvp.Key, kvp.Value));
{
FieldDesc field = kvp.Key;
Value value = kvp.Value;

// If nobody will be reading this field, consider it zero init.
if (!preinit._fieldPolicy.IsStaticFieldRead(field))
value = NewUninitializedLocationValue(field.FieldType, field);

values.Add(new KeyValuePair<FieldDesc, ISerializableValue>(field, value));
}

return new PreinitializationInfo(type, values);
}
Expand All @@ -116,7 +125,7 @@ private bool TryGetNestedPreinitResult(MethodDesc callingMethod, MetadataType ty
{
if (!_nestedPreinitResults.TryGetValue(type, out result))
{
TypePreinit nestedPreinit = new TypePreinit(type, _compilationGroup, _ilProvider, _policy, _readOnlyPolicy, _flowAnnotations);
TypePreinit nestedPreinit = new TypePreinit(type, _compilationGroup, _ilProvider, _policy, _fieldPolicy, _flowAnnotations);
recursionProtect ??= new Stack<MethodDesc>();
recursionProtect.Push(callingMethod);

Expand Down Expand Up @@ -423,7 +432,7 @@ private Status TryScanMethod(MethodIL methodIL, Value[] parameters, Stack<Method
{
fieldValue = _fieldValues[field];
}
else if (_readOnlyPolicy.IsReadOnly(field)
else if (_fieldPolicy.IsReadOnly(field)
&& field.OwningType.HasStaticConstructor
&& _policy.CanPreinitialize(field.OwningType))
{
Expand All @@ -435,7 +444,7 @@ private Status TryScanMethod(MethodIL methodIL, Value[] parameters, Stack<Method
if (!nestedPreinitResult.TryGetFieldValue(this, field, out fieldValue))
return Status.Fail(methodIL.OwningMethod, opcode);
}
else if (_readOnlyPolicy.IsReadOnly(field)
else if (_fieldPolicy.IsReadOnly(field)
&& opcode != ILOpcode.ldsflda // We need to intern these for correctness in ldsfda scenarios
&& !field.OwningType.HasStaticConstructor)
{
Expand Down Expand Up @@ -659,7 +668,7 @@ private Status TryScanMethod(MethodIL methodIL, Value[] parameters, Stack<Method
TypeDesc fieldType = field.FieldType;
if (fieldType.IsGCPointer)
{
if (!_readOnlyPolicy.IsReadOnly(field))
if (!_fieldPolicy.IsReadOnly(field))
{
allGcPointersAreReadonly = false;
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1066,6 +1066,17 @@ private void ImportFieldAccess(int token, bool isStatic, bool? write, string rea

_compilation.NodeFactory.MetadataManager.GetDependenciesDueToAccess(ref _dependencies, _compilation.NodeFactory, _canonMethodIL, canonField);

if (field.IsStatic && (!write.HasValue || write == false))
{
FieldDesc fieldToReport = canonField;
DefType fieldOwningType = canonField.OwningType;
TypeDesc canonFieldOwningType = fieldOwningType.ConvertToCanonForm(CanonicalFormKind.Specific);
if (fieldOwningType != canonFieldOwningType)
fieldToReport = _factory.TypeSystemContext.GetFieldForInstantiatedType(fieldToReport.GetTypicalFieldDefinition(), (InstantiatedType)canonFieldOwningType);

_dependencies.Add(_factory.StaticFieldRead(fieldToReport), "Static field read");
}

// `write` will be null for ld(s)flda. Consider address loads write unless they were
// for initonly static fields. We'll trust the initonly that this is not a write.
write ??= !field.IsInitOnly || !field.IsStatic;
Expand Down
Loading