Merge pull request #15571 from BruceForstall/ReduceCrossgenVerbosity
[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                                        sig->retTypeClass);
217     }
218     switch (isa)
219     {
220         case InstructionSet_SSE:
221             return impSSEIntrinsic(intrinsic, method, sig);
222         case InstructionSet_SSE2:
223             return impSSE2Intrinsic(intrinsic, method, sig);
224         case InstructionSet_SSE3:
225             return impSSE3Intrinsic(intrinsic, method, sig);
226         case InstructionSet_SSSE3:
227             return impSSSE3Intrinsic(intrinsic, method, sig);
228         case InstructionSet_SSE41:
229             return impSSE41Intrinsic(intrinsic, method, sig);
230         case InstructionSet_SSE42:
231             return impSSE42Intrinsic(intrinsic, method, sig);
232         case InstructionSet_AVX:
233             return impAVXIntrinsic(intrinsic, method, sig);
234         case InstructionSet_AVX2:
235             return impAVX2Intrinsic(intrinsic, method, sig);
236
237         case InstructionSet_AES:
238             return impAESIntrinsic(intrinsic, method, sig);
239         case InstructionSet_BMI1:
240             return impBMI1Intrinsic(intrinsic, method, sig);
241         case InstructionSet_BMI2:
242             return impBMI2Intrinsic(intrinsic, method, sig);
243         case InstructionSet_FMA:
244             return impFMAIntrinsic(intrinsic, method, sig);
245         case InstructionSet_LZCNT:
246             return impLZCNTIntrinsic(intrinsic, method, sig);
247         case InstructionSet_PCLMULQDQ:
248             return impPCLMULQDQIntrinsic(intrinsic, method, sig);
249         case InstructionSet_POPCNT:
250             return impPOPCNTIntrinsic(intrinsic, method, sig);
251         default:
252             return nullptr;
253     }
254 }
255
256 CORINFO_CLASS_HANDLE Compiler::gtGetStructHandleForHWSIMD(var_types simdType, var_types simdBaseType)
257 {
258     if (simdType == TYP_SIMD16)
259     {
260         switch (simdBaseType)
261         {
262             case TYP_FLOAT:
263                 return Vector128FloatHandle;
264             case TYP_DOUBLE:
265                 return Vector128DoubleHandle;
266             case TYP_INT:
267                 return Vector128IntHandle;
268             case TYP_USHORT:
269                 return Vector128UShortHandle;
270             case TYP_UBYTE:
271                 return Vector128UByteHandle;
272             case TYP_SHORT:
273                 return Vector128ShortHandle;
274             case TYP_BYTE:
275                 return Vector128ByteHandle;
276             case TYP_LONG:
277                 return Vector128LongHandle;
278             case TYP_UINT:
279                 return Vector128UIntHandle;
280             case TYP_ULONG:
281                 return Vector128ULongHandle;
282             default:
283                 assert(!"Didn't find a class handle for simdType");
284         }
285     }
286     else if (simdType == TYP_SIMD32)
287     {
288         switch (simdBaseType)
289         {
290             case TYP_FLOAT:
291                 return Vector256FloatHandle;
292             case TYP_DOUBLE:
293                 return Vector256DoubleHandle;
294             case TYP_INT:
295                 return Vector256IntHandle;
296             case TYP_USHORT:
297                 return Vector256UShortHandle;
298             case TYP_UBYTE:
299                 return Vector256UByteHandle;
300             case TYP_SHORT:
301                 return Vector256ShortHandle;
302             case TYP_BYTE:
303                 return Vector256ByteHandle;
304             case TYP_LONG:
305                 return Vector256LongHandle;
306             case TYP_UINT:
307                 return Vector256UIntHandle;
308             case TYP_ULONG:
309                 return Vector256ULongHandle;
310             default:
311                 assert(!"Didn't find a class handle for simdType");
312         }
313     }
314
315     return NO_CLASS_HANDLE;
316 }
317
318 GenTree* Compiler::impSSEIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
319 {
320     GenTree* retNode = nullptr;
321     GenTree* op1     = nullptr;
322     GenTree* op2     = nullptr;
323     switch (intrinsic)
324     {
325         case NI_SSE_IsSupported:
326             retNode = gtNewIconNode(featureSIMD && compSupports(InstructionSet_SSE));
327             break;
328
329         case NI_SSE_Add:
330             assert(sig->numArgs == 2);
331             op2     = impSIMDPopStack(TYP_SIMD16);
332             op1     = impSIMDPopStack(TYP_SIMD16);
333             retNode = gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1, op2, NI_SSE_Add, TYP_FLOAT, 16);
334             break;
335
336         default:
337             JITDUMP("Not implemented hardware intrinsic");
338             break;
339     }
340     return retNode;
341 }
342
343 GenTree* Compiler::impSSE2Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
344 {
345     GenTree*  retNode  = nullptr;
346     GenTree*  op1      = nullptr;
347     GenTree*  op2      = nullptr;
348     var_types baseType = TYP_UNKNOWN;
349     switch (intrinsic)
350     {
351         case NI_SSE2_IsSupported:
352             retNode = gtNewIconNode(featureSIMD && compSupports(InstructionSet_SSE2));
353             break;
354
355         case NI_SSE2_Add:
356             assert(sig->numArgs == 2);
357             op2      = impSIMDPopStack(TYP_SIMD16);
358             op1      = impSIMDPopStack(TYP_SIMD16);
359             baseType = getBaseTypeOfSIMDType(sig->retTypeSigClass);
360             retNode  = gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1, op2, NI_SSE2_Add, baseType, 16);
361             break;
362
363         default:
364             JITDUMP("Not implemented hardware intrinsic");
365             break;
366     }
367     return retNode;
368 }
369
370 GenTree* Compiler::impSSE3Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
371 {
372     switch (intrinsic)
373     {
374         case NI_SSE3_IsSupported:
375             return gtNewIconNode(featureSIMD && compSupports(InstructionSet_SSE3));
376
377         default:
378             return nullptr;
379     }
380 }
381
382 GenTree* Compiler::impSSSE3Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
383 {
384     switch (intrinsic)
385     {
386         case NI_SSSE3_IsSupported:
387             return gtNewIconNode(featureSIMD && compSupports(InstructionSet_SSSE3));
388
389         default:
390             return nullptr;
391     }
392 }
393
394 GenTree* Compiler::impSSE41Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
395 {
396     switch (intrinsic)
397     {
398         case NI_SSE41_IsSupported:
399             return gtNewIconNode(featureSIMD && compSupports(InstructionSet_SSE41));
400
401         default:
402             return nullptr;
403     }
404 }
405
406 GenTree* Compiler::impSSE42Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
407 {
408     GenTree*  retNode  = nullptr;
409     GenTree*  op1      = nullptr;
410     GenTree*  op2      = nullptr;
411     var_types callType = JITtype2varType(sig->retType);
412
413     CORINFO_ARG_LIST_HANDLE argLst = sig->args;
414     CORINFO_CLASS_HANDLE    argClass;
415     CorInfoType             corType;
416     switch (intrinsic)
417     {
418         case NI_SSE42_IsSupported:
419             retNode = gtNewIconNode(featureSIMD && compSupports(InstructionSet_SSE42));
420             break;
421
422         case NI_SSE42_Crc32:
423             assert(sig->numArgs == 2);
424             op2 = impPopStack().val;
425             op1 = impPopStack().val;
426 #ifdef _TARGET_X86_
427             if (varTypeIsLong(callType))
428             {
429                 return gtNewMustThrowException(CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, callType, sig->retTypeClass);
430             }
431 #endif
432             argLst  = info.compCompHnd->getArgNext(argLst);                        // the second argument
433             corType = strip(info.compCompHnd->getArgType(sig, argLst, &argClass)); // type of the second argument
434
435             retNode = gtNewScalarHWIntrinsicNode(callType, op1, op2, NI_SSE42_Crc32);
436
437             // TODO - currently we use the BaseType to bring the type of the second argument
438             // to the code generator. May encode the overload info in other way.
439             retNode->gtHWIntrinsic.gtSIMDBaseType = JITtype2varType(corType);
440             break;
441
442         default:
443             JITDUMP("Not implemented hardware intrinsic");
444             break;
445     }
446     return retNode;
447 }
448
449 GenTree* Compiler::impAVXIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
450 {
451     GenTree*  retNode  = nullptr;
452     GenTree*  op1      = nullptr;
453     GenTree*  op2      = nullptr;
454     var_types baseType = TYP_UNKNOWN;
455     switch (intrinsic)
456     {
457         case NI_AVX_IsSupported:
458             retNode = gtNewIconNode(featureSIMD && compSupports(InstructionSet_AVX));
459             break;
460
461         case NI_AVX_Add:
462             assert(sig->numArgs == 2);
463             op2      = impSIMDPopStack(TYP_SIMD32);
464             op1      = impSIMDPopStack(TYP_SIMD32);
465             baseType = getBaseTypeOfSIMDType(sig->retTypeSigClass);
466             retNode  = gtNewSimdHWIntrinsicNode(TYP_SIMD32, op1, op2, NI_AVX_Add, baseType, 32);
467             break;
468
469         default:
470             JITDUMP("Not implemented hardware intrinsic");
471             break;
472     }
473     return retNode;
474 }
475
476 GenTree* Compiler::impAVX2Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
477 {
478     GenTree*  retNode  = nullptr;
479     GenTree*  op1      = nullptr;
480     GenTree*  op2      = nullptr;
481     var_types baseType = TYP_UNKNOWN;
482     switch (intrinsic)
483     {
484         case NI_AVX2_IsSupported:
485             retNode = gtNewIconNode(featureSIMD && compSupports(InstructionSet_AVX2));
486             break;
487
488         case NI_AVX2_Add:
489             assert(sig->numArgs == 2);
490             op2      = impSIMDPopStack(TYP_SIMD32);
491             op1      = impSIMDPopStack(TYP_SIMD32);
492             baseType = getBaseTypeOfSIMDType(sig->retTypeSigClass);
493             retNode  = gtNewSimdHWIntrinsicNode(TYP_SIMD32, op1, op2, NI_AVX2_Add, baseType, 32);
494             break;
495
496         default:
497             JITDUMP("Not implemented hardware intrinsic");
498             break;
499     }
500     return retNode;
501 }
502
503 GenTree* Compiler::impAESIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
504 {
505     switch (intrinsic)
506     {
507         case NI_AES_IsSupported:
508             return gtNewIconNode(featureSIMD && compSupports(InstructionSet_AES));
509
510         default:
511             return nullptr;
512     }
513 }
514
515 GenTree* Compiler::impBMI1Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
516 {
517     switch (intrinsic)
518     {
519         case NI_BMI1_IsSupported:
520             return gtNewIconNode(compSupports(InstructionSet_BMI1));
521
522         default:
523             return nullptr;
524     }
525 }
526
527 GenTree* Compiler::impBMI2Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
528 {
529     switch (intrinsic)
530     {
531         case NI_BMI2_IsSupported:
532             return gtNewIconNode(compSupports(InstructionSet_BMI2));
533
534         default:
535             return nullptr;
536     }
537 }
538
539 GenTree* Compiler::impFMAIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
540 {
541     switch (intrinsic)
542     {
543         case NI_FMA_IsSupported:
544             return gtNewIconNode(featureSIMD && compSupports(InstructionSet_FMA));
545
546         default:
547             return nullptr;
548     }
549 }
550
551 GenTree* Compiler::impLZCNTIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
552 {
553     GenTree*  retNode  = nullptr;
554     GenTree*  op1      = nullptr;
555     var_types callType = JITtype2varType(sig->retType);
556
557     switch (intrinsic)
558     {
559         case NI_LZCNT_IsSupported:
560             retNode = gtNewIconNode(compSupports(InstructionSet_LZCNT));
561             break;
562
563         case NI_LZCNT_LeadingZeroCount:
564             assert(sig->numArgs == 1);
565             op1 = impPopStack().val;
566 #ifdef _TARGET_X86_
567             if (varTypeIsLong(callType))
568             {
569                 return gtNewMustThrowException(CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, callType, sig->retTypeClass);
570             }
571 #endif
572             retNode = gtNewScalarHWIntrinsicNode(callType, op1, NI_LZCNT_LeadingZeroCount);
573             break;
574
575         default:
576             JITDUMP("Not implemented hardware intrinsic");
577             break;
578     }
579     return retNode;
580 }
581
582 GenTree* Compiler::impPCLMULQDQIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
583 {
584     switch (intrinsic)
585     {
586         case NI_PCLMULQDQ_IsSupported:
587             return gtNewIconNode(featureSIMD && compSupports(InstructionSet_PCLMULQDQ));
588
589         default:
590             return nullptr;
591     }
592 }
593
594 GenTree* Compiler::impPOPCNTIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
595 {
596     GenTree*  retNode  = nullptr;
597     GenTree*  op1      = nullptr;
598     var_types callType = JITtype2varType(sig->retType);
599
600     switch (intrinsic)
601     {
602         case NI_POPCNT_IsSupported:
603             retNode = gtNewIconNode(compSupports(InstructionSet_POPCNT));
604             break;
605
606         case NI_POPCNT_PopCount:
607             assert(sig->numArgs == 1);
608             op1 = impPopStack().val;
609 #ifdef _TARGET_X86_
610             if (varTypeIsLong(callType))
611             {
612                 return gtNewMustThrowException(CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, callType, sig->retTypeClass);
613             }
614 #endif
615             retNode = gtNewScalarHWIntrinsicNode(callType, op1, NI_POPCNT_PopCount);
616             break;
617
618         default:
619             JITDUMP("Not implemented hardware intrinsic");
620             break;
621     }
622     return retNode;
623 }
624
625 #endif // FEATURE_HW_INTRINSICS