Skip to content
Merged
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 @@ -626,9 +626,9 @@ protected virtual void Scan(MethodIL methodIL, ref InterproceduralState interpro
case ILOpcode.newarr:
{
StackSlot count = PopUnknown(currentStack, 1, methodIL, offset);
var arrayElement = (TypeDesc)methodIL.GetObject(reader.ReadILToken());
HandleTypeTokenAccess(methodIL, offset, arrayElement);
currentStack.Push(new StackSlot(ArrayValue.Create(count.Value, arrayElement)));
var arrayElementType = (TypeDesc)methodIL.GetObject(reader.ReadILToken());
HandleTypeTokenAccess(methodIL, offset, arrayElementType);
currentStack.Push(new StackSlot(ArrayValue.Create(count.Value, arrayElementType)));
}
break;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public partial bool TryResolveTypeNameAndMark(string typeName, bool needsAssembl
{
if (_reflectionMarker.TryResolveTypeNameAndMark(typeName, _diagnosticContext, needsAssemblyName, _reason, out TypeDesc? foundType))
{
if (foundType.HasInstantiation && _reflectionMarker.Annotations.HasGenericParameterAnnotation(foundType))
if (GenericArgumentDataFlow.RequiresGenericArgumentDataFlow(_reflectionMarker.Annotations, foundType))
{
GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(_diagnosticContext, _reflectionMarker, foundType);
}
Expand Down
6 changes: 6 additions & 0 deletions src/tools/illink/src/linker/CompatibilitySuppressions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,12 @@
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:System.Reflection.RuntimeAssemblyName</Target>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Mono.Linker.Dataflow.TrimAnalysisGenericInstantiationAccessPattern</Target>
<Left>ref/net10.0/illink.dll</Left>
<Right>lib/net10.0/illink.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>F:Mono.Linker.AnnotationStore.assembly_actions</Target>
Expand Down
20 changes: 19 additions & 1 deletion src/tools/illink/src/linker/Linker.Dataflow/FlowAnnotations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,23 @@ public bool RequiresVirtualMethodDataFlowAnalysis(MethodDefinition method) =>
public bool RequiresDataFlowAnalysis(FieldDefinition field) =>
GetAnnotations(field.DeclaringType).TryGetAnnotation(field, out _);

public bool RequiresGenericArgumentDataFlowAnalysis(GenericParameter genericParameter) =>
public bool HasGenericParameterAnnotation(TypeReference type)
{
if (type.ResolveToTypeDefinition(_context) is not TypeDefinition typeDefinition)
return false;

return GetAnnotations(typeDefinition).HasGenericParameterAnnotation();
}

public bool HasGenericParameterAnnotation(MethodReference method)
{
if (_context.TryResolve(method) is not MethodDefinition methodDefinition)
return false;

return GetAnnotations(methodDefinition.DeclaringType).TryGetAnnotation(methodDefinition, out var annotation) && annotation.GenericParameterAnnotations != null;
}

public bool RequiresGenericArgumentDataFlow(GenericParameter genericParameter) =>
GetGenericParameterAnnotation(genericParameter) != DynamicallyAccessedMemberTypes.None;

internal DynamicallyAccessedMemberTypes GetParameterAnnotation(ParameterProxy param)
Expand Down Expand Up @@ -731,6 +747,8 @@ public bool TryGetAnnotation(GenericParameter genericParameter, out DynamicallyA

return false;
}

public bool HasGenericParameterAnnotation() => _genericParameterAnnotations != null;
}

readonly struct MethodAnnotations
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using ILLink.Shared.TrimAnalysis;
Expand All @@ -10,35 +11,111 @@

namespace Mono.Linker.Dataflow
{
public readonly struct GenericArgumentDataFlow
internal static class GenericArgumentDataFlow
{
readonly LinkContext _context;
readonly MarkStep _markStep;
readonly MessageOrigin _origin;
public static void ProcessGenericArgumentDataFlow(in MessageOrigin origin, MarkStep markStep, LinkContext context, TypeReference type)
{
var diagnosticContext = new DiagnosticContext(origin, !context.Annotations.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode(origin.Provider, out _), context);
var reflectionMarker = new ReflectionMarker(context, markStep, enabled: true);
ProcessGenericArgumentDataFlow(in diagnosticContext, reflectionMarker, context, type);
}

public static void ProcessGenericArgumentDataFlow(in DiagnosticContext diagnosticContext, ReflectionMarker reflectionMarker, LinkContext context, TypeReference type)
{
if (type is GenericInstanceType genericInstanceType && context.TryResolve(type) is TypeDefinition typeDefinition)
{
ProcessGenericInstantiation(diagnosticContext, reflectionMarker, context, genericInstanceType, typeDefinition);
}
}

public static void ProcessGenericArgumentDataFlow(in DiagnosticContext diagnosticContext, ReflectionMarker reflectionMarker, LinkContext context, MethodReference method)
{
if (method is GenericInstanceMethod genericInstanceMethod && context.TryResolve(method) is MethodDefinition methodDefinition)
{
ProcessGenericInstantiation(diagnosticContext, reflectionMarker, context, genericInstanceMethod, methodDefinition);
}

ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, context, method.DeclaringType);
}

public static void ProcessGenericArgumentDataFlow(in DiagnosticContext diagnosticContext, ReflectionMarker reflectionMarker, LinkContext context, FieldReference field)
{
ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, context, field.DeclaringType);
}

public GenericArgumentDataFlow(LinkContext context, MarkStep markStep, in MessageOrigin origin)
private static void ProcessGenericInstantiation(in DiagnosticContext diagnosticContext, ReflectionMarker reflectionMarker, LinkContext context, IGenericInstance genericInstance, IGenericParameterProvider genericParameterProvider)
{
_context = context;
_markStep = markStep;
_origin = origin;
var arguments = genericInstance.GenericArguments;
var parameters = genericParameterProvider.GenericParameters;

for (int i = 0; i < arguments.Count; i++)
{
var genericArgument = arguments[i];
var genericParameter = parameters[i];

var genericParameterValue = context.Annotations.FlowAnnotations.GetGenericParameterValue(genericParameter);
if (genericParameterValue.DynamicallyAccessedMemberTypes != DynamicallyAccessedMemberTypes.None)
{
MultiValue genericArgumentValue = context.Annotations.FlowAnnotations.GetTypeValueFromGenericArgument(genericArgument);

var requireDynamicallyAccessedMembersAction = new RequireDynamicallyAccessedMembersAction(context, reflectionMarker, diagnosticContext);
requireDynamicallyAccessedMembersAction.Invoke(genericArgumentValue, genericParameterValue);
}

// Recursively process generic argument data flow on the generic argument if it itself is generic
if (genericArgument.IsGenericInstance)
{
ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, context, genericArgument);
}
}
}

public void ProcessGenericArgumentDataFlow(GenericParameter genericParameter, TypeReference genericArgument)
internal static bool RequiresGenericArgumentDataFlow(FlowAnnotations flowAnnotations, MethodReference method)
{
var genericParameterValue = _context.Annotations.FlowAnnotations.GetGenericParameterValue(genericParameter);
Debug.Assert(genericParameterValue.DynamicallyAccessedMemberTypes != DynamicallyAccessedMemberTypes.None);
// Method callsites can contain generic instantiations which may contain annotations inside nested generics
// so we have to check all of the instantiations for that case.
// For example:
// OuterGeneric<InnerGeneric<Annotated>>.Method<InnerGeneric<AnotherAnnotated>>();

if (method is GenericInstanceMethod genericInstanceMethod)
{
if (flowAnnotations.HasGenericParameterAnnotation(method))
return true;

MultiValue genericArgumentValue = _context.Annotations.FlowAnnotations.GetTypeValueFromGenericArgument(genericArgument);
foreach (var genericArgument in genericInstanceMethod.GenericArguments)
{
if (RequiresGenericArgumentDataFlow(flowAnnotations, genericArgument))
return true;
}
}

var diagnosticContext = new DiagnosticContext(_origin, !_context.Annotations.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode(_origin.Provider, out _), _context);
RequireDynamicallyAccessedMembers(diagnosticContext, genericArgumentValue, genericParameterValue);
return RequiresGenericArgumentDataFlow(flowAnnotations, method.DeclaringType);
}

void RequireDynamicallyAccessedMembers(in DiagnosticContext diagnosticContext, in MultiValue value, ValueWithDynamicallyAccessedMembers targetValue)
internal static bool RequiresGenericArgumentDataFlow(FlowAnnotations flowAnnotations, FieldReference field)
{
var reflectionMarker = new ReflectionMarker(_context, _markStep, enabled: true);
var requireDynamicallyAccessedMembersAction = new RequireDynamicallyAccessedMembersAction(_context, reflectionMarker, diagnosticContext);
requireDynamicallyAccessedMembersAction.Invoke(value, targetValue);
return RequiresGenericArgumentDataFlow(flowAnnotations, field.DeclaringType);
}

internal static bool RequiresGenericArgumentDataFlow(FlowAnnotations flowAnnotations, TypeReference type)
{
if (flowAnnotations.HasGenericParameterAnnotation(type))
{
return true;
}

if (type is GenericInstanceType genericInstanceType)
{
foreach (var genericArgument in genericInstanceType.GenericArguments)
{
if (RequiresGenericArgumentDataFlow(flowAnnotations, genericArgument))
{
return true;
}
}
}

return false;
}
}
}
55 changes: 41 additions & 14 deletions src/tools/illink/src/linker/Linker.Dataflow/MethodBodyScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,9 @@ protected virtual void Scan(MethodIL methodIL, ref InterproceduralState interpro
break;

case Code.Ldftn:
TrackNestedFunctionReference((MethodReference)operation.Operand, ref interproceduralState);
MethodReference methodReference = (MethodReference)operation.Operand;
HandleMethodTokenAccess(methodIL, operation.Offset, methodReference);
TrackNestedFunctionReference(methodReference, ref interproceduralState);
PushUnknown(currentStack);
break;

Expand Down Expand Up @@ -457,7 +459,7 @@ protected virtual void Scan(MethodIL methodIL, ref InterproceduralState interpro
break;

case Code.Ldtoken:
ScanLdtoken(operation, currentStack);
ScanLdtoken(methodIL, operation, currentStack);
break;

case Code.Ldind_I:
Expand Down Expand Up @@ -510,16 +512,21 @@ protected virtual void Scan(MethodIL methodIL, ref InterproceduralState interpro
case Code.Conv_R8:
case Code.Ldind_Ref:
case Code.Ldobj:
case Code.Mkrefany:
case Code.Unbox:
case Code.Unbox_Any:
case Code.Box:
case Code.Neg:
case Code.Not:
PopUnknown(currentStack, 1, methodIL, operation.Offset);
PushUnknown(currentStack);
break;

case Code.Box:
case Code.Mkrefany:
HandleTypeTokenAccess(methodIL, operation.Offset, (TypeReference)operation.Operand);
PopUnknown(currentStack, 1, methodIL, operation.Offset);
PushUnknown(currentStack);
break;

case Code.Isinst:
case Code.Castclass:
// We can consider a NOP because the value doesn't change.
Expand All @@ -537,7 +544,9 @@ protected virtual void Scan(MethodIL methodIL, ref InterproceduralState interpro
case Code.Newarr:
{
StackSlot count = PopUnknown(currentStack, 1, methodIL, operation.Offset);
currentStack.Push(new StackSlot(ArrayValue.Create(count.Value, (TypeReference)operation.Operand)));
var arrayElementType = (TypeReference)operation.Operand;
HandleTypeTokenAccess(methodIL, operation.Offset, arrayElementType);
currentStack.Push(new StackSlot(ArrayValue.Create(count.Value, arrayElementType)));
}
break;

Expand Down Expand Up @@ -828,7 +837,7 @@ private void ScanLdloc(
currentStack.Push(newSlot);
}

void ScanLdtoken(Instruction operation, Stack<StackSlot> currentStack)
void ScanLdtoken(MethodIL methodIL, Instruction operation, Stack<StackSlot> currentStack)
{
switch (operation.Operand)
{
Expand All @@ -846,25 +855,36 @@ void ScanLdtoken(Instruction operation, Stack<StackSlot> currentStack)
var nullableDam = new RuntimeTypeHandleForNullableValueWithDynamicallyAccessedMembers(new TypeProxy(resolvedDefinition, _context),
new RuntimeTypeHandleForGenericParameterValue(genericParam));
currentStack.Push(new StackSlot(nullableDam));
return;
break;
case TypeReference underlyingTypeReference when ResolveToTypeDefinition(underlyingTypeReference) is TypeDefinition underlyingType:
var nullableType = new RuntimeTypeHandleForNullableSystemTypeValue(new TypeProxy(resolvedDefinition, _context), new SystemTypeValue(new(underlyingType, _context)));
currentStack.Push(new StackSlot(nullableType));
return;
break;
default:
PushUnknown(currentStack);
return;
break;
}
}
else
{
var typeHandle = new RuntimeTypeHandleValue(new TypeProxy(resolvedDefinition, _context));
currentStack.Push(new StackSlot(typeHandle));
return;
}

HandleTypeTokenAccess(methodIL, operation.Offset, typeReference);
return;

case MethodReference methodReference when _context.TryResolve(methodReference) is MethodDefinition resolvedMethod:
var method = new RuntimeMethodHandleValue(resolvedMethod);
currentStack.Push(new StackSlot(method));

HandleMethodTokenAccess(methodIL, operation.Offset, methodReference);
return;

case FieldReference fieldReference:
PushUnknown(currentStack);

HandleFieldTokenAccess(methodIL, operation.Offset, fieldReference);
return;
default:
PushUnknown(currentStack);
Expand Down Expand Up @@ -1003,6 +1023,12 @@ protected virtual void HandleReturnValue(MethodIL method, MethodReturnValue this
{
}

protected abstract void HandleTypeTokenAccess(MethodIL methodIL, int offset, TypeReference accessedType);

protected abstract void HandleMethodTokenAccess(MethodIL methodIL, int offset, MethodReference accessedMethod);

protected abstract void HandleFieldTokenAccess(MethodIL methodIL, int offset, FieldReference accessedField);

private void ScanStfld(
MethodIL methodIL,
Instruction operation,
Expand All @@ -1014,12 +1040,13 @@ private void ScanStfld(
if (operation.OpCode.Code == Code.Stfld)
PopUnknown(currentStack, 1, methodIL, operation.Offset);

FieldDefinition? field = _context.TryResolve((FieldReference)operation.Operand);
if (field != null)
var field = (FieldReference)operation.Operand;
FieldDefinition? fieldDefinition = _context.TryResolve(field);
if (fieldDefinition != null)
{
if (CompilerGeneratedState.IsHoistedLocal(field))
if (CompilerGeneratedState.IsHoistedLocal(fieldDefinition))
{
interproceduralState.SetHoistedLocal(new HoistedLocalKey(field), valueToStoreSlot.Value);
interproceduralState.SetHoistedLocal(new HoistedLocalKey(fieldDefinition), valueToStoreSlot.Value);
return;
}

Expand Down
Loading
Loading