Skip to content
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

[net] iOS fat (armv7 + arm64) apps are not optimal #12607

Closed
spouliot opened this issue Sep 1, 2021 · 6 comments
Closed

[net] iOS fat (armv7 + arm64) apps are not optimal #12607

spouliot opened this issue Sep 1, 2021 · 6 comments
Labels
dotnet An issue or pull request related to .NET (6) dotnet-pri3 .NET 6: wishlist for stable release enhancement The issue or pull request is an enhancement
Milestone

Comments

@spouliot
Copy link
Contributor

spouliot commented Sep 1, 2021

iOS fat (armv7 + arm64) app

Assemblies

Assemblies deduplication is not possible (e.g. System.Runtime.dll and MySimpleView.dll should be shared) due to some minor changes, e.g. the path to the .pdb file is not identical between builds. There's no diff when you compare the disassembly of both assemblies.

System.Private.CoreLib.dll is quite different (and afaik the source assembly is not identical anyway) making it hard to share.

System.Private.CoreLib.dll diff

Xamarin.iOS.dll differs due to 32/64 differences. Those could be solved by preserving additional members and moving the bit-aware types to a different (but much smaller) assembly (with type forwarders for compatibility).

--- a.cs	2021-08-25 20:35:18.000000000 -0400
+++ b.cs	2021-08-25 20:35:21.000000000 -0400
@@ -1137,11 +1137,9 @@
 				NSLog(text);
 				throw ErrorHelper.CreateError(8001, text);
 			}
-			if (8 != sizeof(nint))
+			if (4 != sizeof(nint))
 			{
-				_ = 8;
-				_ = 8;
-				string text2 = string.Format("Native type size mismatch between Xamarin.iOS.dll and the executing architecture. Xamarin.iOS.dll was built for {0}-bit, while the current process is {1}-bit.", "32", "64");
+				string text2 = string.Format("Native type size mismatch between Xamarin.iOS.dll and the executing architecture. Xamarin.iOS.dll was built for {0}-bit, while the current process is {1}-bit.", "64", "32");
 				NSLog(text2);
 				throw ErrorHelper.CreateError(8010, text2);
 			}
@@ -2054,7 +2052,7 @@
 
 		private static bool GetIsARM64CallingConvention()
 		{
-			return true;
+			return false;
 		}
 
 		internal static object GetGCHandleTarget(IntPtr P_0)
