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

Integrate changes from NativeAOT branch #59510

Merged
merged 2 commits into from
Sep 29, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Integrate changes from NativeAOT branch
  • Loading branch information
MichalStrehovsky committed Sep 27, 2021
commit c7901ed84a6c6f5884799d70fb304feab8880e68
73 changes: 70 additions & 3 deletions src/coreclr/tools/Common/Compiler/Logger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,30 @@ public class Logger
{
private readonly HashSet<int> _suppressedWarnings;

private readonly bool _isSingleWarn;
private readonly HashSet<string> _singleWarnEnabledAssemblies;
private readonly HashSet<string> _singleWarnDisabledAssemblies;
private readonly HashSet<string> _trimWarnedAssemblies = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
private readonly HashSet<string> _aotWarnedAssemblies = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

public static Logger Null = new Logger(TextWriter.Null, false);

public TextWriter Writer { get; }

public bool IsVerbose { get; }

public Logger(TextWriter writer, bool isVerbose, IEnumerable<int> suppressedWarnings)
public Logger(TextWriter writer, bool isVerbose, IEnumerable<int> suppressedWarnings, bool singleWarn, IEnumerable<string> singleWarnEnabledModules, IEnumerable<string> singleWarnDisabledModules)
{
Writer = TextWriter.Synchronized(writer);
IsVerbose = isVerbose;
_suppressedWarnings = new HashSet<int>(suppressedWarnings);
_isSingleWarn = singleWarn;
_singleWarnEnabledAssemblies = new HashSet<string>(singleWarnEnabledModules, StringComparer.OrdinalIgnoreCase);
_singleWarnDisabledAssemblies = new HashSet<string>(singleWarnDisabledModules, StringComparer.OrdinalIgnoreCase);
}

public Logger(TextWriter writer, bool isVerbose)
: this(writer, isVerbose, Array.Empty<int>())
: this(writer, isVerbose, Array.Empty<int>(), singleWarn: false, Array.Empty<string>(), Array.Empty<string>())
{
}

Expand Down Expand Up @@ -71,10 +80,18 @@ public void LogWarning(string text, int code, MethodIL origin, int ilOffset, str
}
}

MessageOrigin messageOrigin = new MessageOrigin(origin.OwningMethod, document, lineNumber, null);
MethodDesc warnedMethod = CompilerGeneratedState.GetUserDefinedMethodForCompilerGeneratedMember(origin.OwningMethod) ?? origin.OwningMethod;

MessageOrigin messageOrigin = new MessageOrigin(warnedMethod, document, lineNumber, null);
LogWarning(text, code, messageOrigin, subcategory);
}

public void LogWarning(string text, int code, string origin, string subcategory = MessageSubCategory.None)
{
MessageOrigin _origin = new MessageOrigin(origin);
LogWarning(text, code, _origin, subcategory);
}

internal bool IsWarningSuppressed(int code, MessageOrigin origin)
{
if (_suppressedWarnings.Contains(code))
Expand All @@ -87,6 +104,8 @@ internal bool IsWarningSuppressed(int code, MessageOrigin origin)

if (origin.MemberDefinition is MethodDesc method)
{
method = CompilerGeneratedState.GetUserDefinedMethodForCompilerGeneratedMember(method) ?? method;

var ecmaMethod = method.GetTypicalMethodDefinition() as EcmaMethod;
suppressions = ecmaMethod?.GetDecodedCustomAttributes("System.Diagnostics.CodeAnalysis", "UnconditionalSuppressMessageAttribute");
}
Expand Down Expand Up @@ -120,6 +139,54 @@ internal bool IsWarningAsError(int code)
// TODO: warnaserror
return false;
}

