Skip to content
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
4 changes: 3 additions & 1 deletion dotnet/targets/Xamarin.Shared.Sdk.targets
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,7 @@
<_UseDynamicDependenciesForProtocolPreservation Condition="'$(_UseDynamicDependenciesForProtocolPreservation)' == ''">$(_UseDynamicDependenciesInsteadOfMarking)</_UseDynamicDependenciesForProtocolPreservation>
<_UseDynamicDependenciesForSmartEnumPreservation Condition="'$(_UseDynamicDependenciesForSmartEnumPreservation)' == ''">$(_UseDynamicDependenciesInsteadOfMarking)</_UseDynamicDependenciesForSmartEnumPreservation>
<_UseDynamicDependenciesForBlockCodePreservation Condition="'$(_UseDynamicDependenciesForBlockCodePreservation)' == ''">$(_UseDynamicDependenciesInsteadOfMarking)</_UseDynamicDependenciesForBlockCodePreservation>
<_UseDynamicDependenciesForGeneratedCodeOptimizations Condition="'$(_UseDynamicDependenciesForGeneratedCodeOptimizations)' == ''">$(_UseDynamicDependenciesInsteadOfMarking)</_UseDynamicDependenciesForGeneratedCodeOptimizations>
</PropertyGroup>

<PropertyGroup>
Expand Down Expand Up @@ -754,6 +755,7 @@
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="MarkStep" Type="Xamarin.Linker.PreserveProtocolsStep" Condition="'$(_AreAnyAssembliesTrimmed)' == 'true' And '$(_UseDynamicDependenciesForProtocolPreservation)' == 'true'" />
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="MarkStep" Type="Xamarin.Linker.Steps.PreserveSmartEnumConversionsStep" Condition="'$(_AreAnyAssembliesTrimmed)' == 'true' And '$(_UseDynamicDependenciesForSmartEnumPreservation)' == 'true'" />
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="MarkStep" Type="Xamarin.Linker.Steps.PreserveBlockCodeStep" Condition="'$(_AreAnyAssembliesTrimmed)' == 'true' And '$(_UseDynamicDependenciesForBlockCodePreservation)' == 'true'" />
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="MarkStep" Type="Xamarin.Linker.Steps.OptimizeGeneratedCodeStep" Condition="'$(_AreAnyAssembliesTrimmed)' == 'true' And '$(_UseDynamicDependenciesForGeneratedCodeOptimizations)' == 'true'" />
<!-- The final decision to remove/keep the dynamic registrar must be done before the linking step -->
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="MarkStep" Type="MonoTouch.Tuner.RegistrarRemovalTrackingStep" />
<!-- TODO: these steps should probably run after mark. -->
Expand All @@ -764,7 +766,7 @@
IMarkHandlers which run during Mark
-->
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" Condition="'$(_AreAnyAssembliesTrimmed)' == 'true' And '$(_UseDynamicDependenciesForBlockCodePreservation)' != 'true'" Type="Xamarin.Linker.Steps.PreserveBlockCodeHandler" />
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" Condition="'$(_AreAnyAssembliesTrimmed)' == 'true'" Type="Xamarin.Linker.OptimizeGeneratedCodeHandler" />
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" Condition="'$(_AreAnyAssembliesTrimmed)' == 'true' And '$(_UseDynamicDependenciesForGeneratedCodeOptimizations)' != 'true'" Type="Xamarin.Linker.OptimizeGeneratedCodeHandler" />
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" Condition="'$(_AreAnyAssembliesTrimmed)' == 'true'" Type="Xamarin.Linker.BackingFieldDelayHandler" />
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" Condition="'$(_AreAnyAssembliesTrimmed)' == 'true' And '$(_UseDynamicDependenciesForProtocolPreservation)' != 'true'" Type="Xamarin.Linker.MarkIProtocolHandler" />
<!-- MarkDispatcher substeps will run for all marked assemblies. -->
Expand Down
2 changes: 1 addition & 1 deletion src/Network/NWWebSocketResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public bool EnumerateAdditionalHeaders (Action<string?, string?> handler)

