Skip to content

Bring over ILCompiler.Compiler unit tests #63232

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

Merged
merged 3 commits into from
Dec 31, 2021
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
2 changes: 2 additions & 0 deletions eng/Subsets.props
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,8 @@
<ProjectToBuild Condition="'$(TargetArchitecture)' != 'x64' and '$(BuildArchitecture)' == 'x64'" Include="$(CoreClrProjectRoot)tools\aot\crossgen2\crossgen2_crossarch.csproj" Category="clr" />
<ProjectToBuild Include="$(CoreClrProjectRoot)tools\aot\ILCompiler.TypeSystem.ReadyToRun.Tests\ILCompiler.TypeSystem.ReadyToRun.Tests.csproj"
Test="true" Category="clr" Condition="'$(DotNetBuildFromSource)' != 'true'"/>
<ProjectToBuild Include="$(CoreClrProjectRoot)tools\aot\ILCompiler.Compiler.Tests\ILCompiler.Compiler.Tests.csproj"
Test="true" Category="clr" Condition="'$(DotNetBuildFromSource)' != 'true' and '$(NativeAotSupported)' == 'true'"/>
</ItemGroup>

<ItemGroup Condition="$(_subset.Contains('+clr.nativecorelib+'))">
Expand Down
2 changes: 1 addition & 1 deletion eng/pipelines/coreclr/templates/build-job.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 '
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<PropertyGroup>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<DefineConstants>FEATURE_GC_STRESS;$(DefineConstants)</DefineConstants>
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Internal.TypeSystem.TypeDesc>;

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<object[]> GetTestMethods()
{
var target = new TargetDetails(TargetArchitecture.X64, TargetOS.Windows, TargetAbi.CoreRT);
var context = new CompilerTypeSystemContext(target, SharedGenericsMode.CanonicalReferenceTypes, DelegateFeature.All);

context.InputFilePaths = new Dictionary<string, string> {
{ "Test.CoreLib", @"Test.CoreLib.dll" },
{ "ILCompiler.Compiler.Tests.Assets", @"ILCompiler.Compiler.Tests.Assets.dll" },
};
context.ReferenceFilePaths = new Dictionary<string, string>();

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<KeyValuePair<string, bool>>(), Array.Empty<string>(), Array.Empty<string>());

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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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<string, string> {
{ "Test.CoreLib", @"Test.CoreLib.dll" },
{ "ILCompiler.Compiler.Tests.Assets", @"ILCompiler.Compiler.Tests.Assets.dll" },
};
_context.ReferenceFilePaths = new Dictionary<string, string>();

_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")));
}
}
}
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Validates a cast doesn't force a constructed EEType.
/// </summary>
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<T>()
{
return null;
}
}

class Derived : Base
{
public override object GenericVirtualCalledDirectly<T>()
{
return new NeverAllocated();
}

public object CallBaseGenericVirtualDirectly<T>()
{
// This is a call in IL, not callvirt
return base.GenericVirtualCalledDirectly<T>();
}
}

[NoConstructedEEType(typeof(NeverAllocated))]
public static void Entrypoint()
{
new Base();
new Derived().CallBaseGenericVirtualDirectly<object>();
}
}
}

#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
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<AssemblyName>ILCompiler.Compiler.Tests.Assets</AssemblyName>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
<SkipTestRun>true</SkipTestRun>
<TargetFramework>netstandard2.0</TargetFramework>
<!-- Don't add references to the netstandard platform since this is a core assembly -->
<DisableImplicitFrameworkReferences>true</DisableImplicitFrameworkReferences>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\..\nativeaot\Test.CoreLib\src\Test.CoreLib.csproj" />
</ItemGroup>

</Project>
Loading