internal bool IsSingleWarn(ModuleDesc owningModule, string messageSubcategory)
{
string assemblyName = owningModule.Assembly.GetName().Name;

bool result = false;

if ((_isSingleWarn || _singleWarnEnabledAssemblies.Contains(assemblyName))
&& !_singleWarnDisabledAssemblies.Contains(assemblyName))
{
result = true;

if (messageSubcategory == MessageSubCategory.TrimAnalysis)
{
lock (_trimWarnedAssemblies)
{
if (_trimWarnedAssemblies.Add(assemblyName))
{
LogWarning($"Assembly '{assemblyName}' produced trim warnings. For more information see https://aka.ms/dotnet-illink/libraries", 2104, GetModuleFileName(owningModule));
}
}
}
else if (messageSubcategory == MessageSubCategory.AotAnalysis)
{
lock (_aotWarnedAssemblies)
{
if (_aotWarnedAssemblies.Add(assemblyName))
{
LogWarning($"Assembly '{assemblyName}' produced AOT analysis warnings.", 9702, GetModuleFileName(owningModule));
}
}
}
}

return result;
}

private static string GetModuleFileName(ModuleDesc module)
{
string assemblyName = module.Assembly.GetName().Name;
var context = (CompilerTypeSystemContext)module.Context;
if (context.ReferenceFilePaths.TryGetValue(assemblyName, out string result)
|| context.InputFilePaths.TryGetValue(assemblyName, out result))
{
return result;
}
return assemblyName;
}
}

public static class MessageSubCategory
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;

using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;

namespace ILCompiler.Logging
{
// Currently this is implemented using heuristics
public class CompilerGeneratedState
{
private static bool HasRoslynCompilerGeneratedName(DefType type) =>
type.Name.Contains('<') || (type.ContainingType != null && HasRoslynCompilerGeneratedName(type.ContainingType));

public static MethodDesc GetUserDefinedMethodForCompilerGeneratedMember(MethodDesc sourceMember)
{
var compilerGeneratedType = sourceMember.OwningType.GetTypeDefinition() as EcmaType;
if (compilerGeneratedType == null)
return null;

// Only handle async or iterator state machine
// So go to the declaring type and check if it's compiler generated (as a perf optimization)
if (!HasRoslynCompilerGeneratedName(compilerGeneratedType) || compilerGeneratedType.ContainingType == null)
return null;

// Now go to its declaring type and search all methods to find the one which points to the type as its
// state machine implementation.
foreach (EcmaMethod method in compilerGeneratedType.ContainingType.GetMethods())
{
var decodedAttribute = method.GetDecodedCustomAttribute("System.Runtime.CompilerServices", "AsyncIteratorStateMachineAttribute")
?? method.GetDecodedCustomAttribute("System.Runtime.CompilerServices", "AsyncStateMachineAttribute")
?? method.GetDecodedCustomAttribute("System.Runtime.CompilerServices", "IteratorStateMachineAttribute");

if (!decodedAttribute.HasValue)
continue;

if (decodedAttribute.Value.FixedArguments.Length != 1
|| decodedAttribute.Value.FixedArguments[0].Value is not TypeDesc stateMachineType)
continue;

if (stateMachineType == compilerGeneratedType)
return method;
}

return null;
}
}
}
71 changes: 71 additions & 0 deletions src/coreclr/tools/Common/Compiler/Logging/MessageContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
using System;
using System.Text;
using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;

using Debug = System.Diagnostics.Debug;