unsafe {
delegate* unmanaged<IntPtr, IntPtr, IntPtr, void> trampoline = &TrampolineEnumerateHeadersHandler;
using var block = new BlockLiteral (trampoline, handler, typeof (NWWebSocketResponseStatus), nameof (TrampolineEnumerateHeadersHandler));
using var block = new BlockLiteral (trampoline, handler, typeof (NWWebSocketResponse), nameof (TrampolineEnumerateHeadersHandler));
return nw_ws_response_enumerate_additional_headers (GetCheckedHandle (), &block) != 0;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,6 @@ Microsoft.iOS.dll:ObjCRuntime.Runtime.GetINativeObject(System.IntPtr, System.Boo
Microsoft.iOS.dll:ObjCRuntime.Runtime.GetINativeObject`1(System.IntPtr, System.Boolean, System.Boolean)
Microsoft.iOS.dll:ObjCRuntime.Runtime.GetINativeObject`1(System.IntPtr, System.Boolean, System.Type, System.Boolean, System.IntPtr, System.RuntimeMethodHandle)
Microsoft.iOS.dll:ObjCRuntime.Runtime.GetINativeObject`1(System.IntPtr, System.Boolean, System.Type, System.Boolean)
Microsoft.iOS.dll:ObjCRuntime.Runtime.GetIsARM64CallingConvention()
Microsoft.iOS.dll:ObjCRuntime.Runtime.GetMethodAndObjectForSelector(System.IntPtr, System.IntPtr, System.SByte, System.IntPtr, System.IntPtr*, System.IntPtr)
Microsoft.iOS.dll:ObjCRuntime.Runtime.GetMethodForSelector(System.IntPtr, System.IntPtr, System.SByte, System.IntPtr)
Microsoft.iOS.dll:ObjCRuntime.Runtime.GetMethodFromToken(System.UInt32)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,6 @@ Microsoft.iOS.dll:ObjCRuntime.Runtime.GetINativeObject(System.IntPtr, System.Boo
Microsoft.iOS.dll:ObjCRuntime.Runtime.GetINativeObject`1(System.IntPtr, System.Boolean, System.Boolean)
Microsoft.iOS.dll:ObjCRuntime.Runtime.GetINativeObject`1(System.IntPtr, System.Boolean, System.Type, System.Boolean, System.IntPtr, System.RuntimeMethodHandle)
Microsoft.iOS.dll:ObjCRuntime.Runtime.GetINativeObject`1(System.IntPtr, System.Boolean, System.Type, System.Boolean)
Microsoft.iOS.dll:ObjCRuntime.Runtime.GetIsARM64CallingConvention()
Microsoft.iOS.dll:ObjCRuntime.Runtime.GetMethodFromToken(System.UInt32)
Microsoft.iOS.dll:ObjCRuntime.Runtime.GetNSObject(ObjCRuntime.NativeHandle, System.Boolean)
Microsoft.iOS.dll:ObjCRuntime.Runtime.GetNSObject(System.IntPtr, ObjCRuntime.Runtime/MissingCtorResolution, System.Boolean)
Expand Down
134 changes: 0 additions & 134 deletions tests/linker/link sdk/OptimizeGeneratedCodeTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,139 +170,5 @@ public IntPtr FinallyTestMethod ()
finally_invoked = true;
}
}

