Skip to content

Commit

Permalink
Update shared code from linker (#70204)
Browse files Browse the repository at this point in the history
This brings in relatively recent version of the shared code from the dotnet/linker repo.
Main changes:
* ValueNode -> SingleValue - and the partial classes implementation for the values
* Adapt MethodBodyScanner to the SingleValue/MultiValue
  * Includes some fixes around array handling and unknown values
* Removal of ReflectionPatternContext
  * Partially replaced by DiagnosticContext
  * Partially removed (the checks that everything reports something are nto needed anymore)
* Use HandleCallAction and RequireDynamicallyAccessedMembersAction - this replaces most of the functionality in ReflectionMethodBodyScanner.

Formatting:
The files which are shared exactly from linker are kept as is (so tabs and so on)
The files which are AOT specific should follow the formatting of AOT projects

Testing:
Passes smoke tests and some additional validation done via linker tests which is not part of this change

Note: This is not up-to-date with linker but it's getting us much closer. Known "TODOs":
* CompilerGeneratedState - linker has a much newer and much more capable version. Port of that will come later (as it was in heavy development while I was doing this port, and it's not strictly necessary for current functionality)
* Type hierarchy marking - this needs work on the linker side as it's not yet part of the shared codebase
* Correctly handle RUC/RDC for all "reflection accesses" to methods - some of the change improve on this, but it's nowhere near where it needs to be. Future work item.
  • Loading branch information
vitek-karas authored Jun 7, 2022
1 parent 7532658 commit 174c23e
Show file tree
Hide file tree
Showing 110 changed files with 7,394 additions and 8,585 deletions.
29 changes: 29 additions & 0 deletions src/coreclr/tools/aot/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Linker port settings:
# A newline ending every file
# Use tabs as indentation
[ILLink.Shared/**.cs]
indent_style = tab
indent_size = 4
csharp_new_line_before_open_brace = types,methods
csharp_new_line_before_else = false
csharp_new_line_before_catch = false
csharp_new_line_before_finally = false
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_between_query_expression_clauses = true

csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_between_method_declaration_name_and_open_parenthesis = true
csharp_space_between_method_call_name_and_opening_parenthesis = true
csharp_space_before_open_square_brackets = false
csharp_space_after_cast = true

csharp_indent_switch_labels = false

# Sort using and Import directives with System.* appearing first
dotnet_sort_system_directives_first = true

# Prefer property-like constructs to have an expression-body
csharp_style_expression_bodied_properties = true:none
csharp_style_expression_bodied_indexers = true:none
csharp_style_expression_bodied_accessors = true:none
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public void TestDependencyGraphInvariants(EcmaMethod method)
UsageBasedMetadataManager metadataManager = new UsageBasedMetadataManager(compilationGroup, context,
new FullyBlockedMetadataBlockingPolicy(), new FullyBlockedManifestResourceBlockingPolicy(),
null, new NoStackTraceEmissionPolicy(), new NoDynamicInvokeThunkGenerationPolicy(),
new Dataflow.FlowAnnotations(Logger.Null, ilProvider), UsageBasedMetadataGenerationOptions.None,
new ILLink.Shared.TrimAnalysis.FlowAnnotations(Logger.Null, ilProvider), UsageBasedMetadataGenerationOptions.None,
Logger.Null, Array.Empty<KeyValuePair<string, bool>>(), Array.Empty<string>(), Array.Empty<string>());

CompilationBuilder builder = new RyuJitCompilationBuilder(context, compilationGroup)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// 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 System.Text;
using ILCompiler.Dataflow;
using ILLink.Shared.DataFlow;
using Internal.TypeSystem;

using MultiValue = ILLink.Shared.DataFlow.ValueSet<ILLink.Shared.DataFlow.SingleValue>;

#nullable enable

namespace ILLink.Shared.TrimAnalysis
{
partial record ArrayValue
{
public static MultiValue Create(MultiValue size, TypeDesc elementType)
{
MultiValue result = MultiValueLattice.Top;
foreach (var sizeValue in size)
{
result = MultiValueLattice.Meet(result, new MultiValue(new ArrayValue(sizeValue, elementType)));
}

return result;
}

public static MultiValue Create(int size, TypeDesc elementType)
{
return new MultiValue(new ArrayValue(new ConstIntValue(size), elementType));
}

/// <summary>
/// Constructs an array value of the given size
/// </summary>
ArrayValue(SingleValue size, TypeDesc elementType)
{
Size = size;
ElementType = elementType;
IndexValues = new Dictionary<int, ValueBasicBlockPair>();
}

public TypeDesc ElementType { get; }
public Dictionary<int, ValueBasicBlockPair> IndexValues { get; }

public partial bool TryGetValueByIndex(int index, out MultiValue value)
{
if (IndexValues.TryGetValue(index, out var valuePair))
{
value = valuePair.Value;
return true;
}

value = default;
return false;
}

public override int GetHashCode()
{
return HashCode.Combine(GetType().GetHashCode(), Size);
}

public bool Equals(ArrayValue? otherArr)
{
if (otherArr == null)
return false;

bool equals = Size.Equals(otherArr.Size);
equals &= IndexValues.Count == otherArr.IndexValues.Count;
if (!equals)
return false;

// If both sets T and O are the same size and "T intersect O" is empty, then T == O.
HashSet<KeyValuePair<int, ValueBasicBlockPair>> thisValueSet = new(IndexValues);
HashSet<KeyValuePair<int, ValueBasicBlockPair>> otherValueSet = new(otherArr.IndexValues);
thisValueSet.ExceptWith(otherValueSet);
return thisValueSet.Count == 0;
}

public override SingleValue DeepCopy()
{
var newValue = new ArrayValue(Size.DeepCopy(), ElementType);
foreach (var kvp in IndexValues)
{
newValue.IndexValues.Add(kvp.Key, new ValueBasicBlockPair(kvp.Value.Value.Clone(), kvp.Value.BasicBlockIndex));
}

return newValue;
}

public override string ToString()
{
StringBuilder result = new();
result.Append("Array Size:");
result.Append(this.ValueToString(Size));

result.Append(", Values:(");
bool first = true;
foreach (var element in IndexValues)
{
if (!first)
{
result.Append(",");
first = false;
}

result.Append("(");
result.Append(element.Key);
result.Append(",(");
bool firstValue = true;
foreach (var v in element.Value.Value)
{
if (firstValue)
{
result.Append(",");
firstValue = false;
}

result.Append(v.ToString());
}
result.Append("))");
}
result.Append(')');

return result.ToString();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using ILCompiler;
using ILCompiler.Logging;

#nullable enable

namespace ILLink.Shared.TrimAnalysis
{
readonly partial struct DiagnosticContext
{
public readonly MessageOrigin Origin;
public readonly bool DiagnosticsEnabled;
readonly Logger _logger;

public DiagnosticContext(in MessageOrigin origin, bool diagnosticsEnabled, Logger logger)
=> (Origin, DiagnosticsEnabled, _logger) = (origin, diagnosticsEnabled, logger);

public partial void AddDiagnostic(DiagnosticId id, params string[] args)
{
if (DiagnosticsEnabled)
_logger.LogWarning(Origin, id, args);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,12 @@ internal static bool IsInRequiresScope(this MethodDesc method, string requiresAt
method.IsInRequiresScope(requiresAttribute, true);

/// <summary>
/// True if member of a call is considered to be annotated with the Requires... attribute.
/// Doesn't check the associated symbol for overrides and virtual methods because we should warn on mismatched between the property AND the accessors
/// </summary>
/// <param name="method">
/// MethodDesc that is either an overriding member or an overriden/virtual member
/// </param>
/// True if member of a call is considered to be annotated with the Requires... attribute.
/// Doesn't check the associated symbol for overrides and virtual methods because we should warn on mismatched between the property AND the accessors
/// </summary>
/// <param name="method">
/// MethodDesc that is either an overriding member or an overriden/virtual member
/// </param>
internal static bool IsOverrideInRequiresScope(this MethodDesc method, string requiresAttribute) =>
method.IsInRequiresScope(requiresAttribute, false);

Expand Down Expand Up @@ -185,10 +185,10 @@ internal static bool DoesPropertyRequire(this PropertyPseudoDesc property, strin
TryGetRequiresAttribute(property, requiresAttribute, out attribute);

/// <summary>
/// Determines if member requires (and thus any usage of such method should be warned about).
/// </summary>
/// <remarks>Unlike <see cref="IsInRequiresScope(MethodDesc, string)"/> only static methods
/// and .ctors are reported as requires when the declaring type has Requires on it.</remarks>
/// Determines if member requires (and thus any usage of such method should be warned about).
/// </summary>
/// <remarks>Unlike <see cref="IsInRequiresScope(MethodDesc, string)"/> only static methods
/// and .ctors are reported as requires when the declaring type has Requires on it.</remarks>
internal static bool DoesMemberRequire(this TypeSystemEntity member, string requiresAttribute, [NotNullWhen(returnValue: true)] out CustomAttributeValue<TypeDesc>? attribute)
{
attribute = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ public static IEnumerable<EventPseudoDesc> GetEventsOnTypeHierarchy(this TypeDes
type = type.BaseType;
onBaseType = true;
}

while (type != null)
{
if (type.GetTypeDefinition() is not EcmaType ecmaType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public static bool IsPublic(this MethodDesc method)
public static bool IsPublic(this FieldDesc field)
{
return field.GetTypicalFieldDefinition() is EcmaField ecmaField
&& (ecmaField.Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Public;
&& (ecmaField.Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Public;
}

public static bool IsPrivate(this MethodDesc method)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// 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.CodeAnalysis;
using ILCompiler;
using ILCompiler.Dataflow;
using ILLink.Shared.DataFlow;
using Internal.TypeSystem;

#nullable enable

namespace ILLink.Shared.TrimAnalysis
{

/// <summary>
/// A representation of a field. Typically a result of ldfld.
/// </summary>
sealed partial record FieldValue : IValueWithStaticType
{
public FieldValue(FieldDesc field, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes)
{
StaticType = field.FieldType;
Field = field;
DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes;
}

public readonly FieldDesc Field;

public override DynamicallyAccessedMemberTypes DynamicallyAccessedMemberTypes { get; }

public override IEnumerable<string> GetDiagnosticArgumentsForAnnotationMismatch()
=> new string[] { Field.GetDisplayName() };

public TypeDesc? StaticType { get; }

public override SingleValue DeepCopy() => this; // This value is immutable

public override string ToString() => this.ValueToString(Field, DynamicallyAccessedMemberTypes);
}
}
Loading

0 comments on commit 174c23e

Please sign in to comment.