From 723f6c9d8accbd66a6923cb38839f03afda2f42d Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 19 Mar 2026 16:41:35 +0100 Subject: [PATCH 1/6] [dotnet-linker] Use [DynamicDependency] attributes instead of manual marking when optimizing generated code. This makes it easier to move this code out of a custom linker step in the future. Also simplify a few things: * There's no need to compute whether the code is optimizable, because the result is never used. * Unify code to determine whether the InlineIsARM64CallingConvention optimization is to be applied. * Any code that modifies the current assembly now returns a boolean saying so (so that we know if the current assembly has to be saved). This even found a bug in NWWebSocketResponse, so fix that as well. Contributes towards https://github.com/dotnet/macios/issues/17693. --- dotnet/targets/Xamarin.Shared.Sdk.targets | 4 +- src/Network/NWWebSocketResponse.cs | 2 +- tools/common/Target.cs | 27 ++ .../OptimizeGeneratedCodeStep.cs | 38 ++ .../PreserveSmartEnumConversionsStep.cs | 12 +- .../Steps/AssemblyModifierStep.cs | 16 + tools/linker/CoreOptimizeGeneratedCode.cs | 441 +++++++++--------- tools/mtouch/Errors.designer.cs | 13 +- tools/mtouch/Errors.resx | 6 +- 9 files changed, 314 insertions(+), 245 deletions(-) create mode 100644 tools/dotnet-linker/OptimizeGeneratedCodeStep.cs diff --git a/dotnet/targets/Xamarin.Shared.Sdk.targets b/dotnet/targets/Xamarin.Shared.Sdk.targets index 0bdae17861ba..20dd05d523ca 100644 --- a/dotnet/targets/Xamarin.Shared.Sdk.targets +++ b/dotnet/targets/Xamarin.Shared.Sdk.targets @@ -550,6 +550,7 @@ <_UseDynamicDependenciesForProtocolPreservation Condition="'$(_UseDynamicDependenciesForProtocolPreservation)' == ''">$(_UseDynamicDependenciesInsteadOfMarking) <_UseDynamicDependenciesForSmartEnumPreservation Condition="'$(_UseDynamicDependenciesForSmartEnumPreservation)' == ''">$(_UseDynamicDependenciesInsteadOfMarking) <_UseDynamicDependenciesForBlockCodePreservation Condition="'$(_UseDynamicDependenciesForBlockCodePreservation)' == ''">$(_UseDynamicDependenciesInsteadOfMarking) + <_UseDynamicDependenciesForGeneratedCodeOptimizations Condition="'$(_UseDynamicDependenciesForGeneratedCodeOptimizations)' == ''">$(_UseDynamicDependenciesInsteadOfMarking) @@ -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'" /> <_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="MarkStep" Type="MonoTouch.Tuner.RegistrarRemovalTrackingStep" /> @@ -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" /> diff --git a/src/Network/NWWebSocketResponse.cs b/src/Network/NWWebSocketResponse.cs index ee4218a7aa56..80afe495ccce 100644 --- a/src/Network/NWWebSocketResponse.cs +++ b/src/Network/NWWebSocketResponse.cs @@ -74,7 +74,7 @@ public bool EnumerateAdditionalHeaders (Action handler) unsafe { delegate* unmanaged 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; } } diff --git a/tools/common/Target.cs b/tools/common/Target.cs index 9c70601442ef..324f7334174a 100644 --- a/tools/common/Target.cs +++ b/tools/common/Target.cs @@ -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 } } diff --git a/tools/dotnet-linker/OptimizeGeneratedCodeStep.cs b/tools/dotnet-linker/OptimizeGeneratedCodeStep.cs new file mode 100644 index 000000000000..d785f16c94af --- /dev/null +++ b/tools/dotnet-linker/OptimizeGeneratedCodeStep.cs @@ -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); + } + } +} diff --git a/tools/dotnet-linker/PreserveSmartEnumConversionsStep.cs b/tools/dotnet-linker/PreserveSmartEnumConversionsStep.cs index 4d39573b67ad..75ebca8360dd 100644 --- a/tools/dotnet-linker/PreserveSmartEnumConversionsStep.cs +++ b/tools/dotnet-linker/PreserveSmartEnumConversionsStep.cs @@ -61,18 +61,10 @@ bool Preserve (Tuple 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) { diff --git a/tools/dotnet-linker/Steps/AssemblyModifierStep.cs b/tools/dotnet-linker/Steps/AssemblyModifierStep.cs index ab3ef125f652..da3fcb5e5608 100644 --- a/tools/dotnet-linker/Steps/AssemblyModifierStep.cs +++ b/tools/dotnet-linker/Steps/AssemblyModifierStep.cs @@ -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); @@ -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; + } } diff --git a/tools/linker/CoreOptimizeGeneratedCode.cs b/tools/linker/CoreOptimizeGeneratedCode.cs index 325b4ad770f1..b1ec75694e61 100644 --- a/tools/linker/CoreOptimizeGeneratedCode.cs +++ b/tools/linker/CoreOptimizeGeneratedCode.cs @@ -21,98 +21,29 @@ public class OptimizeGeneratedCodeHandler : ExceptionalMarkHandler { protected override string Name { get; } = "Binding Optimizer"; protected override int ErrorCode { get; } = 2020; - Dictionary? _hasOptimizableCode; - Dictionary HasOptimizableCode { - get { - if (_hasOptimizableCode is null) - _hasOptimizableCode = new Dictionary (); - return _hasOptimizableCode; - } - } - - public bool Device { - get { return LinkContext.App.IsDeviceBuild; } - } - - protected Optimizations Optimizations { - get { - return LinkContext.App.Optimizations; - } - } - - bool? is_arm64_calling_convention; + OptimizeGeneratedCodeData? data; public override void Initialize (LinkContext context, MarkContext markContext) { base.Initialize (context); - - if (Optimizations.InlineIsARM64CallingConvention == true) { - var app = LinkContext.App; - // We can usually inline Runtime.InlineIsARM64CallingConvention if the generated code will execute on a single architecture - switch (app.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 (ErrorHelper.CreateWarning (99, Errors.MX0099, $"unknown abi: {app.Abi}")); - break; - } - } markContext.RegisterMarkMethodAction (ProcessMethod); } - bool IsActiveFor (AssemblyDefinition assembly, out bool hasOptimizableCode) + bool IsActiveFor (AssemblyDefinition assembly) { - hasOptimizableCode = false; - if (HasOptimizableCode.TryGetValue (assembly, out bool? optimizable)) { - if (optimizable == true) - hasOptimizableCode = true; - return optimizable is not null; - } - // we're sure "pure" SDK assemblies don't use XamMac.dll (i.e. they are the Product assemblies) - if (Profile.IsSdkAssembly (assembly)) { -#if DEBUG - Console.WriteLine ("Assembly {0} : skipped (SDK)", assembly); -#endif - HasOptimizableCode.Add (assembly, null); - return false; - } + return IsActiveFor (assembly, Profile, Annotations); + } - // process only assemblies where the linker is enabled (e.g. --linksdk, --linkskip) - AssemblyAction action = Annotations.GetAction (assembly); - if (action != AssemblyAction.Link) { -#if DEBUG - Console.WriteLine ("Assembly {0} : skipped ({1})", assembly, action); -#endif - HasOptimizableCode.Add (assembly, null); + public static bool IsActiveFor (AssemblyDefinition assembly, Profile profile, AnnotationStore annotations) + { + // Unless an assembly is or references our platform assembly, then it won't have anything we need to register + if (!profile.IsOrReferencesProductAssembly (assembly)) return false; - } - // if the assembly does not refer to [CompilerGeneratedAttribute] then there's not much we can do - foreach (TypeReference tr in assembly.MainModule.GetTypeReferences ()) { - if (tr.Is (Namespaces.ObjCRuntime, "BindingImplAttribute")) { - hasOptimizableCode = true; - break; - } + // We only care about assemblies that are being linked. + if (annotations.GetAction (assembly) != AssemblyAction.Link) + return false; - if (tr.Is ("System.Runtime.CompilerServices", "CompilerGeneratedAttribute")) { -#if DEBUG - Console.WriteLine ("Assembly {0} : processing", assembly); -#endif - hasOptimizableCode = true; - break; - } - } -#if DEBUG - if (!hasOptimizableCode) - Console.WriteLine ("Assembly {0} : no [CompilerGeneratedAttribute] nor [BindingImplAttribute] present (applying basic optimizations)", assembly); -#endif - // we always apply the step - HasOptimizableCode.Add (assembly, hasOptimizableCode); return true; } @@ -429,10 +360,11 @@ static bool AnyBranchTo (Mono.Collections.Generic.Collection instru return false; } - protected void EliminateDeadCode (MethodDefinition caller) + static bool EliminateDeadCode (OptimizeGeneratedCodeData data, MethodDefinition caller) { - if (Optimizations.DeadCodeElimination != true) - return; + var modified = false; + if (data.Optimizations.DeadCodeElimination != true) + return modified; var instructions = caller.Body.Instructions; var reachable = new bool [instructions.Count]; @@ -442,7 +374,7 @@ protected void EliminateDeadCode (MethodDefinition caller) // can be removed. if (!MarkInstructions (caller, instructions, reachable, 0, instructions.Count)) - return; + return modified; // Handle exception handlers specially, they do not follow normal code flow. bool []? reachableExceptionHandlers = null; @@ -476,25 +408,25 @@ protected void EliminateDeadCode (MethodDefinition caller) } if (!allNops) { if (!MarkInstructions (caller, instructions, reachable, instructions.IndexOf (eh.HandlerStart), instructions.IndexOf (eh.HandlerEnd))) - return; + return modified; } break; case ExceptionHandlerType.Finally: // finally clauses are always executed, even if the protected region is empty if (!MarkInstructions (caller, instructions, reachable, instructions.IndexOf (eh.HandlerStart), instructions.IndexOf (eh.HandlerEnd))) - return; + return modified; break; case ExceptionHandlerType.Fault: case ExceptionHandlerType.Filter: // FIXME: and until fixed, exit gracefully without doing anything Driver.Log (4, "Unhandled exception handler: {0}, skipping dead code elimination for {1}", eh.HandlerType, caller); - return; + return modified; } } } if (Array.IndexOf (reachable, false) == -1) - return; // entire method is reachable + return modified; // entire method is reachable // Kill branch instructions when there are only dead instructions between the branch instruction and the target of the branch for (int i = 0; i < instructions.Count; i++) { @@ -544,7 +476,7 @@ protected void EliminateDeadCode (MethodDefinition caller) var target = (Instruction) ins.Operand; if (target.Offset > last_reachable_offset) { Driver.Log (4, "Can't optimize {0} because of branching beyond last instruction alive: {1}", caller, ins); - return; + return modified; } break; } @@ -562,8 +494,10 @@ protected void EliminateDeadCode (MethodDefinition caller) // Exterminate, exterminate, exterminate for (int i = 0; i < reachable.Length; i++) { - if (!reachable [i]) + if (!reachable [i]) { Nop (instructions [i]); + modified = true; + } } // Remove exception handlers @@ -572,15 +506,20 @@ protected void EliminateDeadCode (MethodDefinition caller) if (reachableExceptionHandlers [i]) continue; caller.Body.ExceptionHandlers.RemoveAt (i); + modified = true; } } // Remove unreachable instructions (nops) at the end, because the last instruction can only be ret/throw/backwards branch. - for (int i = last_reachable + 1; i < reachable.Length; i++) + for (int i = last_reachable + 1; i < reachable.Length; i++) { instructions.RemoveAt (last_reachable + 1); + modified = true; + } + + return modified; } - bool GetIsExtensionType (TypeDefinition type) + static bool GetIsExtensionType (TypeDefinition type) { // if 'type' inherits from NSObject inside an assembly that has [GeneratedCode] // or for static types used for optional members (using extensions methods), they can be optimized too @@ -589,34 +528,49 @@ bool GetIsExtensionType (TypeDefinition type) protected override void Process (MethodDefinition method) { - if (!IsActiveFor (method.DeclaringType.Module.Assembly, out bool hasOptimizableCode)) + if (!IsActiveFor (method.DeclaringType.Module.Assembly)) return; + if (data is null) { + data = new OptimizeGeneratedCodeData { + LinkContext = LinkContext, + InlineIsArm64CallingConvention = LinkContext.App.InlineIsArm64CallingConventionForCurrentAbi, + Optimizations = LinkContext.App.Optimizations, + Device = LinkContext.App.IsDeviceBuild, + }; + } + OptimizeMethod (data, method); + } + + public static bool OptimizeMethod (OptimizeGeneratedCodeData data, MethodDefinition method) + { + var modified = false; + if (!method.HasBody) - return; + return modified; - if (method.IsBindingImplOptimizableCode (LinkContext)) { + if (method.IsBindingImplOptimizableCode (data.LinkContext)) { // We optimize all methods that have the [BindingImpl (BindingImplAttributes.Optimizable)] attribute. - } else if ((method.IsGeneratedCode (LinkContext) && ( + } else if (method.IsGeneratedCode (data.LinkContext) && ( GetIsExtensionType (method.DeclaringType) - || IsExport (method)))) { + || IsExport (method))) { // We optimize methods that have the [GeneratedCodeAttribute] and is either an extension type or an exported method } else { // but it would be too risky to apply on user-generated code - return; + return modified; } - if (Optimizations.InlineIsARM64CallingConvention == true && is_arm64_calling_convention.HasValue && method.Name == "GetIsARM64CallingConvention" && method.DeclaringType.Is (Namespaces.ObjCRuntime, "Runtime")) { + if (data.Optimizations.InlineIsARM64CallingConvention == true && data.InlineIsArm64CallingConvention.HasValue && method.Name == "GetIsARM64CallingConvention" && method.DeclaringType.Is (Namespaces.ObjCRuntime, "Runtime")) { // Rewrite to return the constant value var instr = method.Body.Instructions; instr.Clear (); - instr.Add (Instruction.Create (is_arm64_calling_convention.Value ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0)); + instr.Add (Instruction.Create (data.InlineIsArm64CallingConvention.Value ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0)); instr.Add (Instruction.Create (OpCodes.Ret)); - return; // nothing else to do here. + return true; // nothing else to do here. } - if (ProcessProtocolInterfaceStaticConstructor (method)) - return; + if (ProcessProtocolInterfaceStaticConstructor (data, method)) + return true; var instructions = method.Body.Instructions; for (int i = 0; i < instructions.Count; i++) { @@ -624,104 +578,112 @@ protected override void Process (MethodDefinition method) switch (ins.OpCode.Code) { case Code.Newobj: case Code.Call: - i += ProcessCalls (method, ins); + modified |= ProcessCalls (data, method, ins, out var instructionsAddedOrRemoved); + i += instructionsAddedOrRemoved; break; case Code.Ldsfld: - ProcessLoadStaticField (method, ins); + modified |= ProcessLoadStaticField (data, method, ins); break; } } - EliminateDeadCode (method); + modified |= EliminateDeadCode (data, method); + return modified; } // Returns the number of instructions added (or removed). - protected virtual int ProcessCalls (MethodDefinition caller, Instruction ins) + static bool ProcessCalls (OptimizeGeneratedCodeData data, MethodDefinition caller, Instruction ins, out int instructionsAddedOrRemoved) { + var modified = false; + instructionsAddedOrRemoved = 0; var mr = ins.Operand as MethodReference; switch (mr?.Name) { case "EnsureUIThread": - ProcessEnsureUIThread (caller, ins); + modified |= ProcessEnsureUIThread (data, caller, ins); break; case "get_IsDirectBinding": - ProcessIsDirectBinding (caller, ins); + modified |= ProcessIsDirectBinding (data, caller, ins); break; case "SetupBlock": case "SetupBlockUnsafe": - return ProcessSetupBlock (caller, ins); + modified |= ProcessSetupBlock (data, caller, ins, out instructionsAddedOrRemoved); + break; case ".ctor": if (!mr.DeclaringType.Is (Namespaces.ObjCRuntime, "BlockLiteral")) break; - return ProcessBlockLiteralConstructor (caller, ins); + return ProcessBlockLiteralConstructor (data, caller, ins, out instructionsAddedOrRemoved); } - return 0; + return modified; } - protected virtual void ProcessLoadStaticField (MethodDefinition caller, Instruction ins) + static bool ProcessLoadStaticField (OptimizeGeneratedCodeData data, MethodDefinition caller, Instruction ins) { + var modified = false; var fr = ins.Operand as FieldReference; switch (fr?.Name) { case "IsARM64CallingConvention": - ProcessIsARM64CallingConvention (caller, ins); + modified |= ProcessIsARM64CallingConvention (data, caller, ins); break; case "Arch": // https://app.asana.com/0/77259014252/77812690163 - ProcessRuntimeArch (caller, ins); + modified |= ProcessRuntimeArch (data, caller, ins); break; } + return modified; } - void ProcessEnsureUIThread (MethodDefinition caller, Instruction ins) + static bool ProcessEnsureUIThread (OptimizeGeneratedCodeData data, MethodDefinition caller, Instruction ins) { - if (Optimizations.RemoveUIThreadChecks != true) - return; + if (data.Optimizations.RemoveUIThreadChecks != true) + return false; // Verify we're checking the right get_EnsureUIThread call - var declaringTypeNamespace = LinkContext.App.Platform == Utils.ApplePlatform.MacOSX ? Namespaces.AppKit : Namespaces.UIKit; - var declaringTypeName = LinkContext.App.Platform == Utils.ApplePlatform.MacOSX ? "NSApplication" : "UIApplication"; + var declaringTypeNamespace = data.LinkContext.App.Platform == Utils.ApplePlatform.MacOSX ? Namespaces.AppKit : Namespaces.UIKit; + var declaringTypeName = data.LinkContext.App.Platform == Utils.ApplePlatform.MacOSX ? "NSApplication" : "UIApplication"; var mr = ins.Operand as MethodReference; if (mr is null || !mr.DeclaringType.Is (declaringTypeNamespace, declaringTypeName)) - return; + return false; // Verify a few assumptions before doing anything const string operation = "remove calls to [NS|UI]Application::EnsureUIThread"; if (!ValidateInstruction (caller, ins, operation, Code.Call)) - return; + return false; // This is simple: just remove the call Nop (ins); // call void UIKit.UIApplication::EnsureUIThread() + return true; } - bool? IsDirectBindingConstant (TypeDefinition type) + static bool? IsDirectBindingConstant (OptimizeGeneratedCodeData data, TypeDefinition type) { - return type.IsNSObject (LinkContext) ? type.GetIsDirectBindingConstant (LinkContext) : null; + return type.IsNSObject (data.LinkContext) ? type.GetIsDirectBindingConstant (data.LinkContext) : null; } - void ProcessIsDirectBinding (MethodDefinition caller, Instruction ins) + static bool ProcessIsDirectBinding (OptimizeGeneratedCodeData data, MethodDefinition caller, Instruction ins) { const string operation = "inline IsDirectBinding"; - if (Optimizations.InlineIsDirectBinding != true) - return; + if (data.Optimizations.InlineIsDirectBinding != true) + return false; - bool? isdirectbinding_constant = IsDirectBindingConstant (caller.DeclaringType); + bool? isdirectbinding_constant = IsDirectBindingConstant (data, caller.DeclaringType); // If we don't know the constant isdirectbinding value, then we can't inline anything if (!isdirectbinding_constant.HasValue) - return; + return false; // Verify we're checking the right get_IsDirectBinding call var mr = ins.Operand as MethodReference; if (mr is null || !mr.DeclaringType.Is (Namespaces.Foundation, "NSObject")) - return; + return false; // Verify a few assumptions before doing anything if (!ValidateInstruction (caller, ins.Previous, operation, Code.Ldarg_0)) - return; + return false; if (!ValidateInstruction (caller, ins, operation, Code.Call)) - return; + return false; // Clearing the branch succeeded, so clear the condition too // ldarg.0 @@ -729,12 +691,14 @@ void ProcessIsDirectBinding (MethodDefinition caller, Instruction ins) // call System.Boolean Foundation.NSObject::get_IsDirectBinding() ins.OpCode = isdirectbinding_constant.Value ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0; ins.Operand = null; + return true; } - int ProcessSetupBlock (MethodDefinition caller, Instruction ins) + static bool ProcessSetupBlock (OptimizeGeneratedCodeData data, MethodDefinition caller, Instruction ins, out int instructionsAddedOrRemoved) { - if (Optimizations.OptimizeBlockLiteralSetupBlock != true) - return 0; + instructionsAddedOrRemoved = 0; + if (data.Optimizations.OptimizeBlockLiteralSetupBlock != true) + return false; // This will optimize calls to SetupBlock and SetupBlockUnsafe by calculating the signature for the block // (which both SetupBlock and SetupBlockUnsafe do), and then rewrite the code to call SetupBlockImpl instead @@ -744,14 +708,14 @@ int ProcessSetupBlock (MethodDefinition caller, Instruction ins) // This code is a mirror of the code in BlockLiteral.SetupBlock (to calculate the block signature). var mr = ins.Operand as MethodReference; if (mr is null || !mr.DeclaringType.Is (Namespaces.ObjCRuntime, "BlockLiteral")) - return 0; + return false; if (caller.DeclaringType.Is ("ObjCRuntime", "BlockLiteral")) { switch (caller.Name) { case "GetBlockForDelegate": case "CreateBlockForDelegate": // These methods contain a non-optimizable call to SetupBlock, and this way we don't show any warnings to users about things they can't do anything about. - return 0; + return false; } } @@ -777,11 +741,11 @@ int ProcessSetupBlock (MethodDefinition caller, Instruction ins) prev = prev.Previous; // Skip any nops. if (prev.OpCode.StackBehaviourPush != StackBehaviour.Push1) { //todo: localize mmp error 2106 - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 2106, caller, ins, Errors.MM2106, caller, ins.Offset, mr.Name, prev)); - return 0; + ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106, caller, ins.Offset, mr.Name, prev)); + return false; } else if (prev.OpCode.StackBehaviourPop != StackBehaviour.Pop0) { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 2106, caller, ins, Errors.MM2106, caller, ins.Offset, mr.Name, prev)); - return 0; + ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106, caller, ins.Offset, mr.Name, prev)); + return false; } var loadTrampolineInstruction = prev.Previous; @@ -791,23 +755,23 @@ int ProcessSetupBlock (MethodDefinition caller, Instruction ins) // Then find the type of the previous instruction (the first argument to SetupBlock[Unsafe]) var trampolineDelegateType = GetPushedType (caller, loadTrampolineInstruction); if (trampolineDelegateType is null) { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 2106, caller, ins, Errors.MM2106_A, caller, ins.Offset, mr.Name, loadTrampolineInstruction)); - return 0; + ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106_A, caller, ins.Offset, mr.Name, loadTrampolineInstruction)); + return false; } if (trampolineDelegateType.Is ("System", "Delegate") || trampolineDelegateType.Is ("System", "MulticastDelegate")) { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 2106, caller, ins, Errors.MM2106_B, caller, trampolineDelegateType.FullName, mr.Name)); - return 0; + ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106_B, caller, trampolineDelegateType.FullName, mr.Name)); + return false; } - if (!LinkContext.App.StaticRegistrar.TryComputeBlockSignature (caller, trampolineDelegateType, out var exception, out signature)) { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 2106, exception, caller, ins, Errors.MM2106_D, caller, ins.Offset, exception.Message)); - return 0; + if (!data.LinkContext.App.StaticRegistrar.TryComputeBlockSignature (caller, trampolineDelegateType, out var exception, out signature)) { + ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, exception, caller, ins, Errors.MM2106_D, caller, ins.Offset, exception.Message)); + return false; } } catch (Exception e) { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 2106, e, caller, ins, Errors.MM2106_D, caller, ins.Offset, e.Message)); - return 0; + ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, e, caller, ins, Errors.MM2106_D, caller, ins.Offset, e.Message)); + return false; } // We got the information we need: rewrite the IL. @@ -817,10 +781,11 @@ int ProcessSetupBlock (MethodDefinition caller, Instruction ins) instructions.Insert (index, Instruction.Create (OpCodes.Ldstr, signature)); instructions.Insert (index, Instruction.Create (mr.Name == "SetupBlockUnsafe" ? OpCodes.Ldc_I4_0 : OpCodes.Ldc_I4_1)); // Change the call to call SetupBlockImpl instead - ins.Operand = GetBlockSetupImpl (caller, ins); + ins.Operand = GetBlockSetupImpl (data, caller, ins); //Driver.Log (4, "Optimized call to BlockLiteral.SetupBlock in {0} at offset {1} with delegate type {2} and signature {3}", caller, ins.Offset, delegateType.FullName, signature); - return 2; + instructionsAddedOrRemoved = 2; + return true; } internal static bool IsBlockLiteralCtor_Type_String (MethodDefinition md) @@ -846,10 +811,12 @@ internal static bool IsBlockLiteralCtor_Type_String (MethodDefinition md) return true; } - int ProcessBlockLiteralConstructor (MethodDefinition caller, Instruction ins) + static bool ProcessBlockLiteralConstructor (OptimizeGeneratedCodeData data, MethodDefinition caller, Instruction ins, out int instructionsAddedOrRemoved) { - if (Optimizations.OptimizeBlockLiteralSetupBlock != true) - return 0; + instructionsAddedOrRemoved = 0; + + if (data.Optimizations.OptimizeBlockLiteralSetupBlock != true) + return false; // This will optimize calls to this BlockLiteral constructor: // (void* ptr, object context, Type trampolineType, string trampolineMethod) @@ -862,11 +829,11 @@ int ProcessBlockLiteralConstructor (MethodDefinition caller, Instruction ins) // This code is a mirror of the code in BlockLiteral.SetupBlock (to calculate the block signature). var mr = ins.Operand as MethodReference; if (mr is null || !mr.DeclaringType.Is (Namespaces.ObjCRuntime, "BlockLiteral")) - return 0; + return false; var md = mr.Resolve (); if (md is null || !IsBlockLiteralCtor_Type_String (md)) - return 0; + return false; string? signature = null; Instruction? sequenceStart; @@ -886,48 +853,51 @@ int ProcessBlockLiteralConstructor (MethodDefinition caller, Instruction ins) // Verify 'ldstr ...' var loadString = GetPreviousSkippingNops (ins); if (loadString.OpCode != OpCodes.Ldstr) { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 2106, caller, ins, Errors.MM2106 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the previous instruction was unexpected ({3}) */, caller, ins.Offset, mr.Name, loadString)); - return 0; + ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the previous instruction was unexpected ({3}) */, caller, ins.Offset, mr.Name, loadString)); + return false; } // Verify 'call System.Type System.Type::GetTypeFromHandle(System.RuntimeTypeHandle)' var callGetTypeFromHandle = GetPreviousSkippingNops (loadString); if (callGetTypeFromHandle.OpCode != OpCodes.Call || !(callGetTypeFromHandle.Operand is MethodReference methodOperand) || methodOperand.Name != "GetTypeFromHandle" || !methodOperand.DeclaringType.Is ("System", "Type")) { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 2106, caller, ins, Errors.MM2106 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the previous instruction was unexpected ({3}) */, caller, ins.Offset, mr.Name, callGetTypeFromHandle)); - return 0; + ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the previous instruction was unexpected ({3}) */, caller, ins.Offset, mr.Name, callGetTypeFromHandle)); + return false; } // Verify 'ldtoken ...' var loadType = GetPreviousSkippingNops (callGetTypeFromHandle); if (loadType.OpCode != OpCodes.Ldtoken) { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 2106, caller, ins, Errors.MM2106 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the previous instruction was unexpected ({3}) */, caller, ins.Offset, mr.Name, loadType)); - return 0; + ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the previous instruction was unexpected ({3}) */, caller, ins.Offset, mr.Name, loadType)); + return false; } // Then find the type of the previous instruction var trampolineContainerTypeReference = loadType.Operand as TypeReference; if (trampolineContainerTypeReference is null) { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 2106, caller, ins, Errors.MM2106 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the previous instruction was unexpected ({3}) */, caller, ins.Offset, mr.Name, loadType.Operand)); - return 0; + ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the previous instruction was unexpected ({3}) */, caller, ins.Offset, mr.Name, loadType.Operand)); + return false; } var trampolineContainerType = trampolineContainerTypeReference.Resolve (); if (trampolineContainerType is null) { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 2106, caller, ins, Errors.MM2106 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the previous instruction was unexpected ({3}) */, caller, ins.Offset, mr.Name, trampolineContainerTypeReference)); - return 0; + ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the previous instruction was unexpected ({3}) */, caller, ins.Offset, mr.Name, trampolineContainerTypeReference)); + return false; } // Find the trampoline method var trampolineMethodName = (string) loadString.Operand; var trampolineMethods = trampolineContainerType.Methods.Where (v => v.Name == trampolineMethodName).ToArray (); - if (trampolineMethods.Count () != 1) { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 2106, caller, ins, Errors.MX2106_E /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the more than one method named '{3}' was found in the type '{4}. /* Errors.MM2106 */, caller, ins.Offset, mr.Name, trampolineMethodName, trampolineContainerType.FullName)); - return 0; + if (!trampolineMethods.Any ()) { + ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MX2106_E1 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because no method named '{3}' was found in the type '{4}'. */, caller, ins.Offset, mr.Name, trampolineMethodName, trampolineContainerType.FullName)); + return false; + } else if (trampolineMethods.Count () > 1) { + ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MX2106_E2 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the more than one method named '{3}' was found in the type '{4}'. */, caller, ins.Offset, mr.Name, trampolineMethodName, trampolineContainerType.FullName)); + return false; } var trampolineMethod = trampolineMethods [0]; if (!trampolineMethod.HasParameters || trampolineMethod.Parameters.Count < 1) { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 2106, caller, ins, Errors.MX2106_F /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the method '{3}' must have at least one parameter. */, caller, ins.Offset, mr.Name, trampolineContainerType.FullName + "::" + trampolineMethodName)); - return 0; + ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MX2106_F /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the method '{3}' must have at least one parameter. */, caller, ins.Offset, mr.Name, trampolineContainerType.FullName + "::" + trampolineMethodName)); + return false; } // Check that the method's first parameter is either IntPtr, void* or BlockLiteral* @@ -937,45 +907,45 @@ int ProcessBlockLiteralConstructor (MethodDefinition caller, Instruction ins) } else if (firstParameterType is PointerType ptrType) { var ptrTargetType = ptrType.ElementType; if (!(ptrTargetType.Is ("System", "Void") || ptrTargetType.Is ("ObjCRuntime", "BlockLiteral"))) { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 2106, caller, ins, Errors.MX2106_G /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the first parameter in the method '{3}' isn't 'System.IntPtr', 'void*' or 'ObjCRuntime.BlockLiteral*' (it's '{4}') */, caller, ins.Offset, mr.Name, trampolineContainerType.FullName + "::" + trampolineMethodName, firstParameterType.FullName)); - return 0; + ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MX2106_G /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the first parameter in the method '{3}' isn't 'System.IntPtr', 'void*' or 'ObjCRuntime.BlockLiteral*' (it's '{4}') */, caller, ins.Offset, mr.Name, trampolineContainerType.FullName + "::" + trampolineMethodName, firstParameterType.FullName)); + return false; } // ok } else { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 2106, caller, ins, Errors.MX2106_G /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the first parameter in the method '{3}' isn't 'System.IntPtr', 'void*' or 'ObjCRuntime.BlockLiteral*' (it's '{4}') */, caller, ins.Offset, mr.Name, trampolineContainerType.FullName + "::" + trampolineMethodName, firstParameterType.FullName)); - return 0; + ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MX2106_G /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the first parameter in the method '{3}' isn't 'System.IntPtr', 'void*' or 'ObjCRuntime.BlockLiteral*' (it's '{4}') */, caller, ins.Offset, mr.Name, trampolineContainerType.FullName + "::" + trampolineMethodName, firstParameterType.FullName)); + return false; } // Check that the method has [UnmanagedCallersOnly] if (!trampolineMethod.HasCustomAttributes || !trampolineMethod.CustomAttributes.Any (v => v.AttributeType.Is ("System.Runtime.InteropServices", "UnmanagedCallersOnlyAttribute"))) { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 2106, caller, ins, Errors.MX2106_H /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the method '{3}' does not have an [UnmanagedCallersOnly] attribute. */, caller, ins.Offset, mr.Name, trampolineContainerType.FullName + "::" + trampolineMethodName)); - return 0; + ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MX2106_H /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the method '{3}' does not have an [UnmanagedCallersOnly] attribute. */, caller, ins.Offset, mr.Name, trampolineContainerType.FullName + "::" + trampolineMethodName)); + return false; } - var userDelegateType = LinkContext.App.StaticRegistrar.GetUserDelegateType (trampolineMethod); + var userDelegateType = data.LinkContext.App.StaticRegistrar.GetUserDelegateType (trampolineMethod); MethodReference? userMethod = null; var blockSignature = true; if (userDelegateType is not null) { - userMethod = LinkContext.App.StaticRegistrar.GetDelegateInvoke (userDelegateType); + userMethod = data.LinkContext.App.StaticRegistrar.GetDelegateInvoke (userDelegateType); } else { userMethod = trampolineMethod; } if (userMethod is null) { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 2106, caller, ins, Errors.MM2106_D, caller, ins.Offset, "Could not find delegate invoke method")); - return 0; + ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MM2106_D, caller, ins.Offset, "Could not find delegate invoke method")); + return false; } // Calculate the block signature. var parameters = new TypeReference [userMethod.Parameters.Count]; for (int p = 0; p < parameters.Length; p++) parameters [p] = userMethod.Parameters [p].ParameterType; - signature = LinkContext.App.StaticRegistrar.ComputeSignature (userMethod.DeclaringType, false, userMethod.ReturnType, parameters, userMethod.Resolve (), isBlockSignature: blockSignature); + signature = data.LinkContext.App.StaticRegistrar.ComputeSignature (userMethod.DeclaringType, false, userMethod.ReturnType, parameters, userMethod.Resolve (), isBlockSignature: blockSignature); sequenceStart = loadType; } catch (Exception e) { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 2106, e, caller, ins, Errors.MM2106_D, caller, ins.Offset, e.Message)); - return 0; + ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, e, caller, ins, Errors.MM2106_D, caller, ins.Offset, e.Message)); + return false; } // We got the information we need: rewrite the IL. @@ -990,10 +960,11 @@ int ProcessBlockLiteralConstructor (MethodDefinition caller, Instruction ins) instructions.Insert (index, Instruction.Create (OpCodes.Ldstr, signature)); instructionDiff++; // Change the call to call the ctor with the string signature parameter instead - ins.Operand = GetBlockLiteralConstructor (caller, ins); + ins.Operand = GetBlockLiteralConstructor (data, caller, ins); Driver.Log (4, "Optimized call to BlockLiteral..ctor in {0} at offset {1} with signature {2}", caller, ins.Offset, signature); - return instructionDiff; + instructionsAddedOrRemoved = instructionDiff; + return true; } static Instruction GetPreviousSkippingNops (Instruction ins) @@ -1017,56 +988,57 @@ static Instruction GetPreviousSkippingNops (Instruction ins) return ins; } - int ProcessIsARM64CallingConvention (MethodDefinition caller, Instruction ins) + static bool ProcessIsARM64CallingConvention (OptimizeGeneratedCodeData data, MethodDefinition caller, Instruction ins) { const string operation = "inline Runtime.IsARM64CallingConvention"; - if (Optimizations.InlineIsARM64CallingConvention != true) - return 0; + if (data.Optimizations.InlineIsARM64CallingConvention != true) + return false; - if (!is_arm64_calling_convention.HasValue) - return 0; + if (!data.InlineIsArm64CallingConvention.HasValue) + return false; // Verify we're checking the right IsARM64CallingConvention field var fr = ins.Operand as FieldReference; if (fr is null || !fr.DeclaringType.Is (Namespaces.ObjCRuntime, "Runtime")) - return 0; + return false; if (!ValidateInstruction (caller, ins, operation, Code.Ldsfld)) - return 0; + return false; // We're fine, inline the Runtime.IsARM64CallingConvention value - ins.OpCode = is_arm64_calling_convention.Value ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0; + ins.OpCode = data.InlineIsArm64CallingConvention.Value ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0; ins.Operand = null; - return 0; + return true; } - void ProcessRuntimeArch (MethodDefinition caller, Instruction ins) + static bool ProcessRuntimeArch (OptimizeGeneratedCodeData data, MethodDefinition caller, Instruction ins) { const string operation = "inline Runtime.Arch"; - if (Optimizations.InlineRuntimeArch != true) - return; + if (data.Optimizations.InlineRuntimeArch != true) + return false; // Verify we're checking the right Arch field var fr = ins.Operand as FieldReference; if (fr is null || !fr.DeclaringType.Is (Namespaces.ObjCRuntime, "Runtime")) - return; + return false; // Verify a few assumptions before doing anything if (!ValidateInstruction (caller, ins, operation, Code.Ldsfld)) - return; + return false; // We're fine, inline the Runtime.Arch condition // The enum values are Runtime.DEVICE = 0 and Runtime.SIMULATOR = 1, - ins.OpCode = Device ? OpCodes.Ldc_I4_0 : OpCodes.Ldc_I4_1; + ins.OpCode = data.Device ? OpCodes.Ldc_I4_0 : OpCodes.Ldc_I4_1; ins.Operand = null; + return true; } // Returns the type of the value pushed on the stack by the given instruction. // Returns null for unknown instructions, or for instructions that don't push anything on the stack. - TypeReference? GetPushedType (MethodDefinition method, Instruction ins) + static TypeReference? GetPushedType (MethodDefinition method, Instruction ins) { var index = 0; switch (ins.OpCode.Code) { @@ -1127,29 +1099,27 @@ void ProcessRuntimeArch (MethodDefinition caller, Instruction ins) } } - MethodDefinition? setupblock_def; - MethodReference GetBlockSetupImpl (MethodDefinition caller, Instruction ins) + static MethodReference GetBlockSetupImpl (OptimizeGeneratedCodeData data, MethodDefinition caller, Instruction ins) { - if (setupblock_def is null) { - var type = LinkContext.GetAssembly (Driver.GetProductAssembly (LinkContext.App)).MainModule.GetType (Namespaces.ObjCRuntime, "BlockLiteral"); + if (data.SetupBlockImplDefinition is null) { + var type = data.LinkContext.GetAssembly (Driver.GetProductAssembly (data.LinkContext.App)).MainModule.GetType (Namespaces.ObjCRuntime, "BlockLiteral"); foreach (var method in type.Methods) { if (method.Name != "SetupBlockImpl") continue; - setupblock_def = method; - setupblock_def.IsPublic = true; // Make sure the method is callable from the optimized code. + data.SetupBlockImplDefinition = method; + data.SetupBlockImplDefinition.IsPublic = true; // Make sure the method is callable from the optimized code. break; } - if (setupblock_def is null) - throw ErrorHelper.CreateError (LinkContext.App, 99, caller, ins, Errors.MX0099, $"could not find the method {Namespaces.ObjCRuntime}.BlockLiteral.SetupBlockImpl"); + if (data.SetupBlockImplDefinition is null) + throw ErrorHelper.CreateError (data.LinkContext.App, 99, caller, ins, Errors.MX0099, $"could not find the method {Namespaces.ObjCRuntime}.BlockLiteral.SetupBlockImpl"); } - return caller.Module.ImportReference (setupblock_def); + return caller.Module.ImportReference (data.SetupBlockImplDefinition); } - MethodDefinition? block_ctor_def; - MethodReference GetBlockLiteralConstructor (MethodDefinition caller, Instruction ins) + static MethodReference GetBlockLiteralConstructor (OptimizeGeneratedCodeData data, MethodDefinition caller, Instruction ins) { - if (block_ctor_def is null) { - var type = LinkContext.GetAssembly (Driver.GetProductAssembly (LinkContext.App)).MainModule.GetType (Namespaces.ObjCRuntime, "BlockLiteral"); + if (data.BlockCtorDefinition is null) { + var type = data.LinkContext.GetAssembly (Driver.GetProductAssembly (data.LinkContext.App)).MainModule.GetType (Namespaces.ObjCRuntime, "BlockLiteral"); foreach (var method in type.Methods) { if (!method.IsConstructor) continue; @@ -1163,25 +1133,25 @@ MethodReference GetBlockLiteralConstructor (MethodDefinition caller, Instruction continue; if (!method.Parameters [2].ParameterType.Is ("System", "String")) continue; - block_ctor_def = method; + data.BlockCtorDefinition = method; break; } - if (block_ctor_def is null) - throw ErrorHelper.CreateError (LinkContext.App, 99, caller, ins, Errors.MX0099, $"could not find the constructor ObjCRuntime.BlockLiteral (void*, object, string)"); + if (data.BlockCtorDefinition is null) + throw ErrorHelper.CreateError (data.LinkContext.App, 99, caller, ins, Errors.MX0099, $"could not find the constructor ObjCRuntime.BlockLiteral (void*, object, string)"); } - return caller.Module.ImportReference (block_ctor_def); + return caller.Module.ImportReference (data.BlockCtorDefinition); } - bool ProcessProtocolInterfaceStaticConstructor (MethodDefinition method) + static bool ProcessProtocolInterfaceStaticConstructor (OptimizeGeneratedCodeData data, MethodDefinition method) { // The static cctor in protocol interfaces exists only to preserve the protocol's members, for inspection by the registrar(s). // If we're registering protocols, then we don't need to preserve protocol members, because the registrar // already knows everything about it => we can remove the static cctor. - if (!(method.DeclaringType.IsInterface && method.DeclaringType.IsInterface && method.IsStatic && method.IsConstructor && method.HasBody)) + if (!(method.DeclaringType.IsInterface && method.IsStatic && method.IsConstructor && method.HasBody)) return false; - if (Optimizations.RegisterProtocols != true) { + if (data.Optimizations.RegisterProtocols != true) { Driver.Log (4, "Did not optimize static constructor in the protocol interface {0}: the 'register-protocols' optimization is disabled.", method.DeclaringType.FullName); return false; } @@ -1193,36 +1163,36 @@ bool ProcessProtocolInterfaceStaticConstructor (MethodDefinition method) var ins = SkipNops (method.Body.Instructions.First ()); if (ins is null) { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 2112, method, ins, Errors.MX2112_A /* Could not optimize the static constructor in the interface {0} because it did not have the expected instruction sequence (found end of method too soon). */, method.DeclaringType.FullName)); + ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2112, method, ins, Errors.MX2112_A /* Could not optimize the static constructor in the interface {0} because it did not have the expected instruction sequence (found end of method too soon). */, method.DeclaringType.FullName)); return false; } else if (ins.OpCode != OpCodes.Ldnull) { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 2112, method, ins, Errors.MX2112_B /* Could not optimize the static constructor in the interface {0} because it had an unexpected instruction {1} at offset {2}. */, method.DeclaringType.FullName, ins.OpCode, ins.Offset)); + ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2112, method, ins, Errors.MX2112_B /* Could not optimize the static constructor in the interface {0} because it had an unexpected instruction {1} at offset {2}. */, method.DeclaringType.FullName, ins.OpCode, ins.Offset)); return false; } ins = SkipNops (ins.Next); if (ins is null) { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 2112, method, ins, Errors.MX2112_A /* Could not optimize the static constructor in the interface {0} because it did not have the expected instruction sequence (found end of method too soon). */, method.DeclaringType.FullName)); + ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2112, method, ins, Errors.MX2112_A /* Could not optimize the static constructor in the interface {0} because it did not have the expected instruction sequence (found end of method too soon). */, method.DeclaringType.FullName)); return false; } var callGCKeepAlive = ins; if (callGCKeepAlive.OpCode != OpCodes.Call || !(callGCKeepAlive.Operand is MethodReference methodOperand) || methodOperand.Name != "KeepAlive" || !methodOperand.DeclaringType.Is ("System", "GC")) { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 2112, method, ins, Errors.MX2112_B /* Could not optimize the static constructor in the interface {0} because it had an unexpected instruction {1} at offset {2}. */, method.DeclaringType.FullName, ins.OpCode, ins.Offset)); + ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2112, method, ins, Errors.MX2112_B /* Could not optimize the static constructor in the interface {0} because it had an unexpected instruction {1} at offset {2}. */, method.DeclaringType.FullName, ins.OpCode, ins.Offset)); return false; } ins = SkipNops (ins.Next); if (ins is null) { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 2112, method, ins, Errors.MX2112_A /* Could not optimize the static constructor in the interface {0} because it did not have the expected instruction sequence (found end of method too soon). */, method.DeclaringType.FullName)); + ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2112, method, ins, Errors.MX2112_A /* Could not optimize the static constructor in the interface {0} because it did not have the expected instruction sequence (found end of method too soon). */, method.DeclaringType.FullName)); return false; } else if (ins.OpCode != OpCodes.Ret) { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 2112, method, ins, Errors.MX2112_B /* Could not optimize the static constructor in the interface {0} because it had an unexpected instruction {1} at offset {2}. */, method.DeclaringType.FullName, ins.OpCode, ins.Offset)); + ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2112, method, ins, Errors.MX2112_B /* Could not optimize the static constructor in the interface {0} because it had an unexpected instruction {1} at offset {2}. */, method.DeclaringType.FullName, ins.OpCode, ins.Offset)); return false; } ins = SkipNops (ins.Next); if (ins is not null) { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 2112, method, ins, Errors.MX2112_B /* Could not optimize the static constructor in the interface {0} because it had an unexpected instruction {1} at offset {2}. */, method.DeclaringType.FullName, ins.OpCode, ins.Offset)); + ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2112, method, ins, Errors.MX2112_B /* Could not optimize the static constructor in the interface {0} because it had an unexpected instruction {1} at offset {2}. */, method.DeclaringType.FullName, ins.OpCode, ins.Offset)); return false; } @@ -1254,4 +1224,15 @@ bool ProcessProtocolInterfaceStaticConstructor (MethodDefinition method) return true; } } + + public class OptimizeGeneratedCodeData { + public required Xamarin.Tuner.DerivedLinkContext LinkContext; + public required Optimizations Optimizations; + public required bool Device; + + public MethodDefinition? SetupBlockImplDefinition; + public MethodDefinition? BlockCtorDefinition; + public bool? InlineIsArm64CallingConvention; + } + } diff --git a/tools/mtouch/Errors.designer.cs b/tools/mtouch/Errors.designer.cs index 588591370685..be3e6990ed1a 100644 --- a/tools/mtouch/Errors.designer.cs +++ b/tools/mtouch/Errors.designer.cs @@ -3443,12 +3443,21 @@ public static string MX2103 { } } + /// + /// Looks up a localized string similar to Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because no method named '{3}' was found in the type '{4}'.. + /// + public static string MX2106_E1 { + get { + return ResourceManager.GetString("MX2106_E1", resourceCulture); + } + } + /// /// Looks up a localized string similar to Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the more than one method named '{3}' was found in the type '{4}.. /// - public static string MX2106_E { + public static string MX2106_E2 { get { - return ResourceManager.GetString("MX2106_E", resourceCulture); + return ResourceManager.GetString("MX2106_E2", resourceCulture); } } diff --git a/tools/mtouch/Errors.resx b/tools/mtouch/Errors.resx index 9cadfefe00d1..c5d9ad798b7d 100644 --- a/tools/mtouch/Errors.resx +++ b/tools/mtouch/Errors.resx @@ -1042,7 +1042,11 @@ Could not optimize the call to BlockLiteral.SetupBlock in {0} at offset {1}: {2}. - + + Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because no method named '{3}' was found in the type '{4}'. + + + Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the more than one method named '{3}' was found in the type '{4}. From 56a5d3cc1385cc334d5667e6a073190e2e512fdb Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 31 Mar 2026 09:57:52 +0200 Subject: [PATCH 2/6] Grammar --- tools/linker/CoreOptimizeGeneratedCode.cs | 2 +- tools/mtouch/Errors.resx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/linker/CoreOptimizeGeneratedCode.cs b/tools/linker/CoreOptimizeGeneratedCode.cs index b1ec75694e61..d0dd2b23076e 100644 --- a/tools/linker/CoreOptimizeGeneratedCode.cs +++ b/tools/linker/CoreOptimizeGeneratedCode.cs @@ -891,7 +891,7 @@ static bool ProcessBlockLiteralConstructor (OptimizeGeneratedCodeData data, Meth ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MX2106_E1 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because no method named '{3}' was found in the type '{4}'. */, caller, ins.Offset, mr.Name, trampolineMethodName, trampolineContainerType.FullName)); return false; } else if (trampolineMethods.Count () > 1) { - ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MX2106_E2 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the more than one method named '{3}' was found in the type '{4}'. */, caller, ins.Offset, mr.Name, trampolineMethodName, trampolineContainerType.FullName)); + ErrorHelper.Show (ErrorHelper.CreateWarning (data.LinkContext.App, 2106, caller, ins, Errors.MX2106_E2 /* Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because more than one method named '{3}' was found in the type '{4}'. */, caller, ins.Offset, mr.Name, trampolineMethodName, trampolineContainerType.FullName)); return false; } var trampolineMethod = trampolineMethods [0]; diff --git a/tools/mtouch/Errors.resx b/tools/mtouch/Errors.resx index c5d9ad798b7d..5c5db81701a4 100644 --- a/tools/mtouch/Errors.resx +++ b/tools/mtouch/Errors.resx @@ -1047,7 +1047,7 @@ - Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the more than one method named '{3}' was found in the type '{4}. + Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because more than one method named '{3}' was found in the type '{4}'. From 442da75b4befb4dd31fd3e7afa582c295e870118 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 31 Mar 2026 09:58:46 +0200 Subject: [PATCH 3/6] comments --- tools/linker/CoreOptimizeGeneratedCode.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/linker/CoreOptimizeGeneratedCode.cs b/tools/linker/CoreOptimizeGeneratedCode.cs index d0dd2b23076e..85cf938052a0 100644 --- a/tools/linker/CoreOptimizeGeneratedCode.cs +++ b/tools/linker/CoreOptimizeGeneratedCode.cs @@ -591,7 +591,8 @@ public static bool OptimizeMethod (OptimizeGeneratedCodeData data, MethodDefinit return modified; } - // Returns the number of instructions added (or removed). + // Returns the number of instructions added (or removed) in the 'instructionsAddedOrRemoved' parameter. + // Returns true if any modifications were done. static bool ProcessCalls (OptimizeGeneratedCodeData data, MethodDefinition caller, Instruction ins, out int instructionsAddedOrRemoved) { var modified = false; From 4c459e1430b66a2ef7df0ffc3f62718f5675b0a0 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 31 Mar 2026 10:06:20 +0200 Subject: [PATCH 4/6] regen --- tools/mtouch/Errors.designer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/mtouch/Errors.designer.cs b/tools/mtouch/Errors.designer.cs index be3e6990ed1a..d1f4533d4832 100644 --- a/tools/mtouch/Errors.designer.cs +++ b/tools/mtouch/Errors.designer.cs @@ -3453,7 +3453,7 @@ public static string MX2106_E1 { } /// - /// Looks up a localized string similar to Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because the more than one method named '{3}' was found in the type '{4}.. + /// Looks up a localized string similar to Could not optimize the call to BlockLiteral.{2} in {0} at offset {1} because more than one method named '{3}' was found in the type '{4}'.. /// public static string MX2106_E2 { get { From 6277428f54c2450cb6de6fde569748cf3e844a7f Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 31 Mar 2026 16:42:38 +0200 Subject: [PATCH 5/6] [tests] Update expected sizes. --- .../UnitTests/expected/iOS-MonoVM-interpreter-preservedapis.txt | 1 - tests/dotnet/UnitTests/expected/iOS-MonoVM-preservedapis.txt | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/dotnet/UnitTests/expected/iOS-MonoVM-interpreter-preservedapis.txt b/tests/dotnet/UnitTests/expected/iOS-MonoVM-interpreter-preservedapis.txt index d45c66734650..abb7355d0527 100644 --- a/tests/dotnet/UnitTests/expected/iOS-MonoVM-interpreter-preservedapis.txt +++ b/tests/dotnet/UnitTests/expected/iOS-MonoVM-interpreter-preservedapis.txt @@ -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) diff --git a/tests/dotnet/UnitTests/expected/iOS-MonoVM-preservedapis.txt b/tests/dotnet/UnitTests/expected/iOS-MonoVM-preservedapis.txt index 57299935c70e..48c119c261ff 100644 --- a/tests/dotnet/UnitTests/expected/iOS-MonoVM-preservedapis.txt +++ b/tests/dotnet/UnitTests/expected/iOS-MonoVM-preservedapis.txt @@ -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) From fd74549824ffee663fcbe99f4dbe8b3e113a9b52 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 1 Apr 2026 09:40:42 +0200 Subject: [PATCH 6/6] Remove old tests. --- .../link sdk/OptimizeGeneratedCodeTest.cs | 134 ------------------ 1 file changed, 134 deletions(-) diff --git a/tests/linker/link sdk/OptimizeGeneratedCodeTest.cs b/tests/linker/link sdk/OptimizeGeneratedCodeTest.cs index 719c37c36724..58623aec8774 100644 --- a/tests/linker/link sdk/OptimizeGeneratedCodeTest.cs +++ b/tests/linker/link sdk/OptimizeGeneratedCodeTest.cs @@ -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 (Size8Test, "Size8Test"); - Assert.Throws (Size8Test_Optimizable, "Size8Test_Optimizable"); - passingMethods = S4methods; - failingMethods = S8methods; - break; - case 8: - Size8Test (); - Size8Test_Optimizable (); - Assert.Throws (Size4Test, "Size4Test"); - Assert.Throws (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 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 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"); - } } }