Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 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
@@ -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;
using System.Diagnostics;
using System.Reflection.Metadata;
using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;

#nullable enable

namespace ILCompiler
{
public static class AssemblyExtensions
{
public static Version? GetTargetFrameworkVersion(this EcmaAssembly assembly)
{
// Get the custom attributes from the assembly's metadata
MetadataReader reader = assembly.MetadataReader;
CustomAttributeHandle attrHandle = reader.GetCustomAttributeHandle(assembly.AssemblyDefinition.GetCustomAttributes(),
"System.Runtime.Versioning", "TargetFrameworkAttribute");
if (!attrHandle.IsNil)
{
CustomAttribute attr = reader.GetCustomAttribute(attrHandle);
CustomAttributeValue<TypeDesc> decoded = attr.DecodeValue(new CustomAttributeTypeProvider(assembly));
if (decoded.FixedArguments.Length == 1 && decoded.FixedArguments[0].Value is string tfm && !string.IsNullOrEmpty(tfm))
{
var versionPrefix = "Version=v";
var idx = tfm.IndexOf(versionPrefix);
if (idx >= 0)
{
var versionStr = tfm.Substring(idx + versionPrefix.Length);
if (Version.TryParse(versionStr, out var version))
return version;
}
}
}
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,13 @@ private readonly record struct TypeArgumentInfo(

private readonly Logger _logger;

public CompilerGeneratedState(ILProvider ilProvider, Logger logger)
private readonly bool _disableGeneratedCodeHeuristics;

public CompilerGeneratedState(ILProvider ilProvider, Logger logger, bool disableGeneratedCodeHeuristics)
{
_typeCacheHashtable = new TypeCacheHashtable(ilProvider);
_logger = logger;
_disableGeneratedCodeHeuristics = disableGeneratedCodeHeuristics;
}

private sealed class TypeCacheHashtable : LockFreeReaderHashtable<MetadataType, TypeCache>
Expand Down Expand Up @@ -659,6 +662,14 @@ public bool TryGetCompilerGeneratedCalleesForUserMethod(MethodDesc method, [NotN
MetadataType generatedType = (MetadataType)type.GetTypeDefinition();
Debug.Assert(CompilerGeneratedNames.IsStateMachineOrDisplayClass(generatedType.Name));

// Avoid the heuristics for .NET10+, where DynamicallyAccessedMembers flows to generated code
// because it is annotated with CompilerLoweringPreserveAttribute.
if (_disableGeneratedCodeHeuristics &&
generatedType.Module.Assembly is EcmaAssembly asm && asm.GetTargetFrameworkVersion() >= new Version(10, 0))
{
return null;
}

var typeCache = GetCompilerGeneratedStateForType(generatedType);
if (typeCache is null)
return null;
Expand Down
19 changes: 10 additions & 9 deletions src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class Logger
private readonly bool _treatWarningsAsErrors;
private readonly Dictionary<int, bool> _warningsAsErrors;

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

public bool IsVerbose { get; }

Expand All @@ -51,7 +51,8 @@ public Logger(
IEnumerable<string> singleWarnDisabledModules,
IEnumerable<string> suppressedCategories,
bool treatWarningsAsErrors,
IDictionary<int, bool> warningsAsErrors)
IDictionary<int, bool> warningsAsErrors,
bool disableGeneratedCodeHeuristics)
{
_logWriter = writer;
IsVerbose = isVerbose;
Expand All @@ -62,22 +63,22 @@ public Logger(
_suppressedCategories = new HashSet<string>(suppressedCategories, StringComparer.Ordinal);
_treatWarningsAsErrors = treatWarningsAsErrors;
_warningsAsErrors = new Dictionary<int, bool>(warningsAsErrors);
_compilerGeneratedState = ilProvider == null ? null : new CompilerGeneratedState(ilProvider, this);
_compilerGeneratedState = ilProvider == null ? null : new CompilerGeneratedState(ilProvider, this, disableGeneratedCodeHeuristics);
_unconditionalSuppressMessageAttributeState = new UnconditionalSuppressMessageAttributeState(_compilerGeneratedState, this);
}

public Logger(TextWriter writer, ILProvider ilProvider, bool isVerbose, IEnumerable<int> suppressedWarnings, bool singleWarn, IEnumerable<string> singleWarnEnabledModules, IEnumerable<string> singleWarnDisabledModules, IEnumerable<string> suppressedCategories, bool treatWarningsAsErrors, IDictionary<int, bool> warningsAsErrors)
: this(new TextLogWriter(writer), ilProvider, isVerbose, suppressedWarnings, singleWarn, singleWarnEnabledModules, singleWarnDisabledModules, suppressedCategories, treatWarningsAsErrors, warningsAsErrors)
public Logger(TextWriter writer, ILProvider ilProvider, bool isVerbose, IEnumerable<int> suppressedWarnings, bool singleWarn, IEnumerable<string> singleWarnEnabledModules, IEnumerable<string> singleWarnDisabledModules, IEnumerable<string> suppressedCategories, bool treatWarningsAsErrors, IDictionary<int, bool> warningsAsErrors, bool disableGeneratedCodeHeuristics)
: this(new TextLogWriter(writer), ilProvider, isVerbose, suppressedWarnings, singleWarn, singleWarnEnabledModules, singleWarnDisabledModules, suppressedCategories, treatWarningsAsErrors, warningsAsErrors, disableGeneratedCodeHeuristics)
{
}

public Logger(ILogWriter writer, ILProvider ilProvider, bool isVerbose)
: this(writer, ilProvider, isVerbose, Array.Empty<int>(), singleWarn: false, Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), false, new Dictionary<int, bool>())
public Logger(ILogWriter writer, ILProvider ilProvider, bool isVerbose, bool disableGeneratedCodeHeuristics)
: this(writer, ilProvider, isVerbose, Array.Empty<int>(), singleWarn: false, Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), false, new Dictionary<int, bool>(), disableGeneratedCodeHeuristics)
{
}

public Logger(TextWriter writer, ILProvider ilProvider, bool isVerbose)
: this(new TextLogWriter(writer), ilProvider, isVerbose)
public Logger(TextWriter writer, ILProvider ilProvider, bool isVerbose, bool disableGeneratedCodeHeuristics)
: this(new TextLogWriter(writer), ilProvider, isVerbose, disableGeneratedCodeHeuristics)
{
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@

<Compile Include="Compiler\AnalysisBasedInteropStubManager.cs" />
<Compile Include="Compiler\AnalysisBasedMetadataManager.cs" />
<Compile Include="Compiler\AssemblyExtensions.cs" />
<Compile Include="Compiler\BodySubstitution.cs" />
<Compile Include="Compiler\BodySubstitutionParser.cs" />
<Compile Include="Compiler\DependencyAnalysis\AddressTakenMethodNode.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@
<RuntimeHostConfigurationOption Include="Mono.Linker.Tests.TargetFramework">
<Value>$(TargetFramework)</Value>
</RuntimeHostConfigurationOption>
<RuntimeHostConfigurationOption Include="Mono.Linker.Tests.TargetFrameworkMoniker">
<Value>$(TargetFrameworkMoniker)</Value>
</RuntimeHostConfigurationOption>
<RuntimeHostConfigurationOption Include="Mono.Linker.Tests.TargetFrameworkMonikerDisplayName">
<Value>$(TargetFrameworkMonikerDisplayName)</Value>
</RuntimeHostConfigurationOption>
<RuntimeHostConfigurationOption Include="Mono.Linker.Tests.LinkerTestDir">
<Value>$(ToolsProjectRoot)illink/test/</Value>
</RuntimeHostConfigurationOption>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,22 @@ class LinkedMethodEntity : LinkedEntity

// Note: It's enough to exclude the type name, all of its members will also be excluded then
private static readonly HashSet<string> ExcludeDisplayNames = new() {
// Ignore compiler injected attributes to describe language version
"Microsoft.CodeAnalysis.EmbeddedAttribute",
"System.Runtime.CompilerServices.RefSafetyRulesAttribute",

// Ignore NativeAOT injected members
"<Module>.StartupCodeMain(Int32,IntPtr)",
"<Module>.MainMethodWrapper()",
"<Module>.MainMethodWrapper(String[])",

// Ignore compiler generated code which can't be reasonably matched to the source method
"<PrivateImplementationDetails>",
};
// Ignore compiler injected attributes to describe language version
"Microsoft.CodeAnalysis.EmbeddedAttribute",
"System.Runtime.CompilerServices.RefSafetyRulesAttribute",

// Ignore NativeAOT injected members
"<Module>.StartupCodeMain(Int32,IntPtr)",
"<Module>.MainMethodWrapper()",
"<Module>.MainMethodWrapper(String[])",
"System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute.__GetFieldHelper(Int32,MethodTable*&)",
"System.Runtime.InteropServices.TypeMapping",
"System.Runtime.InteropServices.TypeMapping.GetOrCreateExternalTypeMapping<TTypeMapGroup>()",
"System.Runtime.InteropServices.TypeMapping.GetOrCreateProxyTypeMapping<TTypeMapGroup>()",

// Ignore compiler generated code which can't be reasonably matched to the source method
"<PrivateImplementationDetails>",
};

public AssemblyChecker(
BaseAssemblyResolver originalsResolver,
Expand Down Expand Up @@ -102,7 +106,7 @@ IEnumerable<string> VerifyImpl()

// TODO - this is mostly attribute verification
// foreach (var originalModule in originalAssembly.Modules)
// VerifyModule(originalModule, linkedAssembly.Modules.FirstOrDefault(m => m.Name == originalModule.Name));
// VerifyModule(originalModule, linkedAssembly.Modules.FirstOrDefault (m => m.Name == originalModule.Name));

// TODO
// VerifyResources(originalAssembly, linkedAssembly);
Expand Down Expand Up @@ -291,12 +295,9 @@ static bool ShouldIncludeType(TypeDesc type)
if (metadataType.Namespace.StartsWith("Internal"))
return false;

// Simple way to filter out system assemblies - the best way would be to get a list
// of input/reference assemblies and filter on that, but it's tricky and this should work for basically everything
if (metadataType.Namespace.StartsWith("System"))
if (metadataType.Module.Assembly is EcmaAssembly asm && asm.Assembly.GetName().Name == "System.Private.CoreLib")
return false;


return ShouldIncludeEntityByDisplayName(type);
}

Expand Down Expand Up @@ -2059,7 +2060,10 @@ private IEnumerable<string> VerifyKeptAllTypesAndMembersInAssembly(string assemb
var missingInLinked = originalTypes.Keys.Except(linkedTypes.Keys);

if (missingInLinked.Any())
{
yield return $"Expected all types to exist in the linked assembly {assemblyName}, but one or more were missing";
yield break;
}

foreach (var originalKvp in originalTypes)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ public class ILCompilerOptions
public bool TreatWarningsAsErrors;
public Dictionary<int, bool> WarningsAsErrors = new Dictionary<int, bool>();
public List<string> SuppressedWarningCategories = new List<string>();
public bool DisableGeneratedCodeHeuristics;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,17 +78,17 @@ public virtual void Check(TrimmedTestCaseResult testResult)
{
_originalsResolver.Dispose();
}
}

bool HasActiveSkipKeptItemsValidationAttribute(ICustomAttributeProvider provider)
internal static bool HasActiveSkipKeptItemsValidationAttribute(ICustomAttributeProvider provider)
{
if (TryGetCustomAttribute(provider, nameof(SkipKeptItemsValidationAttribute), out var attribute))
{
if (TryGetCustomAttribute(provider, nameof(SkipKeptItemsValidationAttribute), out var attribute))
{
object? by = attribute.GetPropertyValue(nameof(SkipKeptItemsValidationAttribute.By));
return by is null ? true : ((Tool)by).HasFlag(Tool.NativeAot);
}

return false;
object? by = attribute.GetPropertyValue(nameof(SkipKeptItemsValidationAttribute.By));
return by is null ? true : ((Tool)by).HasFlag(Tool.NativeAot);
}

return false;
}

protected virtual AssemblyChecker CreateAssemblyChecker(AssemblyDefinition original, TrimmedTestCaseResult testResult)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,16 @@ private static string GetReferenceDir()
string runtimeDir = Path.GetDirectoryName(typeof(object).Assembly.Location)!;
string ncaVersion = Path.GetFileName(runtimeDir);
string dotnetDir = Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(runtimeDir)))!;
return Path.Combine(dotnetDir, "packs", "Microsoft.NETCore.App.Ref", ncaVersion, "ref", PathUtilities.TFMDirectoryName);
return Path.Combine(dotnetDir, "packs", "Microsoft.NETCore.App.Ref", ncaVersion, "ref", PathUtilities.TargetFramework);
}

public IEnumerable<NPath> GetCommonSourceFiles()
{
var dam = _testCase.RootCasesDirectory.Parent
.Combine("Mono.Linker.Tests.Cases.Expectations")
.Combine("Support")
.Combine("DynamicallyAccessedMembersAttribute.cs");
yield return dam;
}

public virtual IEnumerable<string> GetCommonReferencedAssemblies(NPath workingDirectory)
Expand Down Expand Up @@ -224,6 +233,11 @@ public virtual IEnumerable<SetupCompileInfo> GetSetupCompileAssembliesAfter()
.Select(CreateSetupCompileAssemblyInfo);
}

public bool GetGenerateTargetFrameworkAttribute()
{
return GetOptionAttributeValue(nameof(GetGenerateTargetFrameworkAttribute), true);
}

private SetupCompileInfo CreateSetupCompileAssemblyInfo(CustomAttribute attribute)
{
var ctorArguments = attribute.ConstructorArguments;
Expand Down
Loading
Loading