[Test]
public void IntPtrSizeTest ()
{
IgnoreIfNotLinkAll ();

var S8methods = new MethodInfo []
{
GetType ().GetMethod (nameof (Size8Test), BindingFlags.NonPublic | BindingFlags.Instance),
GetType ().GetMethod (nameof (Size8Test_Optimizable), BindingFlags.NonPublic | BindingFlags.Instance)
};
var S4methods = new MethodInfo []
{
GetType ().GetMethod (nameof (Size4Test), BindingFlags.NonPublic | BindingFlags.Instance),
GetType ().GetMethod (nameof (Size4Test_Optimizable), BindingFlags.NonPublic | BindingFlags.Instance)
};
MethodInfo [] passingMethods = null;
MethodInfo [] failingMethods = null;
switch (IntPtr.Size) {
case 4:
Size4Test ();
Size4Test_Optimizable ();
Assert.Throws<NUnit.Framework.Internal.NUnitException> (Size8Test, "Size8Test");
Assert.Throws<NUnit.Framework.Internal.NUnitException> (Size8Test_Optimizable, "Size8Test_Optimizable");
passingMethods = S4methods;
failingMethods = S8methods;
break;
case 8:
Size8Test ();
Size8Test_Optimizable ();
Assert.Throws<NUnit.Framework.Internal.NUnitException> (Size4Test, "Size4Test");
Assert.Throws<NUnit.Framework.Internal.NUnitException> (Size4Test_Optimizable, "Size4Test_Optimizable");
passingMethods = S8methods;
failingMethods = S4methods;
break;
default:
Assert.Fail ("Invalid size: {0}", IntPtr.Size);
break;
}

#if !DEBUG
// Verify that the passing method is completely empty (save for nop instructions and a final ret instruction).
// Unfortunately in debug mode csc produces IL sequences the optimizer doesn't understand (a lot of unnecessary instructions),
// which means we can only check this in release mode. Also on device this will probably always pass,
// since we strip assemblies (and the methods will always be empty), but running the test shouldn't hurt.
foreach (var passingMethod in passingMethods) {
IEnumerable<ILInstruction> passingInstructions = new ILReader (passingMethod);
passingInstructions = passingInstructions.Where ((v) => v.OpCode.Name != "nop");
Assert.AreEqual (1, passingInstructions.Count (), "empty body");
}
#else
// Verify that the failing methods are not completely empty (even excluding nop instructions and the final ret instruction).
// This can only be executed in debug mode, because in release mode the IL will be stripped away and the methods will be empty.
foreach (var failingMethod in failingMethods) {
IEnumerable<ILInstruction> failingInstructions = new ILReader (failingMethod);
failingInstructions = failingInstructions.Where ((v) => v.OpCode.Name != "nop");
Assert.That (failingInstructions.Count (), Is.GreaterThan (1), "non-empty body");
}
#endif
}

[CompilerGenerated] // Trigger optimizations
[Foundation.Export ("alsoRequiredForOptimizations")]
void Size8Test ()
{
// Everything in this method should be optimized away (when building for 64-bits)
if (IntPtr.Size != 8)
throw new NUnit.Framework.Internal.NUnitException ("1");
if (IntPtr.Size == 4)
throw new NUnit.Framework.Internal.NUnitException ("2");
if (IntPtr.Size > 8)
throw new NUnit.Framework.Internal.NUnitException ("3");
if (IntPtr.Size < 8)
throw new NUnit.Framework.Internal.NUnitException ("4");
if (IntPtr.Size >= 9)
throw new NUnit.Framework.Internal.NUnitException ("5");
if (IntPtr.Size <= 7)
throw new NUnit.Framework.Internal.NUnitException ("6");
}

[CompilerGenerated] // Trigger optimizations
[Foundation.Export ("alsoRequiredForOptimizations")]
void Size4Test ()
{
// Everything in this method should be optimized away (when building for 32-bits)
if (IntPtr.Size != 4)
throw new NUnit.Framework.Internal.NUnitException ("1");
if (IntPtr.Size == 8)
throw new NUnit.Framework.Internal.NUnitException ("2");
if (IntPtr.Size > 4)
throw new NUnit.Framework.Internal.NUnitException ("3");
if (IntPtr.Size < 4)
throw new NUnit.Framework.Internal.NUnitException ("4");
if (IntPtr.Size >= 5)
throw new NUnit.Framework.Internal.NUnitException ("5");
if (IntPtr.Size <= 3)
throw new NUnit.Framework.Internal.NUnitException ("6");
}

[BindingImplAttribute (BindingImplOptions.Optimizable)]
void Size8Test_Optimizable ()
{
// Everything in this method should be optimized away (when building for 64-bits)
if (IntPtr.Size != 8)
throw new NUnit.Framework.Internal.NUnitException ("1");
if (IntPtr.Size == 4)
throw new NUnit.Framework.Internal.NUnitException ("2");
if (IntPtr.Size > 8)
throw new NUnit.Framework.Internal.NUnitException ("3");
if (IntPtr.Size < 8)
throw new NUnit.Framework.Internal.NUnitException ("4");
if (IntPtr.Size >= 9)
throw new NUnit.Framework.Internal.NUnitException ("5");
if (IntPtr.Size <= 7)
throw new NUnit.Framework.Internal.NUnitException ("6");
}

