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;