Merge pull request #15479 from echesakovMSFT/FixTargetArmMacroTypo
[platform/upstream/coreclr.git] / src / jit / hwintrinsicxarch.cpp
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.
4
5 #include "jitpch.h"
6
7 #if FEATURE_HW_INTRINSICS
8
9 struct HWIntrinsicInfo
10 {
11     NamedIntrinsic intrinsicID;
12     const char*    intrinsicName;
13     InstructionSet isa;
14 }
15
16 static const hwIntrinsicInfoArray[] = {
17 #define HARDWARE_INTRINSIC(id, name, isa) {NI_##id, name, InstructionSet_##isa},
18 #include "hwintrinsiclistxarch.h"
19 };
20
21 extern const char* getHWIntrinsicName(NamedIntrinsic intrinsic)
22 {
23     return hwIntrinsicInfoArray[intrinsic - NI_HW_INTRINSIC_START - 1].intrinsicName;
24 }
25
26 //------------------------------------------------------------------------
27 // lookupHWIntrinsicISA: map class name to InstructionSet value
28 //
29 // Arguments:
30 //    className -- class name in System.Runtime.Intrinsics.X86
31 //
32 // Return Value:
33 //    Id for the ISA class.
34 //
35 InstructionSet Compiler::lookupHWIntrinsicISA(const char* className)
36 {
37     if (className != nullptr)
38     {
39         if (className[0] == 'A')
40         {
41             if (strcmp(className, "Aes") == 0)
42             {
43                 return InstructionSet_AES;
44             }
45             else if (strcmp(className, "Avx") == 0)
46             {
47                 return InstructionSet_AVX;
48             }
49             else if (strcmp(className, "Avx2") == 0)
50             {
51                 return InstructionSet_AVX2;
52             }
53         }
54         if (className[0] == 'S')
55         {
56             if (strcmp(className, "Sse") == 0)
57             {
58                 return InstructionSet_SSE;
59             }
60             else if (strcmp(className, "Sse2") == 0)
61             {
62                 return InstructionSet_SSE2;
63             }
64             else if (strcmp(className, "Sse3") == 0)
65             {
66                 return InstructionSet_SSE3;
67             }
68             else if (strcmp(className, "Ssse3") == 0)
69             {
70                 return InstructionSet_SSSE3;
71             }
72             else if (strcmp(className, "Sse41") == 0)
73             {
74                 return InstructionSet_SSE41;
75             }
76             else if (strcmp(className, "Sse42") == 0)
77             {
78                 return InstructionSet_SSE42;
79             }
80         }
81
82         if (strcmp(className, "Bmi1") == 0)
83         {
84             return InstructionSet_BMI1;
85         }
86         else if (strcmp(className, "Bmi2") == 0)
87         {
88             return InstructionSet_BMI2;
89         }
90         else if (strcmp(className, "Fma") == 0)
91         {
92             return InstructionSet_FMA;
93         }
94         else if (strcmp(className, "Lzcnt") == 0)
95         {
96             return InstructionSet_LZCNT;
97         }
98         else if (strcmp(className, "Pclmulqdq") == 0)
99         {
100             return InstructionSet_PCLMULQDQ;
101         }
102         else if (strcmp(className, "Popcnt") == 0)
103         {
104             return InstructionSet_POPCNT;
105         }
106     }
107
108     JITDUMP("Unsupported ISA.\n");
109     return InstructionSet_ILLEGAL;
110 }
111
112 //------------------------------------------------------------------------
113 // lookupHWIntrinsic: map intrinsic name to named intrinsic value
114 //
115 // Arguments:
116 //    methodName -- name of the intrinsic function.
117 //    isa        -- instruction set of the intrinsic.
118 //
119 // Return Value:
120 //    Id for the hardware intrinsic.
121 //
122 // TODO-Throughput: replace sequential search by binary search
123 NamedIntrinsic Compiler::lookupHWIntrinsic(const char* methodName, InstructionSet isa)
124 {
125     NamedIntrinsic result = NI_Illegal;
126     if (isa != InstructionSet_ILLEGAL)
127     {
128         for (int i = 0; i < NI_HW_INTRINSIC_END - NI_HW_INTRINSIC_START; i++)
129         {
130             if (isa == hwIntrinsicInfoArray[i].isa && strcmp(methodName, hwIntrinsicInfoArray[i].intrinsicName) == 0)
131             {
132                 result = hwIntrinsicInfoArray[i].intrinsicID;
133             }
134         }
135     }
136     return result;
137 }
138
139 //------------------------------------------------------------------------
140 // isaOfHWIntrinsic: map named intrinsic value to its instruction set
141 //
142 // Arguments:
143 //    intrinsic -- id of the intrinsic function.
144 //
145 // Return Value:
146 //    instruction set of the intrinsic.
147 //
148 InstructionSet Compiler::isaOfHWIntrinsic(NamedIntrinsic intrinsic)
149 {
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;
153 }
154
155 //------------------------------------------------------------------------
156 // isIntrinsicAnIsSupportedPropertyGetter: return true if the intrinsic is "get_IsSupported"
157 //
158 // Arguments:
159 //    intrinsic -- id of the intrinsic function.
160 //
161 // Return Value:
162 //    true if the intrinsic is "get_IsSupported"
163 //    Sometimes we need to specially treat "get_IsSupported"
164 bool Compiler::isIntrinsicAnIsSupportedPropertyGetter(NamedIntrinsic intrinsic)
165 {
166     switch (intrinsic)
167     {
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:
183             return true;
184         default:
185             return false;
186     }
187 }
188
189 //------------------------------------------------------------------------
190 // impX86HWIntrinsic: dispatch hardware intrinsics to their own implementation
191 // function
192 //
193 // Arguments:
194 //    intrinsic -- id of the intrinsic function.
195 //    method    -- method handle of the intrinsic function.
196 //    sig       -- signature of the intrinsic call
197 //
198 // Return Value:
199 //    the expanded intrinsic.
200 //
201 GenTree* Compiler::impX86HWIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
202 {
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))
210     {
211         for (unsigned i = 0; i < sig->numArgs; i++)
212         {
213             impPopStack();
214         }
215         return gtNewMustThrowException(CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, JITtype2varType(sig->retType));
216     }
217     switch (isa)
218     {
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);
235
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);
250         default:
251             return nullptr;
252     }
253 }
254
255 CORINFO_CLASS_HANDLE Compiler::gtGetStructHandleForHWSIMD(var_types simdType, var_types simdBaseType)
256 {
257     if (simdType == TYP_SIMD16)
258     {
259         switch (simdBaseType)
260         {
261             case TYP_FLOAT:
262                 return Vector128FloatHandle;
263             case TYP_DOUBLE:
264                 return Vector128DoubleHandle;
265             case TYP_INT:
266                 return Vector128IntHandle;
267             case TYP_USHORT:
268                 return Vector128UShortHandle;
269             case TYP_UBYTE:
270                 return Vector128UByteHandle;
271             case TYP_SHORT:
272                 return Vector128ShortHandle;
273             case TYP_BYTE:
274                 return Vector128ByteHandle;
275             case TYP_LONG:
276                 return Vector128LongHandle;
277             case TYP_UINT:
278                 return Vector128UIntHandle;
279             case TYP_ULONG:
280                 return Vector128ULongHandle;
281             default:
282                 assert(!"Didn't find a class handle for simdType");
283         }
284     }
285     else if (simdType == TYP_SIMD32)
286     {
287         switch (simdBaseType)
288         {
289             case TYP_FLOAT:
290                 return Vector256FloatHandle;
291             case TYP_DOUBLE:
292                 return Vector256DoubleHandle;
293             case TYP_INT:
294                 return Vector256IntHandle;
295             case TYP_USHORT:
296                 return Vector256UShortHandle;
297             case TYP_UBYTE:
298                 return Vector256UByteHandle;
299             case TYP_SHORT:
300                 return Vector256ShortHandle;
301             case TYP_BYTE:
302                 return Vector256ByteHandle;
303             case TYP_LONG:
304                 return Vector256LongHandle;
305             case TYP_UINT:
306                 return Vector256UIntHandle;
307             case TYP_ULONG:
308                 return Vector256ULongHandle;
309             default:
310                 assert(!"Didn't find a class handle for simdType");
311         }
312     }
313
314     return NO_CLASS_HANDLE;
315 }
316
317 GenTree* Compiler::impSSEIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
318 {
319     GenTree* retNode = nullptr;
320     GenTree* op1     = nullptr;
321     GenTree* op2     = nullptr;
322     switch (intrinsic)
323     {
324         case NI_SSE_IsSupported:
325             retNode = gtNewIconNode(featureSIMD && compSupports(InstructionSet_SSE));
326             break;
327
328         case NI_SSE_Add:
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);
333             break;
334
335         default:
336             JITDUMP("Not implemented hardware intrinsic");
337             break;
338     }
339     return retNode;
340 }
341
342 GenTree* Compiler::impSSE2Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
343 {
344     GenTree*  retNode  = nullptr;
345     GenTree*  op1      = nullptr;
346     GenTree*  op2      = nullptr;
347     var_types baseType = TYP_UNKNOWN;
348     switch (intrinsic)
349     {
350         case NI_SSE2_IsSupported:
351             retNode = gtNewIconNode(featureSIMD && compSupports(InstructionSet_SSE2));
352             break;
353
354         case NI_SSE2_Add:
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);
360             break;
361
362         default:
363             JITDUMP("Not implemented hardware intrinsic");
364             break;
365     }
366     return retNode;
367 }
368
369 GenTree* Compiler::impSSE3Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
370 {
371     switch (intrinsic)
372     {
373         case NI_SSE3_IsSupported:
374             return gtNewIconNode(featureSIMD && compSupports(InstructionSet_SSE3));
375
376         default:
377             return nullptr;
378     }
379 }
380
381 GenTree* Compiler::impSSSE3Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
382 {
383     switch (intrinsic)
384     {
385         case NI_SSSE3_IsSupported:
386             return gtNewIconNode(featureSIMD && compSupports(InstructionSet_SSSE3));
387
388         default:
389             return nullptr;
390     }
391 }
392
393 GenTree* Compiler::impSSE41Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
394 {
395     switch (intrinsic)
396     {
397         case NI_SSE41_IsSupported:
398             return gtNewIconNode(featureSIMD && compSupports(InstructionSet_SSE41));
399
400         default:
401             return nullptr;
402     }
403 }
404
405 GenTree* Compiler::impSSE42Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
406 {
407     GenTree*  retNode  = nullptr;
408     GenTree*  op1      = nullptr;
409     GenTree*  op2      = nullptr;
410     var_types callType = JITtype2varType(sig->retType);
411
412     CORINFO_ARG_LIST_HANDLE argLst = sig->args;
413     CORINFO_CLASS_HANDLE    argClass;
414     CorInfoType             corType;
415     switch (intrinsic)
416     {
417         case NI_SSE42_IsSupported:
418             retNode = gtNewIconNode(featureSIMD && compSupports(InstructionSet_SSE42));
419             break;
420
421         case NI_SSE42_Crc32:
422             assert(sig->numArgs == 2);
423             op2 = impPopStack().val;
424             op1 = impPopStack().val;
425 #ifdef _TARGET_X86_
426             if (varTypeIsLong(callType))
427             {
428                 return gtNewMustThrowException(CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, callType);
429             }
430 #endif
431             argLst  = info.compCompHnd->getArgNext(argLst);                        // the second argument
432             corType = strip(info.compCompHnd->getArgType(sig, argLst, &argClass)); // type of the second argument
433
434             retNode = gtNewScalarHWIntrinsicNode(callType, op1, op2, NI_SSE42_Crc32);
435
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);
439             break;
440
441         default:
442             JITDUMP("Not implemented hardware intrinsic");
443             break;
444     }
445     return retNode;
446 }
447
448 GenTree* Compiler::impAVXIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
449 {
450     GenTree*  retNode  = nullptr;
451     GenTree*  op1      = nullptr;
452     GenTree*  op2      = nullptr;
453     var_types baseType = TYP_UNKNOWN;
454     switch (intrinsic)
455     {
456         case NI_AVX_IsSupported:
457             retNode = gtNewIconNode(featureSIMD && compSupports(InstructionSet_AVX));
458             break;
459
460         case NI_AVX_Add:
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);
466             break;
467
468         default:
469             JITDUMP("Not implemented hardware intrinsic");
470             break;
471     }
472     return retNode;
473 }
474
475 GenTree* Compiler::impAVX2Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
476 {
477     GenTree*  retNode  = nullptr;
478     GenTree*  op1      = nullptr;
479     GenTree*  op2      = nullptr;
480     var_types baseType = TYP_UNKNOWN;
481     switch (intrinsic)
482     {
483         case NI_AVX2_IsSupported:
484             retNode = gtNewIconNode(featureSIMD && compSupports(InstructionSet_AVX2));
485             break;
486
487         case NI_AVX2_Add:
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);
493             break;
494
495         default:
496             JITDUMP("Not implemented hardware intrinsic");
497             break;
498     }
499     return retNode;
500 }
501
502 GenTree* Compiler::impAESIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
503 {
504     switch (intrinsic)
505     {
506         case NI_AES_IsSupported:
507             return gtNewIconNode(featureSIMD && compSupports(InstructionSet_AES));
508
509         default:
510             return nullptr;
511     }
512 }
513
514 GenTree* Compiler::impBMI1Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
515 {
516     switch (intrinsic)
517     {
518         case NI_BMI1_IsSupported:
519             return gtNewIconNode(compSupports(InstructionSet_BMI1));
520
521         default:
522             return nullptr;
523     }
524 }
525
526 GenTree* Compiler::impBMI2Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
527 {
528     switch (intrinsic)
529     {
530         case NI_BMI2_IsSupported:
531             return gtNewIconNode(compSupports(InstructionSet_BMI2));
532
533         default:
534             return nullptr;
535     }
536 }
537
538 GenTree* Compiler::impFMAIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
539 {
540     switch (intrinsic)
541     {
542         case NI_FMA_IsSupported:
543             return gtNewIconNode(featureSIMD && compSupports(InstructionSet_FMA));
544
545         default:
546             return nullptr;
547     }
548 }
549
550 GenTree* Compiler::impLZCNTIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
551 {
552     GenTree*  retNode  = nullptr;
553     GenTree*  op1      = nullptr;
554     var_types callType = JITtype2varType(sig->retType);
555
556     switch (intrinsic)
557     {
558         case NI_LZCNT_IsSupported:
559             retNode = gtNewIconNode(compSupports(InstructionSet_LZCNT));
560             break;
561
562         case NI_LZCNT_LeadingZeroCount:
563             assert(sig->numArgs == 1);
564             op1 = impPopStack().val;
565 #ifdef _TARGET_X86_
566             if (varTypeIsLong(callType))
567             {
568                 return gtNewMustThrowException(CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, callType);
569             }
570 #endif
571             retNode = gtNewScalarHWIntrinsicNode(callType, op1, NI_LZCNT_LeadingZeroCount);
572             break;
573
574         default:
575             JITDUMP("Not implemented hardware intrinsic");
576             break;
577     }
578     return retNode;
579 }
580
581 GenTree* Compiler::impPCLMULQDQIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
582 {
583     switch (intrinsic)
584     {
585         case NI_PCLMULQDQ_IsSupported:
586             return gtNewIconNode(featureSIMD && compSupports(InstructionSet_PCLMULQDQ));
587
588         default:
589             return nullptr;
590     }
591 }
592
593 GenTree* Compiler::impPOPCNTIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
594 {
595     GenTree*  retNode  = nullptr;
596     GenTree*  op1      = nullptr;
597     var_types callType = JITtype2varType(sig->retType);
598
599     switch (intrinsic)
600     {
601         case NI_POPCNT_IsSupported:
602             retNode = gtNewIconNode(compSupports(InstructionSet_POPCNT));
603             break;
604
605         case NI_POPCNT_PopCount:
606             assert(sig->numArgs == 1);
607             op1 = impPopStack().val;
608 #ifdef _TARGET_X86_
609             if (varTypeIsLong(callType))
610             {
611                 return gtNewMustThrowException(CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, callType);
612             }
613 #endif
614             retNode = gtNewScalarHWIntrinsicNode(callType, op1, NI_POPCNT_PopCount);
615             break;
616
617         default:
618             JITDUMP("Not implemented hardware intrinsic");
619             break;
620     }
621     return retNode;
622 }
623
624 #endif // FEATURE_HW_INTRINSICS