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.
6 #include "hwintrinsicArm64.h"
8 #ifdef FEATURE_HW_INTRINSICS
14 #define HARDWARE_INTRINSIC_CLASS(flag, isa) isa = 1ULL << InstructionSet_##isa,
15 #include "hwintrinsiclistArm64.h"
17 Base = 1ULL << InstructionSet_Base,
21 Flag operator|(Flag a, Flag b)
23 return Flag(uint64_t(a) | uint64_t(b));
26 Flag flag(InstructionSet isa)
28 return Flag(1ULL << isa);
33 static const HWIntrinsicInfo hwIntrinsicInfoArray[] = {
34 // Add lookupHWIntrinsic special cases see lookupHWIntrinsic() below
35 // NI_ARM64_IsSupported_True is used to expand get_IsSupported to const true
36 // NI_ARM64_IsSupported_False is used to expand get_IsSupported to const false
37 // NI_ARM64_PlatformNotSupported to throw PlatformNotSupported exception for every intrinsic not supported on the running platform
38 {NI_ARM64_IsSupported_True, "get_IsSupported", IsaFlag::EveryISA, HWIntrinsicInfo::IsSupported, HWIntrinsicInfo::None, {}},
39 {NI_ARM64_IsSupported_False, "::NI_ARM64_IsSupported_False", IsaFlag::EveryISA, HWIntrinsicInfo::IsSupported, HWIntrinsicInfo::None, {}},
40 {NI_ARM64_PlatformNotSupported, "::NI_ARM64_PlatformNotSupported", IsaFlag::EveryISA, HWIntrinsicInfo::Unsupported, HWIntrinsicInfo::None, {}},
41 #define HARDWARE_INTRINSIC(id, isa, name, form, i0, i1, i2, flags) \
42 {id, #name, IsaFlag::isa, HWIntrinsicInfo::form, HWIntrinsicInfo::flags, { i0, i1, i2 }},
43 #include "hwintrinsiclistArm64.h"
47 extern const char* getHWIntrinsicName(NamedIntrinsic intrinsic)
49 return hwIntrinsicInfoArray[intrinsic - NI_HW_INTRINSIC_START - 1].intrinsicName;
52 const HWIntrinsicInfo& Compiler::getHWIntrinsicInfo(NamedIntrinsic intrinsic)
54 assert(intrinsic > NI_HW_INTRINSIC_START);
55 assert(intrinsic < NI_HW_INTRINSIC_END);
57 return hwIntrinsicInfoArray[intrinsic - NI_HW_INTRINSIC_START - 1];
60 //------------------------------------------------------------------------
61 // lookupHWIntrinsicISA: map class name to InstructionSet value
64 // className -- class name in System.Runtime.Intrinsics.Arm.Arm64
67 // Id for the ISA class if enabled.
69 InstructionSet Compiler::lookupHWIntrinsicISA(const char* className)
71 if (className != nullptr)
73 if (strcmp(className, "Base") == 0)
74 return InstructionSet_Base;
75 #define HARDWARE_INTRINSIC_CLASS(flag, isa) \
76 if (strcmp(className, #isa) == 0) \
77 return InstructionSet_##isa;
78 #include "hwintrinsiclistArm64.h"
81 return InstructionSet_NONE;
84 //------------------------------------------------------------------------
85 // lookupHWIntrinsic: map intrinsic name to named intrinsic value
88 // methodName -- name of the intrinsic function.
89 // isa -- instruction set of the intrinsic.
92 // Id for the hardware intrinsic.
94 // TODO-Throughput: replace sequential search by hash lookup
95 NamedIntrinsic Compiler::lookupHWIntrinsic(const char* className, const char* methodName)
97 InstructionSet isa = lookupHWIntrinsicISA(className);
98 NamedIntrinsic result = NI_Illegal;
99 if (isa != InstructionSet_NONE)
101 IsaFlag::Flag isaFlag = IsaFlag::flag(isa);
102 for (int i = 0; i < NI_HW_INTRINSIC_END - NI_HW_INTRINSIC_START; i++)
104 if ((isaFlag & hwIntrinsicInfoArray[i].isaflags) &&
105 strcmp(methodName, hwIntrinsicInfoArray[i].intrinsicName) == 0)
107 if (compSupports(isa))
109 // Intrinsic is supported on platform
110 result = hwIntrinsicInfoArray[i].intrinsicID;
114 // When the intrinsic class is not supported
115 // Return NI_ARM64_PlatformNotSupported for all intrinsics
116 // Return NI_ARM64_IsSupported_False for the IsSupported property
117 result = (hwIntrinsicInfoArray[i].intrinsicID != NI_ARM64_IsSupported_True)
118 ? NI_ARM64_PlatformNotSupported
119 : NI_ARM64_IsSupported_False;
128 //------------------------------------------------------------------------
129 // impCheckImmediate: check if immediate is const and in range for inlining
131 bool Compiler::impCheckImmediate(GenTree* immediateOp, unsigned int max)
133 return immediateOp->IsCnsIntOrI() && (immediateOp->AsIntConCommon()->IconValue() < max);
136 //------------------------------------------------------------------------
137 // impHWIntrinsic: dispatch hardware intrinsics to their own implementation
141 // intrinsic -- id of the intrinsic function.
142 // method -- method handle of the intrinsic function.
143 // sig -- signature of the intrinsic call
146 // the expanded intrinsic.
148 GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic,
149 CORINFO_METHOD_HANDLE method,
150 CORINFO_SIG_INFO* sig,
153 GenTree* retNode = nullptr;
154 GenTree* op1 = nullptr;
155 GenTree* op2 = nullptr;
156 GenTree* op3 = nullptr;
157 CORINFO_CLASS_HANDLE simdClass = nullptr;
158 var_types simdType = TYP_UNKNOWN;
159 var_types simdBaseType = TYP_UNKNOWN;
160 unsigned simdSizeBytes = 0;
162 switch (getHWIntrinsicInfo(intrinsic).form)
164 case HWIntrinsicInfo::SimdBinaryOp:
165 case HWIntrinsicInfo::SimdInsertOp:
166 case HWIntrinsicInfo::SimdSelectOp:
167 case HWIntrinsicInfo::SimdSetAllOp:
168 case HWIntrinsicInfo::SimdUnaryOp:
169 simdClass = sig->retTypeClass;
171 case HWIntrinsicInfo::SimdExtractOp:
172 info.compCompHnd->getArgType(sig, sig->args, &simdClass);
178 // Simd instantiation type check
179 if (simdClass != nullptr)
181 simdBaseType = getBaseTypeAndSizeOfSIMDType(simdClass, &simdSizeBytes);
183 if (simdBaseType == TYP_UNKNOWN)
185 return impUnsupportedHWIntrinsic(CORINFO_HELP_THROW_TYPE_NOT_SUPPORTED, method, sig, mustExpand);
187 simdType = getSIMDTypeForSize(simdSizeBytes);
190 switch (getHWIntrinsicInfo(intrinsic).form)
192 case HWIntrinsicInfo::IsSupported:
193 return gtNewIconNode((intrinsic == NI_ARM64_IsSupported_True) ? 1 : 0);
195 case HWIntrinsicInfo::Unsupported:
196 return impUnsupportedHWIntrinsic(CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, method, sig, mustExpand);
198 case HWIntrinsicInfo::SimdBinaryOp:
199 // op1 is the first operand
200 // op2 is the second operand
201 op2 = impSIMDPopStack(simdType);
202 op1 = impSIMDPopStack(simdType);
204 return gtNewSimdHWIntrinsicNode(simdType, op1, op2, intrinsic, simdBaseType, simdSizeBytes);
206 case HWIntrinsicInfo::SimdSelectOp:
207 // op1 is the first operand
208 // op2 is the second operand
209 // op3 is the third operand
210 op3 = impSIMDPopStack(simdType);
211 op2 = impSIMDPopStack(simdType);
212 op1 = impSIMDPopStack(simdType);
214 return gtNewSimdHWIntrinsicNode(simdType, op1, op2, op3, intrinsic, simdBaseType, simdSizeBytes);
216 case HWIntrinsicInfo::SimdSetAllOp:
217 op1 = impPopStack().val;
219 return gtNewSimdHWIntrinsicNode(simdType, op1, intrinsic, simdBaseType, simdSizeBytes);
221 case HWIntrinsicInfo::SimdUnaryOp:
222 op1 = impSIMDPopStack(simdType);
224 return gtNewSimdHWIntrinsicNode(simdType, op1, intrinsic, simdBaseType, simdSizeBytes);
226 case HWIntrinsicInfo::SimdExtractOp:
227 if (!mustExpand && !impCheckImmediate(impStackTop(0).val, getSIMDVectorLength(simdSizeBytes, simdBaseType)))
229 // Immediate lane not constant or out of range
232 op2 = impPopStack().val;
233 op1 = impSIMDPopStack(simdType);
235 return gtNewScalarHWIntrinsicNode(JITtype2varType(sig->retType), op1, op2, intrinsic);
237 case HWIntrinsicInfo::SimdInsertOp:
238 if (!mustExpand && !impCheckImmediate(impStackTop(1).val, getSIMDVectorLength(simdSizeBytes, simdBaseType)))
240 // Immediate lane not constant or out of range
243 op3 = impPopStack().val;
244 op2 = impPopStack().val;
245 op1 = impSIMDPopStack(simdType);
247 return gtNewSimdHWIntrinsicNode(simdType, op1, op2, op3, intrinsic, simdBaseType, simdSizeBytes);
250 JITDUMP("Not implemented hardware intrinsic form");
251 assert(!"Unimplemented SIMD Intrinsic form");
258 #endif // FEATURE_HW_INTRINSICS