@@ -3100,16 +3098,16 @@
 		public static extern bool bool_objc_msgSendSuper_IntPtr(IntPtr P_0, IntPtr P_1, IntPtr P_2);
 
 		[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
-		public static extern void void_objc_msgSend_IntPtr_UInt64(IntPtr P_0, IntPtr P_1, IntPtr P_2, ulong P_3);
+		public static extern void void_objc_msgSend_IntPtr_UInt32(IntPtr P_0, IntPtr P_1, IntPtr P_2, uint P_3);
 
 		[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
 		public static extern void void_objc_msgSend(IntPtr P_0, IntPtr P_1);
 
-		[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
-		public static extern CGRect CGRect_objc_msgSend(IntPtr P_0, IntPtr P_1);
+		[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend_stret")]
+		public static extern void CGRect_objc_msgSend_stret(out CGRect P_0, IntPtr P_1, IntPtr P_2);
 
-		[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSendSuper")]
-		public static extern CGRect CGRect_objc_msgSendSuper(IntPtr P_0, IntPtr P_1);
+		[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSendSuper_stret")]
+		public static extern void CGRect_objc_msgSendSuper_stret(out CGRect P_0, IntPtr P_1, IntPtr P_2);
 
 		[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
 		public static extern void void_objc_msgSend_IntPtr_IntPtr_bool(IntPtr P_0, IntPtr P_1, IntPtr P_2, IntPtr P_3, [MarshalAs(UnmanagedType.I1)] bool P_4);
@@ -3234,8 +3232,7 @@
 		public void SetTitle(string? P_0, UIControlState P_1)
 		{
 			IntPtr intPtr = CFString.CreateNative(P_0);
-			_ = 8;
-			Messaging.void_objc_msgSend_IntPtr_UInt64(base.Handle, Selector.GetHandle("setTitle:forState:"), intPtr, (ulong)P_1);
+			Messaging.void_objc_msgSend_IntPtr_UInt32(base.Handle, Selector.GetHandle("setTitle:forState:"), intPtr, (uint)P_1);
 			CFString.ReleaseNative(intPtr);
 		}
 	}
@@ -3282,7 +3279,8 @@
 			[Export("bounds")]
 			get
 			{
-				return Messaging.CGRect_objc_msgSend(base.Handle, Selector.GetHandle("bounds"));
+				Messaging.CGRect_objc_msgSend_stret(out var result, base.Handle, Selector.GetHandle("bounds"));
+				return result;
 			}
 		}
 
@@ -3321,12 +3319,17 @@
 			[Export("bounds")]
 			get
 			{
-				//Discarded unreachable code: IL_0028
+				//Discarded unreachable code: IL_0035
+				CGRect result;
 				if (base.IsDirectBinding)
 				{
-					return Messaging.CGRect_objc_msgSend(base.Handle, Selector.GetHandle("bounds"));
+					Messaging.CGRect_objc_msgSend_stret(out result, base.Handle, Selector.GetHandle("bounds"));
+				}
+				else
+				{
+					Messaging.CGRect_objc_msgSendSuper_stret(out result, base.SuperHandle, Selector.GetHandle("bounds"));
 				}
-				return Messaging.CGRect_objc_msgSendSuper(base.SuperHandle, Selector.GetHandle("bounds"));
+				return result;
 			}
 		}
 
@@ -4477,15 +4480,15 @@
 	[Serializable]
 	public struct nint : IFormattable, IConvertible, IComparable, IComparable<nint>, IEquatable<nint>
 	{
-		public static readonly int Size = 8;
+		public static readonly int Size = 4;
 
-		public static readonly nint MaxValue = (nint)9223372036854775807L;
+		public static readonly nint MaxValue = 2147483647;
 
-		public static readonly nint MinValue = (nint)(-9223372036854775808L);
+		public static readonly nint MinValue = -2147483648;
 
-		internal long v;
+		internal int v;
 
-		public nint(long P_0)
+		public nint(int P_0)
 		{
 			v = P_0;
 		}
@@ -4497,12 +4500,7 @@
 
 		public static explicit operator int(nint P_0)
 		{
-			return (int)P_0.v;
-		}
-
-		public static explicit operator nint(long P_0)
-		{
-			return new nint(P_0);
+			return P_0.v;
 		}
 
 		public static implicit operator long(nint P_0)
@@ -4576,15 +4574,15 @@
 	[Serializable]
 	public struct nuint : IFormattable, IConvertible, IComparable, IComparable<nuint>, IEquatable<nuint>
 	{
-		public static readonly int Size = 8;
+		public static readonly int Size = 4;
 
-		public static readonly nuint MaxValue = (nuint)18446744073709551615uL;
+		public static readonly nuint MaxValue = 4294967295u;
 
-		public static readonly nuint MinValue = (nuint)0uL;
+		public static readonly nuint MinValue = 0u;
 
-		internal ulong v;
+		internal uint v;
 
-		public nuint(ulong P_0)
+		public nuint(uint P_0)
 		{
 			v = P_0;
 		}
@@ -4594,7 +4592,7 @@
 			return (int)P_0.v;
 		}
 
-		public static explicit operator nuint(ulong P_0)
+		public static implicit operator nuint(uint P_0)
 		{
 			return new nuint(P_0);
 		}
@@ -4655,28 +4653,28 @@
 	[Serializable]
 	public struct nfloat : IFormattable, IConvertible, IComparable, IComparable<nfloat>, IEquatable<nfloat>
 	{
-		public static readonly int Size = 8;
+		public static readonly int Size = 4;
 
-		public static readonly nfloat MaxValue = (nfloat)1.7976931348623157E+308;
+		public static readonly nfloat MaxValue = 3.4028235E+38f;
 
-		public static readonly nfloat MinValue = (nfloat)(-1.7976931348623157E+308);
+		public static readonly nfloat MinValue = -3.4028235E+38f;
 
-		public static readonly nfloat Epsilon = (nfloat)5E-324;
+		public static readonly nfloat Epsilon = 1E-45f;
 
-		public static readonly nfloat NaN = (nfloat)(0.0 / 0.0);
+		public static readonly nfloat NaN = 0f / 0f;
 
-		public static readonly nfloat NegativeInfinity = (nfloat)(-1.0 / 0.0);
+		public static readonly nfloat NegativeInfinity = -1f / 0f;
 
-		public static readonly nfloat PositiveInfinity = (nfloat)(1.0 / 0.0);
+		public static readonly nfloat PositiveInfinity = 1f / 0f;
 
-		internal double v;
+		internal float v;
 
-		public nfloat(double P_0)
+		public nfloat(float P_0)
 		{
 			v = P_0;
 		}
 
-		public static explicit operator nfloat(double P_0)
+		public static implicit operator nfloat(float P_0)
 		{
 			return new nfloat(P_0);
 		}

App Bundle Comparison

Directories / Files Legacy net6 rc.1 diff %
.//_CodeSignature
.//
    embedded.mobileprovision 12,391 12,391 0 0.0%
    icudt.dat 0 2,176,304 2,176,304 -
    Info.plist 1,027 1,027 0 0.0%
    MySingleView 7,288,688 14,379,440 7,090,752 97.3%
    MySingleView.aotdata.arm64 1,120 816 -304 -27.1%
    MySingleView.aotdata.armv7 1,120 816 -304 -27.1%
    MySingleView.exe 4,608 0 -4,608 -100.0%
    PkgInfo 8 8 0 0.0%
    runtimeconfig.bin 0 733 733 -
    System.aotdata.arm64 968 0 -968 -100.0%
    System.aotdata.armv7 968 0 -968 -100.0%
    System.dll 4,608 0 -4,608 -100.0%
    System.Private.CoreLib.aotdata.arm64 0 731,992 731,992 -
    System.Private.CoreLib.aotdata.armv7 0 683,728 683,728 -
    System.Runtime.aotdata.arm64 0 504 504 -
    System.Runtime.aotdata.armv7 0 504 504 -
    Xamarin.iOS.aotdata.arm64 0 48,328 48,328 -
    Xamarin.iOS.aotdata.armv7 0 48,512 48,512 -
.//.monotouch-32
    mscorlib.dll 407,552 0 -407,552 -100.0%
    Xamarin.iOS.aotdata.armv7 36,480 0 -36,480 -100.0%
    Xamarin.iOS.dll 56,832 0 -56,832 -100.0%
.//.monotouch-64
    mscorlib.dll 407,552 0 -407,552 -100.0%
    Xamarin.iOS.aotdata.arm64 36,568 0 -36,568 -100.0%
    Xamarin.iOS.dll 56,832 0 -56,832 -100.0%
.//.xamarin/ios-arm
    System.Private.CoreLib.dll 0 765,440 765,440 -
    System.Runtime.dll 0 5,120 5,120 -
    Xamarin.iOS.dll 0 73,728 73,728 -
.//.xamarin/ios-arm64
    System.Private.CoreLib.dll 0 775,680 775,680 -
    System.Runtime.dll 0 5,120 5,120 -
    Xamarin.iOS.dll 0 73,728 73,728 -
Statistics
Native subtotal 7,612,208 15,161,080 7,548,872 99.2%
    Executable 7,288,688 14,379,440 7,090,752 97.3%
    AOT data 323,520 781,640 458,120 141.6%
Managed *.dll/exe 937,984 1,708,032 770,048 82.1%
TOTAL 8,895,347 19,801,784 10,906,437 122.6%

Comparison with legacy is a bit harder since the file's location is not identical.

The situation for macOS should be a little different (and simpler) since both slices would be 64 bits.

@spouliot spouliot added enhancement The issue or pull request is an enhancement dotnet An issue or pull request related to .NET (6) dotnet-pri3 .NET 6: wishlist for stable release labels Sep 1, 2021
@spouliot spouliot added this to the .NET 6 milestone Sep 1, 2021
@spouliot
Copy link
Contributor Author

spouliot commented Sep 8, 2021

At least for iOS using App Thinning would be better (for end-users) than doing a better deduplication of the assemblies assemblies -> #12665

@rolfbjarne rolfbjarne changed the title [net6] iOS fat (armv7 + arm64) apps are not optimal [net] iOS fat (armv7 + arm64) apps are not optimal Dec 9, 2021
@rolfbjarne rolfbjarne modified the milestones: .NET 6, .NET 7 Dec 9, 2021
@filipnavara
Copy link
Contributor

We get significant differences for all BCL assemblies. I commented on that in dotnet/runtime#62953 and I think that needs to be investigated on the runtime side. Xamarin.Android apparently seems to be doing some deduplication based on ModuleVersionId, presumably the same could be done in the merge task for Xamarin.Mac/iOS.

@filipnavara
Copy link
Contributor

Turns out that the assembly size/content difference we hit on osx-x64/osx-arm64 is from the R2R images used by CoreCLR and Crossgen2. It should not affect the Mono runtime packs and mobile platforms but it's something interesting to keep in mind.

Additionally I discussed it with the MonoVM folks and we found that the deduplication by MVID scenario is broken at the moment. There was an issue with Cecil/Linker in .NET 6 that caused some assemblies to get same MVID despite them not being identical (dotnet/linker#2203). Now that issue was supposedly fixed for .NET 7 but it appears it caused identical assemblies to get different MVIDs too (at least in two runtime assembly packs we compared) so an opposite problem.

@rolfbjarne rolfbjarne modified the milestones: .NET 7, .NET 8 Aug 19, 2022
@rolfbjarne
Copy link
Member

This will not be done in the .NET 7 time frame due to time constraints, so moving to .NET 8.

@rolfbjarne
Copy link
Member

This won't be an issue in .NET 8, because we're dropping support for 32-bit iOS (#15216).

@rolfbjarne rolfbjarne closed this as not planned Won't fix, can't repro, duplicate, stale Oct 10, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Nov 9, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
dotnet An issue or pull request related to .NET (6) dotnet-pri3 .NET 6: wishlist for stable release enhancement The issue or pull request is an enhancement
Projects
None yet
Development

No branches or pull requests

3 participants