diff options
author | Marek Safar <marek.safar@gmail.com> | 2019-10-26 09:20:15 +0300 |
---|---|---|
committer | Marek Safar <marek.safar@gmail.com> | 2019-12-11 02:05:36 +0300 |
commit | 132d8afe7ac5eae64b7597f120319fd2ccca5d5a (patch) | |
tree | 195bf01f9ee9602bb0833a941b436576bafbe6c9 /test/Mono.Linker.Tests | |
parent | b4770592e35a54823ef5705cc339bf6327229570 (diff) |
Add new optimization steps to make Mark step more effective
Two new steps have been introduced
BodySubstituterStep
This step removes any conditional blocks when the condition or conditions
are evaluated as constants. This step does not do any inlining. The conditional
logic is kept but based on the known values only one branch is always taken.
A simple example which can be detected by linker.
```c#
class C
{
static void Main ()
{
if (Result)
Console.WriteLine (); // <- this call will be marked and removed
}
static bool Result () => false;
}
```
RemoveUnreachableBlocksStep
A new command-line option called `--substitutions` allow external customization of any
methoda for assemblies which are linked. The syntax is same as for existing linker descriptor
XML files but it add way to control body modifications.
An example of XML file
```xml
<linker>
<assembly fullname="test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
<type fullname="Mono.Linker.Tests.Cases.Substitutions.StubBodyWithValue">
<method signature="System.String TestMethod_1()" body="stub" value="abcd">
</method>
</type>
</assembly>
</linker>
```
The `value` attribute is optional and only required when the method stub should not
fallback to default behaviour.
Additional to `stub` value also `remove` value is supported to forcibly remove body
of the method when the method is marked. This is useful when the conditional logic
cannot be evaluated by linker and the method will be marked but never actually reached.
Applicability
Both steps can be combined to achieve the effect of externally customizing which
conditional branches can be removed during the linking.
I can illustrate that on IntPtr.Size property. With following substitution any code
that has compiled in conditional logic for 64 bits handling by checking IntPtr.Size will
be removed during linking.
```xml
<linker>
<assembly fullname="mscorlib">
<type fullname="System.IntPtr">
<method signature="System.Int32 get_Size()" body="stub" value="4">
</method>
</type>
</assembly>
</linker>
```
Implements #752
Diffstat (limited to 'test/Mono.Linker.Tests')
7 files changed, 55 insertions, 5 deletions
diff --git a/test/Mono.Linker.Tests/TestCases/TestDatabase.cs b/test/Mono.Linker.Tests/TestCases/TestDatabase.cs index 893b55508..726516d21 100644 --- a/test/Mono.Linker.Tests/TestCases/TestDatabase.cs +++ b/test/Mono.Linker.Tests/TestCases/TestDatabase.cs @@ -141,6 +141,16 @@ namespace Mono.Linker.Tests.TestCases return NUnitCasesBySuiteName ("CodegenAnnotation"); } + public static IEnumerable<TestCaseData> UnreachableBlockTests () + { + return NUnitCasesBySuiteName ("UnreachableBlock"); + } + + public static IEnumerable<TestCaseData> SubstitutionsTests () + { + return NUnitCasesBySuiteName ("Substitutions"); + } + public static TestCaseCollector CreateCollector () { string rootSourceDirectory; diff --git a/test/Mono.Linker.Tests/TestCases/TestSuites.cs b/test/Mono.Linker.Tests/TestCases/TestSuites.cs index b3aa7ad50..c7d906c36 100644 --- a/test/Mono.Linker.Tests/TestCases/TestSuites.cs +++ b/test/Mono.Linker.Tests/TestCases/TestSuites.cs @@ -169,6 +169,18 @@ namespace Mono.Linker.Tests.TestCases Run (testCase); } + [TestCaseSource (typeof (TestDatabase), nameof (TestDatabase.UnreachableBlockTests))] + public void UnreachableBlockTests (TestCase testCase) + { + Run (testCase); + } + + [TestCaseSource (typeof (TestDatabase), nameof (TestDatabase.SubstitutionsTests))] + public void SubstitutionsTests (TestCase testCase) + { + Run (testCase); + } + protected virtual void Run (TestCase testCase) { var runner = new TestRunner (new ObjectFactory ()); diff --git a/test/Mono.Linker.Tests/TestCasesRunner/LinkerArgumentBuilder.cs b/test/Mono.Linker.Tests/TestCasesRunner/LinkerArgumentBuilder.cs index 98f93d782..3a3f2a055 100644 --- a/test/Mono.Linker.Tests/TestCasesRunner/LinkerArgumentBuilder.cs +++ b/test/Mono.Linker.Tests/TestCasesRunner/LinkerArgumentBuilder.cs @@ -111,6 +111,12 @@ namespace Mono.Linker.Tests.TestCasesRunner { } } + public virtual void AddSubstitutions (string file) + { + Append ("--substitutions"); + Append (file); + } + public string [] ToArgs () { return _arguments.ToArray (); @@ -171,6 +177,9 @@ namespace Mono.Linker.Tests.TestCasesRunner { AddStripResources (options.StripResources); + foreach (var substitutions in options.Substitutions) + AddSubstitutions (substitutions); + // Unity uses different argument format and needs to be able to translate to their format. In order to make that easier // we keep the information in flag + values format for as long as we can so that this information doesn't have to be parsed out of a single string foreach (var additionalArgument in options.AdditionalArguments) diff --git a/test/Mono.Linker.Tests/TestCasesRunner/TestCaseLinkerOptions.cs b/test/Mono.Linker.Tests/TestCasesRunner/TestCaseLinkerOptions.cs index f13b4eafc..5becea9ad 100644 --- a/test/Mono.Linker.Tests/TestCasesRunner/TestCaseLinkerOptions.cs +++ b/test/Mono.Linker.Tests/TestCasesRunner/TestCaseLinkerOptions.cs @@ -17,5 +17,7 @@ namespace Mono.Linker.Tests.TestCasesRunner { public bool StripResources; public List<KeyValuePair<string, string[]>> AdditionalArguments = new List<KeyValuePair<string, string[]>> (); + + public List<string> Substitutions = new List<string> (); } }
\ No newline at end of file diff --git a/test/Mono.Linker.Tests/TestCasesRunner/TestCaseMetadaProvider.cs b/test/Mono.Linker.Tests/TestCasesRunner/TestCaseMetadaProvider.cs index 8aeaf9c36..1b2383a5f 100644 --- a/test/Mono.Linker.Tests/TestCasesRunner/TestCaseMetadaProvider.cs +++ b/test/Mono.Linker.Tests/TestCasesRunner/TestCaseMetadaProvider.cs @@ -25,7 +25,7 @@ namespace Mono.Linker.Tests.TestCasesRunner { throw new InvalidOperationException ($"Could not find the type definition for {_testCase.Name} in {_testCase.SourceFile}"); } - public virtual TestCaseLinkerOptions GetLinkerOptions () + public virtual TestCaseLinkerOptions GetLinkerOptions (NPath inputPath) { var tclo = new TestCaseLinkerOptions { Il8n = GetOptionAttributeValue (nameof (Il8nAttribute), "none"), @@ -36,7 +36,7 @@ namespace Mono.Linker.Tests.TestCasesRunner { CoreAssembliesAction = GetOptionAttributeValue<string> (nameof (SetupLinkerCoreActionAttribute), null), UserAssembliesAction = GetOptionAttributeValue<string> (nameof (SetupLinkerUserActionAttribute), null), SkipUnresolved = GetOptionAttributeValue (nameof (SkipUnresolvedAttribute), false), - StripResources = GetOptionAttributeValue (nameof (StripResourcesAttribute), true) + StripResources = GetOptionAttributeValue (nameof (StripResourcesAttribute), true), }; foreach (var assemblyAction in _testCaseTypeDefinition.CustomAttributes.Where (attr => attr.AttributeType.Name == nameof (SetupLinkerActionAttribute))) @@ -45,6 +45,12 @@ namespace Mono.Linker.Tests.TestCasesRunner { tclo.AssembliesAction.Add (new KeyValuePair<string, string> ((string)ca [0].Value, (string)ca [1].Value)); } + foreach (var subsFile in _testCaseTypeDefinition.CustomAttributes.Where (attr => attr.AttributeType.Name == nameof (SetupLinkerSubstitutionFileAttribute))) { + var ca = subsFile.ConstructorArguments; + var file = (string)ca [0].Value; + tclo.Substitutions.Add (Path.Combine (inputPath, file)); + } + foreach (var additionalArgumentAttr in _testCaseTypeDefinition.CustomAttributes.Where (attr => attr.AttributeType.Name == nameof (SetupLinkerArgumentAttribute))) { var ca = additionalArgumentAttr.ConstructorArguments; @@ -136,6 +142,13 @@ namespace Mono.Linker.Tests.TestCasesRunner { .Select (GetSourceAndRelativeDestinationValue); } + public virtual IEnumerable<SourceAndDestinationPair> GetSubstitutionFiles () + { + return _testCaseTypeDefinition.CustomAttributes + .Where (attr => attr.AttributeType.Name == nameof (SetupLinkerSubstitutionFileAttribute)) + .Select (GetSourceAndRelativeDestinationValue); + } + public virtual IEnumerable<NPath> GetExtraLinkerSearchDirectories () { #if NETCOREAPP diff --git a/test/Mono.Linker.Tests/TestCasesRunner/TestCaseSandbox.cs b/test/Mono.Linker.Tests/TestCasesRunner/TestCaseSandbox.cs index 5ec502878..d1866881e 100644 --- a/test/Mono.Linker.Tests/TestCasesRunner/TestCaseSandbox.cs +++ b/test/Mono.Linker.Tests/TestCasesRunner/TestCaseSandbox.cs @@ -98,6 +98,10 @@ namespace Mono.Linker.Tests.TestCasesRunner { res.Source.FileMustExist ().Copy (InputDirectory.Combine (res.DestinationFileName)); } + foreach (var res in metadataProvider.GetSubstitutionFiles ()) { + res.Source.FileMustExist ().Copy (InputDirectory.Combine (res.DestinationFileName)); + } + foreach (var compileRefInfo in metadataProvider.GetSetupCompileAssembliesBefore ()) { var destination = BeforeReferenceSourceDirectoryFor (compileRefInfo.OutputName).EnsureDirectoryExists (); diff --git a/test/Mono.Linker.Tests/TestCasesRunner/TestRunner.cs b/test/Mono.Linker.Tests/TestCasesRunner/TestRunner.cs index b9a1cc71b..9fbec7c1e 100644 --- a/test/Mono.Linker.Tests/TestCasesRunner/TestRunner.cs +++ b/test/Mono.Linker.Tests/TestCasesRunner/TestRunner.cs @@ -104,14 +104,14 @@ namespace Mono.Linker.Tests.TestCasesRunner { protected virtual void AddLinkOptions (TestCaseSandbox sandbox, ManagedCompilationResult compilationResult, LinkerArgumentBuilder builder, TestCaseMetadaProvider metadataProvider) { - var caseDefinedOptions = metadataProvider.GetLinkerOptions (); + var caseDefinedOptions = metadataProvider.GetLinkerOptions (sandbox.InputDirectory); builder.AddOutputDirectory (sandbox.OutputDirectory); foreach (var linkXmlFile in sandbox.LinkXmlFiles) builder.AddLinkXmlFile (linkXmlFile); - foreach (var linkXmlFile in sandbox.ResponseFiles) - builder.AddResponseFile (linkXmlFile); + foreach (var rspFile in sandbox.ResponseFiles) + builder.AddResponseFile (rspFile); builder.AddSearchDirectory (sandbox.InputDirectory); foreach (var extraSearchDir in metadataProvider.GetExtraLinkerSearchDirectories ()) |