From 4f406a276e31fc7f04f5a7b326ec61aba9eb0355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 31 Dec 2021 13:28:32 +0900 Subject: [PATCH 1/3] Bring over ILCompiler.Compiler unit tests ...from CoreRT repo since we skipped them in runtimelab. --- eng/Subsets.props | 2 + .../Test.CoreLib/src/Test.CoreLib.csproj | 1 + .../DependencyGraphTests.cs | 135 ++++++++++++++++++ .../DevirtualizationTests.cs | 70 +++++++++ .../DependencyGraph.cs | 96 +++++++++++++ .../Devirtualization.cs | 37 +++++ .../ILCompiler.Compiler.Tests.Assets.csproj | 17 +++ .../ILCompiler.Compiler.Tests.csproj | 47 ++++++ src/coreclr/tools/aot/ilc.sln | 20 +++ 9 files changed, 425 insertions(+) create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DevirtualizationTests.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/DependencyGraph.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/Devirtualization.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/ILCompiler.Compiler.Tests.Assets.csproj create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.csproj diff --git a/eng/Subsets.props b/eng/Subsets.props index 05470bc9aae954..c22e9af962a7a5 100644 --- a/eng/Subsets.props +++ b/eng/Subsets.props @@ -246,6 +246,8 @@ + diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj b/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj index c95cc9c17b8a39..29a54764b2f69b 100644 --- a/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj +++ b/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj @@ -2,6 +2,7 @@ false false + netstandard2.0 FEATURE_GC_STRESS;$(DefineConstants) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs new file mode 100644 index 00000000000000..954bb7d8b9c6f9 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs @@ -0,0 +1,135 @@ +// 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 Internal.IL; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +using Xunit; + +using CustomAttributeValue = System.Reflection.Metadata.CustomAttributeValue; + +namespace ILCompiler.Compiler.Tests +{ + // + // This test uses IL scanner to scan a dependency graph, starting with a + // single method from the test assembly. + // It then checks various invariants about the resulting dependency graph. + // The test method declares these invariants using custom attributes. + // + // The invariants to check for are: + // * Whether an EEType was/was not generated + // * Whether a method body was/was not generated + // * Etc. + // + // The most valuable tests are the ones that check that something was not + // generated. These let us create unit tests for size on disk regressions. + // + + public class DependencyGraphTests + { + public static IEnumerable GetTestMethods() + { + var target = new TargetDetails(TargetArchitecture.X64, TargetOS.Windows, TargetAbi.CoreRT); + var context = new CompilerTypeSystemContext(target, SharedGenericsMode.CanonicalReferenceTypes, DelegateFeature.All); + + context.InputFilePaths = new Dictionary { + { "Test.CoreLib", @"Test.CoreLib.dll" }, + { "ILCompiler.Compiler.Tests.Assets", @"ILCompiler.Compiler.Tests.Assets.dll" }, + }; + context.ReferenceFilePaths = new Dictionary(); + + context.SetSystemModule(context.GetModuleForSimpleName("Test.CoreLib")); + var testModule = context.GetModuleForSimpleName("ILCompiler.Compiler.Tests.Assets"); + + bool foundSomethingToCheck = false; + foreach (var type in testModule.GetType("ILCompiler.Compiler.Tests.Assets", "DependencyGraph").GetNestedTypes()) + { + foundSomethingToCheck = true; + yield return new object[] { type.GetMethod("Entrypoint", null) }; + } + + Assert.True(foundSomethingToCheck, "No methods to check?"); + } + + [Theory] + [MemberData(nameof(GetTestMethods))] + public void TestDependencyGraphInvariants(EcmaMethod method) + { + // + // Scan the input method + // + + var context = (CompilerTypeSystemContext)method.Context; + CompilationModuleGroup compilationGroup = new SingleFileCompilationModuleGroup(); + + CoreRTILProvider ilProvider = new CoreRTILProvider(); + + UsageBasedMetadataManager metadataManager = new UsageBasedMetadataManager(compilationGroup, context, + new FullyBlockedMetadataBlockingPolicy(), new FullyBlockedManifestResourceBlockingPolicy(), + null, new NoStackTraceEmissionPolicy(), new NoDynamicInvokeThunkGenerationPolicy(), + new Dataflow.FlowAnnotations(Logger.Null, ilProvider), UsageBasedMetadataGenerationOptions.None, + Logger.Null, Array.Empty>(), Array.Empty(), Array.Empty()); + + CompilationBuilder builder = new RyuJitCompilationBuilder(context, compilationGroup) + .UseILProvider(ilProvider); + + IILScanner scanner = builder.GetILScannerBuilder() + .UseCompilationRoots(new ICompilationRootProvider[] { new SingleMethodRootProvider(method) }) + .UseMetadataManager(metadataManager) + .ToILScanner(); + + ILScanResults results = scanner.Scan(); + + // + // Check invariants + // + + const string assetsNamespace = "ILCompiler.Compiler.Tests.Assets"; + bool foundSomethingToCheck = false; + + foreach (var attr in method.GetDecodedCustomAttributes(assetsNamespace, "GeneratesConstructedEETypeAttribute")) + { + foundSomethingToCheck = true; + Assert.Contains((TypeDesc)attr.FixedArguments[0].Value, results.ConstructedEETypes); + } + + foreach (var attr in method.GetDecodedCustomAttributes(assetsNamespace, "NoConstructedEETypeAttribute")) + { + foundSomethingToCheck = true; + Assert.DoesNotContain((TypeDesc)attr.FixedArguments[0].Value, results.ConstructedEETypes); + } + + foreach (var attr in method.GetDecodedCustomAttributes(assetsNamespace, "GeneratesMethodBodyAttribute")) + { + foundSomethingToCheck = true; + MethodDesc methodToCheck = GetMethodFromAttribute(attr); + Assert.Contains(methodToCheck.GetCanonMethodTarget(CanonicalFormKind.Specific), results.CompiledMethodBodies); + } + + foreach (var attr in method.GetDecodedCustomAttributes(assetsNamespace, "NoMethodBodyAttribute")) + { + foundSomethingToCheck = true; + MethodDesc methodToCheck = GetMethodFromAttribute(attr); + Assert.DoesNotContain(methodToCheck.GetCanonMethodTarget(CanonicalFormKind.Specific), results.CompiledMethodBodies); + } + + // + // Make sure we checked something + // + + Assert.True(foundSomethingToCheck, "No invariants to check?"); + } + + private static MethodDesc GetMethodFromAttribute(CustomAttributeValue attr) + { + if (attr.NamedArguments.Length > 0) + throw new NotImplementedException(); // TODO: parse sig and instantiation + + return ((TypeDesc)attr.FixedArguments[0].Value).GetMethod((string)attr.FixedArguments[1].Value, null); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DevirtualizationTests.cs b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DevirtualizationTests.cs new file mode 100644 index 00000000000000..37a0436e468ca1 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DevirtualizationTests.cs @@ -0,0 +1,70 @@ +// 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 Internal.IL; +using Internal.TypeSystem; + +using Xunit; + +namespace ILCompiler.Compiler.Tests +{ + public class DevirtualizationTests + { + private readonly CompilerTypeSystemContext _context; + private readonly ModuleDesc _testModule; + + public DevirtualizationTests() + { + var target = new TargetDetails(TargetArchitecture.X64, TargetOS.Windows, TargetAbi.CoreRT); + _context = new CompilerTypeSystemContext(target, SharedGenericsMode.CanonicalReferenceTypes, DelegateFeature.All); + + _context.InputFilePaths = new Dictionary { + { "Test.CoreLib", @"Test.CoreLib.dll" }, + { "ILCompiler.Compiler.Tests.Assets", @"ILCompiler.Compiler.Tests.Assets.dll" }, + }; + _context.ReferenceFilePaths = new Dictionary(); + + _context.SetSystemModule(_context.GetModuleForSimpleName("Test.CoreLib")); + _testModule = _context.GetModuleForSimpleName("ILCompiler.Compiler.Tests.Assets"); + } + + private DevirtualizationManager GetDevirtualizationManagerFromScan(MethodDesc method) + { + CompilationModuleGroup compilationGroup = new SingleFileCompilationModuleGroup(); + + CompilationBuilder builder = new RyuJitCompilationBuilder(_context, compilationGroup); + IILScanner scanner = builder.GetILScannerBuilder() + .UseCompilationRoots(new ICompilationRootProvider[] { new SingleMethodRootProvider(method) }) + .ToILScanner(); + + return scanner.Scan().GetDevirtualizationManager(); + } + + [Fact] + public void TestDevirtualizeSimple() + { + MetadataType testType = _testModule.GetType("Devirtualization", "DevirtualizeSimple"); + DevirtualizationManager scanDevirt = GetDevirtualizationManagerFromScan(testType.GetMethod("Run", null)); + + MethodDesc implMethod = testType.GetNestedType("Derived").GetMethod("Virtual", null); + + // The impl method should be treated as sealed + Assert.True(scanDevirt.IsEffectivelySealed(implMethod)); + + // Even though the metadata based algorithm would say it isn't + var devirt = new DevirtualizationManager(); + Assert.False(devirt.IsEffectivelySealed(implMethod)); + } + + [Fact] + public void TestDevirtualizeAbstract() + { + MetadataType testType = _testModule.GetType("Devirtualization", "DevirtualizeAbstract"); + DevirtualizationManager scanDevirt = GetDevirtualizationManagerFromScan(testType.GetMethod("Run", null)); + + Assert.False(scanDevirt.IsEffectivelySealed(testType.GetNestedType("Abstract"))); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/DependencyGraph.cs b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/DependencyGraph.cs new file mode 100644 index 00000000000000..ef2fdab5b8b790 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/DependencyGraph.cs @@ -0,0 +1,96 @@ +// 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.Runtime.InteropServices; + +namespace ILCompiler.Compiler.Tests.Assets +{ + // + // Classes nested under this class gets automatically discovered by the unit test. + // The unit test will locate the Entrypoint method, run the IL scanner on it, + // and validate the invariants declared by the Entrypoint method with custom attributes. + // + + class DependencyGraph + { + /// + /// Validates a cast doesn't force a constructed EEType. + /// + class PInvokeCctorDependencyTest + { + class TypeThatWasNeverAllocated + { + public static object O = null; + } + + [NoConstructedEEType(typeof(TypeThatWasNeverAllocated))] + public static void Entrypoint() + { + ((TypeThatWasNeverAllocated)TypeThatWasNeverAllocated.O).GetHashCode(); + } + } + + class GenericVirtualMethodDirectCallDependencyTest + { + class NeverAllocated { } + + class Base + { + public virtual object GenericVirtualCalledDirectly() + { + return null; + } + } + + class Derived : Base + { + public override object GenericVirtualCalledDirectly() + { + return new NeverAllocated(); + } + + public object CallBaseGenericVirtualDirectly() + { + // This is a call in IL, not callvirt + return base.GenericVirtualCalledDirectly(); + } + } + + [NoConstructedEEType(typeof(NeverAllocated))] + public static void Entrypoint() + { + new Base(); + new Derived().CallBaseGenericVirtualDirectly(); + } + } + } + + #region Custom attributes that define invariants to check + public class GeneratesConstructedEETypeAttribute : Attribute + { + public GeneratesConstructedEETypeAttribute(Type type) { } + } + + public class NoConstructedEETypeAttribute : Attribute + { + public NoConstructedEETypeAttribute(Type type) { } + } + + public class GeneratesMethodBodyAttribute : Attribute + { + public GeneratesMethodBodyAttribute(Type owningType, string methodName) { } + + public Type[] GenericArguments; + public Type[] Signature; + } + + public class NoMethodBodyAttribute : Attribute + { + public NoMethodBodyAttribute(Type owningType, string methodName) { } + + public Type[] GenericArguments; + public Type[] Signature; + } + #endregion +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/Devirtualization.cs b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/Devirtualization.cs new file mode 100644 index 00000000000000..a1c894bd6a8133 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/Devirtualization.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Devirtualization +{ + class DevirtualizeSimple + { + abstract class Base + { + public abstract void Virtual(); + } + + class Derived : Base + { + public override void Virtual() + { + new Derived(); + } + } + + static void Run() + { + Base p = new Derived(); + p.Virtual(); + } + } + + class DevirtualizeAbstract + { + abstract class Abstract { } + + static void Run() + { + typeof(Abstract).GetHashCode(); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/ILCompiler.Compiler.Tests.Assets.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/ILCompiler.Compiler.Tests.Assets.csproj new file mode 100644 index 00000000000000..ea167e092b0318 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/ILCompiler.Compiler.Tests.Assets.csproj @@ -0,0 +1,17 @@ + + + Library + ILCompiler.Compiler.Tests.Assets + false + false + true + netstandard2.0 + + true + + + + + + + diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.csproj new file mode 100644 index 00000000000000..5967b093841b15 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.csproj @@ -0,0 +1,47 @@ + + + ILCompiler.Compiler.Tests + $(NetCoreAppToolCurrent) + Debug;Release;Checked + + true + -notrait category=failing + + $(NoWarn);NU1701 + + false + x86;x64 + AnyCPU + true + + + + + + + + + + + + + + + + false + Content + PreserveNewest + + + false + Content + PreserveNewest + + + + + + + + diff --git a/src/coreclr/tools/aot/ilc.sln b/src/coreclr/tools/aot/ilc.sln index 225476433362ff..be90b9bc202d73 100644 --- a/src/coreclr/tools/aot/ilc.sln +++ b/src/coreclr/tools/aot/ilc.sln @@ -18,6 +18,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "repro", "ILCompiler\repro\r EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ILLink.Shared", "ILLink.Shared\ILLink.Shared.shproj", "{FF598E93-8E9E-4091-9F50-61A7572663AE}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.Compiler.Tests", "ILCompiler.Compiler.Tests\ILCompiler.Compiler.Tests.csproj", "{24CBA9C6-EDBA-47D6-A0B5-04417BDE5FE3}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution ILLink.Shared\ILLink.Shared.projitems*{ff598e93-8e9e-4091-9f50-61a7572663ae}*SharedItemsImports = 13 @@ -140,6 +142,24 @@ Global {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.Release|x64.Build.0 = Release|x64 {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.Release|x86.ActiveCfg = Release|x86 {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.Release|x86.Build.0 = Release|x86 + {24CBA9C6-EDBA-47D6-A0B5-04417BDE5FE3}.Checked|Any CPU.ActiveCfg = Checked|x64 + {24CBA9C6-EDBA-47D6-A0B5-04417BDE5FE3}.Checked|Any CPU.Build.0 = Checked|x64 + {24CBA9C6-EDBA-47D6-A0B5-04417BDE5FE3}.Checked|x64.ActiveCfg = Checked|x64 + {24CBA9C6-EDBA-47D6-A0B5-04417BDE5FE3}.Checked|x64.Build.0 = Checked|x64 + {24CBA9C6-EDBA-47D6-A0B5-04417BDE5FE3}.Checked|x86.ActiveCfg = Checked|x86 + {24CBA9C6-EDBA-47D6-A0B5-04417BDE5FE3}.Checked|x86.Build.0 = Checked|x86 + {24CBA9C6-EDBA-47D6-A0B5-04417BDE5FE3}.Debug|Any CPU.ActiveCfg = Debug|x64 + {24CBA9C6-EDBA-47D6-A0B5-04417BDE5FE3}.Debug|Any CPU.Build.0 = Debug|x64 + {24CBA9C6-EDBA-47D6-A0B5-04417BDE5FE3}.Debug|x64.ActiveCfg = Debug|x64 + {24CBA9C6-EDBA-47D6-A0B5-04417BDE5FE3}.Debug|x64.Build.0 = Debug|x64 + {24CBA9C6-EDBA-47D6-A0B5-04417BDE5FE3}.Debug|x86.ActiveCfg = Debug|x86 + {24CBA9C6-EDBA-47D6-A0B5-04417BDE5FE3}.Debug|x86.Build.0 = Debug|x86 + {24CBA9C6-EDBA-47D6-A0B5-04417BDE5FE3}.Release|Any CPU.ActiveCfg = Release|x64 + {24CBA9C6-EDBA-47D6-A0B5-04417BDE5FE3}.Release|Any CPU.Build.0 = Release|x64 + {24CBA9C6-EDBA-47D6-A0B5-04417BDE5FE3}.Release|x64.ActiveCfg = Release|x64 + {24CBA9C6-EDBA-47D6-A0B5-04417BDE5FE3}.Release|x64.Build.0 = Release|x64 + {24CBA9C6-EDBA-47D6-A0B5-04417BDE5FE3}.Release|x86.ActiveCfg = Release|x86 + {24CBA9C6-EDBA-47D6-A0B5-04417BDE5FE3}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 3cf59a895ad25c35bb6103b0d86dd50914149c99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 31 Dec 2021 14:04:27 +0900 Subject: [PATCH 2/3] Update Subsets.props --- eng/Subsets.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/Subsets.props b/eng/Subsets.props index c22e9af962a7a5..1fb58e81239a9a 100644 --- a/eng/Subsets.props +++ b/eng/Subsets.props @@ -247,7 +247,7 @@ + Test="true" Category="clr" Condition="'$(DotNetBuildFromSource)' != 'true' and '$(NativeAotSupported)' == 'true'"/> From c0f5e5bf05270f85b6024a91464fea9dd9a8a4c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 31 Dec 2021 15:41:35 +0900 Subject: [PATCH 3/3] Update build-job.yml --- eng/pipelines/coreclr/templates/build-job.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/pipelines/coreclr/templates/build-job.yml b/eng/pipelines/coreclr/templates/build-job.yml index a8c57eadac6255..b89cd621eda1ee 100644 --- a/eng/pipelines/coreclr/templates/build-job.yml +++ b/eng/pipelines/coreclr/templates/build-job.yml @@ -132,7 +132,7 @@ jobs: value: '' - ${{ if ne(parameters.testGroup, 'innerloop') }}: - name: clrRuntimeComponentsBuildArg - value: '-component runtime -component alljits -component paltests ' + value: '-component runtime -component alljits -component paltests -component nativeaot ' - ${{ if and(eq(parameters.osGroup, 'Linux'), eq(parameters.archType, 'x86')) }}: - name: clrRuntimeComponentsBuildArg value: '-component runtime -component jit -component iltools '