Fix reading Time zone rules using Julian days (#17672)
[platform/upstream/coreclr.git] / src / jit / hwintrinsicArm64.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 #include "hwintrinsicArm64.h"
7
8 #ifdef FEATURE_HW_INTRINSICS
9
10 namespace IsaFlag
11 {
12 enum Flag
13 {
14 #define HARDWARE_INTRINSIC_CLASS(flag, isa) isa = 1ULL << InstructionSet_##isa,
15 #include "hwintrinsiclistArm64.h"
16     None     = 0,
17     Base     = 1ULL << InstructionSet_Base,
18     EveryISA = ~0ULL
19 };
20
21 Flag operator|(Flag a, Flag b)
22 {
23     return Flag(uint64_t(a) | uint64_t(b));
24 }
25
26 Flag flag(InstructionSet isa)
27 {
28     return Flag(1ULL << isa);
29 }
30 }
31
32 // clang-format off
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"
44 };
45 // clang-format on
46
47 extern const char* getHWIntrinsicName(NamedIntrinsic intrinsic)
48 {
49     return hwIntrinsicInfoArray[intrinsic - NI_HW_INTRINSIC_START - 1].intrinsicName;
50 }
51
52 const HWIntrinsicInfo& Compiler::getHWIntrinsicInfo(NamedIntrinsic intrinsic)
53 {
54     assert(intrinsic > NI_HW_INTRINSIC_START);
55     assert(intrinsic < NI_HW_INTRINSIC_END);
56
57     return hwIntrinsicInfoArray[intrinsic - NI_HW_INTRINSIC_START - 1];
58 }
59
60 //------------------------------------------------------------------------
61 // lookupHWIntrinsicISA: map class name to InstructionSet value
62 //
63 // Arguments:
64 //    className -- class name in System.Runtime.Intrinsics.Arm.Arm64
65 //
66 // Return Value:
67 //    Id for the ISA class if enabled.
68 //
69 InstructionSet Compiler::lookupHWIntrinsicISA(const char* className)
70 {
71     if (className != nullptr)
72     {
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"
79     }
80
81     return InstructionSet_NONE;
82 }
83
84 //------------------------------------------------------------------------
85 // lookupHWIntrinsic: map intrinsic name to named intrinsic value
86 //
87 // Arguments:
88 //    methodName -- name of the intrinsic function.
89 //    isa        -- instruction set of the intrinsic.
90 //
91 // Return Value:
92 //    Id for the hardware intrinsic.
93 //
94 // TODO-Throughput: replace sequential search by hash lookup
95 NamedIntrinsic Compiler::lookupHWIntrinsic(const char* className, const char* methodName)
96 {
97     InstructionSet isa    = lookupHWIntrinsicISA(className);
98     NamedIntrinsic result = NI_Illegal;
99     if (isa != InstructionSet_NONE)
100     {
101         IsaFlag::Flag isaFlag = IsaFlag::flag(isa);
102         for (int i = 0; i < NI_HW_INTRINSIC_END - NI_HW_INTRINSIC_START; i++)
103         {
104             if ((isaFlag & hwIntrinsicInfoArray[i].isaflags) &&
105                 strcmp(methodName, hwIntrinsicInfoArray[i].intrinsicName) == 0)
106             {
107                 if (compSupports(isa))
108                 {
109                     // Intrinsic is supported on platform
110                     result = hwIntrinsicInfoArray[i].intrinsicID;
111                 }
112                 else
113                 {
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;
120                 }
121                 break;
122             }
123         }
124     }
125     return result;
126 }
127
128 //------------------------------------------------------------------------
129 // impCheckImmediate: check if immediate is const and in range for inlining
130 //
131 bool Compiler::impCheckImmediate(GenTree* immediateOp, unsigned int max)
132 {
133     return immediateOp->IsCnsIntOrI() && (immediateOp->AsIntConCommon()->IconValue() < max);
134 }
135
136 //------------------------------------------------------------------------
137 // impHWIntrinsic: dispatch hardware intrinsics to their own implementation
138 // function
139 //
140 // Arguments:
141 //    intrinsic -- id of the intrinsic function.
142 //    method    -- method handle of the intrinsic function.
143 //    sig       -- signature of the intrinsic call
144 //
145 // Return Value:
146 //    the expanded intrinsic.
147 //
148 GenTree* Compiler::impHWIntrinsic(NamedIntrinsic        intrinsic,
149                                   CORINFO_METHOD_HANDLE method,
150                                   CORINFO_SIG_INFO*     sig,
151                                   bool                  mustExpand)
152 {
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;
161
162     switch (getHWIntrinsicInfo(intrinsic).form)
163     {
164         case HWIntrinsicInfo::SimdBinaryOp:
165         case HWIntrinsicInfo::SimdInsertOp:
166         case HWIntrinsicInfo::SimdSelectOp:
167         case HWIntrinsicInfo::SimdSetAllOp:
168         case HWIntrinsicInfo::SimdUnaryOp:
169         case HWIntrinsicInfo::SimdBinaryRMWOp:
170         case HWIntrinsicInfo::SimdTernaryRMWOp:
171         case HWIntrinsicInfo::Sha1HashOp:
172             simdClass = sig->retTypeClass;
173             break;
174         case HWIntrinsicInfo::SimdExtractOp:
175             info.compCompHnd->getArgType(sig, sig->args, &simdClass);
176             break;
177         default:
178             break;
179     }
180
181     // Simd instantiation type check
182     if (simdClass != nullptr)
183     {
184         simdBaseType = getBaseTypeAndSizeOfSIMDType(simdClass, &simdSizeBytes);
185
186         if (simdBaseType == TYP_UNKNOWN)
187         {
188             return impUnsupportedHWIntrinsic(CORINFO_HELP_THROW_TYPE_NOT_SUPPORTED, method, sig, mustExpand);
189         }
190         simdType = getSIMDTypeForSize(simdSizeBytes);
191     }
192
193     switch (getHWIntrinsicInfo(intrinsic).form)
194     {
195         case HWIntrinsicInfo::IsSupported:
196             return gtNewIconNode((intrinsic == NI_ARM64_IsSupported_True) ? 1 : 0);
197
198         case HWIntrinsicInfo::Unsupported:
199             return impUnsupportedHWIntrinsic(CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, method, sig, mustExpand);
200
201         case HWIntrinsicInfo::SimdBinaryOp:
202         case HWIntrinsicInfo::SimdBinaryRMWOp:
203             // op1 is the first operand
204             // op2 is the second operand
205             op2 = impSIMDPopStack(simdType);
206             op1 = impSIMDPopStack(simdType);
207
208             return gtNewSimdHWIntrinsicNode(simdType, op1, op2, intrinsic, simdBaseType, simdSizeBytes);
209
210         case HWIntrinsicInfo::SimdTernaryRMWOp:
211         case HWIntrinsicInfo::SimdSelectOp:
212             // op1 is the first operand
213             // op2 is the second operand
214             // op3 is the third operand
215             op3 = impSIMDPopStack(simdType);
216             op2 = impSIMDPopStack(simdType);
217             op1 = impSIMDPopStack(simdType);
218
219             return gtNewSimdHWIntrinsicNode(simdType, op1, op2, op3, intrinsic, simdBaseType, simdSizeBytes);
220
221         case HWIntrinsicInfo::SimdSetAllOp:
222             op1 = impPopStack().val;
223
224             return gtNewSimdHWIntrinsicNode(simdType, op1, intrinsic, simdBaseType, simdSizeBytes);
225
226         case HWIntrinsicInfo::SimdUnaryOp:
227             op1 = impSIMDPopStack(simdType);
228
229             return gtNewSimdHWIntrinsicNode(simdType, op1, intrinsic, simdBaseType, simdSizeBytes);
230
231         case HWIntrinsicInfo::SimdExtractOp:
232             if (!mustExpand && !impCheckImmediate(impStackTop(0).val, getSIMDVectorLength(simdSizeBytes, simdBaseType)))
233             {
234                 // Immediate lane not constant or out of range
235                 return nullptr;
236             }
237             op2 = impPopStack().val;
238             op1 = impSIMDPopStack(simdType);
239
240             return gtNewScalarHWIntrinsicNode(JITtype2varType(sig->retType), op1, op2, intrinsic);
241
242         case HWIntrinsicInfo::SimdInsertOp:
243             if (!mustExpand && !impCheckImmediate(impStackTop(1).val, getSIMDVectorLength(simdSizeBytes, simdBaseType)))
244             {
245                 // Immediate lane not constant or out of range
246                 return nullptr;
247             }
248             op3 = impPopStack().val;
249             op2 = impPopStack().val;
250             op1 = impSIMDPopStack(simdType);
251
252             return gtNewSimdHWIntrinsicNode(simdType, op1, op2, op3, intrinsic, simdBaseType, simdSizeBytes);
253
254         case HWIntrinsicInfo::Sha1HashOp:
255             op3 = impSIMDPopStack(simdType);
256             op2 = impPopStack().val;
257             op1 = impSIMDPopStack(simdType);
258
259             return gtNewSimdHWIntrinsicNode(simdType, op1, op2, op3, intrinsic, simdBaseType, simdSizeBytes);
260
261         case HWIntrinsicInfo::Sha1RotateOp:
262             assert(sig->numArgs == 1);
263             return gtNewScalarHWIntrinsicNode(TYP_UINT, impPopStack().val, NI_ARM64_Sha1FixedRotate);
264
265         default:
266             JITDUMP("Not implemented hardware intrinsic form");
267             assert(!"Unimplemented SIMD Intrinsic form");
268
269             break;
270     }
271     return retNode;
272 }
273
274 #endif // FEATURE_HW_INTRINSICS