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 // See the LICENSE file in the project root for more information.
7 #if FEATURE_HW_INTRINSICS
11 NamedIntrinsic intrinsicID;
12 const char* intrinsicName;
16 static const hwIntrinsicInfoArray[] = {
17 #define HARDWARE_INTRINSIC(id, name, isa) {NI_##id, name, InstructionSet_##isa},
18 #include "hwintrinsiclistxarch.h"
21 extern const char* getHWIntrinsicName(NamedIntrinsic intrinsic)
23 return hwIntrinsicInfoArray[intrinsic - NI_HW_INTRINSIC_START - 1].intrinsicName;
26 //------------------------------------------------------------------------
27 // lookupHWIntrinsicISA: map class name to InstructionSet value
30 // className -- class name in System.Runtime.Intrinsics.X86
33 // Id for the ISA class.
35 InstructionSet Compiler::lookupHWIntrinsicISA(const char* className)
37 if (className != nullptr)
39 if (className[0] == 'A')
41 if (strcmp(className, "Aes") == 0)
43 return InstructionSet_AES;
45 else if (strcmp(className, "Avx") == 0)
47 return InstructionSet_AVX;
49 else if (strcmp(className, "Avx2") == 0)
51 return InstructionSet_AVX2;
54 if (className[0] == 'S')
56 if (strcmp(className, "Sse") == 0)
58 return InstructionSet_SSE;
60 else if (strcmp(className, "Sse2") == 0)
62 return InstructionSet_SSE2;
64 else if (strcmp(className, "Sse3") == 0)
66 return InstructionSet_SSE3;
68 else if (strcmp(className, "Ssse3") == 0)
70 return InstructionSet_SSSE3;
72 else if (strcmp(className, "Sse41") == 0)
74 return InstructionSet_SSE41;
76 else if (strcmp(className, "Sse42") == 0)
78 return InstructionSet_SSE42;
82 if (strcmp(className, "Bmi1") == 0)
84 return InstructionSet_BMI1;
86 else if (strcmp(className, "Bmi2") == 0)
88 return InstructionSet_BMI2;
90 else if (strcmp(className, "Fma") == 0)
92 return InstructionSet_FMA;
94 else if (strcmp(className, "Lzcnt") == 0)
96 return InstructionSet_LZCNT;
98 else if (strcmp(className, "Pclmulqdq") == 0)
100 return InstructionSet_PCLMULQDQ;
102 else if (strcmp(className, "Popcnt") == 0)
104 return InstructionSet_POPCNT;
108 JITDUMP("Unsupported ISA.\n");
109 return InstructionSet_ILLEGAL;
112 //------------------------------------------------------------------------
113 // lookupHWIntrinsic: map intrinsic name to named intrinsic value
116 // methodName -- name of the intrinsic function.
117 // isa -- instruction set of the intrinsic.
120 // Id for the hardware intrinsic.
122 // TODO-Throughput: replace sequential search by binary search
123 NamedIntrinsic Compiler::lookupHWIntrinsic(const char* methodName, InstructionSet isa)
125 NamedIntrinsic result = NI_Illegal;
126 if (isa != InstructionSet_ILLEGAL)
128 for (int i = 0; i < NI_HW_INTRINSIC_END - NI_HW_INTRINSIC_START; i++)
130 if (isa == hwIntrinsicInfoArray[i].isa && strcmp(methodName, hwIntrinsicInfoArray[i].intrinsicName) == 0)
132 result = hwIntrinsicInfoArray[i].intrinsicID;
139 //------------------------------------------------------------------------
140 // isaOfHWIntrinsic: map named intrinsic value to its instruction set
143 // intrinsic -- id of the intrinsic function.
146 // instruction set of the intrinsic.
148 InstructionSet Compiler::isaOfHWIntrinsic(NamedIntrinsic intrinsic)
150 assert(intrinsic != NI_Illegal);
151 assert(intrinsic > NI_HW_INTRINSIC_START && intrinsic < NI_HW_INTRINSIC_END);
152 return hwIntrinsicInfoArray[intrinsic - NI_HW_INTRINSIC_START - 1].isa;
155 //------------------------------------------------------------------------
156 // isIntrinsicAnIsSupportedPropertyGetter: return true if the intrinsic is "get_IsSupported"
159 // intrinsic -- id of the intrinsic function.
162 // true if the intrinsic is "get_IsSupported"
163 // Sometimes we need to specially treat "get_IsSupported"
164 bool Compiler::isIntrinsicAnIsSupportedPropertyGetter(NamedIntrinsic intrinsic)
168 case NI_SSE_IsSupported:
169 case NI_SSE2_IsSupported:
170 case NI_SSE3_IsSupported:
171 case NI_SSSE3_IsSupported:
172 case NI_SSE41_IsSupported:
173 case NI_SSE42_IsSupported:
174 case NI_AVX_IsSupported:
175 case NI_AVX2_IsSupported:
176 case NI_AES_IsSupported:
177 case NI_BMI1_IsSupported:
178 case NI_BMI2_IsSupported:
179 case NI_FMA_IsSupported:
180 case NI_LZCNT_IsSupported:
181 case NI_PCLMULQDQ_IsSupported:
182 case NI_POPCNT_IsSupported:
189 //------------------------------------------------------------------------
190 // impX86HWIntrinsic: dispatch hardware intrinsics to their own implementation
194 // intrinsic -- id of the intrinsic function.
195 // method -- method handle of the intrinsic function.
196 // sig -- signature of the intrinsic call
199 // the expanded intrinsic.
201 GenTree* Compiler::impX86HWIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
203 InstructionSet isa = isaOfHWIntrinsic(intrinsic);
204 // Will throw PlatformNotSupportedException if
205 // - calling hardware intrinsics on unsupported hardware
206 // - calling SIMD hardware intrinsics with featureSIMD=false
207 if ((!compSupports(isa) || (!featureSIMD && isa != InstructionSet_BMI1 && isa != InstructionSet_BMI2 &&
208 isa != InstructionSet_LZCNT && isa != InstructionSet_POPCNT)) &&
209 !isIntrinsicAnIsSupportedPropertyGetter(intrinsic))
211 for (unsigned i = 0; i < sig->numArgs; i++)
215 return gtNewMustThrowException(CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, JITtype2varType(sig->retType));
219 case InstructionSet_SSE:
220 return impSSEIntrinsic(intrinsic, method, sig);
221 case InstructionSet_SSE2:
222 return impSSE2Intrinsic(intrinsic, method, sig);
223 case InstructionSet_SSE3:
224 return impSSE3Intrinsic(intrinsic, method, sig);
225 case InstructionSet_SSSE3:
226 return impSSSE3Intrinsic(intrinsic, method, sig);
227 case InstructionSet_SSE41:
228 return impSSE41Intrinsic(intrinsic, method, sig);
229 case InstructionSet_SSE42:
230 return impSSE42Intrinsic(intrinsic, method, sig);
231 case InstructionSet_AVX:
232 return impAVXIntrinsic(intrinsic, method, sig);
233 case InstructionSet_AVX2:
234 return impAVX2Intrinsic(intrinsic, method, sig);
236 case InstructionSet_AES:
237 return impAESIntrinsic(intrinsic, method, sig);
238 case InstructionSet_BMI1:
239 return impBMI1Intrinsic(intrinsic, method, sig);
240 case InstructionSet_BMI2:
241 return impBMI2Intrinsic(intrinsic, method, sig);
242 case InstructionSet_FMA:
243 return impFMAIntrinsic(intrinsic, method, sig);
244 case InstructionSet_LZCNT:
245 return impLZCNTIntrinsic(intrinsic, method, sig);
246 case InstructionSet_PCLMULQDQ:
247 return impPCLMULQDQIntrinsic(intrinsic, method, sig);
248 case InstructionSet_POPCNT:
249 return impPOPCNTIntrinsic(intrinsic, method, sig);
255 CORINFO_CLASS_HANDLE Compiler::gtGetStructHandleForHWSIMD(var_types simdType, var_types simdBaseType)
257 if (simdType == TYP_SIMD16)
259 switch (simdBaseType)
262 return Vector128FloatHandle;
264 return Vector128DoubleHandle;
266 return Vector128IntHandle;
268 return Vector128UShortHandle;
270 return Vector128UByteHandle;
272 return Vector128ShortHandle;
274 return Vector128ByteHandle;
276 return Vector128LongHandle;
278 return Vector128UIntHandle;
280 return Vector128ULongHandle;
282 assert(!"Didn't find a class handle for simdType");
285 else if (simdType == TYP_SIMD32)
287 switch (simdBaseType)
290 return Vector256FloatHandle;
292 return Vector256DoubleHandle;
294 return Vector256IntHandle;
296 return Vector256UShortHandle;
298 return Vector256UByteHandle;
300 return Vector256ShortHandle;
302 return Vector256ByteHandle;
304 return Vector256LongHandle;
306 return Vector256UIntHandle;
308 return Vector256ULongHandle;
310 assert(!"Didn't find a class handle for simdType");
314 return NO_CLASS_HANDLE;
317 GenTree* Compiler::impSSEIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
319 GenTree* retNode = nullptr;
320 GenTree* op1 = nullptr;
321 GenTree* op2 = nullptr;
324 case NI_SSE_IsSupported:
325 retNode = gtNewIconNode(featureSIMD && compSupports(InstructionSet_SSE));
329 assert(sig->numArgs == 2);
330 op2 = impSIMDPopStack(TYP_SIMD16);
331 op1 = impSIMDPopStack(TYP_SIMD16);
332 retNode = gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1, op2, NI_SSE_Add, TYP_FLOAT, 16);
336 JITDUMP("Not implemented hardware intrinsic");
342 GenTree* Compiler::impSSE2Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
344 GenTree* retNode = nullptr;
345 GenTree* op1 = nullptr;
346 GenTree* op2 = nullptr;
347 var_types baseType = TYP_UNKNOWN;
350 case NI_SSE2_IsSupported:
351 retNode = gtNewIconNode(featureSIMD && compSupports(InstructionSet_SSE2));
355 assert(sig->numArgs == 2);
356 op2 = impSIMDPopStack(TYP_SIMD16);
357 op1 = impSIMDPopStack(TYP_SIMD16);
358 baseType = getBaseTypeOfSIMDType(sig->retTypeSigClass);
359 retNode = gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1, op2, NI_SSE2_Add, baseType, 16);
363 JITDUMP("Not implemented hardware intrinsic");
369 GenTree* Compiler::impSSE3Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
373 case NI_SSE3_IsSupported:
374 return gtNewIconNode(featureSIMD && compSupports(InstructionSet_SSE3));
381 GenTree* Compiler::impSSSE3Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
385 case NI_SSSE3_IsSupported:
386 return gtNewIconNode(featureSIMD && compSupports(InstructionSet_SSSE3));
393 GenTree* Compiler::impSSE41Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
397 case NI_SSE41_IsSupported:
398 return gtNewIconNode(featureSIMD && compSupports(InstructionSet_SSE41));
405 GenTree* Compiler::impSSE42Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
407 GenTree* retNode = nullptr;
408 GenTree* op1 = nullptr;
409 GenTree* op2 = nullptr;
410 var_types callType = JITtype2varType(sig->retType);
412 CORINFO_ARG_LIST_HANDLE argLst = sig->args;
413 CORINFO_CLASS_HANDLE argClass;
417 case NI_SSE42_IsSupported:
418 retNode = gtNewIconNode(featureSIMD && compSupports(InstructionSet_SSE42));
422 assert(sig->numArgs == 2);
423 op2 = impPopStack().val;
424 op1 = impPopStack().val;
426 if (varTypeIsLong(callType))
428 return gtNewMustThrowException(CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, callType);
431 argLst = info.compCompHnd->getArgNext(argLst); // the second argument
432 corType = strip(info.compCompHnd->getArgType(sig, argLst, &argClass)); // type of the second argument
434 retNode = gtNewScalarHWIntrinsicNode(callType, op1, op2, NI_SSE42_Crc32);
436 // TODO - currently we use the BaseType to bring the type of the second argument
437 // to the code generator. May encode the overload info in other way.
438 retNode->gtHWIntrinsic.gtSIMDBaseType = JITtype2varType(corType);
442 JITDUMP("Not implemented hardware intrinsic");
448 GenTree* Compiler::impAVXIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
450 GenTree* retNode = nullptr;
451 GenTree* op1 = nullptr;
452 GenTree* op2 = nullptr;
453 var_types baseType = TYP_UNKNOWN;
456 case NI_AVX_IsSupported:
457 retNode = gtNewIconNode(featureSIMD && compSupports(InstructionSet_AVX));
461 assert(sig->numArgs == 2);
462 op2 = impSIMDPopStack(TYP_SIMD32);
463 op1 = impSIMDPopStack(TYP_SIMD32);
464 baseType = getBaseTypeOfSIMDType(sig->retTypeSigClass);
465 retNode = gtNewSimdHWIntrinsicNode(TYP_SIMD32, op1, op2, NI_AVX_Add, baseType, 32);
469 JITDUMP("Not implemented hardware intrinsic");
475 GenTree* Compiler::impAVX2Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
477 GenTree* retNode = nullptr;
478 GenTree* op1 = nullptr;
479 GenTree* op2 = nullptr;
480 var_types baseType = TYP_UNKNOWN;
483 case NI_AVX2_IsSupported:
484 retNode = gtNewIconNode(featureSIMD && compSupports(InstructionSet_AVX2));
488 assert(sig->numArgs == 2);
489 op2 = impSIMDPopStack(TYP_SIMD32);
490 op1 = impSIMDPopStack(TYP_SIMD32);
491 baseType = getBaseTypeOfSIMDType(sig->retTypeSigClass);
492 retNode = gtNewSimdHWIntrinsicNode(TYP_SIMD32, op1, op2, NI_AVX2_Add, baseType, 32);
496 JITDUMP("Not implemented hardware intrinsic");
502 GenTree* Compiler::impAESIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
506 case NI_AES_IsSupported:
507 return gtNewIconNode(featureSIMD && compSupports(InstructionSet_AES));
514 GenTree* Compiler::impBMI1Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
518 case NI_BMI1_IsSupported:
519 return gtNewIconNode(compSupports(InstructionSet_BMI1));
526 GenTree* Compiler::impBMI2Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
530 case NI_BMI2_IsSupported:
531 return gtNewIconNode(compSupports(InstructionSet_BMI2));
538 GenTree* Compiler::impFMAIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
542 case NI_FMA_IsSupported:
543 return gtNewIconNode(featureSIMD && compSupports(InstructionSet_FMA));
550 GenTree* Compiler::impLZCNTIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
552 GenTree* retNode = nullptr;
553 GenTree* op1 = nullptr;
554 var_types callType = JITtype2varType(sig->retType);
558 case NI_LZCNT_IsSupported:
559 retNode = gtNewIconNode(compSupports(InstructionSet_LZCNT));
562 case NI_LZCNT_LeadingZeroCount:
563 assert(sig->numArgs == 1);
564 op1 = impPopStack().val;
566 if (varTypeIsLong(callType))
568 return gtNewMustThrowException(CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, callType);
571 retNode = gtNewScalarHWIntrinsicNode(callType, op1, NI_LZCNT_LeadingZeroCount);
575 JITDUMP("Not implemented hardware intrinsic");
581 GenTree* Compiler::impPCLMULQDQIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
585 case NI_PCLMULQDQ_IsSupported:
586 return gtNewIconNode(featureSIMD && compSupports(InstructionSet_PCLMULQDQ));
593 GenTree* Compiler::impPOPCNTIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
595 GenTree* retNode = nullptr;
596 GenTree* op1 = nullptr;
597 var_types callType = JITtype2varType(sig->retType);
601 case NI_POPCNT_IsSupported:
602 retNode = gtNewIconNode(compSupports(InstructionSet_POPCNT));
605 case NI_POPCNT_PopCount:
606 assert(sig->numArgs == 1);
607 op1 = impPopStack().val;
609 if (varTypeIsLong(callType))
611 return gtNewMustThrowException(CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, callType);
614 retNode = gtNewScalarHWIntrinsicNode(callType, op1, NI_POPCNT_PopCount);
618 JITDUMP("Not implemented hardware intrinsic");
624 #endif // FEATURE_HW_INTRINSICS