Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linker to NativeAOT sync #71485

Merged
merged 22 commits into from
Jul 19, 2022
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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 @@ -3,7 +3,7 @@

using System;
using System.Collections.Generic;

using ILCompiler.Dataflow;
using Internal.IL;
using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;
Expand Down Expand Up @@ -67,11 +67,12 @@ public void TestDependencyGraphInvariants(EcmaMethod method)
CompilationModuleGroup compilationGroup = new SingleFileCompilationModuleGroup();

NativeAotILProvider ilProvider = new NativeAotILProvider();
CompilerGeneratedState compilerGeneratedState = new CompilerGeneratedState(ilProvider);

UsageBasedMetadataManager metadataManager = new UsageBasedMetadataManager(compilationGroup, context,
new FullyBlockedMetadataBlockingPolicy(), new FullyBlockedManifestResourceBlockingPolicy(),
null, new NoStackTraceEmissionPolicy(), new NoDynamicInvokeThunkGenerationPolicy(),
new ILLink.Shared.TrimAnalysis.FlowAnnotations(Logger.Null, ilProvider), UsageBasedMetadataGenerationOptions.None,
new ILLink.Shared.TrimAnalysis.FlowAnnotations(Logger.Null, ilProvider, compilerGeneratedState), 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
Expand Up @@ -27,9 +27,9 @@ public static MultiValue Create(MultiValue size, TypeDesc elementType)
return result;
}

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

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// 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.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection.Metadata;

using ILCompiler.DependencyAnalysis;
using ILCompiler.Logging;

using ILLink.Shared.TrimAnalysis;

using Internal.TypeSystem;

using CustomAttributeValue = System.Reflection.Metadata.CustomAttributeValue<Internal.TypeSystem.TypeDesc>;
using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore<ILCompiler.DependencyAnalysis.NodeFactory>.DependencyList;
using MultiValue = ILLink.Shared.DataFlow.ValueSet<ILLink.Shared.DataFlow.SingleValue>;

#nullable enable