[BindingImplAttribute (BindingImplOptions.Optimizable)]
void Size4Test_Optimizable ()
{
// Everything in this method should be optimized away (when building for 32-bits)
if (IntPtr.Size != 4)
throw new NUnit.Framework.Internal.NUnitException ("1");
if (IntPtr.Size == 8)
throw new NUnit.Framework.Internal.NUnitException ("2");
if (IntPtr.Size > 4)
throw new NUnit.Framework.Internal.NUnitException ("3");
if (IntPtr.Size < 4)
throw new NUnit.Framework.Internal.NUnitException ("4");
if (IntPtr.Size >= 5)
throw new NUnit.Framework.Internal.NUnitException ("5");
if (IntPtr.Size <= 3)
throw new NUnit.Framework.Internal.NUnitException ("6");
}
}
}
27 changes: 27 additions & 0 deletions tools/common/Target.cs
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,33 @@ static bool IsBoundAssembly (Assembly s)

return false;
}

bool _set_arm64_calling_convention;
bool? _is_arm64_calling_convention;
public bool? InlineIsArm64CallingConventionForCurrentAbi {
get {
if (!_set_arm64_calling_convention) {
if (Optimizations.InlineIsARM64CallingConvention == true) {
// We can usually inline Runtime.InlineIsARM64CallingConvention if the generated code will execute on a single architecture
switch (Abi & Abi.ArchMask) {
case Abi.x86_64:
_is_arm64_calling_convention = false;
break;
case Abi.ARM64:
case Abi.ARM64e:
_is_arm64_calling_convention = true;
break;
default:
LinkContext.Exceptions.Add (Xamarin.Bundler.ErrorHelper.CreateWarning (99, Xamarin.Bundler.Errors.MX0099, $"unknown abi: {Abi}"));
break;
}
}
_set_arm64_calling_convention = true;
}
return _is_arm64_calling_convention;
}
}

#endif // !LEGACY_TOOLS
}
}
38 changes: 38 additions & 0 deletions tools/dotnet-linker/OptimizeGeneratedCodeStep.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Mono.Cecil;

#nullable enable

namespace Xamarin.Linker.Steps {
public class OptimizeGeneratedCodeStep : AssemblyModifierStep {
protected override string Name { get; } = "Binding Optimizer";
protected override int ErrorCode { get; } = 2020;

OptimizeGeneratedCodeData? data;

protected override bool IsActiveFor (AssemblyDefinition assembly)
{
return OptimizeGeneratedCodeHandler.IsActiveFor (assembly, Configuration.Profile, DerivedLinkContext.Annotations);
}

protected override bool ProcessType (TypeDefinition type)
{
return ProcessMethods (type);
}

protected override bool ProcessMethod (MethodDefinition method)
{
if (data is null) {
data = new OptimizeGeneratedCodeData {
LinkContext = DerivedLinkContext,
InlineIsArm64CallingConvention = App.InlineIsArm64CallingConventionForCurrentAbi,
Optimizations = App.Optimizations,
Device = App.IsDeviceBuild,
};
}
return OptimizeGeneratedCodeHandler.OptimizeMethod (data, method);
}
}
}
12 changes: 2 additions & 10 deletions tools/dotnet-linker/PreserveSmartEnumConversionsStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,18 +61,10 @@ bool Preserve (Tuple<MethodDefinition, MethodDefinition> pair, bool alreadyProce

protected override bool ProcessType (TypeDefinition type)
{
var modified = false;

if (!type.HasMethods)
return modified;

foreach (var method in type.Methods)
modified |= ProcessMethod (method);

return modified;
return base.ProcessMethods (type);
}

bool ProcessMethod (MethodDefinition method)
protected override bool ProcessMethod (MethodDefinition method)
{
static bool IsPropertyMethod (MethodDefinition method)
{
Expand Down
16 changes: 16 additions & 0 deletions tools/dotnet-linker/Steps/AssemblyModifierStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ protected virtual bool ProcessType (TypeDefinition type)
return false;
}

protected virtual bool ProcessMethod (MethodDefinition method)
{
return false;
}

bool ProcessTypeImpl (TypeDefinition type)
{
var modified = ProcessType (type);
Expand All @@ -44,4 +49,15 @@ bool ProcessTypeImpl (TypeDefinition type)
}
return modified;
}

protected bool ProcessMethods (TypeDefinition type)
{
if (!type.HasMethods)
return false;

var modified = false;
foreach (var method in type.Methods)
modified |= ProcessMethod (method);
return modified;
}
}
Loading
Loading