diff --git a/src/coreclr/tools/Common/InstructionSetHelpers.cs b/src/coreclr/tools/Common/InstructionSetHelpers.cs index 5c77e6ef7f7302..7dcf031a9e0131 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 @@ -87,11 +92,6 @@ 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; - bool throttleAvx512 = false; if (instructionSet == "native") 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..00219f0425e28f --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunHardwareIntrinsicRootProvider.cs @@ -0,0 +1,51 @@ +// 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; +using Internal.JitInterface; +using System.Diagnostics; + +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"); + } + } + else + { + MethodDesc isSupportedMethod = type.GetMethod("get_IsSupported"u8, new MethodSignature(MethodSignatureFlags.Static, 0, context.GetWellKnownType(WellKnownType.Boolean), [])); + if (isSupportedMethod is not null) + { + rootProvider.AddCompilationRoot(isSupportedMethod, rootMinimalDependencies: false, "IsSupported getter for unsupported hardware intrinsic"); + } + } + } + } + } +} 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/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..1b5b724e0e4eaf 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()); @@ -562,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;