1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
7 #ifdef FEATURE_HW_INTRINSICS
11 NamedIntrinsic intrinsicID;
12 const char* intrinsicName;
18 HWIntrinsicCategory category;
19 HWIntrinsicFlag flags;
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"
28 extern const char* getHWIntrinsicName(NamedIntrinsic intrinsic)
30 return hwIntrinsicInfoArray[intrinsic - NI_HW_INTRINSIC_START - 1].intrinsicName;
33 //------------------------------------------------------------------------
34 // lookupHWIntrinsicISA: map class name to InstructionSet value
37 // className -- class name in System.Runtime.Intrinsics.X86
40 // Id for the ISA class.
42 InstructionSet Compiler::lookupHWIntrinsicISA(const char* className)
44 if (className != nullptr)
46 if (className[0] == 'A')
48 if (strcmp(className, "Aes") == 0)
50 return InstructionSet_AES;
52 else if (strcmp(className, "Avx") == 0)
54 return InstructionSet_AVX;
56 else if (strcmp(className, "Avx2") == 0)
58 return InstructionSet_AVX2;
61 if (className[0] == 'S')
63 if (strcmp(className, "Sse") == 0)
65 return InstructionSet_SSE;
67 else if (strcmp(className, "Sse2") == 0)
69 return InstructionSet_SSE2;
71 else if (strcmp(className, "Sse3") == 0)
73 return InstructionSet_SSE3;
75 else if (strcmp(className, "Ssse3") == 0)
77 return InstructionSet_SSSE3;
79 else if (strcmp(className, "Sse41") == 0)
81 return InstructionSet_SSE41;
83 else if (strcmp(className, "Sse42") == 0)
85 return InstructionSet_SSE42;
89 if (strcmp(className, "Bmi1") == 0)
91 return InstructionSet_BMI1;
93 else if (strcmp(className, "Bmi2") == 0)
95 return InstructionSet_BMI2;
97 else if (strcmp(className, "Fma") == 0)
99 return InstructionSet_FMA;
101 else if (strcmp(className, "Lzcnt") == 0)
103 return InstructionSet_LZCNT;
105 else if (strcmp(className, "Pclmulqdq") == 0)
107 return InstructionSet_PCLMULQDQ;
109 else if (strcmp(className, "Popcnt") == 0)
111 return InstructionSet_POPCNT;
115 JITDUMP("Unsupported ISA.\n");
116 return InstructionSet_ILLEGAL;
119 //------------------------------------------------------------------------
120 // lookupHWIntrinsic: map intrinsic name to named intrinsic value
123 // methodName -- name of the intrinsic function.
124 // isa -- instruction set of the intrinsic.
127 // Id for the hardware intrinsic
129 // TODO-Throughput: replace sequential search by binary search
130 NamedIntrinsic Compiler::lookupHWIntrinsic(const char* methodName, InstructionSet isa)
132 NamedIntrinsic result = NI_Illegal;
133 if (isa != InstructionSet_ILLEGAL)
135 for (int i = 0; i < NI_HW_INTRINSIC_END - NI_HW_INTRINSIC_START - 1; i++)
137 if (isa == hwIntrinsicInfoArray[i].isa && strcmp(methodName, hwIntrinsicInfoArray[i].intrinsicName) == 0)
139 result = hwIntrinsicInfoArray[i].intrinsicID;
147 //------------------------------------------------------------------------
148 // isaOfHWIntrinsic: map named intrinsic value to its instruction set
151 // intrinsic -- id of the intrinsic function.
154 // instruction set of the intrinsic.
156 InstructionSet Compiler::isaOfHWIntrinsic(NamedIntrinsic intrinsic)
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;
163 //------------------------------------------------------------------------
164 // ivalOfHWIntrinsic: get the imm8 value of this intrinsic from the hwIntrinsicInfoArray table
167 // intrinsic -- id of the intrinsic function.
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.
173 int Compiler::ivalOfHWIntrinsic(NamedIntrinsic intrinsic)
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;
180 //------------------------------------------------------------------------
181 // simdSizeOfHWIntrinsic: get the SIMD size of this intrinsic
184 // intrinsic -- id of the intrinsic function.
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
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)
195 assert(intrinsic > NI_HW_INTRINSIC_START && intrinsic < NI_HW_INTRINSIC_END);
197 HWIntrinsicFlag flags = flagsOfHWIntrinsic(intrinsic);
199 if ((flags & HW_Flag_UnfixedSIMDSize) == 0)
201 return hwIntrinsicInfoArray[intrinsic - NI_HW_INTRINSIC_START - 1].simdSize;
204 CORINFO_CLASS_HANDLE typeHnd = nullptr;
206 if (JITtype2varType(sig->retType) == TYP_STRUCT)
208 typeHnd = sig->retTypeSigClass;
212 assert((flags & HW_Flag_BaseTypeFromFirstArg) != 0);
213 typeHnd = info.compCompHnd->getArgClass(sig, sig->args);
216 unsigned simdSize = 0;
217 var_types baseType = getBaseTypeAndSizeOfSIMDType(typeHnd, &simdSize);
218 assert(simdSize > 0 && baseType != TYP_UNKNOWN);
222 // TODO_XARCH-CQ - refactoring of numArgsOfHWIntrinsic fast path into inlinable
223 // function and slow local static function may increase performance significantly
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.
231 // node -- GenTreeHWIntrinsic* node with nullptr default value
234 // number of arguments
236 int Compiler::numArgsOfHWIntrinsic(GenTreeHWIntrinsic* node)
238 assert(node != nullptr);
240 NamedIntrinsic intrinsic = node->gtHWIntrinsicId;
242 assert(intrinsic != NI_Illegal);
243 assert(intrinsic > NI_HW_INTRINSIC_START && intrinsic < NI_HW_INTRINSIC_END);
245 int numArgs = hwIntrinsicInfoArray[intrinsic - NI_HW_INTRINSIC_START - 1].numArgs;
251 assert(numArgs == -1);
253 GenTree* op1 = node->gtGetOp1();
254 GenTree* op2 = node->gtGetOp2();
263 if (op1->OperIsList())
266 GenTreeArgList* list = op1->AsArgList();
268 while (list != nullptr)
288 //------------------------------------------------------------------------
289 // lastOpOfHWIntrinsic: get the last operand of a HW intrinsic
292 // node -- the intrinsic node.
293 // numArgs-- number of argument
296 // number of arguments
298 GenTree* Compiler::lastOpOfHWIntrinsic(GenTreeHWIntrinsic* node, int numArgs)
300 GenTree* op1 = node->gtGetOp1();
301 GenTree* op2 = node->gtGetOp2();
307 assert(op1 != nullptr);
310 assert(op2 != nullptr);
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();
323 //------------------------------------------------------------------------
324 // insOfHWIntrinsic: get the instruction of the given intrinsic
327 // intrinsic -- id of the intrinsic function.
328 // type -- vector base type of this intrinsic
331 // the instruction of the given intrinsic on the base type
332 // return INS_invalid for unsupported base types
334 instruction Compiler::insOfHWIntrinsic(NamedIntrinsic intrinsic, var_types type)
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];
342 //------------------------------------------------------------------------
343 // categoryOfHWIntrinsic: get the category of the given intrinsic
346 // intrinsic -- id of the intrinsic function.
349 // the category of the given intrinsic
351 HWIntrinsicCategory Compiler::categoryOfHWIntrinsic(NamedIntrinsic intrinsic)
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;
358 //------------------------------------------------------------------------
359 // HWIntrinsicFlag: get the flags of the given intrinsic
362 // intrinsic -- id of the intrinsic function.
365 // the flags of the given intrinsic
367 HWIntrinsicFlag Compiler::flagsOfHWIntrinsic(NamedIntrinsic intrinsic)
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;
374 //------------------------------------------------------------------------
375 // getArgForHWIntrinsic: get the argument from the stack and match the signature
378 // argType -- the required type of argument
379 // argClass -- the class handle of argType
382 // get the argument at the given index from the stack and match the signature
384 GenTree* Compiler::getArgForHWIntrinsic(var_types argType, CORINFO_CLASS_HANDLE argClass)
386 GenTree* arg = nullptr;
387 if (argType == TYP_STRUCT)
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));
398 assert(varTypeIsArithmetic(argType));
399 arg = impPopStack().val;
400 assert(varTypeIsArithmetic(arg->TypeGet()));
401 assert(genActualType(arg->gtType) == genActualType(argType));
406 //------------------------------------------------------------------------
407 // immUpperBoundOfHWIntrinsic: get the max imm-value of non-full-range IMM intrinsic
410 // intrinsic -- intrinsic ID
413 // the max imm-value of non-full-range IMM intrinsic
415 int Compiler::immUpperBoundOfHWIntrinsic(NamedIntrinsic intrinsic)
417 assert(categoryOfHWIntrinsic(intrinsic) == HW_Category_IMM);
421 case NI_AVX_CompareScalar:
422 return 31; // enum FloatComparisonMode has 32 values
425 assert((flagsOfHWIntrinsic(intrinsic) & HW_Flag_FullRangeIMM) != 0);
430 //------------------------------------------------------------------------
431 // impNonConstFallback: convert certain SSE2/AVX2 shift intrinsic to its semantic alternative when the imm-arg is
432 // not a compile-time constant
435 // intrinsic -- intrinsic ID
436 // simdType -- Vector type
437 // baseType -- base type of the Vector128/256<T>
440 // return the IR of semantic alternative on non-const imm-arg
442 GenTree* Compiler::impNonConstFallback(NamedIntrinsic intrinsic, var_types simdType, var_types baseType)
444 assert((flagsOfHWIntrinsic(intrinsic) & HW_Flag_NoJmpTableIMM) != 0);
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:
454 GenTree* op2 = impPopStack().val;
455 GenTree* op1 = impSIMDPopStack(simdType);
457 gtNewSimdHWIntrinsicNode(TYP_SIMD16, op2, NI_SSE2_ConvertScalarToVector128Int32, TYP_INT, 16);
458 return gtNewSimdHWIntrinsicNode(simdType, op1, tmpOp, intrinsic, baseType, genTypeSize(simdType));
467 //------------------------------------------------------------------------
468 // isImmHWIntrinsic: check the intrinsic is a imm-intrinsic overload or not
471 // intrinsic -- intrinsic ID
472 // lastOp -- the last operand of the intrinsic that may point to the imm-arg
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.
479 bool Compiler::isImmHWIntrinsic(NamedIntrinsic intrinsic, GenTree* lastOp)
481 if (categoryOfHWIntrinsic(intrinsic) != HW_Category_IMM)
486 if ((flagsOfHWIntrinsic(intrinsic) & HW_Flag_MaybeIMM) != 0 && genActualType(lastOp->TypeGet()) != TYP_INT)
494 //------------------------------------------------------------------------
495 // addRangeCheckIfNeeded: add a GT_HW_INTRINSIC_CHK node for non-full-range imm-intrinsic
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
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
506 GenTree* Compiler::addRangeCheckIfNeeded(NamedIntrinsic intrinsic, GenTree* lastOp, bool mustExpand)
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))
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)
519 index = fgInsertCommaFormTemp(&lastOp);
523 index = gtCloneExpr(lastOp);
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);
536 //------------------------------------------------------------------------
537 // isFullyImplmentedISAClass: return true if all the hardware intrinsics
538 // of this ISA are implemented in RyuJIT.
541 // isa - Instruction set
543 // true - all the hardware intrinsics of "isa" are implemented in RyuJIT.
545 bool Compiler::isFullyImplmentedISAClass(InstructionSet isa)
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:
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:
573 //------------------------------------------------------------------------
577 // isa - Instruction set
579 // true - if "isa" only contains scalar instructions
581 bool Compiler::isScalarISA(InstructionSet isa)
585 case InstructionSet_BMI1:
586 case InstructionSet_BMI2:
587 case InstructionSet_LZCNT:
588 case InstructionSet_POPCNT:
596 //------------------------------------------------------------------------
597 // compSupportsHWIntrinsic: compiler support of hardware intrinsics
600 // isa - Instruction set
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)
608 return (featureSIMD || isScalarISA(isa)) && (
610 JitConfig.EnableIncompleteISAClass() ||
612 isFullyImplmentedISAClass(isa));
615 //------------------------------------------------------------------------
616 // hwIntrinsicSignatureTypeSupported: platform support of hardware intrinsics
619 // retType - return type
620 // sig - intrinsic signature
621 // flags - flags of the intrinsics
624 // Returns true iff the given type signature is supported
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)
631 CORINFO_CLASS_HANDLE argClass;
633 if ((flags & HW_Flag_64BitOnly) != 0)
637 else if ((flags & HW_Flag_SecondArgMaybe64Bit) != 0)
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));
645 return !varTypeIsLong(retType);
651 //------------------------------------------------------------------------
652 // impIsTableDrivenHWIntrinsic:
655 // category - category of a HW intrinsic
658 // returns true if this category can be table-driven in the importer
660 static bool impIsTableDrivenHWIntrinsic(HWIntrinsicCategory category, HWIntrinsicFlag flags)
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;
666 //------------------------------------------------------------------------
667 // impHWIntrinsic: dispatch hardware intrinsics to their own implementation
670 // intrinsic -- id of the intrinsic function.
671 // method -- method handle of the intrinsic function.
672 // sig -- signature of the intrinsic call
675 // the expanded intrinsic.
677 GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic,
678 CORINFO_METHOD_HANDLE method,
679 CORINFO_SIG_INFO* sig,
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;
689 if ((retType == TYP_STRUCT) && featureSIMD)
691 unsigned int sizeBytes;
692 baseType = getBaseTypeAndSizeOfSIMDType(sig->retTypeSigClass, &sizeBytes);
693 retType = getSIMDTypeForSize(sizeBytes);
694 assert(sizeBytes != 0);
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
703 compSupports(isa) && compSupportsHWIntrinsic(isa) && hwIntrinsicSignatureTypeSupported(retType, sig, flags);
705 if (category == HW_Category_IsSupportedProperty)
707 return gtNewIconNode(issupported);
709 // - calling to unsupported intrinsics must throw PlatforNotSupportedException
710 else if (!issupported)
712 return impUnsupportedHWIntrinsic(CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, method, sig, mustExpand);
714 // Avoid checking stacktop for 0-op intrinsics
715 if (sig->numArgs > 0 && isImmHWIntrinsic(intrinsic, impStackTop().val))
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)
722 if (!mustExpand && lastOp->IsCnsIntOrI() &&
723 lastOp->AsIntCon()->IconValue() > immUpperBoundOfHWIntrinsic(intrinsic))
729 if (!lastOp->IsCnsIntOrI())
731 if ((flags & HW_Flag_NoJmpTableIMM) == 0 && !mustExpand)
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.
739 else if ((flags & HW_Flag_NoJmpTableIMM) != 0)
741 return impNonConstFallback(intrinsic, retType, baseType);
746 bool isTableDriven = impIsTableDrivenHWIntrinsic(category, flags);
748 if (isTableDriven && ((category == HW_Category_MemoryStore) ||
749 ((flags & (HW_Flag_BaseTypeFromFirstArg | HW_Flag_BaseTypeFromSecondArg)) != 0)))
751 if ((flags & HW_Flag_BaseTypeFromFirstArg) != 0)
753 baseType = getBaseTypeOfSIMDType(info.compCompHnd->getArgClass(sig, sig->args));
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);
762 if (baseType == TYP_UNKNOWN) // the second argument is not a vector
764 baseType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, secondArg, &secondArgClass)));
765 assert(baseType != TYP_STRUCT);
769 assert(baseType != TYP_UNKNOWN);
772 if ((flags & (HW_Flag_OneTypeGeneric | HW_Flag_TwoTypeGeneric)) != 0)
774 if (!varTypeIsArithmetic(baseType))
776 return impUnsupportedHWIntrinsic(CORINFO_HELP_THROW_TYPE_NOT_SUPPORTED, method, sig, mustExpand);
779 if ((flags & HW_Flag_TwoTypeGeneric) != 0)
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))
786 return impUnsupportedHWIntrinsic(CORINFO_HELP_THROW_TYPE_NOT_SUPPORTED, method, sig, mustExpand);
791 if ((flags & HW_Flag_NoFloatingPointUsed) == 0)
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;
798 // table-driven importer of simple intrinsics
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;
806 assert(numArgs >= 0);
807 assert(insOfHWIntrinsic(intrinsic, baseType) != INS_invalid);
808 assert(simdSize == 32 || simdSize == 16);
810 GenTree* retNode = nullptr;
811 GenTree* op1 = nullptr;
812 GenTree* op2 = nullptr;
817 retNode = gtNewSimdHWIntrinsicNode(retType, intrinsic, baseType, simdSize);
820 argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, argList, &argClass)));
821 op1 = getArgForHWIntrinsic(argType, argClass);
822 retNode = gtNewSimdHWIntrinsicNode(retType, op1, intrinsic, baseType, simdSize);
825 argType = JITtype2varType(
826 strip(info.compCompHnd->getArgType(sig, info.compCompHnd->getArgNext(argList), &argClass)));
827 op2 = getArgForHWIntrinsic(argType, argClass);
829 op2 = addRangeCheckIfNeeded(intrinsic, op2, mustExpand);
831 argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, argList, &argClass)));
832 op1 = getArgForHWIntrinsic(argType, argClass);
834 retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, intrinsic, baseType, simdSize);
839 CORINFO_ARG_LIST_HANDLE arg2 = info.compCompHnd->getArgNext(argList);
840 CORINFO_ARG_LIST_HANDLE arg3 = info.compCompHnd->getArgNext(arg2);
842 argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg3, &argClass)));
843 GenTree* op3 = getArgForHWIntrinsic(argType, argClass);
845 op3 = addRangeCheckIfNeeded(intrinsic, op3, mustExpand);
847 argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg2, &argClass)));
848 op2 = getArgForHWIntrinsic(argType, argClass);
850 argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, argList, &argClass)));
851 op1 = getArgForHWIntrinsic(argType, argClass);
853 retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, op3, intrinsic, baseType, simdSize);
862 // other intrinsics need special importation
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);
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);
895 GenTree* Compiler::impSSEIntrinsic(NamedIntrinsic intrinsic,
896 CORINFO_METHOD_HANDLE method,
897 CORINFO_SIG_INFO* sig,
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);
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));
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);
921 case NI_SSE_Prefetch0:
922 case NI_SSE_Prefetch1:
923 case NI_SSE_Prefetch2:
924 case NI_SSE_PrefetchNonTemporal:
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);
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);
940 JITDUMP("Not implemented hardware intrinsic");
946 GenTree* Compiler::impSSE2Intrinsic(NamedIntrinsic intrinsic,
947 CORINFO_METHOD_HANDLE method,
948 CORINFO_SIG_INFO* sig,
951 GenTree* retNode = nullptr;
952 GenTree* op1 = nullptr;
953 GenTree* op2 = nullptr;
955 int simdSize = simdSizeOfHWIntrinsic(intrinsic, sig);
956 var_types baseType = TYP_UNKNOWN;
957 var_types retType = TYP_UNKNOWN;
959 // The fencing intrinsics don't take any operands and simdSize is 0
960 assert((simdSize == 16) || (simdSize == 0));
962 CORINFO_ARG_LIST_HANDLE argList = sig->args;
963 CORINFO_CLASS_HANDLE argClass;
964 var_types argType = TYP_UNKNOWN;
968 case NI_SSE2_CompareLessThan:
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)
976 retNode = gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1, op2, intrinsic, baseType, simdSize);
981 gtNewSimdHWIntrinsicNode(TYP_SIMD16, op2, op1, NI_SSE2_CompareGreaterThan, baseType, simdSize);
986 case NI_SSE2_LoadFence:
987 case NI_SSE2_MemoryFence:
989 assert(sig->numArgs == 0);
990 assert(JITtype2varType(sig->retType) == TYP_VOID);
991 assert(simdSize == 0);
993 retNode = gtNewSimdHWIntrinsicNode(TYP_VOID, intrinsic, TYP_VOID, simdSize);
997 case NI_SSE2_MoveMask:
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);
1008 case NI_SSE2_StoreNonTemporal:
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);
1019 JITDUMP("Not implemented hardware intrinsic");
1025 GenTree* Compiler::impSSE42Intrinsic(NamedIntrinsic intrinsic,
1026 CORINFO_METHOD_HANDLE method,
1027 CORINFO_SIG_INFO* sig,
1030 GenTree* retNode = nullptr;
1031 GenTree* op1 = nullptr;
1032 GenTree* op2 = nullptr;
1033 var_types callType = JITtype2varType(sig->retType);
1035 CORINFO_ARG_LIST_HANDLE argList = sig->args;
1036 CORINFO_CLASS_HANDLE argClass;
1037 CorInfoType corType;
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
1047 retNode = gtNewScalarHWIntrinsicNode(callType, op1, op2, NI_SSE42_Crc32);
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);
1055 JITDUMP("Not implemented hardware intrinsic");
1061 GenTree* Compiler::impAVXIntrinsic(NamedIntrinsic intrinsic,
1062 CORINFO_METHOD_HANDLE method,
1063 CORINFO_SIG_INFO* sig,
1066 GenTree* retNode = nullptr;
1067 GenTree* op1 = nullptr;
1068 GenTree* op2 = nullptr;
1069 var_types baseType = TYP_UNKNOWN;
1073 JITDUMP("Not implemented hardware intrinsic");
1079 GenTree* Compiler::impAVX2Intrinsic(NamedIntrinsic intrinsic,
1080 CORINFO_METHOD_HANDLE method,
1081 CORINFO_SIG_INFO* sig,
1084 GenTree* retNode = nullptr;
1085 GenTree* op1 = nullptr;
1086 GenTree* op2 = nullptr;
1087 var_types baseType = TYP_UNKNOWN;
1091 JITDUMP("Not implemented hardware intrinsic");
1097 GenTree* Compiler::impAESIntrinsic(NamedIntrinsic intrinsic,
1098 CORINFO_METHOD_HANDLE method,
1099 CORINFO_SIG_INFO* sig,
1105 GenTree* Compiler::impBMI1Intrinsic(NamedIntrinsic intrinsic,
1106 CORINFO_METHOD_HANDLE method,
1107 CORINFO_SIG_INFO* sig,
1113 GenTree* Compiler::impBMI2Intrinsic(NamedIntrinsic intrinsic,
1114 CORINFO_METHOD_HANDLE method,
1115 CORINFO_SIG_INFO* sig,
1121 GenTree* Compiler::impFMAIntrinsic(NamedIntrinsic intrinsic,
1122 CORINFO_METHOD_HANDLE method,
1123 CORINFO_SIG_INFO* sig,
1129 GenTree* Compiler::impLZCNTIntrinsic(NamedIntrinsic intrinsic,
1130 CORINFO_METHOD_HANDLE method,
1131 CORINFO_SIG_INFO* sig,
1134 assert(sig->numArgs == 1);
1135 var_types callType = JITtype2varType(sig->retType);
1136 return gtNewScalarHWIntrinsicNode(callType, impPopStack().val, NI_LZCNT_LeadingZeroCount);
1139 GenTree* Compiler::impPCLMULQDQIntrinsic(NamedIntrinsic intrinsic,
1140 CORINFO_METHOD_HANDLE method,
1141 CORINFO_SIG_INFO* sig,
1147 GenTree* Compiler::impPOPCNTIntrinsic(NamedIntrinsic intrinsic,
1148 CORINFO_METHOD_HANDLE method,
1149 CORINFO_SIG_INFO* sig,
1152 assert(sig->numArgs == 1);
1153 var_types callType = JITtype2varType(sig->retType);
1154 return gtNewScalarHWIntrinsicNode(callType, impPopStack().val, NI_POPCNT_PopCount);
1157 #endif // FEATURE_HW_INTRINSICS