Skip to content

Commit d1d6a6b

Browse files
Make it possible to preinitialize HW intrinsic IsSupported (#92666)
* Move the IL rewriting for HW intrinsics `IsSuported` calls to `ILProvider` from `RyuJitCompilation` * Also rewrite constant true/false
1 parent 43f236d commit d1d6a6b

File tree

5 files changed

+111
-32
lines changed

5 files changed

+111
-32
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
7+
using Internal.IL;
8+
using Internal.IL.Stubs;
9+
using Internal.JitInterface;
10+
using Internal.TypeSystem;
11+
12+
namespace ILCompiler
13+
{
14+
public sealed class HardwareIntrinsicILProvider : ILProvider
15+
{
16+
private readonly InstructionSetSupport _isaSupport;
17+
private readonly TypeSystemContext _context;
18+
private readonly FieldDesc _isSupportedField;
19+
private readonly ILProvider _nestedProvider;
20+
private readonly Dictionary<string, InstructionSet> _instructionSetMap;
21+
22+
public HardwareIntrinsicILProvider(InstructionSetSupport isaSupport, FieldDesc isSupportedField, ILProvider nestedProvider)
23+
{
24+
_isaSupport = isaSupport;
25+
_context = isSupportedField.Context;
26+
_isSupportedField = isSupportedField;
27+
_nestedProvider = nestedProvider;
28+
29+
_instructionSetMap = new Dictionary<string, InstructionSet>();
30+
foreach (var instructionSetInfo in InstructionSetFlags.ArchitectureToValidInstructionSets(_context.Target.Architecture))
31+
{
32+
if (instructionSetInfo.ManagedName != "")
33+
_instructionSetMap.Add(instructionSetInfo.ManagedName, instructionSetInfo.InstructionSet);
34+
}
35+
}
36+
37+
public override MethodIL GetMethodIL(MethodDesc method)
38+
{
39+
TypeDesc owningType = method.OwningType;
40+
string intrinsicId = InstructionSetSupport.GetHardwareIntrinsicId(_context.Target.Architecture, owningType);
41+
if (!string.IsNullOrEmpty(intrinsicId)
42+
&& HardwareIntrinsicHelpers.IsIsSupportedMethod(method))
43+
{
44+
InstructionSet instructionSet = _instructionSetMap[intrinsicId];
45+
46+
bool isSupported = _isaSupport.IsInstructionSetSupported(instructionSet);
47+
bool isOptimisticallySupported = _isaSupport.OptimisticFlags.HasInstructionSet(instructionSet);
48+
49+
// If this is an instruction set that is optimistically supported, but is not one of the
50+
// intrinsics that are known to be always available, emit IL that checks the support level
51+
// at runtime.
52+
if (!isSupported && isOptimisticallySupported)
53+
{
54+
return HardwareIntrinsicHelpers.EmitIsSupportedIL(method, _isSupportedField, instructionSet);
55+
}
56+
else
57+
{
58+
ILOpcode flag = isSupported ? ILOpcode.ldc_i4_1 : ILOpcode.ldc_i4_0;
59+
return new ILStubMethodIL(method,
60+
new byte[] { (byte)flag, (byte)ILOpcode.ret },
61+
Array.Empty<LocalVariableDefinition>(),
62+
null);
63+
}
64+
}
65+
66+
return _nestedProvider.GetMethodIL(method);
67+
}
68+
}
69+
}

src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,7 @@
451451
<Compile Include="Compiler\GeneratingMetadataManager.cs" />
452452
<Compile Include="Compiler\GenericRootProvider.cs" />
453453
<Compile Include="Compiler\HardwareIntrinsicHelpers.Aot.cs" />
454+
<Compile Include="Compiler\HardwareIntrinsicILProvider.cs" />
454455
<Compile Include="Compiler\IInliningPolicy.cs" />
455456
<Compile Include="Compiler\Logging\NativeAotFatalErrorException.cs" />
456457
<Compile Include="Compiler\ManifestResourceBlockingPolicy.cs" />

src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ public sealed class RyuJitCompilation : Compilation
2222
{
2323
private readonly ConditionalWeakTable<Thread, CorInfoImpl> _corinfos = new ConditionalWeakTable<Thread, CorInfoImpl>();
2424
internal readonly RyuJitCompilationOptions _compilationOptions;
25-
private readonly ExternSymbolMappedField _hardwareIntrinsicFlags;
26-
private readonly Dictionary<string, InstructionSet> _instructionSetMap;
2725
private readonly ProfileDataManager _profileDataManager;
2826
private readonly MethodImportationErrorProvider _methodImportationErrorProvider;
2927
private readonly int _parallelism;
@@ -47,16 +45,8 @@ internal RyuJitCompilation(
4745
: base(dependencyGraph, nodeFactory, roots, ilProvider, debugInformationProvider, devirtualizationManager, inliningPolicy, logger)
4846
{
4947
_compilationOptions = options;
50-
_hardwareIntrinsicFlags = new ExternSymbolMappedField(nodeFactory.TypeSystemContext.GetWellKnownType(WellKnownType.Int32), "g_cpuFeatures");
5148
InstructionSetSupport = instructionSetSupport;
5249

53-
_instructionSetMap = new Dictionary<string, InstructionSet>();
54-
foreach (var instructionSetInfo in InstructionSetFlags.ArchitectureToValidInstructionSets(TypeSystemContext.Target.Architecture))
55-
{
56-
if (instructionSetInfo.ManagedName != "")
57-
_instructionSetMap.Add(instructionSetInfo.ManagedName, instructionSetInfo.InstructionSet);
58-
}
59-
6050
_profileDataManager = profileDataManager;
6151

6252
_methodImportationErrorProvider = errorProvider;
@@ -213,28 +203,6 @@ private void CompileSingleMethod(CorInfoImpl corInfo, MethodCodeNode methodCodeN
213203
Logger.LogError($"Method will always throw because: {exception.Message}", 1005, method, MessageSubCategory.AotAnalysis);
214204
}
215205
}
216-
217-
public override MethodIL GetMethodIL(MethodDesc method)
218-
{
219-
TypeDesc owningType = method.OwningType;
220-
string intrinsicId = InstructionSetSupport.GetHardwareIntrinsicId(TypeSystemContext.Target.Architecture, owningType);
221-
if (!string.IsNullOrEmpty(intrinsicId)
222-
&& HardwareIntrinsicHelpers.IsIsSupportedMethod(method))
223-
{
224-
InstructionSet instructionSet = _instructionSetMap[intrinsicId];
225-
226-
// If this is an instruction set that is optimistically supported, but is not one of the
227-
// intrinsics that are known to be always available, emit IL that checks the support level
228-
// at runtime.
229-
if (!InstructionSetSupport.IsInstructionSetSupported(instructionSet)
230-
&& InstructionSetSupport.OptimisticFlags.HasInstructionSet(instructionSet))
231-
{
232-
return HardwareIntrinsicHelpers.EmitIsSupportedIL(method, _hardwareIntrinsicFlags, instructionSet);
233-
}
234-
}
235-
236-
return base.GetMethodIL(method);
237-
}
238206
}
239207

240208
[Flags]

src/coreclr/tools/aot/ILCompiler/Program.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
using Internal.TypeSystem.Ecma;
2121

2222
using ILCompiler.Dataflow;
23+
using ILCompiler.DependencyAnalysis;
2324
using ILLink.Shared;
2425

2526
using Debug = System.Diagnostics.Debug;
@@ -143,6 +144,11 @@ public int Run()
143144
if (typeSystemContext.InputFilePaths.Count == 0)
144145
throw new CommandLineException("No input files specified");
145146

147+
ilProvider = new HardwareIntrinsicILProvider(
148+
instructionSetSupport,
149+
new ExternSymbolMappedField(typeSystemContext.GetWellKnownType(WellKnownType.Int32), "g_cpuFeatures"),
150+
ilProvider);
151+
146152
SecurityMitigationOptions securityMitigationOptions = 0;
147153
string guard = Get(_command.Guard);
148154
if (StringComparer.OrdinalIgnoreCase.Equals(guard, "cf"))

src/tests/nativeaot/SmokeTests/Preinitialization/Preinitialization.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Runtime;
77
using System.Runtime.CompilerServices;
88
using System.Runtime.InteropServices;
9+
using System.Runtime.Intrinsics.X86;
910

1011
using BindingFlags = System.Reflection.BindingFlags;
1112

@@ -14,6 +15,7 @@ internal class Program
1415
private static int Main()
1516
{
1617
#if !MULTIMODULE_BUILD
18+
TestHardwareIntrinsics.Run();
1719
TestLdstr.Run();
1820
TestException.Run();
1921
TestThreadStaticNotInitialized.Run();
@@ -62,6 +64,39 @@ private static int Main()
6264
}
6365
}
6466

67+
class TestHardwareIntrinsics
68+
{
69+
class Simple1
70+
{
71+
public static bool IsSseSupported = Sse.IsSupported;
72+
}
73+
74+
class Simple2
75+
{
76+
public static bool IsAvxVnniSupported = AvxVnni.IsSupported;
77+
}
78+
79+
class Complex
80+
{
81+
public static bool IsPopcntSupported = Popcnt.IsSupported;
82+
}
83+
84+
public static void Run()
85+
{
86+
Assert.IsPreinitialized(typeof(Simple1));
87+
Assert.AreEqual(Sse.IsSupported, Simple1.IsSseSupported);
88+
89+
Assert.IsPreinitialized(typeof(Simple2));
90+
Assert.AreEqual(AvxVnni.IsSupported, Simple2.IsAvxVnniSupported);
91+
92+
if (RuntimeInformation.ProcessArchitecture is Architecture.X86 or Architecture.X64)
93+
Assert.IsLazyInitialized(typeof(Complex));
94+
else
95+
Assert.IsPreinitialized(typeof(Complex));
96+
Assert.AreEqual(Popcnt.IsSupported, Complex.IsPopcntSupported);
97+
}
98+
}
99+
65100
class TestLdstr
66101
{
67102
static string s_mine;

0 commit comments

Comments
 (0)