Merge pull request #16832 from dotnetrt/StoreNonTemporal
[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 #ifdef FEATURE_HW_INTRINSICS
8
9 struct HWIntrinsicInfo
10 {
11     NamedIntrinsic      intrinsicID;
12     const char*         intrinsicName;
13     InstructionSet      isa;
14     int                 ival;
15     unsigned            simdSize;
16     int                 numArgs;
17     instruction         ins[10];
18     HWIntrinsicCategory category;
19     HWIntrinsicFlag     flags;
20 };
21
22 static const HWIntrinsicInfo hwIntrinsicInfoArray[] = {
23 #define HARDWARE_INTRINSIC(id, name, isa, ival, size, numarg, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, category, flag) \
24     {NI_##id, name, InstructionSet_##isa, ival, size, numarg, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, category, flag},
25 #include "hwintrinsiclistxarch.h"
26 };
27
28 extern const char* getHWIntrinsicName(NamedIntrinsic intrinsic)
29 {
30     return hwIntrinsicInfoArray[intrinsic - NI_HW_INTRINSIC_START - 1].intrinsicName;
31 }
32
33 //------------------------------------------------------------------------
34 // lookupHWIntrinsicISA: map class name to InstructionSet value
35 //
36 // Arguments:
37 //    className -- class name in System.Runtime.Intrinsics.X86
38 //
39 // Return Value:
40 //    Id for the ISA class.
41 //
42 InstructionSet Compiler::lookupHWIntrinsicISA(const char* className)
43 {
44     if (className != nullptr)
45     {
46         if (className[0] == 'A')
47         {
48             if (strcmp(className, "Aes") == 0)
49             {
50                 return InstructionSet_AES;
51             }
52             else if (strcmp(className, "Avx") == 0)
53             {
54                 return InstructionSet_AVX;
55             }
56             else if (strcmp(className, "Avx2") == 0)
57             {
58                 return InstructionSet_AVX2;
59             }
60         }
61         if (className[0] == 'S')
62         {
63             if (strcmp(className, "Sse") == 0)
64             {
65                 return InstructionSet_SSE;
66             }
67             else if (strcmp(className, "Sse2") == 0)
68             {
69                 return InstructionSet_SSE2;
70             }
71             else if (strcmp(className, "Sse3") == 0)
72             {
73                 return InstructionSet_SSE3;
74             }
75             else if (strcmp(className, "Ssse3") == 0)
76             {
77                 return InstructionSet_SSSE3;
78             }
79             else if (strcmp(className, "Sse41") == 0)
80             {
81                 return InstructionSet_SSE41;
82             }
83             else if (strcmp(className, "Sse42") == 0)
84             {
85                 return InstructionSet_SSE42;
86             }
87         }
88
89         if (strcmp(className, "Bmi1") == 0)
90         {
91             return InstructionSet_BMI1;
92         }
93         else if (strcmp(className, "Bmi2") == 0)
94         {
95             return InstructionSet_BMI2;
96         }
97         else if (strcmp(className, "Fma") == 0)
98         {
99             return InstructionSet_FMA;
100         }
101         else if (strcmp(className, "Lzcnt") == 0)
102         {
103             return InstructionSet_LZCNT;
104         }
105         else if (strcmp(className, "Pclmulqdq") == 0)
106         {
107             return InstructionSet_PCLMULQDQ;
108         }
109         else if (strcmp(className, "Popcnt") == 0)
110         {
111             return InstructionSet_POPCNT;
112         }
113     }
114
115     JITDUMP("Unsupported ISA.\n");
116     return InstructionSet_ILLEGAL;
117 }
118
119 //------------------------------------------------------------------------
120 // lookupHWIntrinsic: map intrinsic name to named intrinsic value
121 //
122 // Arguments:
123 //    methodName -- name of the intrinsic function.
124 //    isa        -- instruction set of the intrinsic.
125 //
126 // Return Value:
127 //    Id for the hardware intrinsic
128 //
129 // TODO-Throughput: replace sequential search by binary search
130 NamedIntrinsic Compiler::lookupHWIntrinsic(const char* methodName, InstructionSet isa)
131 {
132     NamedIntrinsic result = NI_Illegal;
133     if (isa != InstructionSet_ILLEGAL)
134     {
135         for (int i = 0; i < NI_HW_INTRINSIC_END - NI_HW_INTRINSIC_START - 1; i++)
136         {
137             if (isa == hwIntrinsicInfoArray[i].isa && strcmp(methodName, hwIntrinsicInfoArray[i].intrinsicName) == 0)
138             {
139                 result = hwIntrinsicInfoArray[i].intrinsicID;
140                 break;
141             }
142         }
143     }
144     return result;
145 }
146
147 //------------------------------------------------------------------------
148 // isaOfHWIntrinsic: map named intrinsic value to its instruction set
149 //
150 // Arguments:
151 //    intrinsic -- id of the intrinsic function.
152 //
153 // Return Value:
154 //    instruction set of the intrinsic.
155 //
156 InstructionSet Compiler::isaOfHWIntrinsic(NamedIntrinsic intrinsic)
157 {
158     assert(intrinsic != NI_Illegal);
159     assert(intrinsic > NI_HW_INTRINSIC_START && intrinsic < NI_HW_INTRINSIC_END);
160     return hwIntrinsicInfoArray[intrinsic - NI_HW_INTRINSIC_START - 1].isa;
161 }
162
163 //------------------------------------------------------------------------
164 // ivalOfHWIntrinsic: get the imm8 value of this intrinsic from the hwIntrinsicInfoArray table
165 //
166 // Arguments:
167 //    intrinsic -- id of the intrinsic function.
168 //
169 // Return Value:
170 //     The imm8 value that is implicit for this intrinsic, or -1 for intrinsics that do not take an immediate, or for
171 //     which the immediate is an explicit argument.
172 //
173 int Compiler::ivalOfHWIntrinsic(NamedIntrinsic intrinsic)
174 {
175     assert(intrinsic != NI_Illegal);
176     assert(intrinsic > NI_HW_INTRINSIC_START && intrinsic < NI_HW_INTRINSIC_END);
177     return hwIntrinsicInfoArray[intrinsic - NI_HW_INTRINSIC_START - 1].ival;
178 }
179
180 //------------------------------------------------------------------------
181 // simdSizeOfHWIntrinsic: get the SIMD size of this intrinsic
182 //
183 // Arguments:
184 //    intrinsic -- id of the intrinsic function.
185 //
186 // Return Value:
187 //     the SIMD size of this intrinsic
188 //         - from the hwIntrinsicInfoArray table if intrinsic has NO HW_Flag_UnfixedSIMDSize
189 //         - from the signature if intrinsic has HW_Flag_UnfixedSIMDSize
190 //
191 // Note - this function is only used by the importer
192 //        after importation (i.e., codegen), we can get the SIMD size from GenTreeHWIntrinsic IR
193 unsigned Compiler::simdSizeOfHWIntrinsic(NamedIntrinsic intrinsic, CORINFO_SIG_INFO* sig)
194 {
195     assert(intrinsic > NI_HW_INTRINSIC_START && intrinsic < NI_HW_INTRINSIC_END);
196
197     HWIntrinsicFlag flags = flagsOfHWIntrinsic(intrinsic);
198
199     if ((flags & HW_Flag_UnfixedSIMDSize) == 0)
200     {
201         return hwIntrinsicInfoArray[intrinsic - NI_HW_INTRINSIC_START - 1].simdSize;
202     }
203
204     CORINFO_CLASS_HANDLE typeHnd = nullptr;
205
206     if (JITtype2varType(sig->retType) == TYP_STRUCT)
207     {
208         typeHnd = sig->retTypeSigClass;
209     }
210     else
211     {
212         assert((flags & HW_Flag_BaseTypeFromFirstArg) != 0);
213         typeHnd = info.compCompHnd->getArgClass(sig, sig->args);
214     }
215
216     unsigned  simdSize = 0;
217     var_types baseType = getBaseTypeAndSizeOfSIMDType(typeHnd, &simdSize);
218     assert(simdSize > 0 && baseType != TYP_UNKNOWN);
219     return simdSize;
220 }
221
222 // TODO_XARCH-CQ - refactoring of numArgsOfHWIntrinsic fast path into inlinable
223 // function and slow local static function may increase performance significantly
224
225 //------------------------------------------------------------------------
226 // numArgsOfHWIntrinsic: gets the number of arguments for the hardware intrinsic.
227 // This attempts to do a table based lookup but will fallback to the number
228 // of operands in 'node' if the table entry is -1.
229 //
230 // Arguments:
231 //    node      -- GenTreeHWIntrinsic* node with nullptr default value
232 //
233 // Return Value:
234 //     number of arguments
235 //
236 int Compiler::numArgsOfHWIntrinsic(GenTreeHWIntrinsic* node)
237 {
238     assert(node != nullptr);
239
240     NamedIntrinsic intrinsic = node->gtHWIntrinsicId;
241
242     assert(intrinsic != NI_Illegal);
243     assert(intrinsic > NI_HW_INTRINSIC_START && intrinsic < NI_HW_INTRINSIC_END);
244
245     int numArgs = hwIntrinsicInfoArray[intrinsic - NI_HW_INTRINSIC_START - 1].numArgs;
246     if (numArgs >= 0)
247     {
248         return numArgs;
249     }
250
251     assert(numArgs == -1);
252
253     GenTree* op1 = node->gtGetOp1();
254     GenTree* op2 = node->gtGetOp2();
255
256     if (op2 != nullptr)
257     {
258         return 2;
259     }
260
261     if (op1 != nullptr)
262     {
263         if (op1->OperIsList())
264         {
265             numArgs              = 0;
266             GenTreeArgList* list = op1->AsArgList();
267
268             while (list != nullptr)
269             {
270                 numArgs++;
271                 list = list->Rest();
272             }
273
274             assert(numArgs > 0);
275             return numArgs;
276         }
277         else
278         {
279             return 1;
280         }
281     }
282     else
283     {
284         return 0;
285     }
286 }
287
288 //------------------------------------------------------------------------
289 // lastOpOfHWIntrinsic: get the last operand of a HW intrinsic
290 //
291 // Arguments:
292 //    node   -- the intrinsic node.
293 //    numArgs-- number of argument
294 //
295 // Return Value:
296 //     number of arguments
297 //
298 GenTree* Compiler::lastOpOfHWIntrinsic(GenTreeHWIntrinsic* node, int numArgs)
299 {
300     GenTree* op1 = node->gtGetOp1();
301     GenTree* op2 = node->gtGetOp2();
302     switch (numArgs)
303     {
304         case 0:
305             return nullptr;
306         case 1:
307             assert(op1 != nullptr);
308             return op1;
309         case 2:
310             assert(op2 != nullptr);
311             return op2;
312         case 3:
313             assert(op1->OperIsList());
314             assert(op1->AsArgList()->Rest()->Rest()->Current() != nullptr);
315             assert(op1->AsArgList()->Rest()->Rest()->Rest() == nullptr);
316             return op1->AsArgList()->Rest()->Rest()->Current();
317         default:
318             unreached();
319             return nullptr;
320     }
321 }
322
323 //------------------------------------------------------------------------
324 // insOfHWIntrinsic: get the instruction of the given intrinsic
325 //
326 // Arguments:
327 //    intrinsic -- id of the intrinsic function.
328 //    type      -- vector base type of this intrinsic
329 //
330 // Return Value:
331 //     the instruction of the given intrinsic on the base type
332 //     return INS_invalid for unsupported base types
333 //
334 instruction Compiler::insOfHWIntrinsic(NamedIntrinsic intrinsic, var_types type)
335 {
336     assert(intrinsic != NI_Illegal);
337     assert(intrinsic > NI_HW_INTRINSIC_START && intrinsic < NI_HW_INTRINSIC_END);
338     assert(type >= TYP_BYTE && type <= TYP_DOUBLE);
339     return hwIntrinsicInfoArray[intrinsic - NI_HW_INTRINSIC_START - 1].ins[type - TYP_BYTE];
340 }
341
342 //------------------------------------------------------------------------
343 // categoryOfHWIntrinsic: get the category of the given intrinsic
344 //
345 // Arguments:
346 //    intrinsic -- id of the intrinsic function.
347 //
348 // Return Value:
349 //     the category of the given intrinsic
350 //
351 HWIntrinsicCategory Compiler::categoryOfHWIntrinsic(NamedIntrinsic intrinsic)
352 {
353     assert(intrinsic != NI_Illegal);
354     assert(intrinsic > NI_HW_INTRINSIC_START && intrinsic < NI_HW_INTRINSIC_END);
355     return hwIntrinsicInfoArray[intrinsic - NI_HW_INTRINSIC_START - 1].category;
356 }
357
358 //------------------------------------------------------------------------
359 // HWIntrinsicFlag: get the flags of the given intrinsic
360 //
361 // Arguments:
362 //    intrinsic -- id of the intrinsic function.
363 //
364 // Return Value:
365 //     the flags of the given intrinsic
366 //
367 HWIntrinsicFlag Compiler::flagsOfHWIntrinsic(NamedIntrinsic intrinsic)
368 {
369     assert(intrinsic != NI_Illegal);
370     assert(intrinsic > NI_HW_INTRINSIC_START && intrinsic < NI_HW_INTRINSIC_END);
371     return hwIntrinsicInfoArray[intrinsic - NI_HW_INTRINSIC_START - 1].flags;
372 }
373
374 //------------------------------------------------------------------------
375 // getArgForHWIntrinsic: get the argument from the stack and match  the signature
376 //
377 // Arguments:
378 //    argType   -- the required type of argument
379 //    argClass  -- the class handle of argType
380 //
381 // Return Value:
382 //     get the argument at the given index from the stack and match  the signature
383 //
384 GenTree* Compiler::getArgForHWIntrinsic(var_types argType, CORINFO_CLASS_HANDLE argClass)
385 {
386     GenTree* arg = nullptr;
387     if (argType == TYP_STRUCT)
388     {
389         unsigned int argSizeBytes;
390         var_types    base = getBaseTypeAndSizeOfSIMDType(argClass, &argSizeBytes);
391         argType           = getSIMDTypeForSize(argSizeBytes);
392         assert((argType == TYP_SIMD32) || (argType == TYP_SIMD16));
393         arg = impSIMDPopStack(argType);
394         assert((arg->TypeGet() == TYP_SIMD16) || (arg->TypeGet() == TYP_SIMD32));
395     }
396     else
397     {
398         assert(varTypeIsArithmetic(argType));
399         arg = impPopStack().val;
400         assert(varTypeIsArithmetic(arg->TypeGet()));
401         assert(genActualType(arg->gtType) == genActualType(argType));
402     }
403     return arg;
404 }
405
406 //------------------------------------------------------------------------
407 // immUpperBoundOfHWIntrinsic: get the max imm-value of non-full-range IMM intrinsic
408 //
409 // Arguments:
410 //    intrinsic  -- intrinsic ID
411 //
412 // Return Value:
413 //     the max imm-value of non-full-range IMM intrinsic
414 //
415 int Compiler::immUpperBoundOfHWIntrinsic(NamedIntrinsic intrinsic)
416 {
417     assert(categoryOfHWIntrinsic(intrinsic) == HW_Category_IMM);
418     switch (intrinsic)
419     {
420         case NI_AVX_Compare:
421         case NI_AVX_CompareScalar:
422             return 31; // enum FloatComparisonMode has 32 values
423
424         default:
425             assert((flagsOfHWIntrinsic(intrinsic) & HW_Flag_FullRangeIMM) != 0);
426             return 255;
427     }
428 }
429
430 //------------------------------------------------------------------------
431 // impNonConstFallback: convert certain SSE2/AVX2 shift intrinsic to its semantic alternative when the imm-arg is
432 // not a compile-time constant
433 //
434 // Arguments:
435 //    intrinsic  -- intrinsic ID
436 //    simdType   -- Vector type
437 //    baseType   -- base type of the Vector128/256<T>
438 //
439 // Return Value:
440 //     return the IR of semantic alternative on non-const imm-arg
441 //
442 GenTree* Compiler::impNonConstFallback(NamedIntrinsic intrinsic, var_types simdType, var_types baseType)
443 {
444     assert((flagsOfHWIntrinsic(intrinsic) & HW_Flag_NoJmpTableIMM) != 0);
445     switch (intrinsic)
446     {
447         case NI_SSE2_ShiftLeftLogical:
448         case NI_SSE2_ShiftRightArithmetic:
449         case NI_SSE2_ShiftRightLogical:
450         case NI_AVX2_ShiftLeftLogical:
451         case NI_AVX2_ShiftRightArithmetic:
452         case NI_AVX2_ShiftRightLogical:
453         {
454             GenTree* op2 = impPopStack().val;
455             GenTree* op1 = impSIMDPopStack(simdType);
456             GenTree* tmpOp =
457                 gtNewSimdHWIntrinsicNode(TYP_SIMD16, op2, NI_SSE2_ConvertScalarToVector128Int32, TYP_INT, 16);
458             return gtNewSimdHWIntrinsicNode(simdType, op1, tmpOp, intrinsic, baseType, genTypeSize(simdType));
459         }
460
461         default:
462             unreached();
463             return nullptr;
464     }
465 }
466
467 //------------------------------------------------------------------------
468 // isImmHWIntrinsic: check the intrinsic is a imm-intrinsic overload or not
469 //
470 // Arguments:
471 //    intrinsic  -- intrinsic ID
472 //    lastOp     -- the last operand of the intrinsic that may point to the imm-arg
473 //
474 // Return Value:
475 //        Return true iff the intrinsics is an imm-intrinsic overload.
476 //        Note: that some intrinsics, with HW_Flag_MaybeIMM set, have both imm (integer immediate) and vector (i.e.
477 //        non-TYP_INT) overloads.
478 //
479 bool Compiler::isImmHWIntrinsic(NamedIntrinsic intrinsic, GenTree* lastOp)
480 {
481     if (categoryOfHWIntrinsic(intrinsic) != HW_Category_IMM)
482     {
483         return false;
484     }
485
486     if ((flagsOfHWIntrinsic(intrinsic) & HW_Flag_MaybeIMM) != 0 && genActualType(lastOp->TypeGet()) != TYP_INT)
487     {
488         return false;
489     }
490
491     return true;
492 }
493
494 //------------------------------------------------------------------------
495 // addRangeCheckIfNeeded: add a GT_HW_INTRINSIC_CHK node for non-full-range imm-intrinsic
496 //
497 // Arguments:
498 //    intrinsic  -- intrinsic ID
499 //    lastOp     -- the last operand of the intrinsic that points to the imm-arg
500 //    mustExpand -- true if the compiler is compiling the fallback(GT_CALL) of this intrinsics
501 //
502 // Return Value:
503 //     add a GT_HW_INTRINSIC_CHK node for non-full-range imm-intrinsic, which would throw ArgumentOutOfRangeException
504 //     when the imm-argument is not in the valid range
505 //
506 GenTree* Compiler::addRangeCheckIfNeeded(NamedIntrinsic intrinsic, GenTree* lastOp, bool mustExpand)
507 {
508     assert(lastOp != nullptr);
509     // Full-range imm-intrinsics do not need the range-check
510     // because the imm-parameter of the intrinsic method is a byte.
511     if (mustExpand && ((flagsOfHWIntrinsic(intrinsic) & HW_Flag_FullRangeIMM) == 0) &&
512         isImmHWIntrinsic(intrinsic, lastOp))
513     {
514         assert(!lastOp->IsCnsIntOrI());
515         GenTree* upperBoundNode = new (this, GT_CNS_INT) GenTreeIntCon(TYP_INT, immUpperBoundOfHWIntrinsic(intrinsic));
516         GenTree* index          = nullptr;
517         if ((lastOp->gtFlags & GTF_SIDE_EFFECT) != 0)
518         {
519             index = fgInsertCommaFormTemp(&lastOp);
520         }
521         else
522         {
523             index = gtCloneExpr(lastOp);
524         }
525         GenTreeBoundsChk* hwIntrinsicChk = new (this, GT_HW_INTRINSIC_CHK)
526             GenTreeBoundsChk(GT_HW_INTRINSIC_CHK, TYP_VOID, index, upperBoundNode, SCK_RNGCHK_FAIL);
527         hwIntrinsicChk->gtThrowKind = SCK_ARG_RNG_EXCPN;
528         return gtNewOperNode(GT_COMMA, lastOp->TypeGet(), hwIntrinsicChk, lastOp);
529     }
530     else
531     {
532         return lastOp;
533     }
534 }
535
536 //------------------------------------------------------------------------
537 // isFullyImplmentedISAClass: return true if all the hardware intrinsics
538 //    of this ISA are implemented in RyuJIT.
539 //
540 // Arguments:
541 //    isa - Instruction set
542 // Return Value:
543 //    true - all the hardware intrinsics of "isa" are implemented in RyuJIT.
544 //
545 bool Compiler::isFullyImplmentedISAClass(InstructionSet isa)
546 {
547     switch (isa)
548     {
549         case InstructionSet_SSE42:
550         case InstructionSet_AVX:
551         case InstructionSet_AVX2:
552         case InstructionSet_AES:
553         case InstructionSet_BMI1:
554         case InstructionSet_BMI2:
555         case InstructionSet_FMA:
556         case InstructionSet_PCLMULQDQ:
557             return false;
558
559         case InstructionSet_SSE:
560         case InstructionSet_SSE2:
561         case InstructionSet_SSE3:
562         case InstructionSet_SSSE3:
563         case InstructionSet_SSE41:
564         case InstructionSet_LZCNT:
565         case InstructionSet_POPCNT:
566             return true;
567
568         default:
569             unreached();
570     }
571 }
572
573 //------------------------------------------------------------------------
574 // isScalarISA:
575 //
576 // Arguments:
577 //    isa - Instruction set
578 // Return Value:
579 //    true - if "isa" only contains scalar instructions
580 //
581 bool Compiler::isScalarISA(InstructionSet isa)
582 {
583     switch (isa)
584     {
585         case InstructionSet_BMI1:
586         case InstructionSet_BMI2:
587         case InstructionSet_LZCNT:
588         case InstructionSet_POPCNT:
589             return true;
590
591         default:
592             return false;
593     }
594 }
595
596 //------------------------------------------------------------------------
597 // compSupportsHWIntrinsic: compiler support of hardware intrinsics
598 //
599 // Arguments:
600 //    isa - Instruction set
601 // Return Value:
602 //    true if
603 //    - isa is a scalar ISA
604 //    - isa is a SIMD ISA and featureSIMD=true
605 //    - isa is fully implemented or EnableIncompleteISAClass=true
606 bool Compiler::compSupportsHWIntrinsic(InstructionSet isa)
607 {
608     return (featureSIMD || isScalarISA(isa)) && (
609 #ifdef DEBUG
610                                                     JitConfig.EnableIncompleteISAClass() ||
611 #endif
612                                                     isFullyImplmentedISAClass(isa));
613 }
614
615 //------------------------------------------------------------------------
616 // hwIntrinsicSignatureTypeSupported: platform support of hardware intrinsics
617 //
618 // Arguments:
619 //    retType - return type
620 //    sig     - intrinsic signature
621 //    flags   - flags of the intrinsics
622 //
623 // Return Value:
624 //    Returns true iff the given type signature is supported
625 // Notes:
626 //    - This is only used on 32-bit systems to determine whether the signature uses no 64-bit registers.
627 //    - The `retType` is passed to avoid another call to the type system, as it has already been retrieved.
628 bool Compiler::hwIntrinsicSignatureTypeSupported(var_types retType, CORINFO_SIG_INFO* sig, HWIntrinsicFlag flags)
629 {
630 #ifdef _TARGET_X86_
631     CORINFO_CLASS_HANDLE argClass;
632
633     if ((flags & HW_Flag_64BitOnly) != 0)
634     {
635         return false;
636     }
637     else if ((flags & HW_Flag_SecondArgMaybe64Bit) != 0)
638     {
639         assert(sig->numArgs >= 2);
640         CorInfoType corType =
641             strip(info.compCompHnd->getArgType(sig, info.compCompHnd->getArgNext(sig->args), &argClass));
642         return !varTypeIsLong(JITtype2varType(corType));
643     }
644
645     return !varTypeIsLong(retType);
646 #else
647     return true;
648 #endif
649 }
650
651 //------------------------------------------------------------------------
652 // impIsTableDrivenHWIntrinsic:
653 //
654 // Arguments:
655 //    category - category of a HW intrinsic
656 //
657 // Return Value:
658 //    returns true if this category can be table-driven in the importer
659 //
660 static bool impIsTableDrivenHWIntrinsic(HWIntrinsicCategory category, HWIntrinsicFlag flags)
661 {
662     // HW_Flag_NoCodeGen implies this intrinsic should be manually morphed in the importer.
663     return category != HW_Category_Special && category != HW_Category_Scalar && (flags & HW_Flag_NoCodeGen) == 0;
664 }
665
666 //------------------------------------------------------------------------
667 // impHWIntrinsic: dispatch hardware intrinsics to their own implementation
668 //
669 // Arguments:
670 //    intrinsic -- id of the intrinsic function.
671 //    method    -- method handle of the intrinsic function.
672 //    sig       -- signature of the intrinsic call
673 //
674 // Return Value:
675 //    the expanded intrinsic.
676 //
677 GenTree* Compiler::impHWIntrinsic(NamedIntrinsic        intrinsic,
678                                   CORINFO_METHOD_HANDLE method,
679                                   CORINFO_SIG_INFO*     sig,
680                                   bool                  mustExpand)
681 {
682     InstructionSet      isa      = isaOfHWIntrinsic(intrinsic);
683     HWIntrinsicCategory category = categoryOfHWIntrinsic(intrinsic);
684     HWIntrinsicFlag     flags    = flagsOfHWIntrinsic(intrinsic);
685     int                 numArgs  = sig->numArgs;
686     var_types           retType  = JITtype2varType(sig->retType);
687     var_types           baseType = TYP_UNKNOWN;
688
689     if ((retType == TYP_STRUCT) && featureSIMD)
690     {
691         unsigned int sizeBytes;
692         baseType = getBaseTypeAndSizeOfSIMDType(sig->retTypeSigClass, &sizeBytes);
693         retType  = getSIMDTypeForSize(sizeBytes);
694         assert(sizeBytes != 0);
695     }
696
697     // This intrinsic is supported if
698     // - the ISA is available on the underlying hardware (compSupports returns true)
699     // - the compiler supports this hardware intrinsics (compSupportsHWIntrinsic returns true)
700     // - intrinsics do not require 64-bit registers (r64) on 32-bit platforms (signatureTypeSupproted returns
701     // true)
702     bool issupported =
703         compSupports(isa) && compSupportsHWIntrinsic(isa) && hwIntrinsicSignatureTypeSupported(retType, sig, flags);
704
705     if (category == HW_Category_IsSupportedProperty)
706     {
707         return gtNewIconNode(issupported);
708     }
709     // - calling to unsupported intrinsics must throw PlatforNotSupportedException
710     else if (!issupported)
711     {
712         return impUnsupportedHWIntrinsic(CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, method, sig, mustExpand);
713     }
714     // Avoid checking stacktop for 0-op intrinsics
715     if (sig->numArgs > 0 && isImmHWIntrinsic(intrinsic, impStackTop().val))
716     {
717         GenTree* lastOp = impStackTop().val;
718         // The imm-HWintrinsics that do not accept all imm8 values may throw
719         // ArgumentOutOfRangeException when the imm argument is not in the valid range
720         if ((flags & HW_Flag_FullRangeIMM) == 0)
721         {
722             if (!mustExpand && lastOp->IsCnsIntOrI() &&
723                 lastOp->AsIntCon()->IconValue() > immUpperBoundOfHWIntrinsic(intrinsic))
724             {
725                 return nullptr;
726             }
727         }
728
729         if (!lastOp->IsCnsIntOrI())
730         {
731             if ((flags & HW_Flag_NoJmpTableIMM) == 0 && !mustExpand)
732             {
733                 // When the imm-argument is not a constant and we are not being forced to expand, we need to
734                 // return nullptr so a GT_CALL to the intrinsic method is emitted instead. The
735                 // intrinsic method is recursive and will be forced to expand, at which point
736                 // we emit some less efficient fallback code.
737                 return nullptr;
738             }
739             else if ((flags & HW_Flag_NoJmpTableIMM) != 0)
740             {
741                 return impNonConstFallback(intrinsic, retType, baseType);
742             }
743         }
744     }
745
746     bool isTableDriven = impIsTableDrivenHWIntrinsic(category, flags);
747
748     if (isTableDriven && ((category == HW_Category_MemoryStore) ||
749                           ((flags & (HW_Flag_BaseTypeFromFirstArg | HW_Flag_BaseTypeFromSecondArg)) != 0)))
750     {
751         if ((flags & HW_Flag_BaseTypeFromFirstArg) != 0)
752         {
753             baseType = getBaseTypeOfSIMDType(info.compCompHnd->getArgClass(sig, sig->args));
754         }
755         else
756         {
757             assert((category == HW_Category_MemoryStore) || ((flags & HW_Flag_BaseTypeFromSecondArg) != 0));
758             CORINFO_ARG_LIST_HANDLE secondArg      = info.compCompHnd->getArgNext(sig->args);
759             CORINFO_CLASS_HANDLE    secondArgClass = info.compCompHnd->getArgClass(sig, secondArg);
760             baseType                               = getBaseTypeOfSIMDType(secondArgClass);
761
762             if (baseType == TYP_UNKNOWN) // the second argument is not a vector
763             {
764                 baseType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, secondArg, &secondArgClass)));
765                 assert(baseType != TYP_STRUCT);
766             }
767         }
768
769         assert(baseType != TYP_UNKNOWN);
770     }
771
772     if ((flags & (HW_Flag_OneTypeGeneric | HW_Flag_TwoTypeGeneric)) != 0)
773     {
774         if (!varTypeIsArithmetic(baseType))
775         {
776             return impUnsupportedHWIntrinsic(CORINFO_HELP_THROW_TYPE_NOT_SUPPORTED, method, sig, mustExpand);
777         }
778
779         if ((flags & HW_Flag_TwoTypeGeneric) != 0)
780         {
781             // StaticCast<T, U> has two type parameters.
782             assert(numArgs == 1);
783             var_types srcType = getBaseTypeOfSIMDType(info.compCompHnd->getArgClass(sig, sig->args));
784             if (!varTypeIsArithmetic(srcType))
785             {
786                 return impUnsupportedHWIntrinsic(CORINFO_HELP_THROW_TYPE_NOT_SUPPORTED, method, sig, mustExpand);
787             }
788         }
789     }
790
791     if ((flags & HW_Flag_NoFloatingPointUsed) == 0)
792     {
793         // Set `compFloatingPointUsed` to cover the scenario where an intrinsic is being on SIMD fields, but
794         // where no SIMD local vars are in use. This is the same logic as is used for FEATURE_SIMD.
795         compFloatingPointUsed = true;
796     }
797
798     // table-driven importer of simple intrinsics
799     if (isTableDriven)
800     {
801         unsigned                simdSize = simdSizeOfHWIntrinsic(intrinsic, sig);
802         CORINFO_ARG_LIST_HANDLE argList  = sig->args;
803         CORINFO_CLASS_HANDLE    argClass;
804         var_types               argType = TYP_UNKNOWN;
805
806         assert(numArgs >= 0);
807         assert(insOfHWIntrinsic(intrinsic, baseType) != INS_invalid);
808         assert(simdSize == 32 || simdSize == 16);
809
810         GenTree* retNode = nullptr;
811         GenTree* op1     = nullptr;
812         GenTree* op2     = nullptr;
813
814         switch (numArgs)
815         {
816             case 0:
817                 retNode = gtNewSimdHWIntrinsicNode(retType, intrinsic, baseType, simdSize);
818                 break;
819             case 1:
820                 argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, argList, &argClass)));
821                 op1     = getArgForHWIntrinsic(argType, argClass);
822                 retNode = gtNewSimdHWIntrinsicNode(retType, op1, intrinsic, baseType, simdSize);
823                 break;
824             case 2:
825                 argType = JITtype2varType(
826                     strip(info.compCompHnd->getArgType(sig, info.compCompHnd->getArgNext(argList), &argClass)));
827                 op2 = getArgForHWIntrinsic(argType, argClass);
828
829                 op2 = addRangeCheckIfNeeded(intrinsic, op2, mustExpand);
830
831                 argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, argList, &argClass)));
832                 op1     = getArgForHWIntrinsic(argType, argClass);
833
834                 retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, intrinsic, baseType, simdSize);
835                 break;
836
837             case 3:
838             {
839                 CORINFO_ARG_LIST_HANDLE arg2 = info.compCompHnd->getArgNext(argList);
840                 CORINFO_ARG_LIST_HANDLE arg3 = info.compCompHnd->getArgNext(arg2);
841
842                 argType      = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg3, &argClass)));
843                 GenTree* op3 = getArgForHWIntrinsic(argType, argClass);
844
845                 op3 = addRangeCheckIfNeeded(intrinsic, op3, mustExpand);
846
847                 argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg2, &argClass)));
848                 op2     = getArgForHWIntrinsic(argType, argClass);
849
850                 argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, argList, &argClass)));
851                 op1     = getArgForHWIntrinsic(argType, argClass);
852
853                 retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, op3, intrinsic, baseType, simdSize);
854                 break;
855             }
856             default:
857                 unreached();
858         }
859         return retNode;
860     }
861
862     // other intrinsics need special importation
863     switch (isa)
864     {
865         case InstructionSet_SSE:
866             return impSSEIntrinsic(intrinsic, method, sig, mustExpand);
867         case InstructionSet_SSE2:
868             return impSSE2Intrinsic(intrinsic, method, sig, mustExpand);
869         case InstructionSet_SSE42:
870             return impSSE42Intrinsic(intrinsic, method, sig, mustExpand);
871         case InstructionSet_AVX:
872             return impAVXIntrinsic(intrinsic, method, sig, mustExpand);
873         case InstructionSet_AVX2:
874             return impAVX2Intrinsic(intrinsic, method, sig, mustExpand);
875
876         case InstructionSet_AES:
877             return impAESIntrinsic(intrinsic, method, sig, mustExpand);
878         case InstructionSet_BMI1:
879             return impBMI1Intrinsic(intrinsic, method, sig, mustExpand);
880         case InstructionSet_BMI2:
881             return impBMI2Intrinsic(intrinsic, method, sig, mustExpand);
882         case InstructionSet_FMA:
883             return impFMAIntrinsic(intrinsic, method, sig, mustExpand);
884         case InstructionSet_LZCNT:
885             return impLZCNTIntrinsic(intrinsic, method, sig, mustExpand);
886         case InstructionSet_PCLMULQDQ:
887             return impPCLMULQDQIntrinsic(intrinsic, method, sig, mustExpand);
888         case InstructionSet_POPCNT:
889             return impPOPCNTIntrinsic(intrinsic, method, sig, mustExpand);
890         default:
891             return nullptr;
892     }
893 }
894
895 GenTree* Compiler::impSSEIntrinsic(NamedIntrinsic        intrinsic,
896                                    CORINFO_METHOD_HANDLE method,
897                                    CORINFO_SIG_INFO*     sig,
898                                    bool                  mustExpand)
899 {
900     GenTree* retNode  = nullptr;
901     GenTree* op1      = nullptr;
902     GenTree* op2      = nullptr;
903     GenTree* op3      = nullptr;
904     GenTree* op4      = nullptr;
905     int      simdSize = simdSizeOfHWIntrinsic(intrinsic, sig);
906
907     // The Prefetch and StoreFence intrinsics don't take any SIMD operands
908     // and have a simdSize of 0
909     assert((simdSize == 16) || (simdSize == 0));
910
911     switch (intrinsic)
912     {
913         case NI_SSE_MoveMask:
914             assert(sig->numArgs == 1);
915             assert(JITtype2varType(sig->retType) == TYP_INT);
916             assert(getBaseTypeOfSIMDType(info.compCompHnd->getArgClass(sig, sig->args)) == TYP_FLOAT);
917             op1     = impSIMDPopStack(TYP_SIMD16);
918             retNode = gtNewSimdHWIntrinsicNode(TYP_INT, op1, intrinsic, TYP_FLOAT, simdSize);
919             break;
920
921         case NI_SSE_Prefetch0:
922         case NI_SSE_Prefetch1:
923         case NI_SSE_Prefetch2:
924         case NI_SSE_PrefetchNonTemporal:
925         {
926             assert(sig->numArgs == 1);
927             assert(JITtype2varType(sig->retType) == TYP_VOID);
928             op1     = impPopStack().val;
929             retNode = gtNewSimdHWIntrinsicNode(TYP_VOID, op1, intrinsic, TYP_UBYTE, 0);
930             break;
931         }
932
933         case NI_SSE_StoreFence:
934             assert(sig->numArgs == 0);
935             assert(JITtype2varType(sig->retType) == TYP_VOID);
936             retNode = gtNewSimdHWIntrinsicNode(TYP_VOID, intrinsic, TYP_VOID, 0);
937             break;
938
939         default:
940             JITDUMP("Not implemented hardware intrinsic");
941             break;
942     }
943     return retNode;
944 }
945
946 GenTree* Compiler::impSSE2Intrinsic(NamedIntrinsic        intrinsic,
947                                     CORINFO_METHOD_HANDLE method,
948                                     CORINFO_SIG_INFO*     sig,
949                                     bool                  mustExpand)
950 {
951     GenTree*  retNode  = nullptr;
952     GenTree*  op1      = nullptr;
953     GenTree*  op2      = nullptr;
954     int       ival     = -1;
955     int       simdSize = simdSizeOfHWIntrinsic(intrinsic, sig);
956     var_types baseType = TYP_UNKNOWN;
957     var_types retType  = TYP_UNKNOWN;
958
959     // The  fencing intrinsics don't take any operands and simdSize is 0
960     assert((simdSize == 16) || (simdSize == 0));
961
962     CORINFO_ARG_LIST_HANDLE argList = sig->args;
963     CORINFO_CLASS_HANDLE    argClass;
964     var_types               argType = TYP_UNKNOWN;
965
966     switch (intrinsic)
967     {
968         case NI_SSE2_CompareLessThan:
969         {
970             assert(sig->numArgs == 2);
971             op2      = impSIMDPopStack(TYP_SIMD16);
972             op1      = impSIMDPopStack(TYP_SIMD16);
973             baseType = getBaseTypeOfSIMDType(sig->retTypeSigClass);
974             if (baseType == TYP_DOUBLE)
975             {
976                 retNode = gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1, op2, intrinsic, baseType, simdSize);
977             }
978             else
979             {
980                 retNode =
981                     gtNewSimdHWIntrinsicNode(TYP_SIMD16, op2, op1, NI_SSE2_CompareGreaterThan, baseType, simdSize);
982             }
983             break;
984         }
985
986         case NI_SSE2_LoadFence:
987         case NI_SSE2_MemoryFence:
988         {
989             assert(sig->numArgs == 0);
990             assert(JITtype2varType(sig->retType) == TYP_VOID);
991             assert(simdSize == 0);
992
993             retNode = gtNewSimdHWIntrinsicNode(TYP_VOID, intrinsic, TYP_VOID, simdSize);
994             break;
995         }
996
997         case NI_SSE2_MoveMask:
998         {
999             assert(sig->numArgs == 1);
1000             retType = JITtype2varType(sig->retType);
1001             assert(retType == TYP_INT);
1002             op1      = impSIMDPopStack(TYP_SIMD16);
1003             baseType = getBaseTypeOfSIMDType(info.compCompHnd->getArgClass(sig, sig->args));
1004             retNode  = gtNewSimdHWIntrinsicNode(retType, op1, intrinsic, baseType, simdSize);
1005             break;
1006         }
1007
1008         case NI_SSE2_StoreNonTemporal:
1009         {
1010             assert(sig->numArgs == 2);
1011             assert(JITtype2varType(sig->retType) == TYP_VOID);
1012             op2     = impPopStack().val;
1013             op1     = impPopStack().val;
1014             retNode = gtNewSimdHWIntrinsicNode(TYP_VOID, op1, op2, NI_SSE2_StoreNonTemporal, op2->TypeGet(), 0);
1015             break;
1016         }
1017
1018         default:
1019             JITDUMP("Not implemented hardware intrinsic");
1020             break;
1021     }
1022     return retNode;
1023 }
1024
1025 GenTree* Compiler::impSSE42Intrinsic(NamedIntrinsic        intrinsic,
1026                                      CORINFO_METHOD_HANDLE method,
1027                                      CORINFO_SIG_INFO*     sig,
1028                                      bool                  mustExpand)
1029 {
1030     GenTree*  retNode  = nullptr;
1031     GenTree*  op1      = nullptr;
1032     GenTree*  op2      = nullptr;
1033     var_types callType = JITtype2varType(sig->retType);
1034
1035     CORINFO_ARG_LIST_HANDLE argList = sig->args;
1036     CORINFO_CLASS_HANDLE    argClass;
1037     CorInfoType             corType;
1038     switch (intrinsic)
1039     {
1040         case NI_SSE42_Crc32:
1041             assert(sig->numArgs == 2);
1042             op2     = impPopStack().val;
1043             op1     = impPopStack().val;
1044             argList = info.compCompHnd->getArgNext(argList);                        // the second argument
1045             corType = strip(info.compCompHnd->getArgType(sig, argList, &argClass)); // type of the second argument
1046
1047             retNode = gtNewScalarHWIntrinsicNode(callType, op1, op2, NI_SSE42_Crc32);
1048
1049             // TODO - currently we use the BaseType to bring the type of the second argument
1050             // to the code generator. May encode the overload info in other way.
1051             retNode->gtHWIntrinsic.gtSIMDBaseType = JITtype2varType(corType);
1052             break;
1053
1054         default:
1055             JITDUMP("Not implemented hardware intrinsic");
1056             break;
1057     }
1058     return retNode;
1059 }
1060
1061 GenTree* Compiler::impAVXIntrinsic(NamedIntrinsic        intrinsic,
1062                                    CORINFO_METHOD_HANDLE method,
1063                                    CORINFO_SIG_INFO*     sig,
1064                                    bool                  mustExpand)
1065 {
1066     GenTree*  retNode  = nullptr;
1067     GenTree*  op1      = nullptr;
1068     GenTree*  op2      = nullptr;
1069     var_types baseType = TYP_UNKNOWN;
1070     switch (intrinsic)
1071     {
1072         default:
1073             JITDUMP("Not implemented hardware intrinsic");
1074             break;
1075     }
1076     return retNode;
1077 }
1078
1079 GenTree* Compiler::impAVX2Intrinsic(NamedIntrinsic        intrinsic,
1080                                     CORINFO_METHOD_HANDLE method,
1081                                     CORINFO_SIG_INFO*     sig,
1082                                     bool                  mustExpand)
1083 {
1084     GenTree*  retNode  = nullptr;
1085     GenTree*  op1      = nullptr;
1086     GenTree*  op2      = nullptr;
1087     var_types baseType = TYP_UNKNOWN;
1088     switch (intrinsic)
1089     {
1090         default:
1091             JITDUMP("Not implemented hardware intrinsic");
1092             break;
1093     }
1094     return retNode;
1095 }
1096
1097 GenTree* Compiler::impAESIntrinsic(NamedIntrinsic        intrinsic,
1098                                    CORINFO_METHOD_HANDLE method,
1099                                    CORINFO_SIG_INFO*     sig,
1100                                    bool                  mustExpand)
1101 {
1102     return nullptr;
1103 }
1104
1105 GenTree* Compiler::impBMI1Intrinsic(NamedIntrinsic        intrinsic,
1106                                     CORINFO_METHOD_HANDLE method,
1107                                     CORINFO_SIG_INFO*     sig,
1108                                     bool                  mustExpand)
1109 {
1110     return nullptr;
1111 }
1112
1113 GenTree* Compiler::impBMI2Intrinsic(NamedIntrinsic        intrinsic,
1114                                     CORINFO_METHOD_HANDLE method,
1115                                     CORINFO_SIG_INFO*     sig,
1116                                     bool                  mustExpand)
1117 {
1118     return nullptr;
1119 }
1120
1121 GenTree* Compiler::impFMAIntrinsic(NamedIntrinsic        intrinsic,
1122                                    CORINFO_METHOD_HANDLE method,
1123                                    CORINFO_SIG_INFO*     sig,
1124                                    bool                  mustExpand)
1125 {
1126     return nullptr;
1127 }
1128
1129 GenTree* Compiler::impLZCNTIntrinsic(NamedIntrinsic        intrinsic,
1130                                      CORINFO_METHOD_HANDLE method,
1131                                      CORINFO_SIG_INFO*     sig,
1132                                      bool                  mustExpand)
1133 {
1134     assert(sig->numArgs == 1);
1135     var_types callType = JITtype2varType(sig->retType);
1136     return gtNewScalarHWIntrinsicNode(callType, impPopStack().val, NI_LZCNT_LeadingZeroCount);
1137 }
1138
1139 GenTree* Compiler::impPCLMULQDQIntrinsic(NamedIntrinsic        intrinsic,
1140                                          CORINFO_METHOD_HANDLE method,
1141                                          CORINFO_SIG_INFO*     sig,
1142                                          bool                  mustExpand)
1143 {
1144     return nullptr;
1145 }
1146
1147 GenTree* Compiler::impPOPCNTIntrinsic(NamedIntrinsic        intrinsic,
1148                                       CORINFO_METHOD_HANDLE method,
1149                                       CORINFO_SIG_INFO*     sig,
1150                                       bool                  mustExpand)
1151 {
1152     assert(sig->numArgs == 1);
1153     var_types callType = JITtype2varType(sig->retType);
1154     return gtNewScalarHWIntrinsicNode(callType, impPopStack().val, NI_POPCNT_PopCount);
1155 }
1156
1157 #endif // FEATURE_HW_INTRINSICS