From 34eb5a33bc203ccd51cb6d25b723560400ed85d4 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Tue, 10 Mar 2026 13:17:23 +0200 Subject: [PATCH 1/2] [r2r] Fix rule for skipping compilation of methods with CompExactlyDependsOn If a method has `CompExactlyDependsOn` attribute, it means the correctness of its code depends on matching support for the instruction set between r2r compilation time and run time jit compilation. For the example of ShuffleNativeModified: ``` [CompExactlyDependsOn(typeof(Ssse3))] internal static Vector128 ShuffleNativeModified(Vector128 vector, Vector128 indices) { if (Ssse3.IsSupported) return Ssse3.Shuffle(vector, indices); return Vector128.Shuffle(vector, indices); } ``` Ssse3 is an intel specific instruction set that is never used on arm64. The check in code was returning an ILLEGAL instruction set. This case was treated as `continue` instead of setting `doBypass` to false. Because of this, this method was skipped from r2r compilation. The fix avoids interpreting this method on ios-arm64, making JSON serialization/deserialization 2x faster. --- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 26 ++++++------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index faf5ab5bd7b747..78ff18a3d33256 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -611,30 +611,20 @@ public static bool ShouldCodeNotBeCompiledIntoFinalImage(InstructionSetSupport i if (compExactlyDependsOnList != null && compExactlyDependsOnList.Count > 0) { - // Default to true, and set to false if at least one of the types is actually supported in the current environment, and none of the - // intrinsic types are in an opportunistic state. - bool doBypass = true; - foreach (var intrinsicType in compExactlyDependsOnList) { InstructionSet instructionSet = InstructionSetParser.LookupPlatformIntrinsicInstructionSet(intrinsicType.Context.Target.Architecture, intrinsicType); - if (instructionSet == InstructionSet.ILLEGAL) - { - // This instruction set isn't supported on the current platform at all. - continue; - } - if (instructionSetSupport.IsInstructionSetSupported(instructionSet) || instructionSetSupport.IsInstructionSetExplicitlyUnsupported(instructionSet)) + // If the instruction set is ILLEGAL, it means it is never supported by the current architecture so the behavior at runtime is known + if (instructionSet != InstructionSet.ILLEGAL) { - doBypass = false; - } - else - { - // If we reach here this is an instruction set generally supported on this platform, but we don't know what the behavior will be at runtime - return true; + if (!instructionSetSupport.IsInstructionSetSupported(instructionSet) && + !instructionSetSupport.IsInstructionSetExplicitlyUnsupported(instructionSet)) + { + // If we reach here this is an instruction set generally supported on this platform, but we don't know what the behavior will be at runtime + return true; + } } } - - return doBypass; } // No reason to bypass compilation and code generation. From a90d822da4cf0263dc7e9708dd5e88faabb12acd Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Fri, 13 Mar 2026 14:08:30 +0200 Subject: [PATCH 2/2] [r2r] Don't compile methods if the target doesn't support any of their CompExactlyDependsOn instruction sets Unless they are decorated with the CompHasFallback attribute. --- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 21 +++++++++++++++++-- .../System.Private.CoreLib.Shared.projitems | 1 + .../CompHasFallbackAttribute.cs | 18 ++++++++++++++++ .../src/System/SearchValues/SearchValues.cs | 1 + .../src/System/Text/Ascii.Utility.cs | 1 + 5 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CompHasFallbackAttribute.cs diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 78ff18a3d33256..a7c65283f91893 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -573,6 +573,7 @@ public static bool ShouldCodeNotBeCompiledIntoFinalImage(InstructionSetSupport i var handle = ecmaMethod.Handle; List compExactlyDependsOnList = null; + bool hasCompHasFallback = false; foreach (var attributeHandle in metadataReader.GetMethodDefinition(handle).GetCustomAttributes()) { @@ -606,25 +607,41 @@ public static bool ShouldCodeNotBeCompiledIntoFinalImage(InstructionSetSupport i compExactlyDependsOnList.Add(typeForBypass); } } + else if (metadataReader.StringComparer.Equals(nameHandle, "CompHasFallbackAttribute")) + { + hasCompHasFallback = true; + } } } if (compExactlyDependsOnList != null && compExactlyDependsOnList.Count > 0) { + bool anySupported = false; + foreach (var intrinsicType in compExactlyDependsOnList) { InstructionSet instructionSet = InstructionSetParser.LookupPlatformIntrinsicInstructionSet(intrinsicType.Context.Target.Architecture, intrinsicType); // If the instruction set is ILLEGAL, it means it is never supported by the current architecture so the behavior at runtime is known if (instructionSet != InstructionSet.ILLEGAL) { - if (!instructionSetSupport.IsInstructionSetSupported(instructionSet) && - !instructionSetSupport.IsInstructionSetExplicitlyUnsupported(instructionSet)) + if (instructionSetSupport.IsInstructionSetSupported(instructionSet)) + { + anySupported = true; + } + else if (!instructionSetSupport.IsInstructionSetExplicitlyUnsupported(instructionSet)) { // If we reach here this is an instruction set generally supported on this platform, but we don't know what the behavior will be at runtime return true; } } } + + if (!anySupported && !hasCompHasFallback) + { + // If none of the instruction sets are supported (all are either illegal or explicitly unsupported), + // skip compilation unless the method has a functional fallback path + return true; + } } // No reason to bypass compilation and code generation. diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index c744f85d3e05ce..6308788f0d0789 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -837,6 +837,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CompHasFallbackAttribute.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CompHasFallbackAttribute.cs new file mode 100644 index 00000000000000..9ce5344c277d86 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CompHasFallbackAttribute.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +namespace System.Runtime.CompilerServices +{ + // Use this attribute alongside CompExactlyDependsOnAttribute to indicate that a method has + // a functional fallback path and should still be compiled into the Ready2Run image even when + // none of the CompExactlyDependsOn instruction sets are supported. + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)] +#if MONO + [Conditional("unnecessary")] // Mono doesn't use Ready2Run so we can remove this attribute to reduce size +#endif + internal sealed class CompHasFallbackAttribute : Attribute + { + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.cs index 73de2d970c626d..ecbbea5f8744fc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.cs @@ -301,6 +301,7 @@ internal interface IRuntimeConst /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [CompExactlyDependsOn(typeof(Ssse3))] + [CompHasFallback] internal static Vector128 ShuffleNativeModified(Vector128 vector, Vector128 indices) { if (Ssse3.IsSupported) diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.Utility.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.Utility.cs index cdec5bb675b312..6c84d46a1ab6e0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.Utility.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.Utility.cs @@ -1632,6 +1632,7 @@ private static bool AllCharsInVectorAreAscii(Vector128 vector) [MethodImpl(MethodImplOptions.AggressiveInlining)] [CompExactlyDependsOn(typeof(Avx))] + [CompHasFallback] private static bool AllCharsInVectorAreAscii(Vector256 vector) where T : unmanaged {