1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #ifndef BERBERIS_INTRINSICS_COMMON_TO_X86_INTRINSICS_BINDINGS_H_
18 #define BERBERIS_INTRINSICS_COMMON_TO_X86_INTRINSICS_BINDINGS_H_
19
20 #include <xmmintrin.h>
21
22 #include <cstdint>
23
24 #include "berberis/base/dependent_false.h"
25 #include "berberis/intrinsics/intrinsics_args.h"
26 #include "berberis/intrinsics/type_traits.h"
27
28 namespace berberis::intrinsics::bindings {
29
30 class Imm2 {
31 public:
32 using Type = int8_t;
33 static constexpr bool kIsImmediate = true;
34 };
35
36 class Imm8 {
37 public:
38 using Type = int8_t;
39 static constexpr bool kIsImmediate = true;
40 };
41
42 class Imm16 {
43 public:
44 using Type = int16_t;
45 static constexpr bool kIsImmediate = true;
46 };
47
48 class Imm32 {
49 public:
50 using Type = int32_t;
51 static constexpr bool kIsImmediate = true;
52 };
53
54 class Imm64 {
55 public:
56 using Type = int64_t;
57 static constexpr bool kIsImmediate = true;
58 };
59
60 class AL {
61 public:
62 using Type = uint8_t;
63 static constexpr bool kIsImmediate = false;
64 static constexpr bool kIsImplicitReg = true;
65 static constexpr char kAsRegister = 'a';
66 };
67
68 class AX {
69 public:
70 using Type = uint16_t;
71 static constexpr bool kIsImmediate = false;
72 static constexpr bool kIsImplicitReg = true;
73 static constexpr char kAsRegister = 'a';
74 };
75
76 class EAX {
77 public:
78 using Type = uint32_t;
79 static constexpr bool kIsImmediate = false;
80 static constexpr bool kIsImplicitReg = true;
81 static constexpr char kAsRegister = 'a';
82 template <typename MachineInsnArch>
83 static constexpr auto kRegClass = MachineInsnArch::kEAX;
84 };
85
86 class RAX {
87 public:
88 using Type = uint32_t;
89 static constexpr bool kIsImmediate = false;
90 static constexpr bool kIsImplicitReg = true;
91 static constexpr char kAsRegister = 'a';
92 template <typename MachineInsnArch>
93 static constexpr auto kRegClass = MachineInsnArch::kRAX;
94 };
95
96 class CL {
97 public:
98 using Type = uint8_t;
99 static constexpr bool kIsImmediate = false;
100 static constexpr bool kIsImplicitReg = true;
101 static constexpr char kAsRegister = 'c';
102 template <typename MachineInsnArch>
103 static constexpr auto kRegClass = MachineInsnArch::kCL;
104 };
105
106 class CX {
107 public:
108 using Type = uint16_t;
109 static constexpr bool kIsImmediate = false;
110 static constexpr bool kIsImplicitReg = true;
111 static constexpr char kAsRegister = 'c';
112 };
113
114 class ECX {
115 public:
116 using Type = uint32_t;
117 static constexpr bool kIsImmediate = false;
118 static constexpr bool kIsImplicitReg = true;
119 static constexpr char kAsRegister = 'c';
120 template <typename MachineInsnArch>
121 static constexpr auto kRegClass = MachineInsnArch::kECX;
122 };
123
124 class RCX {
125 public:
126 using Type = uint32_t;
127 static constexpr bool kIsImmediate = false;
128 static constexpr bool kIsImplicitReg = true;
129 static constexpr char kAsRegister = 'c';
130 template <typename MachineInsnArch>
131 static constexpr auto kRegClass = MachineInsnArch::kRCX;
132 };
133
134 class DL {
135 public:
136 using Type = uint8_t;
137 static constexpr bool kIsImmediate = false;
138 static constexpr bool kIsImplicitReg = true;
139 static constexpr char kAsRegister = 'd';
140 };
141
142 class DX {
143 public:
144 using Type = uint16_t;
145 static constexpr bool kIsImmediate = false;
146 static constexpr bool kIsImplicitReg = true;
147 static constexpr char kAsRegister = 'd';
148 };
149
150 class EDX {
151 public:
152 using Type = uint32_t;
153 static constexpr bool kIsImmediate = false;
154 static constexpr bool kIsImplicitReg = true;
155 static constexpr char kAsRegister = 'd';
156 template <typename MachineInsnArch>
157 static constexpr auto kRegClass = MachineInsnArch::kEDX;
158 };
159
160 class RDX {
161 public:
162 using Type = uint32_t;
163 static constexpr bool kIsImmediate = false;
164 static constexpr bool kIsImplicitReg = true;
165 static constexpr char kAsRegister = 'd';
166 template <typename MachineInsnArch>
167 static constexpr auto kRegClass = MachineInsnArch::kRDX;
168 };
169
170 class GeneralReg8 {
171 public:
172 using Type = uint8_t;
173 static constexpr bool kIsImmediate = false;
174 static constexpr bool kIsImplicitReg = false;
175 static constexpr char kAsRegister = 'q';
176 template <typename MachineInsnArch>
177 static constexpr auto kRegClass = MachineInsnArch::kGeneralReg8;
178 };
179
180 class GeneralReg16 {
181 public:
182 using Type = uint16_t;
183 static constexpr bool kIsImmediate = false;
184 static constexpr bool kIsImplicitReg = false;
185 static constexpr char kAsRegister = 'r';
186 template <typename MachineInsnArch>
187 static constexpr auto kRegClass = MachineInsnArch::kGeneralReg16;
188 };
189
190 class GeneralReg32 {
191 public:
192 using Type = uint32_t;
193 static constexpr bool kIsImmediate = false;
194 static constexpr bool kIsImplicitReg = false;
195 static constexpr char kAsRegister = 'r';
196 template <typename MachineInsnArch>
197 static constexpr auto kRegClass = MachineInsnArch::kGeneralReg32;
198 };
199
200 class GeneralReg64 {
201 public:
202 using Type = uint64_t;
203 static constexpr bool kIsImmediate = false;
204 static constexpr bool kIsImplicitReg = false;
205 static constexpr char kAsRegister = 'r';
206 template <typename MachineInsnArch>
207 static constexpr auto kRegClass = MachineInsnArch::kGeneralReg64;
208 };
209
210 class FLAGS {
211 public:
212 static constexpr bool kIsImmediate = false;
213 static constexpr bool kIsImplicitReg = true;
214 static constexpr char kAsRegister = 0;
215 template <typename MachineInsnArch>
216 static constexpr auto kRegClass = MachineInsnArch::kFLAGS;
217 };
218
219 class FpReg32 {
220 public:
221 using Type = __m128;
222 static constexpr bool kIsImmediate = false;
223 static constexpr bool kIsImplicitReg = false;
224 static constexpr char kAsRegister = 'x';
225 template <typename MachineInsnArch>
226 static constexpr auto kRegClass = MachineInsnArch::kFpReg32;
227 };
228
229 class FpReg64 {
230 public:
231 using Type = __m128;
232 static constexpr bool kIsImmediate = false;
233 static constexpr bool kIsImplicitReg = false;
234 static constexpr char kAsRegister = 'x';
235 template <typename MachineInsnArch>
236 static constexpr auto kRegClass = MachineInsnArch::kFpReg64;
237 };
238
239 class VecReg128 {
240 public:
241 using Type = __m128;
242 static constexpr bool kIsImmediate = false;
243 static constexpr bool kIsImplicitReg = false;
244 static constexpr char kAsRegister = 'x';
245 template <typename MachineInsnArch>
246 static constexpr auto kRegClass = MachineInsnArch::kVecReg128;
247 };
248
249 class XmmReg {
250 public:
251 using Type = __m128;
252 static constexpr bool kIsImmediate = false;
253 static constexpr bool kIsImplicitReg = false;
254 static constexpr char kAsRegister = 'x';
255 template <typename MachineInsnArch>
256 static constexpr auto kRegClass = MachineInsnArch::kXmmReg;
257 };
258
259 class Mem8 {
260 public:
261 using Type = uint8_t;
262 static constexpr bool kIsImmediate = false;
263 static constexpr char kAsRegister = 'm';
264 };
265
266 class Mem16 {
267 public:
268 using Type = uint16_t;
269 static constexpr bool kIsImmediate = false;
270 static constexpr char kAsRegister = 'm';
271 };
272
273 class Mem32 {
274 public:
275 using Type = uint32_t;
276 static constexpr bool kIsImmediate = false;
277 static constexpr char kAsRegister = 'm';
278 };
279
280 class Mem64 {
281 public:
282 using Type = uint64_t;
283 static constexpr bool kIsImmediate = false;
284 static constexpr char kAsRegister = 'm';
285 };
286
287 class MemX87 {
288 public:
289 static constexpr bool kIsImmediate = false;
290 static constexpr char kAsRegister = 'm';
291 };
292
293 // // Tag classes. They are never instantioned, only used as tags to pass information about
294 // bindings.
295 class Def;
296 class DefEarlyClobber;
297 class Use;
298 class UseDef;
299
300 template <typename Tag, typename MachineRegKind>
ToRegKind()301 constexpr auto ToRegKind() {
302 if constexpr (std::is_same_v<Tag, Def>) {
303 return MachineRegKind::kDef;
304 } else if constexpr (std::is_same_v<Tag, DefEarlyClobber>) {
305 return MachineRegKind::kDefEarlyClobber;
306 } else if constexpr (std::is_same_v<Tag, Use>) {
307 return MachineRegKind::kUse;
308 } else if constexpr (std::is_same_v<Tag, UseDef>) {
309 return MachineRegKind::kUseDef;
310 } else {
311 static_assert(kDependentTypeFalse<Tag>);
312 }
313 }
314
315 template <typename Tag, typename MachineRegKind>
316 inline constexpr auto kRegKind = ToRegKind<Tag, MachineRegKind>();
317
318 enum CPUIDRestriction : int {
319 kNoCPUIDRestriction = 0,
320 kHas3DNOW,
321 kHas3DNOWP,
322 kHasADX,
323 kHasAES,
324 kHasAESAVX,
325 kHasAMXBF16,
326 kHasAMXFP16,
327 kHasAMXINT8,
328 kHasAMXTILE,
329 kHasAVX,
330 kHasAVX2,
331 kHasAVX5124FMAPS,
332 kHasAVX5124VNNIW,
333 kHasAVX512BF16,
334 kHasAVX512BITALG,
335 kHasAVX512BW,
336 kHasAVX512CD,
337 kHasAVX512DQ,
338 kHasAVX512ER,
339 kHasAVX512F,
340 kHasAVX512FP16,
341 kHasAVX512IFMA,
342 kHasAVX512PF,
343 kHasAVX512VBMI,
344 kHasAVX512VBMI2,
345 kHasAVX512VL,
346 kHasAVX512VNNI,
347 kHasAVX512VPOPCNTDQ,
348 kHasBMI,
349 kHasBMI2,
350 kHasCLMUL,
351 kHasCMOV,
352 kHasCMPXCHG16B,
353 kHasCMPXCHG8B,
354 kHasF16C,
355 kHasFMA,
356 kHasFMA4,
357 kHasFXSAVE,
358 kHasLZCNT,
359 // BMI2 is set and PDEP/PEXT are ok to use. See more here:
360 // https://twitter.com/instlatx64/status/1322503571288559617
361 kHashPDEP,
362 kHasPOPCNT,
363 kHasRDSEED,
364 kHasSERIALIZE,
365 kHasSHA,
366 kHasSSE,
367 kHasSSE2,
368 kHasSSE3,
369 kHasSSE4_1,
370 kHasSSE4_2,
371 kHasSSE4a,
372 kHasSSSE3,
373 kHasTBM,
374 kHasVAES,
375 kHasX87,
376 kIsAuthenticAMD
377 };
378
379 enum PreciseNanOperationsHandling : int {
380 kNoNansOperation = 0,
381 kPreciseNanOperationsHandling,
382 kImpreciseNanOperationsHandling
383 };
384
385 template <auto kIntrinsicTemplateName,
386 auto kMacroInstructionTemplateName,
387 auto kMnemo,
388 typename GetOpcode,
389 CPUIDRestriction kCPUIDRestrictionTemplateValue,
390 PreciseNanOperationsHandling kPreciseNanOperationsHandlingTemplateValue,
391 bool kSideEffectsTemplateValue,
392 typename... Types>
393 class AsmCallInfo;
394
395 template <auto kIntrinsicTemplateName,
396 auto kMacroInstructionTemplateName,
397 auto kMnemo,
398 typename GetOpcode,
399 CPUIDRestriction kCPUIDRestrictionTemplateValue,
400 PreciseNanOperationsHandling kPreciseNanOperationsHandlingTemplateValue,
401 bool kSideEffectsTemplateValue,
402 typename... InputArgumentsTypes,
403 typename... OutputArgumentsTypes,
404 typename... BindingsTypes>
405 class AsmCallInfo<kIntrinsicTemplateName,
406 kMacroInstructionTemplateName,
407 kMnemo,
408 GetOpcode,
409 kCPUIDRestrictionTemplateValue,
410 kPreciseNanOperationsHandlingTemplateValue,
411 kSideEffectsTemplateValue,
412 std::tuple<InputArgumentsTypes...>,
413 std::tuple<OutputArgumentsTypes...>,
414 BindingsTypes...>
415 final {
416 public:
417 static constexpr auto kIntrinsic = kIntrinsicTemplateName;
418 static constexpr auto kMacroInstruction = kMacroInstructionTemplateName;
419 // TODO(b/260725458): Use lambda template argument after C++20 becomes available.
420 template <typename Opcode>
421 static constexpr auto kOpcode = GetOpcode{}.template operator()<Opcode>();
422 static constexpr CPUIDRestriction kCPUIDRestriction = kCPUIDRestrictionTemplateValue;
423 static constexpr PreciseNanOperationsHandling kPreciseNanOperationsHandling =
424 kPreciseNanOperationsHandlingTemplateValue;
425 static constexpr bool kSideEffects = kSideEffectsTemplateValue;
426 static constexpr const char* InputArgumentsTypeNames[] = {
427 TypeTraits<InputArgumentsTypes>::kName...};
428 static constexpr const char* OutputArgumentsTypeNames[] = {
429 TypeTraits<OutputArgumentsTypes>::kName...};
430 template <typename Callback, typename... Args>
ProcessBindings(Callback && callback,Args &&...args)431 constexpr static void ProcessBindings(Callback&& callback, Args&&... args) {
432 (callback(ArgTraits<BindingsTypes>(), std::forward<Args>(args)...), ...);
433 }
434 template <typename Callback, typename... Args>
MakeTuplefromBindings(Callback && callback,Args &&...args)435 constexpr static auto MakeTuplefromBindings(Callback&& callback, Args&&... args) {
436 return std::tuple_cat(callback(ArgTraits<BindingsTypes>(), std::forward<Args>(args)...)...);
437 }
438 using InputArguments = std::tuple<InputArgumentsTypes...>;
439 using OutputArguments = std::tuple<OutputArgumentsTypes...>;
440 using Bindings = std::tuple<BindingsTypes...>;
441 using IntrinsicType = std::conditional_t<std::tuple_size_v<OutputArguments> == 0,
442 void (*)(InputArgumentsTypes...),
443 OutputArguments (*)(InputArgumentsTypes...)>;
444 template <template <typename, auto, auto, typename...> typename MachineInsnType,
445 template <typename...>
446 typename ConstructorArgs,
447 typename Opcode>
448 using MachineInsn = MachineInsnType<AsmCallInfo,
449 kMnemo,
450 kOpcode<Opcode>,
451 ConstructorArgs<BindingsTypes...>,
452 BindingsTypes...>;
453 };
454
455 } // namespace berberis::intrinsics::bindings
456
457 #endif // BERBERIS_INTRINSICS_COMMON_TO_X86_INTRINSICS_BINDINGS_H_
458