namespace ILCompiler.Dataflow
{
public readonly struct AttributeDataFlow
{
readonly Logger _logger;
readonly NodeFactory _factory;
readonly FlowAnnotations _annotations;
readonly MessageOrigin _origin;

public AttributeDataFlow(Logger logger, NodeFactory factory, FlowAnnotations annotations, in MessageOrigin origin)
{
_annotations = annotations;
_factory = factory;
_logger = logger;
_origin = origin;
}

public DependencyList? ProcessAttributeDataflow(MethodDesc method, CustomAttributeValue arguments)
{
DependencyList? result = null;

// First do the dataflow for the constructor parameters if necessary.
if (_annotations.RequiresDataflowAnalysis(method))
{
var builder = ImmutableArray.CreateBuilder<object?>(arguments.FixedArguments.Length);
foreach (var argument in arguments.FixedArguments)
{
builder.Add(argument.Value);
}

ProcessAttributeDataflow(method, builder.ToImmutableArray(), ref result);
}

// Named arguments next
TypeDesc attributeType = method.OwningType;
foreach (var namedArgument in arguments.NamedArguments)
{
if (namedArgument.Kind == CustomAttributeNamedArgumentKind.Field)
{
FieldDesc field = attributeType.GetField(namedArgument.Name);
if (field != null)
{
ProcessAttributeDataflow(field, namedArgument.Value, ref result);
}
}
else
{
Debug.Assert(namedArgument.Kind == CustomAttributeNamedArgumentKind.Property);
PropertyPseudoDesc property = ((MetadataType)attributeType).GetProperty(namedArgument.Name, null);
MethodDesc setter = property.SetMethod;
if (setter != null && setter.Signature.Length > 0 && !setter.Signature.IsStatic)
{
ProcessAttributeDataflow(setter, ImmutableArray.Create(namedArgument.Value), ref result);
}
}
}

return result;
}

void ProcessAttributeDataflow(MethodDesc method, ImmutableArray<object?> arguments, ref DependencyList? result)
{
for (int i = 0; i < method.Signature.Length; i++)
{
var parameterValue = _annotations.GetMethodParameterValue(method, i);
if (parameterValue.DynamicallyAccessedMemberTypes != DynamicallyAccessedMemberTypes.None)
{
MultiValue value = GetValueForCustomAttributeArgument(arguments[i]);
var diagnosticContext = new DiagnosticContext(_origin, diagnosticsEnabled: true, _logger);
RequireDynamicallyAccessedMembers(diagnosticContext, value, parameterValue, parameterValue.ParameterOrigin, ref result);
}
}
}

public void ProcessAttributeDataflow(FieldDesc field, object? value, ref DependencyList? result)
{
var fieldValueCandidate = _annotations.GetFieldValue(field);
if (fieldValueCandidate is ValueWithDynamicallyAccessedMembers fieldValue
&& fieldValue.DynamicallyAccessedMemberTypes != DynamicallyAccessedMemberTypes.None)
{
MultiValue valueNode = GetValueForCustomAttributeArgument(value);
var diagnosticContext = new DiagnosticContext(_origin, diagnosticsEnabled: true, _logger);
RequireDynamicallyAccessedMembers(diagnosticContext, valueNode, fieldValue, new FieldOrigin(field), ref result);
}
}

MultiValue GetValueForCustomAttributeArgument(object? argument)
{
if (argument is TypeDesc td)
{
return new SystemTypeValue(td);
}

if (argument is string str)
{
return new KnownStringValue(str);
}

// We shouldn't have gotten a None annotation from flow annotations since only string/Type can have annotations
throw new InvalidOperationException();
}
vitek-karas marked this conversation as resolved.
Show resolved Hide resolved

void RequireDynamicallyAccessedMembers(
in DiagnosticContext diagnosticContext,
in MultiValue value,
ValueWithDynamicallyAccessedMembers targetValue,
Origin memberWithRequirements,
ref DependencyList? result)
{
var reflectionMarker = new ReflectionMarker(_logger, _factory, _annotations, typeHierarchyDataFlow: false, enabled: true);
var requireDynamicallyAccessedMembersAction = new RequireDynamicallyAccessedMembersAction(reflectionMarker, diagnosticContext, memberWithRequirements);
requireDynamicallyAccessedMembersAction.Invoke(value, targetValue);

if (result == null)
{
result = reflectionMarker.Dependencies;
}
else
{
result.AddRange(reflectionMarker.Dependencies);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// 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 Internal.TypeSystem;
using Internal.TypeSystem.Ecma;

#nullable enable

namespace ILCompiler.Dataflow
{
sealed class CompilerGeneratedCallGraph
{
readonly Dictionary<TypeSystemEntity, HashSet<TypeSystemEntity>> callGraph;
vitek-karas marked this conversation as resolved.
Show resolved Hide resolved

public CompilerGeneratedCallGraph() => callGraph = new Dictionary<TypeSystemEntity, HashSet<TypeSystemEntity>>();

void TrackCallInternal(TypeSystemEntity fromMember, TypeSystemEntity toMember)
{
if (!callGraph.TryGetValue(fromMember, out HashSet<TypeSystemEntity>? toMembers))
{
toMembers = new HashSet<TypeSystemEntity>();
callGraph.Add(fromMember, toMembers);
}
toMembers.Add(toMember);
}

public void TrackCall(MethodDesc fromMethod, MethodDesc toMethod)
vitek-karas marked this conversation as resolved.
Show resolved Hide resolved
{
Debug.Assert(CompilerGeneratedNames.IsLambdaOrLocalFunction(toMethod.Name));
TrackCallInternal(fromMethod, toMethod);
}

public void TrackCall(MethodDesc fromMethod, DefType toType)
{
Debug.Assert(CompilerGeneratedNames.IsStateMachineType(toType.Name));
TrackCallInternal(fromMethod, toType);
}

public void TrackCall(DefType fromType, MethodDesc toMethod)
{
Debug.Assert(CompilerGeneratedNames.IsStateMachineType(fromType.Name));
Debug.Assert(CompilerGeneratedNames.IsLambdaOrLocalFunction(toMethod.Name));
TrackCallInternal(fromType, toMethod);
}

public IEnumerable<TypeSystemEntity> GetReachableMembers(MethodDesc start)
{
Queue<TypeSystemEntity> queue = new();
HashSet<TypeSystemEntity> visited = new();
visited.Add(start);
queue.Enqueue(start);
while (queue.TryDequeue(out TypeSystemEntity? method))
{
if (!callGraph.TryGetValue(method, out HashSet<TypeSystemEntity>? callees))
continue;

foreach (var callee in callees)
{
if (visited.Add(callee))
{
queue.Enqueue(callee);
yield return callee;
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable enable

namespace ILCompiler.Dataflow
{
sealed class CompilerGeneratedNames
{
internal static bool IsGeneratedMemberName(string memberName)
{
return memberName.Length > 0 && memberName[0] == '<';
}

internal static bool IsLambdaDisplayClass(string className)
{
if (!IsGeneratedMemberName(className))
return false;

// This is true for static lambdas (which are emitted into a class like <>c)
// and for instance lambdas (which are emitted into a class like <>c__DisplayClass1_0)
return className.StartsWith("<>c");
}

internal static bool IsStateMachineType(string typeName)
vitek-karas marked this conversation as resolved.
Show resolved Hide resolved
{
if (!IsGeneratedMemberName(typeName))
return false;

int i = typeName.LastIndexOf('>');
if (i == -1)
return false;

return typeName.Length > i + 1 && typeName[i + 1] == 'd';
}

internal static bool IsGeneratedType(string name) => IsStateMachineType(name) || IsLambdaDisplayClass(name);

internal static bool IsLambdaOrLocalFunction(string methodName) => IsLambdaMethod(methodName) || IsLocalFunction(methodName);

// Lambda methods have generated names like "<UserMethod>b__0_1" where "UserMethod" is the name
// of the original user code that contains the lambda method declaration.
internal static bool IsLambdaMethod(string methodName)
{
if (!IsGeneratedMemberName(methodName))
return false;

int i = methodName.IndexOf('>', 1);
if (i == -1)
return false;

// Ignore the method ordinal/generation and lambda ordinal/generation.
return methodName.Length > i + 1 && methodName[i + 1] == 'b';
}

// Local functions have generated names like "<UserMethod>g__LocalFunction|0_1" where "UserMethod" is the name
// of the original user code that contains the lambda method declaration, and "LocalFunction" is the name of
// the local function.
internal static bool IsLocalFunction(string methodName)
{
if (!IsGeneratedMemberName(methodName))
return false;

int i = methodName.IndexOf('>', 1);
if (i == -1)
return false;

// Ignore the method ordinal/generation and local function ordinal/generation.
return methodName.Length > i + 1 && methodName[i + 1] == 'g';
}
}
}
Loading