namespace ILCompiler.Logging
{
Expand Down Expand Up @@ -87,12 +90,80 @@ internal static MessageContainer CreateErrorMessage(string text, int code, strin
if (context.IsWarningSuppressed(code, origin))
return null;

if (TryLogSingleWarning(context, code, origin, subcategory))
return null;

if (context.IsWarningAsError(code))
return new MessageContainer(MessageCategory.WarningAsError, text, code, subcategory, origin);

return new MessageContainer(MessageCategory.Warning, text, code, subcategory, origin);
}

private static bool TryLogSingleWarning(Logger context, int code, MessageOrigin origin, string subcategory)
{
if (subcategory != MessageSubCategory.AotAnalysis && subcategory != MessageSubCategory.TrimAnalysis)
return false;

var declaringType = origin.MemberDefinition switch
{
TypeDesc type => type,
MethodDesc method => method.OwningType,
FieldDesc field => field.OwningType,
#if !READYTORUN
PropertyPseudoDesc property => property.OwningType,
EventPseudoDesc @event => @event.OwningType,
#endif
_ => null,
};

ModuleDesc declaringAssembly = (declaringType as MetadataType)?.Module;
Debug.Assert(declaringAssembly != null);
if (declaringAssembly == null)
return false;

// Any IL2026 warnings left in an assembly with an IsTrimmable attribute are considered intentional
// and should not be collapsed, so that the user-visible RUC message gets printed.
if (code == 2026 && IsTrimmableAssembly(declaringAssembly))
return false;

if (context.IsSingleWarn(declaringAssembly, subcategory))
return true;

return false;
}

private static bool IsTrimmableAssembly(ModuleDesc assembly)
{
if (assembly is EcmaAssembly ecmaAssembly)
{
foreach (var attribute in ecmaAssembly.GetDecodedCustomAttributes("System.Reflection", "AssemblyMetadataAttribute"))
{
if (attribute.FixedArguments.Length != 2)
continue;

if (!attribute.FixedArguments[0].Type.IsString
|| ((string)(attribute.FixedArguments[0].Value)).Equals("IsTrimmable", StringComparison.Ordinal))
continue;

if (!attribute.FixedArguments[1].Type.IsString)
continue;

string value = (string)attribute.FixedArguments[1].Value;

if (value.Equals("True", StringComparison.OrdinalIgnoreCase))
{
return true;
}
else
{
//LogWarning($"Invalid AssemblyMetadata(\"IsTrimmable\", \"{args[1].Value}\") attribute in assembly '{assembly.Name.Name}'. Value must be \"True\"", 2102, GetAssemblyLocation(assembly));
}
}
}

return false;
}

/// <summary>
/// Create a info message.
/// </summary>
Expand Down
15 changes: 11 additions & 4 deletions src/coreclr/tools/Common/Compiler/Logging/MessageOrigin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public struct MessageOrigin
public int? SourceLine { get; }
public int? SourceColumn { get; }

public MessageOrigin(string fileName, int sourceLine = 0, int sourceColumn = 0)
public MessageOrigin(string fileName, int? sourceLine = null, int? sourceColumn = null)
{
FileName = fileName;
SourceLine = sourceLine;
Expand Down Expand Up @@ -55,7 +55,7 @@ public override string ToString()

#if false
public bool Equals(MessageOrigin other) =>
(FileName, MemberDefinition, SourceLine, SourceColumn) == (other.FileName, other.MemberDefinition, other.SourceLine, other.SourceColumn);
(FileName, MemberDefinition, SourceLine, SourceColumn, ILOffset) == (other.FileName, other.MemberDefinition, other.SourceLine, other.SourceColumn, other.ILOffset);

public override bool Equals(object obj) => obj is MessageOrigin messageOrigin && Equals(messageOrigin);
public override int GetHashCode() => (FileName, MemberDefinition, SourceLine, SourceColumn).GetHashCode();
Expand All @@ -66,8 +66,15 @@ public int CompareTo(MessageOrigin other)
{
if (MemberDefinition != null && other.MemberDefinition != null)
{
return (MemberDefinition.DeclaringType?.Module?.Assembly?.Name?.Name, MemberDefinition.DeclaringType?.Name, MemberDefinition?.Name).CompareTo
((other.MemberDefinition.DeclaringType?.Module?.Assembly?.Name?.Name, other.MemberDefinition.DeclaringType?.Name, other.MemberDefinition?.Name));
TypeDefinition thisTypeDef = (MemberDefinition as TypeDefinition) ?? MemberDefinition.DeclaringType;
TypeDefinition otherTypeDef = (other.MemberDefinition as TypeDefinition) ?? other.MemberDefinition.DeclaringType;
int result = (thisTypeDef?.Module?.Assembly?.Name?.Name, thisTypeDef?.Name, MemberDefinition?.Name).CompareTo
((otherTypeDef?.Module?.Assembly?.Name?.Name, otherTypeDef?.Name, other.MemberDefinition?.Name));
if (result != 0)
return result;
if (ILOffset != null && other.ILOffset != null)
return ILOffset.Value.CompareTo (other.ILOffset);
return ILOffset == null ? (other.ILOffset == null ? 0 : 1) : -1;
}
else if (MemberDefinition == null && other.MemberDefinition == null)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using Mono.Cecil;

namespace Mono.Linker
{
// Currently this is implemented using heuristics
public class CompilerGeneratedState
{
readonly LinkContext _context;
readonly Dictionary<TypeDefinition, MethodDefinition> _compilerGeneratedTypeToUserCodeMethod;
readonly HashSet<TypeDefinition> _typesWithPopulatedCache;

public CompilerGeneratedState (LinkContext context)
{
_context = context;
_compilerGeneratedTypeToUserCodeMethod = new Dictionary<TypeDefinition, MethodDefinition> ();
_typesWithPopulatedCache = new HashSet<TypeDefinition> ();
}

static bool HasRoslynCompilerGeneratedName (TypeDefinition type) =>
type.Name.Contains ('<') || (type.DeclaringType != null && HasRoslynCompilerGeneratedName (type.DeclaringType));

void PopulateCacheForType (TypeDefinition type)
{
// Avoid repeat scans of the same type
if (!_typesWithPopulatedCache.Add (type))
return;

foreach (MethodDefinition method in type.Methods) {
if (!method.HasCustomAttributes)
continue;

foreach (var attribute in method.CustomAttributes) {
if (attribute.AttributeType.Namespace != "System.Runtime.CompilerServices")
continue;

switch (attribute.AttributeType.Name) {
case "AsyncIteratorStateMachineAttribute":
case "AsyncStateMachineAttribute":
case "IteratorStateMachineAttribute":
TypeDefinition stateMachineType = GetFirstConstructorArgumentAsType (attribute);
if (stateMachineType != null) {
if (!_compilerGeneratedTypeToUserCodeMethod.TryAdd (stateMachineType, method)) {
var alreadyAssociatedMethod = _compilerGeneratedTypeToUserCodeMethod[stateMachineType];
_context.LogWarning (
$"Methods '{method.GetDisplayName ()}' and '{alreadyAssociatedMethod.GetDisplayName ()}' are both associated with state machine type '{stateMachineType.GetDisplayName ()}'. This is currently unsupported and may lead to incorrectly reported warnings.",
2107,
new MessageOrigin (method),
MessageSubCategory.TrimAnalysis);
}
}

break;
}
}
}
}

static TypeDefinition GetFirstConstructorArgumentAsType (CustomAttribute attribute)
{
if (!attribute.HasConstructorArguments)
return null;

return attribute.ConstructorArguments[0].Value as TypeDefinition;
}

public MethodDefinition GetUserDefinedMethodForCompilerGeneratedMember (IMemberDefinition sourceMember)
{
if (sourceMember == null)
return null;

TypeDefinition compilerGeneratedType = (sourceMember as TypeDefinition) ?? sourceMember.DeclaringType;
if (_compilerGeneratedTypeToUserCodeMethod.TryGetValue (compilerGeneratedType, out MethodDefinition userDefinedMethod))
return userDefinedMethod;

// Only handle async or iterator state machine
// So go to the declaring type and check if it's compiler generated (as a perf optimization)
if (!HasRoslynCompilerGeneratedName (compilerGeneratedType) || compilerGeneratedType.DeclaringType == null)
return null;

// Now go to its declaring type and search all methods to find the one which points to the type as its
// state machine implementation.
PopulateCacheForType (compilerGeneratedType.DeclaringType);
if (_compilerGeneratedTypeToUserCodeMethod.TryGetValue (compilerGeneratedType, out userDefinedMethod))
return userDefinedMethod;

return null;
}
}
}
Loading