Merge pull request #14781 from fiigii/x86reg
[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 // impX86HWIntrinsic: dispatch hardware intrinsics to their own implementation
157 // function
158 //
159 // Arguments:
160 //    intrinsic -- id of the intrinsic function.
161 //    method    -- method handle of the intrinsic function.
162 //    sig       -- signature of the intrinsic call
163 //
164 // Return Value:
165 //    the expanded intrinsic.
166 //
167 GenTree* Compiler::impX86HWIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
168 {
169     InstructionSet isa = isaOfHWIntrinsic(intrinsic);
170     if (!compSupports(isa) && strcmp("get_IsSupported", getHWIntrinsicName(intrinsic)) != 0)
171     {
172         for (unsigned i = 0; i < sig->numArgs; i++)
173         {
174             impPopStack();
175         }
176         return gtNewMustThrowException(CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, JITtype2varType(sig->retType));
177     }
178     switch (isa)
179     {
180         case InstructionSet_SSE:
181             return impSSEIntrinsic(intrinsic, method, sig);
182         case InstructionSet_SSE2:
183             return impSSE2Intrinsic(intrinsic, method, sig);
184         case InstructionSet_SSE3:
185             return impSSE3Intrinsic(intrinsic, method, sig);
186         case InstructionSet_SSSE3:
187             return impSSSE3Intrinsic(intrinsic, method, sig);
188         case InstructionSet_SSE41:
189             return impSSE41Intrinsic(intrinsic, method, sig);
190         case InstructionSet_SSE42:
191             return impSSE42Intrinsic(intrinsic, method, sig);
192         case InstructionSet_AVX:
193             return impAVXIntrinsic(intrinsic, method, sig);
194         case InstructionSet_AVX2:
195             return impAVX2Intrinsic(intrinsic, method, sig);
196
197         case InstructionSet_AES:
198             return impAESIntrinsic(intrinsic, method, sig);
199         case InstructionSet_BMI1:
200             return impBMI1Intrinsic(intrinsic, method, sig);
201         case InstructionSet_BMI2:
202             return impBMI2Intrinsic(intrinsic, method, sig);
203         case InstructionSet_FMA:
204             return impFMAIntrinsic(intrinsic, method, sig);
205         case InstructionSet_LZCNT:
206             return impLZCNTIntrinsic(intrinsic, method, sig);
207         case InstructionSet_PCLMULQDQ:
208             return impPCLMULQDQIntrinsic(intrinsic, method, sig);
209         case InstructionSet_POPCNT:
210             return impPOPCNTIntrinsic(intrinsic, method, sig);
211         default:
212             return nullptr;
213     }
214 }
215
216 GenTree* Compiler::impSSEIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
217 {
218     switch (intrinsic)
219     {
220         case NI_SSE_IsSupported:
221             return gtNewIconNode(compSupports(InstructionSet_SSE));
222
223         default:
224             return nullptr;
225     }
226 }
227
228 GenTree* Compiler::impSSE2Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
229 {
230     switch (intrinsic)
231     {
232         case NI_SSE2_IsSupported:
233             return gtNewIconNode(compSupports(InstructionSet_SSE2));
234
235         default:
236             return nullptr;
237     }
238 }
239
240 GenTree* Compiler::impSSE3Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
241 {
242     switch (intrinsic)
243     {
244         case NI_SSE3_IsSupported:
245             return gtNewIconNode(compSupports(InstructionSet_SSE3));
246
247         default:
248             return nullptr;
249     }
250 }
251
252 GenTree* Compiler::impSSSE3Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
253 {
254     switch (intrinsic)
255     {
256         case NI_SSSE3_IsSupported:
257             return gtNewIconNode(compSupports(InstructionSet_SSSE3));
258
259         default:
260             return nullptr;
261     }
262 }
263
264 GenTree* Compiler::impSSE41Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
265 {
266     switch (intrinsic)
267     {
268         case NI_SSE41_IsSupported:
269             return gtNewIconNode(compSupports(InstructionSet_SSE41));
270
271         default:
272             return nullptr;
273     }
274 }
275
276 GenTree* Compiler::impSSE42Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
277 {
278     GenTree*  retNode  = nullptr;
279     GenTree*  op1      = nullptr;
280     GenTree*  op2      = nullptr;
281     var_types callType = JITtype2varType(sig->retType);
282
283     CORINFO_ARG_LIST_HANDLE argLst = sig->args;
284     CORINFO_CLASS_HANDLE    argClass;
285     CorInfoType             corType;
286     switch (intrinsic)
287     {
288         case NI_SSE42_IsSupported:
289             retNode = gtNewIconNode(compSupports(InstructionSet_SSE42));
290             break;
291
292         case NI_SSE42_Crc32:
293             assert(sig->numArgs == 2);
294             op2 = impPopStack().val;
295             op1 = impPopStack().val;
296 #ifdef _TARGET_X86_
297             if (varTypeIsLong(callType))
298             {
299                 return gtNewMustThrowException(CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, callType);
300             }
301 #endif
302             argLst  = info.compCompHnd->getArgNext(argLst);                        // the second argument
303             corType = strip(info.compCompHnd->getArgType(sig, argLst, &argClass)); // type of the second argument
304
305             retNode = gtNewScalarHWIntrinsicNode(callType, op1, op2, NI_SSE42_Crc32);
306
307             // TODO - currently we use the BaseType to bring the type of the second argument
308             // to the code generator. May encode the overload info in other way.
309             retNode->gtHWIntrinsic.gtSIMDBaseType = JITtype2varType(corType);
310             break;
311
312         default:
313             JITDUMP("Not implemented hardware intrinsic");
314             break;
315     }
316     return retNode;
317 }
318
319 GenTree* Compiler::impAVXIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
320 {
321     switch (intrinsic)
322     {
323         case NI_AVX_IsSupported:
324             return gtNewIconNode(compSupports(InstructionSet_AVX));
325
326         default:
327             return nullptr;
328     }
329 }
330
331 GenTree* Compiler::impAVX2Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
332 {
333     switch (intrinsic)
334     {
335         case NI_AVX2_IsSupported:
336             return gtNewIconNode(compSupports(InstructionSet_AVX2));
337
338         default:
339             return nullptr;
340     }
341 }
342
343 GenTree* Compiler::impAESIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
344 {
345     switch (intrinsic)
346     {
347         case NI_AES_IsSupported:
348             return gtNewIconNode(compSupports(InstructionSet_AES));
349
350         default:
351             return nullptr;
352     }
353 }
354
355 GenTree* Compiler::impBMI1Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
356 {
357     switch (intrinsic)
358     {
359         case NI_BMI1_IsSupported:
360             return gtNewIconNode(compSupports(InstructionSet_BMI1));
361
362         default:
363             return nullptr;
364     }
365 }
366
367 GenTree* Compiler::impBMI2Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
368 {
369     switch (intrinsic)
370     {
371         case NI_BMI2_IsSupported:
372             return gtNewIconNode(compSupports(InstructionSet_BMI2));
373
374         default:
375             return nullptr;
376     }
377 }
378
379 GenTree* Compiler::impFMAIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
380 {
381     switch (intrinsic)
382     {
383         case NI_FMA_IsSupported:
384             return gtNewIconNode(compSupports(InstructionSet_FMA));
385
386         default:
387             return nullptr;
388     }
389 }
390
391 GenTree* Compiler::impLZCNTIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
392 {
393     GenTree*  retNode  = nullptr;
394     GenTree*  op1      = nullptr;
395     var_types callType = JITtype2varType(sig->retType);
396
397     switch (intrinsic)
398     {
399         case NI_LZCNT_IsSupported:
400             retNode = gtNewIconNode(compSupports(InstructionSet_LZCNT));
401             break;
402
403         case NI_LZCNT_LeadingZeroCount:
404             assert(sig->numArgs == 1);
405             op1 = impPopStack().val;
406 #ifdef _TARGET_X86_
407             if (varTypeIsLong(callType))
408             {
409                 return gtNewMustThrowException(CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, callType);
410             }
411 #endif
412             retNode = gtNewScalarHWIntrinsicNode(callType, op1, NI_LZCNT_LeadingZeroCount);
413             break;
414
415         default:
416             JITDUMP("Not implemented hardware intrinsic");
417             break;
418     }
419     return retNode;
420 }
421
422 GenTree* Compiler::impPCLMULQDQIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
423 {
424     switch (intrinsic)
425     {
426         case NI_PCLMULQDQ_IsSupported:
427             return gtNewIconNode(compSupports(InstructionSet_PCLMULQDQ));
428
429         default:
430             return nullptr;
431     }
432 }
433
434 GenTree* Compiler::impPOPCNTIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig)
435 {
436     GenTree*  retNode  = nullptr;
437     GenTree*  op1      = nullptr;
438     var_types callType = JITtype2varType(sig->retType);
439
440     switch (intrinsic)
441     {
442         case NI_POPCNT_IsSupported:
443             retNode = gtNewIconNode(compSupports(InstructionSet_POPCNT));
444             break;
445
446         case NI_POPCNT_PopCount:
447             assert(sig->numArgs == 1);
448             op1 = impPopStack().val;
449 #ifdef _TARGET_X86_
450             if (varTypeIsLong(callType))
451             {
452                 return gtNewMustThrowException(CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, callType);
453             }
454 #endif
455             retNode = gtNewScalarHWIntrinsicNode(callType, op1, NI_POPCNT_PopCount);
456             break;
457
458         default:
459             JITDUMP("Not implemented hardware intrinsic");
460             break;
461     }
462     return retNode;
463 }
464
465 #endif // FEATURE_HW_INTRINSICS