diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/HardwareIntrinsicILProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/HardwareIntrinsicILProvider.cs new file mode 100644 index 00000000000000..5e3f9e3f8c0a39 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/HardwareIntrinsicILProvider.cs @@ -0,0 +1,69 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using Internal.IL; +using Internal.IL.Stubs; +using Internal.JitInterface; +using Internal.TypeSystem; + +namespace ILCompiler +{ + public sealed class HardwareIntrinsicILProvider : ILProvider + { + private readonly InstructionSetSupport _isaSupport; + private readonly TypeSystemContext _context; + private readonly FieldDesc _isSupportedField; + private readonly ILProvider _nestedProvider; + private readonly Dictionary _instructionSetMap; + + public HardwareIntrinsicILProvider(InstructionSetSupport isaSupport, FieldDesc isSupportedField, ILProvider nestedProvider) + { + _isaSupport = isaSupport; + _context = isSupportedField.Context; + _isSupportedField = isSupportedField; + _nestedProvider = nestedProvider; + + _instructionSetMap = new Dictionary(); + foreach (var instructionSetInfo in InstructionSetFlags.ArchitectureToValidInstructionSets(_context.Target.Architecture)) + { + if (instructionSetInfo.ManagedName != "") + _instructionSetMap.Add(instructionSetInfo.ManagedName, instructionSetInfo.InstructionSet); + } + } + + public override MethodIL GetMethodIL(MethodDesc method) + { + TypeDesc owningType = method.OwningType; + string intrinsicId = InstructionSetSupport.GetHardwareIntrinsicId(_context.Target.Architecture, owningType); + if (!string.IsNullOrEmpty(intrinsicId) + && HardwareIntrinsicHelpers.IsIsSupportedMethod(method)) + { + InstructionSet instructionSet = _instructionSetMap[intrinsicId]; + + bool isSupported = _isaSupport.IsInstructionSetSupported(instructionSet); + bool isOptimisticallySupported = _isaSupport.OptimisticFlags.HasInstructionSet(instructionSet); + + // If this is an instruction set that is optimistically supported, but is not one of the + // intrinsics that are known to be always available, emit IL that checks the support level + // at runtime. + if (!isSupported && isOptimisticallySupported) + { + return HardwareIntrinsicHelpers.EmitIsSupportedIL(method, _isSupportedField, instructionSet); + } + else + { + ILOpcode flag = isSupported ? ILOpcode.ldc_i4_1 : ILOpcode.ldc_i4_0; + return new ILStubMethodIL(method, + new byte[] { (byte)flag, (byte)ILOpcode.ret }, + Array.Empty(), + null); + } + } + + return _nestedProvider.GetMethodIL(method); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index b67f3fa966e0d1..513b2f95d64c5a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -451,6 +451,7 @@ + diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs index a7615fc54fc872..1f3252f3ddc4be 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs @@ -22,8 +22,6 @@ public sealed class RyuJitCompilation : Compilation { private readonly ConditionalWeakTable _corinfos = new ConditionalWeakTable(); internal readonly RyuJitCompilationOptions _compilationOptions; - private readonly ExternSymbolMappedField _hardwareIntrinsicFlags; - private readonly Dictionary _instructionSetMap; private readonly ProfileDataManager _profileDataManager; private readonly MethodImportationErrorProvider _methodImportationErrorProvider; private readonly int _parallelism; @@ -47,16 +45,8 @@ internal RyuJitCompilation( : base(dependencyGraph, nodeFactory, roots, ilProvider, debugInformationProvider, devirtualizationManager, inliningPolicy, logger) { _compilationOptions = options; - _hardwareIntrinsicFlags = new ExternSymbolMappedField(nodeFactory.TypeSystemContext.GetWellKnownType(WellKnownType.Int32), "g_cpuFeatures"); InstructionSetSupport = instructionSetSupport; - _instructionSetMap = new Dictionary(); - foreach (var instructionSetInfo in InstructionSetFlags.ArchitectureToValidInstructionSets(TypeSystemContext.Target.Architecture)) - { - if (instructionSetInfo.ManagedName != "") - _instructionSetMap.Add(instructionSetInfo.ManagedName, instructionSetInfo.InstructionSet); - } - _profileDataManager = profileDataManager; _methodImportationErrorProvider = errorProvider; @@ -213,28 +203,6 @@ private void CompileSingleMethod(CorInfoImpl corInfo, MethodCodeNode methodCodeN Logger.LogError($"Method will always throw because: {exception.Message}", 1005, method, MessageSubCategory.AotAnalysis); } } - - public override MethodIL GetMethodIL(MethodDesc method) - { - TypeDesc owningType = method.OwningType; - string intrinsicId = InstructionSetSupport.GetHardwareIntrinsicId(TypeSystemContext.Target.Architecture, owningType); - if (!string.IsNullOrEmpty(intrinsicId) - && HardwareIntrinsicHelpers.IsIsSupportedMethod(method)) - { - InstructionSet instructionSet = _instructionSetMap[intrinsicId]; - - // If this is an instruction set that is optimistically supported, but is not one of the - // intrinsics that are known to be always available, emit IL that checks the support level - // at runtime. - if (!InstructionSetSupport.IsInstructionSetSupported(instructionSet) - && InstructionSetSupport.OptimisticFlags.HasInstructionSet(instructionSet)) - { - return HardwareIntrinsicHelpers.EmitIsSupportedIL(method, _hardwareIntrinsicFlags, instructionSet); - } - } - - return base.GetMethodIL(method); - } } [Flags] diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index ed7b013ae3d942..ce3912742aad7c 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -20,6 +20,7 @@ using Internal.TypeSystem.Ecma; using ILCompiler.Dataflow; +using ILCompiler.DependencyAnalysis; using ILLink.Shared; using Debug = System.Diagnostics.Debug; @@ -143,6 +144,11 @@ public int Run() if (typeSystemContext.InputFilePaths.Count == 0) throw new CommandLineException("No input files specified"); + ilProvider = new HardwareIntrinsicILProvider( + instructionSetSupport, + new ExternSymbolMappedField(typeSystemContext.GetWellKnownType(WellKnownType.Int32), "g_cpuFeatures"), + ilProvider); + SecurityMitigationOptions securityMitigationOptions = 0; string guard = Get(_command.Guard); if (StringComparer.OrdinalIgnoreCase.Equals(guard, "cf")) diff --git a/src/tests/nativeaot/SmokeTests/Preinitialization/Preinitialization.cs b/src/tests/nativeaot/SmokeTests/Preinitialization/Preinitialization.cs index ed280830098392..28abe12c221850 100644 --- a/src/tests/nativeaot/SmokeTests/Preinitialization/Preinitialization.cs +++ b/src/tests/nativeaot/SmokeTests/Preinitialization/Preinitialization.cs @@ -6,6 +6,7 @@ using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Intrinsics.X86; using BindingFlags = System.Reflection.BindingFlags; @@ -14,6 +15,7 @@ internal class Program private static int Main() { #if !MULTIMODULE_BUILD + TestHardwareIntrinsics.Run(); TestLdstr.Run(); TestException.Run(); TestThreadStaticNotInitialized.Run(); @@ -58,6 +60,39 @@ private static int Main() } } +class TestHardwareIntrinsics +{ + class Simple1 + { + public static bool IsSseSupported = Sse.IsSupported; + } + + class Simple2 + { + public static bool IsAvxVnniSupported = AvxVnni.IsSupported; + } + + class Complex + { + public static bool IsPopcntSupported = Popcnt.IsSupported; + } + + public static void Run() + { + Assert.IsPreinitialized(typeof(Simple1)); + Assert.AreEqual(Sse.IsSupported, Simple1.IsSseSupported); + + Assert.IsPreinitialized(typeof(Simple2)); + Assert.AreEqual(AvxVnni.IsSupported, Simple2.IsAvxVnniSupported); + + if (RuntimeInformation.ProcessArchitecture is Architecture.X86 or Architecture.X64) + Assert.IsLazyInitialized(typeof(Complex)); + else + Assert.IsPreinitialized(typeof(Complex)); + Assert.AreEqual(Popcnt.IsSupported, Complex.IsPopcntSupported); + } +} + class TestLdstr { static string s_mine;