xamarin-macios/tests/mtouch/InlinerTest.cs

158 строки
4.4 KiB
C#
Исходник Обычный вид История

using NUnit.Framework;
[linker] Add an well known candidate inliner substep along with tests (#1513) TL&DR: This is *how* it should be done and tested, it's not complete (single, simple case) nor the most interesting case ;-) The trick is to make sure each case is covered by tests so a mono _bump_ won't give us a BCL that does not conform to what the linker expect. What's the impact ? 1. There is the expected reduction of metadata in mscorlib. Since both methods don't call other API there's no indirect effect (removal). --- before 2017-01-15 11:12:44.000000000 -0500 +++ after 2017-01-15 11:12:56.000000000 -0500 @@ -13166,9 +13166,6 @@ System.Void System.Security.SecurityException::.ctor(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext) System.Void System.Security.SecurityException::.ctor(System.String) System.Void System.Security.SecurityException::GetObjectData(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext) -System.Boolean System.Security.SecurityManager::CheckElevatedPermissions() -System.Security.SecurityManager -System.Void System.Security.SecurityManager::EnsureElevatedPermissions() System.Security.SecurityRulesAttribute System.Security.SecurityRuleSet System.Security.SecurityRulesAttribute::m_ruleSet System.Void System.Security.SecurityRulesAttribute::.ctor(System.Security.SecurityRuleSet) 2. There is no visible size change (even with #1) in mscorlib.dll due to padding (compiler /filealign) mscorlib.dll 793,600 793,600 0 0.00 % 3. there's a *very* small reduction of mscorlib.*.aotdata size mscorlib.armv7.aotdata 717,264 717,216 -48 -0.01 % mscorlib.arm64.aotdata 712,840 712,704 -136 -0.02 % AOT data *.aotdata 6,460,064 6,459,880 -184 0.00 % 4. there's no change in executable size - normal as the AOT compiler has _likely_ already doing the same optimization (before this commit) Executable 29,270,272 29,270,272 0 0.00 % Full comparison: https://gist.github.com/spouliot/0464c8fa3a92b6486dfd90595d9eb718
2017-01-18 05:49:44 +03:00
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Xamarin.Tests;
namespace Xamarin.Linker {
public static partial class Extensions {
// note: direct check, no inheritance
public static bool Is (this TypeReference type, string @namespace, string name)
{
return ((type != null) && (type.Name == name) && (type.Namespace == @namespace));
}
}
public abstract class InlinerTest {
// note: not every candidate should be handled by the linker
// most of them are _naturally_ eliminated by not being used/marked
static bool DisplayCandidates = false;
protected abstract string Assembly { get; }
AssemblyDefinition assembly;
protected virtual AssemblyDefinition AssemblyDefinition {
get {
if (assembly == null)
assembly = AssemblyDefinition.ReadAssembly (Assembly);
return assembly;
}
}
protected string ListMethods (HashSet<string> h)
{
string result = Path.GetFileName (Assembly);
if (h.Count == 0)
return $" {result}: success";
return result + "\n" + h.Aggregate ((arg1, arg2) => arg1 + "\n" + arg2);
}
bool IsCandidateForInlining (MethodDefinition m)
{
// candidates must be methods
if (m.IsConstructor)
return false;
// must have a body (IL)
if (!m.HasBody)
return false;
// must be static or not virtual (can't be overrriden)
if (!m.IsStatic && m.IsVirtual)
return false;
// the body must not have exception handlers or variables
var b = m.Body;
return !(b.HasExceptionHandlers || b.HasVariables || b.InitLocals);
}
/// <summary>
/// We look for candidates, without parameters, that only do `return true;`.
/// E.g. Such a static method can be inlined by replacing the `call` with a `ldc.i4.1` instruction
/// We must ensure that the list of methods we inline remains unchanged in the BCL we ship.
/// </summary>
protected void NoParameterReturnOnlyConstant (Code code, HashSet<string> list)
{
if (DisplayCandidates)
Console.WriteLine ($"### NoParameterReturnOnlyConstant {code}: {Path.GetFileName (Assembly)}");
var ad = AssemblyDefinition.ReadAssembly (Assembly);
foreach (var t in ad.MainModule.Types) {
if (!t.HasMethods)
continue;
foreach (var m in t.Methods) {
if (!IsCandidateForInlining (m))
continue;
if (m.HasParameters)
continue;
var b = m.Body;
if (b.Instructions.Count != 2)
continue;
var ins = b.Instructions [0];
if (code == ins.OpCode.Code) {
var s = m.ToString ();
list.Remove (s);
if (DisplayCandidates)
Console.WriteLine ($"* `{s}`");
}
}
}
}
/// <summary>
/// We look for candidates, without parameters and return value, that does nothing (only `ret`).
/// E.g. Such a static method can be inlined by replacing the `call` with a nop` instruction.
/// We must ensure that the list of methods we inline remains unchanged in the BCL we ship.
/// </summary>
protected void NoParameterNoReturnNoCode (HashSet<string> list)
{
if (DisplayCandidates)
Console.WriteLine ($"### ReturnOnly: {Path.GetFileName (Assembly)}");
foreach (var t in AssemblyDefinition.MainModule.Types) {
if (!t.HasMethods)
continue;
foreach (var m in t.Methods) {
if (!IsCandidateForInlining (m))
continue;
if (m.HasParameters)
continue;
if (!m.ReturnType.Is ("System", "Void"))
continue;
var b = m.Body;
if (b.Instructions.Count == 1) {
var s = m.ToString ();
list.Remove (s);
if (DisplayCandidates)
Console.WriteLine ($"* `{s}`");
}
}
}
}
}
[TestFixture]
public class MscorlibInlinerTest : InlinerTest {
protected override string Assembly {
get { return Path.Combine (Configuration.MonoTouchRootDirectory, "lib", "mono", "Xamarin.iOS", "mscorlib.dll"); }
}
[Test]
public void True ()
{
// list MUST be kept in sync with InlinerSubStep.cs
var h = new HashSet<string> {
"System.Boolean System.Security.SecurityManager::CheckElevatedPermissions()",
};
NoParameterReturnOnlyConstant (Code.Ldc_I4_1, h);
Assert.That (h, Is.Empty, ListMethods (h));
}
[Test]
public void Nop ()
{
// this list MUST be kept in sync with InlinerSubStep.cs
var h = new HashSet<string> {
"System.Void System.Security.SecurityManager::EnsureElevatedPermissions()",
};
NoParameterNoReturnNoCode (h);
Assert.That (h, Is.Empty, ListMethods (h));
}
}
}