From ba1cf762afba16da2e8b960e852d7dcf4df7f7f9 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 5 Dec 2025 13:04:46 -0800 Subject: [PATCH 1/6] Correctly configure baselines and disable optimistic instruction sets by default for Apple Mobile --- src/coreclr/tools/Common/InstructionSetHelpers.cs | 13 ++++++++----- src/coreclr/tools/aot/ILCompiler/Program.cs | 2 +- src/coreclr/tools/aot/crossgen2/Program.cs | 12 +++++++++++- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/coreclr/tools/Common/InstructionSetHelpers.cs b/src/coreclr/tools/Common/InstructionSetHelpers.cs index 5c77e6ef7f7302..269d812f321aa5 100644 --- a/src/coreclr/tools/Common/InstructionSetHelpers.cs +++ b/src/coreclr/tools/Common/InstructionSetHelpers.cs @@ -17,7 +17,7 @@ namespace System.CommandLine internal static partial class Helpers { public static InstructionSetSupport ConfigureInstructionSetSupport(string instructionSet, int maxVectorTBitWidth, bool isVectorTOptimistic, TargetArchitecture targetArchitecture, TargetOS targetOS, - string mustNotBeMessage, string invalidImplicationMessage, Logger logger, bool optimizingForSize, bool isReadyToRun) + string mustNotBeMessage, string invalidImplicationMessage, Logger logger, bool allowOptimistic, bool isReadyToRun) { InstructionSetSupportBuilder instructionSetSupportBuilder = new(targetArchitecture); @@ -31,7 +31,7 @@ public static InstructionSetSupport ConfigureInstructionSetSupport(string instru if ((targetArchitecture == TargetArchitecture.X86) || (targetArchitecture == TargetArchitecture.X64)) { - if (isReadyToRun && (targetOS != TargetOS.OSX)) + if (isReadyToRun && ((targetOS == TargetOS.OSX) || (targetOS == TargetOS.MacCatalyst))) { // ReadyToRun can presume AVX2, BMI1, BMI2, F16C, FMA, LZCNT, and MOVBE instructionSetSupportBuilder.AddSupportedInstructionSet("x86-64-v3"); @@ -67,6 +67,11 @@ public static InstructionSetSupport ConfigureInstructionSetSupport(string instru instructionSetSupportBuilder.AddSupportedInstructionSet("armv8.2-a"); instructionSetSupportBuilder.AddSupportedInstructionSet("rcpc"); } + else if (targetOS is TargetOS.iOS or TargetOS.iOSSimulator or TargetOS.tvOS or TargetOS.tvOSSimulator) + { + // ReadyToRun on iOS/tvOS can only presume armv8.0-a + instructionSetSupportBuilder.AddSupportedInstructionSet("armv8-a"); + } else { // While Unix needs a lower baseline due to things like Raspberry PI @@ -88,9 +93,7 @@ public static InstructionSetSupport ConfigureInstructionSetSupport(string instru } // Whether to allow optimistically expanding the instruction sets beyond what was specified. - // We seed this from optimizingForSize - if we're size-optimizing, we don't want to unnecessarily - // compile both branches of IsSupported checks. - bool allowOptimistic = !optimizingForSize; + // This value is provided by the caller. bool throttleAvx512 = false; diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index 2054c723bc8a07..fb7910a086712a 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -108,7 +108,7 @@ public int Run() TargetOS targetOS = Get(_command.TargetOS); InstructionSetSupport instructionSetSupport = Helpers.ConfigureInstructionSetSupport(Get(_command.InstructionSet), Get(_command.MaxVectorTBitWidth), isVectorTOptimistic, targetArchitecture, targetOS, "Unrecognized instruction set {0}", "Unsupported combination of instruction sets: {0}/{1}", logger, - optimizingForSize: _command.OptimizationMode == OptimizationMode.PreferSize, + allowOptimistic: _command.OptimizationMode != OptimizationMode.PreferSize, isReadyToRun: false); string systemModuleName = Get(_command.SystemModuleName); diff --git a/src/coreclr/tools/aot/crossgen2/Program.cs b/src/coreclr/tools/aot/crossgen2/Program.cs index dfc8dd737d358c..7f588eaecaa55a 100644 --- a/src/coreclr/tools/aot/crossgen2/Program.cs +++ b/src/coreclr/tools/aot/crossgen2/Program.cs @@ -85,9 +85,19 @@ public int Run() TargetArchitecture targetArchitecture = Get(_command.TargetArchitecture); TargetOS targetOS = Get(_command.TargetOS); + bool allowOptimistic = _command.OptimizationMode != OptimizationMode.PreferSize; + + if (targetOS is TargetOS.iOS or TargetOS.tvOS or TargetOS.iOSSimulator or TargetOS.tvOSSimulator or TargetOS.MacCatalyst) + { + // These platforms do not support jitted code, so we want to ensure that we don't + // need to fall back to the interpreter for any hardware-intrinsic optimizations. + // Disable optimistic instruction sets by default. + allowOptimistic = false; + } + InstructionSetSupport instructionSetSupport = Helpers.ConfigureInstructionSetSupport(Get(_command.InstructionSet), Get(_command.MaxVectorTBitWidth), isVectorTOptimistic, targetArchitecture, targetOS, SR.InstructionSetMustNotBe, SR.InstructionSetInvalidImplication, logger, - optimizingForSize: _command.OptimizationMode == OptimizationMode.PreferSize, + allowOptimistic: allowOptimistic, isReadyToRun: true); SharedGenericsMode genericsMode = SharedGenericsMode.CanonicalReferenceTypes; var targetDetails = new TargetDetails(targetArchitecture, targetOS, Crossgen2RootCommand.IsArmel ? TargetAbi.NativeAotArmel : TargetAbi.NativeAot, instructionSetSupport.GetVectorTSimdVector()); From ade9cc9d47f346681d1e940896ea6dbf5eaeda7c Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 5 Dec 2025 14:41:36 -0800 Subject: [PATCH 2/6] Add automatic rooting in CoreLib for all supported HWIntrinsic methods in case someone decides to use one that we don't generate R2R code for. --- .../Compiler/ReadyToRunCompilerContext.cs | 23 +++++++++- ...ReadyToRunHardwareIntrinsicRootProvider.cs | 44 +++++++++++++++++++ .../ILCompiler.ReadyToRun.csproj | 1 + src/coreclr/tools/aot/crossgen2/Program.cs | 10 +++++ 4 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunHardwareIntrinsicRootProvider.cs diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs index 1189d599d2c9e5..4088fe402e1db9 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs @@ -57,11 +57,12 @@ public partial class ReadyToRunCompilerContext : CompilerTypeSystemContext public ReadyToRunCompilerContext( TargetDetails details, SharedGenericsMode genericsMode, - bool bubbleIncludesCorelib, + bool bubbleIncludesCoreModule, InstructionSetSupport instructionSetSupport, CompilerTypeSystemContext oldTypeSystemContext) : base(details, genericsMode) { + BubbleIncludesCoreModule = bubbleIncludesCoreModule; InstructionSetSupport = instructionSetSupport; _r2rFieldLayoutAlgorithm = new ReadyToRunMetadataFieldLayoutAlgorithm(); _systemObjectFieldLayoutAlgorithm = new SystemObjectFieldLayoutAlgorithm(_r2rFieldLayoutAlgorithm); @@ -94,8 +95,28 @@ public ReadyToRunCompilerContext( } } + public bool BubbleIncludesCoreModule { get; } + public InstructionSetSupport InstructionSetSupport { get; } + public bool TargetAllowsRuntimeCodeGeneration + { + get + { + if (Target.OperatingSystem is TargetOS.iOS or TargetOS.iOSSimulator or TargetOS.MacCatalyst or TargetOS.tvOS or TargetOS.tvOSSimulator) + { + return false; + } + + if (Target.Architecture is TargetArchitecture.Wasm32) + { + return false; + } + + return true; + } + } + public override FieldLayoutAlgorithm GetLayoutAlgorithmForType(DefType type) { if (type.IsObject) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunHardwareIntrinsicRootProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunHardwareIntrinsicRootProvider.cs new file mode 100644 index 00000000000000..1043b503bb0ba9 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunHardwareIntrinsicRootProvider.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +using Internal.TypeSystem.Ecma; +using Internal.TypeSystem; +using Internal.JitInterface; +using System.Reflection.Metadata; + +namespace ILCompiler +{ + /// + /// Root all methods on supported hardware intrinsic classes. + /// + public class ReadyToRunHardwareIntrinsicRootProvider(ReadyToRunCompilerContext context) : ICompilationRootProvider + { + public void AddCompilationRoots(IRootingServiceProvider rootProvider) + { + InstructionSetSupport specifiedInstructionSet = context.InstructionSetSupport; + TargetArchitecture targetArch = context.Target.Architecture; + + // Hardware intrinsics can only live in the system module. + foreach (MetadataType type in context.SystemModule.GetAllTypes()) + { + InstructionSet instructionSet = InstructionSetParser.LookupPlatformIntrinsicInstructionSet(targetArch, type); + + if (instructionSet == InstructionSet.ILLEGAL) + { + // Not a HardwareIntrinsics type for our platform. + continue; + } + + if (specifiedInstructionSet.IsInstructionSetSupported(instructionSet)) + { + foreach (MethodDesc method in type.GetMethods()) + { + rootProvider.AddCompilationRoot(method, rootMinimalDependencies: false, "Hardware intrinsic method fallback implementation"); + } + } + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index f2f89b4735bf3f..c4e7f070c4cc6c 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -189,6 +189,7 @@ + diff --git a/src/coreclr/tools/aot/crossgen2/Program.cs b/src/coreclr/tools/aot/crossgen2/Program.cs index 7f588eaecaa55a..1b5b724e0e4eaf 100644 --- a/src/coreclr/tools/aot/crossgen2/Program.cs +++ b/src/coreclr/tools/aot/crossgen2/Program.cs @@ -572,6 +572,16 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru } } } + + if (!typeSystemContext.TargetAllowsRuntimeCodeGeneration && typeSystemContext.BubbleIncludesCoreModule) + { + // For some platforms, we cannot JIT. + // As a result, we need to ensure that we have a fallback implementation for all hardware intrinsics + // that are marked as supported. + // Otherwise, the interpreter won't have an implementation it can call for any non-ReadyToRun code. + compilationRoots.Add(new ReadyToRunHardwareIntrinsicRootProvider(typeSystemContext)); + } + // In single-file compilation mode, use the assembly's DebuggableAttribute to determine whether to optimize // or produce debuggable code if an explicit optimization level was not specified on the command line OptimizationMode optimizationMode = _command.OptimizationMode; From 5a31bd489824a324a2e6d682d4e28aab14867858 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 5 Dec 2025 14:44:52 -0800 Subject: [PATCH 3/6] Remove comment left by copilot --- src/coreclr/tools/Common/InstructionSetHelpers.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/coreclr/tools/Common/InstructionSetHelpers.cs b/src/coreclr/tools/Common/InstructionSetHelpers.cs index 269d812f321aa5..39bbdde999c0a1 100644 --- a/src/coreclr/tools/Common/InstructionSetHelpers.cs +++ b/src/coreclr/tools/Common/InstructionSetHelpers.cs @@ -92,9 +92,6 @@ public static InstructionSetSupport ConfigureInstructionSetSupport(string instru } } - // Whether to allow optimistically expanding the instruction sets beyond what was specified. - // This value is provided by the caller. - bool throttleAvx512 = false; if (instructionSet == "native") From 886b7d137af2545a1febee69755fcd3b345eea65 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 5 Dec 2025 14:45:31 -0800 Subject: [PATCH 4/6] Remove extra namespaces --- .../Compiler/ReadyToRunHardwareIntrinsicRootProvider.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunHardwareIntrinsicRootProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunHardwareIntrinsicRootProvider.cs index 1043b503bb0ba9..ccb00da54b22dc 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunHardwareIntrinsicRootProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunHardwareIntrinsicRootProvider.cs @@ -3,10 +3,8 @@ using System.Collections.Generic; -using Internal.TypeSystem.Ecma; using Internal.TypeSystem; using Internal.JitInterface; -using System.Reflection.Metadata; namespace ILCompiler { From c08de0f5960bf79db7a62cc9d47d83d0b9bb2d7d Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 5 Dec 2025 14:59:03 -0800 Subject: [PATCH 5/6] Root the IsSupported method for all unsupported hardware intrinsics to ensure we don't need to opportunistically jit all fallback implementations for every ISA. --- .../Compiler/ReadyToRunHardwareIntrinsicRootProvider.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunHardwareIntrinsicRootProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunHardwareIntrinsicRootProvider.cs index ccb00da54b22dc..93a114f7c5edb1 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunHardwareIntrinsicRootProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunHardwareIntrinsicRootProvider.cs @@ -5,6 +5,7 @@ using Internal.TypeSystem; using Internal.JitInterface; +using System.Diagnostics; namespace ILCompiler { @@ -36,6 +37,11 @@ public void AddCompilationRoots(IRootingServiceProvider rootProvider) rootProvider.AddCompilationRoot(method, rootMinimalDependencies: false, "Hardware intrinsic method fallback implementation"); } } + else + { + MethodDesc isSupportedMethod = type.GetMethod("get_IsSupported"u8, new MethodSignature(MethodSignatureFlags.Static, 0, context.GetWellKnownType(WellKnownType.Boolean), [])); + rootProvider.AddCompilationRoot(isSupportedMethod, rootMinimalDependencies: false, "IsSupported getter for unsupported hardware intrinsic"); + } } } } From 702226d20755bf0c0d46be51e8b101c86e17b462 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 5 Dec 2025 15:25:10 -0800 Subject: [PATCH 6/6] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/coreclr/tools/Common/InstructionSetHelpers.cs | 2 +- .../Compiler/ReadyToRunHardwareIntrinsicRootProvider.cs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/Common/InstructionSetHelpers.cs b/src/coreclr/tools/Common/InstructionSetHelpers.cs index 39bbdde999c0a1..7dcf031a9e0131 100644 --- a/src/coreclr/tools/Common/InstructionSetHelpers.cs +++ b/src/coreclr/tools/Common/InstructionSetHelpers.cs @@ -31,7 +31,7 @@ public static InstructionSetSupport ConfigureInstructionSetSupport(string instru if ((targetArchitecture == TargetArchitecture.X86) || (targetArchitecture == TargetArchitecture.X64)) { - if (isReadyToRun && ((targetOS == TargetOS.OSX) || (targetOS == TargetOS.MacCatalyst))) + if (isReadyToRun && targetOS != TargetOS.OSX && targetOS != TargetOS.MacCatalyst) { // ReadyToRun can presume AVX2, BMI1, BMI2, F16C, FMA, LZCNT, and MOVBE instructionSetSupportBuilder.AddSupportedInstructionSet("x86-64-v3"); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunHardwareIntrinsicRootProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunHardwareIntrinsicRootProvider.cs index 93a114f7c5edb1..00219f0425e28f 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunHardwareIntrinsicRootProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunHardwareIntrinsicRootProvider.cs @@ -40,7 +40,10 @@ public void AddCompilationRoots(IRootingServiceProvider rootProvider) else { MethodDesc isSupportedMethod = type.GetMethod("get_IsSupported"u8, new MethodSignature(MethodSignatureFlags.Static, 0, context.GetWellKnownType(WellKnownType.Boolean), [])); - rootProvider.AddCompilationRoot(isSupportedMethod, rootMinimalDependencies: false, "IsSupported getter for unsupported hardware intrinsic"); + if (isSupportedMethod is not null) + { + rootProvider.AddCompilationRoot(isSupportedMethod, rootMinimalDependencies: false, "IsSupported getter for unsupported hardware intrinsic"); + } } } }