1 | //===- SveEmitter.cpp - Generate arm_sve.h for use with clang -*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This tablegen backend is responsible for emitting arm_sve.h, which includes |
10 | // a declaration and definition of each function specified by the ARM C/C++ |
11 | // Language Extensions (ACLE). |
12 | // |
13 | // For details, visit: |
14 | // https://developer.arm.com/architectures/system-architectures/software-standards/acle |
15 | // |
16 | // Each SVE instruction is implemented in terms of 1 or more functions which |
17 | // are suffixed with the element type of the input vectors. Functions may be |
18 | // implemented in terms of generic vector operations such as +, *, -, etc. or |
19 | // by calling a __builtin_-prefixed function which will be handled by clang's |
20 | // CodeGen library. |
21 | // |
22 | // See also the documentation in include/clang/Basic/arm_sve.td. |
23 | // |
24 | //===----------------------------------------------------------------------===// |
25 | |
26 | #include "llvm/ADT/ArrayRef.h" |
27 | #include "llvm/ADT/STLExtras.h" |
28 | #include "llvm/ADT/StringExtras.h" |
29 | #include "llvm/ADT/StringMap.h" |
30 | #include "llvm/TableGen/Error.h" |
31 | #include "llvm/TableGen/Record.h" |
32 | #include <array> |
33 | #include <cctype> |
34 | #include <set> |
35 | #include <sstream> |
36 | #include <string> |
37 | #include <tuple> |
38 | |
39 | using namespace llvm; |
40 | |
41 | enum ClassKind { |
42 | ClassNone, |
43 | ClassS, // signed/unsigned, e.g., "_s8", "_u8" suffix |
44 | ClassG, // Overloaded name without type suffix |
45 | }; |
46 | |
47 | enum class ACLEKind { SVE, SME }; |
48 | |
49 | using TypeSpec = std::string; |
50 | |
51 | namespace { |
52 | |
53 | class ImmCheck { |
54 | unsigned Arg; |
55 | unsigned Kind; |
56 | unsigned ElementSizeInBits; |
57 | |
58 | public: |
59 | ImmCheck(unsigned Arg, unsigned Kind, unsigned ElementSizeInBits = 0) |
60 | : Arg(Arg), Kind(Kind), ElementSizeInBits(ElementSizeInBits) {} |
61 | ImmCheck(const ImmCheck &Other) = default; |
62 | ~ImmCheck() = default; |
63 | |
64 | unsigned getArg() const { return Arg; } |
65 | unsigned getKind() const { return Kind; } |
66 | unsigned getElementSizeInBits() const { return ElementSizeInBits; } |
67 | }; |
68 | |
69 | class SVEType { |
70 | bool Float, Signed, Immediate, Void, Constant, Pointer, BFloat; |
71 | bool DefaultType, IsScalable, Predicate, PredicatePattern, PrefetchOp, |
72 | Svcount; |
73 | unsigned Bitwidth, ElementBitwidth, NumVectors; |
74 | |
75 | public: |
76 | SVEType() : SVEType("" , 'v') {} |
77 | |
78 | SVEType(StringRef TS, char CharMod, unsigned NumVectors = 1) |
79 | : Float(false), Signed(true), Immediate(false), Void(false), |
80 | Constant(false), Pointer(false), BFloat(false), DefaultType(false), |
81 | IsScalable(true), Predicate(false), PredicatePattern(false), |
82 | PrefetchOp(false), Svcount(false), Bitwidth(128), ElementBitwidth(~0U), |
83 | NumVectors(NumVectors) { |
84 | if (!TS.empty()) |
85 | applyTypespec(TS); |
86 | applyModifier(Mod: CharMod); |
87 | } |
88 | |
89 | SVEType(const SVEType &Base, unsigned NumV) : SVEType(Base) { |
90 | NumVectors = NumV; |
91 | } |
92 | |
93 | bool isPointer() const { return Pointer; } |
94 | bool isVoidPointer() const { return Pointer && Void; } |
95 | bool isSigned() const { return Signed; } |
96 | bool isImmediate() const { return Immediate; } |
97 | bool isScalar() const { return NumVectors == 0; } |
98 | bool isVector() const { return NumVectors > 0; } |
99 | bool isScalableVector() const { return isVector() && IsScalable; } |
100 | bool isFixedLengthVector() const { return isVector() && !IsScalable; } |
101 | bool isChar() const { return ElementBitwidth == 8; } |
102 | bool isVoid() const { return Void && !Pointer; } |
103 | bool isDefault() const { return DefaultType; } |
104 | bool isFloat() const { return Float && !BFloat; } |
105 | bool isBFloat() const { return BFloat && !Float; } |
106 | bool isFloatingPoint() const { return Float || BFloat; } |
107 | bool isInteger() const { |
108 | return !isFloatingPoint() && !Predicate && !Svcount; |
109 | } |
110 | bool isScalarPredicate() const { |
111 | return !isFloatingPoint() && Predicate && NumVectors == 0; |
112 | } |
113 | bool isPredicateVector() const { return Predicate; } |
114 | bool isPredicatePattern() const { return PredicatePattern; } |
115 | bool isPrefetchOp() const { return PrefetchOp; } |
116 | bool isSvcount() const { return Svcount; } |
117 | bool isConstant() const { return Constant; } |
118 | unsigned getElementSizeInBits() const { return ElementBitwidth; } |
119 | unsigned getNumVectors() const { return NumVectors; } |
120 | |
121 | unsigned getNumElements() const { |
122 | assert(ElementBitwidth != ~0U); |
123 | return Bitwidth / ElementBitwidth; |
124 | } |
125 | unsigned getSizeInBits() const { |
126 | return Bitwidth; |
127 | } |
128 | |
129 | /// Return the string representation of a type, which is an encoded |
130 | /// string for passing to the BUILTIN() macro in Builtins.def. |
131 | std::string builtin_str() const; |
132 | |
133 | /// Return the C/C++ string representation of a type for use in the |
134 | /// arm_sve.h header file. |
135 | std::string str() const; |
136 | |
137 | private: |
138 | /// Creates the type based on the typespec string in TS. |
139 | void applyTypespec(StringRef TS); |
140 | |
141 | /// Applies a prototype modifier to the type. |
142 | void applyModifier(char Mod); |
143 | }; |
144 | |
145 | class SVEEmitter; |
146 | |
147 | /// The main grunt class. This represents an instantiation of an intrinsic with |
148 | /// a particular typespec and prototype. |
149 | class Intrinsic { |
150 | /// The unmangled name. |
151 | std::string Name; |
152 | |
153 | /// The name of the corresponding LLVM IR intrinsic. |
154 | std::string LLVMName; |
155 | |
156 | /// Intrinsic prototype. |
157 | std::string Proto; |
158 | |
159 | /// The base type spec for this intrinsic. |
160 | TypeSpec BaseTypeSpec; |
161 | |
162 | /// The base class kind. Most intrinsics use ClassS, which has full type |
163 | /// info for integers (_s32/_u32), or ClassG which is used for overloaded |
164 | /// intrinsics. |
165 | ClassKind Class; |
166 | |
167 | /// The architectural #ifdef guard. |
168 | std::string Guard; |
169 | |
170 | // The merge suffix such as _m, _x or _z. |
171 | std::string MergeSuffix; |
172 | |
173 | /// The types of return value [0] and parameters [1..]. |
174 | std::vector<SVEType> Types; |
175 | |
176 | /// The "base type", which is VarType('d', BaseTypeSpec). |
177 | SVEType BaseType; |
178 | |
179 | uint64_t Flags; |
180 | |
181 | SmallVector<ImmCheck, 2> ImmChecks; |
182 | |
183 | public: |
184 | Intrinsic(StringRef Name, StringRef Proto, uint64_t MergeTy, |
185 | StringRef MergeSuffix, uint64_t MemoryElementTy, StringRef LLVMName, |
186 | uint64_t Flags, ArrayRef<ImmCheck> ImmChecks, TypeSpec BT, |
187 | ClassKind Class, SVEEmitter &Emitter, StringRef Guard); |
188 | |
189 | ~Intrinsic()=default; |
190 | |
191 | std::string getName() const { return Name; } |
192 | std::string getLLVMName() const { return LLVMName; } |
193 | std::string getProto() const { return Proto; } |
194 | TypeSpec getBaseTypeSpec() const { return BaseTypeSpec; } |
195 | SVEType getBaseType() const { return BaseType; } |
196 | |
197 | StringRef getGuard() const { return Guard; } |
198 | ClassKind getClassKind() const { return Class; } |
199 | |
200 | SVEType getReturnType() const { return Types[0]; } |
201 | ArrayRef<SVEType> getTypes() const { return Types; } |
202 | SVEType getParamType(unsigned I) const { return Types[I + 1]; } |
203 | unsigned getNumParams() const { |
204 | return Proto.size() - (2 * llvm::count(Range: Proto, Element: '.')) - 1; |
205 | } |
206 | |
207 | uint64_t getFlags() const { return Flags; } |
208 | bool isFlagSet(uint64_t Flag) const { return Flags & Flag;} |
209 | |
210 | ArrayRef<ImmCheck> getImmChecks() const { return ImmChecks; } |
211 | |
212 | /// Return the type string for a BUILTIN() macro in Builtins.def. |
213 | std::string getBuiltinTypeStr(); |
214 | |
215 | /// Return the name, mangled with type information. The name is mangled for |
216 | /// ClassS, so will add type suffixes such as _u32/_s32. |
217 | std::string getMangledName() const { return mangleName(LocalCK: ClassS); } |
218 | |
219 | /// As above, but mangles the LLVM name instead. |
220 | std::string getMangledLLVMName() const { return mangleLLVMName(); } |
221 | |
222 | /// Returns true if the intrinsic is overloaded, in that it should also generate |
223 | /// a short form without the type-specifiers, e.g. 'svld1(..)' instead of |
224 | /// 'svld1_u32(..)'. |
225 | static bool isOverloadedIntrinsic(StringRef Name) { |
226 | auto BrOpen = Name.find(C: '['); |
227 | auto BrClose = Name.find(C: ']'); |
228 | return BrOpen != std::string::npos && BrClose != std::string::npos; |
229 | } |
230 | |
231 | /// Return true if the intrinsic takes a splat operand. |
232 | bool hasSplat() const { |
233 | // These prototype modifiers are described in arm_sve.td. |
234 | return Proto.find_first_of(s: "ajfrKLR@" ) != std::string::npos; |
235 | } |
236 | |
237 | /// Return the parameter index of the splat operand. |
238 | unsigned getSplatIdx() const { |
239 | unsigned I = 1, Param = 0; |
240 | for (; I < Proto.size(); ++I, ++Param) { |
241 | if (Proto[I] == 'a' || Proto[I] == 'j' || Proto[I] == 'f' || |
242 | Proto[I] == 'r' || Proto[I] == 'K' || Proto[I] == 'L' || |
243 | Proto[I] == 'R' || Proto[I] == '@') |
244 | break; |
245 | |
246 | // Multivector modifier can be skipped |
247 | if (Proto[I] == '.') |
248 | I += 2; |
249 | } |
250 | assert(I != Proto.size() && "Prototype has no splat operand" ); |
251 | return Param; |
252 | } |
253 | |
254 | /// Emits the intrinsic declaration to the ostream. |
255 | void emitIntrinsic(raw_ostream &OS, SVEEmitter &Emitter, ACLEKind Kind) const; |
256 | |
257 | private: |
258 | std::string getMergeSuffix() const { return MergeSuffix; } |
259 | std::string mangleName(ClassKind LocalCK) const; |
260 | std::string mangleLLVMName() const; |
261 | std::string replaceTemplatedArgs(std::string Name, TypeSpec TS, |
262 | std::string Proto) const; |
263 | }; |
264 | |
265 | class SVEEmitter { |
266 | private: |
267 | // The reinterpret builtins are generated separately because they |
268 | // need the cross product of all types (121 functions in total), |
269 | // which is inconvenient to specify in the arm_sve.td file or |
270 | // generate in CGBuiltin.cpp. |
271 | struct ReinterpretTypeInfo { |
272 | SVEType BaseType; |
273 | const char *Suffix; |
274 | }; |
275 | |
276 | static const std::array<ReinterpretTypeInfo, 12> Reinterprets; |
277 | |
278 | RecordKeeper &Records; |
279 | llvm::StringMap<uint64_t> EltTypes; |
280 | llvm::StringMap<uint64_t> MemEltTypes; |
281 | llvm::StringMap<uint64_t> FlagTypes; |
282 | llvm::StringMap<uint64_t> MergeTypes; |
283 | llvm::StringMap<uint64_t> ImmCheckTypes; |
284 | |
285 | public: |
286 | SVEEmitter(RecordKeeper &R) : Records(R) { |
287 | for (auto *RV : Records.getAllDerivedDefinitions(ClassName: "EltType" )) |
288 | EltTypes[RV->getNameInitAsString()] = RV->getValueAsInt(FieldName: "Value" ); |
289 | for (auto *RV : Records.getAllDerivedDefinitions(ClassName: "MemEltType" )) |
290 | MemEltTypes[RV->getNameInitAsString()] = RV->getValueAsInt(FieldName: "Value" ); |
291 | for (auto *RV : Records.getAllDerivedDefinitions(ClassName: "FlagType" )) |
292 | FlagTypes[RV->getNameInitAsString()] = RV->getValueAsInt(FieldName: "Value" ); |
293 | for (auto *RV : Records.getAllDerivedDefinitions(ClassName: "MergeType" )) |
294 | MergeTypes[RV->getNameInitAsString()] = RV->getValueAsInt(FieldName: "Value" ); |
295 | for (auto *RV : Records.getAllDerivedDefinitions(ClassName: "ImmCheckType" )) |
296 | ImmCheckTypes[RV->getNameInitAsString()] = RV->getValueAsInt(FieldName: "Value" ); |
297 | } |
298 | |
299 | /// Returns the enum value for the immcheck type |
300 | unsigned getEnumValueForImmCheck(StringRef C) const { |
301 | auto It = ImmCheckTypes.find(Key: C); |
302 | if (It != ImmCheckTypes.end()) |
303 | return It->getValue(); |
304 | llvm_unreachable("Unsupported imm check" ); |
305 | } |
306 | |
307 | /// Returns the enum value for the flag type |
308 | uint64_t getEnumValueForFlag(StringRef C) const { |
309 | auto Res = FlagTypes.find(Key: C); |
310 | if (Res != FlagTypes.end()) |
311 | return Res->getValue(); |
312 | llvm_unreachable("Unsupported flag" ); |
313 | } |
314 | |
315 | // Returns the SVETypeFlags for a given value and mask. |
316 | uint64_t encodeFlag(uint64_t V, StringRef MaskName) const { |
317 | auto It = FlagTypes.find(Key: MaskName); |
318 | if (It != FlagTypes.end()) { |
319 | uint64_t Mask = It->getValue(); |
320 | unsigned Shift = llvm::countr_zero(Val: Mask); |
321 | assert(Shift < 64 && "Mask value produced an invalid shift value" ); |
322 | return (V << Shift) & Mask; |
323 | } |
324 | llvm_unreachable("Unsupported flag" ); |
325 | } |
326 | |
327 | // Returns the SVETypeFlags for the given element type. |
328 | uint64_t encodeEltType(StringRef EltName) { |
329 | auto It = EltTypes.find(Key: EltName); |
330 | if (It != EltTypes.end()) |
331 | return encodeFlag(V: It->getValue(), MaskName: "EltTypeMask" ); |
332 | llvm_unreachable("Unsupported EltType" ); |
333 | } |
334 | |
335 | // Returns the SVETypeFlags for the given memory element type. |
336 | uint64_t encodeMemoryElementType(uint64_t MT) { |
337 | return encodeFlag(V: MT, MaskName: "MemEltTypeMask" ); |
338 | } |
339 | |
340 | // Returns the SVETypeFlags for the given merge type. |
341 | uint64_t encodeMergeType(uint64_t MT) { |
342 | return encodeFlag(V: MT, MaskName: "MergeTypeMask" ); |
343 | } |
344 | |
345 | // Returns the SVETypeFlags for the given splat operand. |
346 | unsigned encodeSplatOperand(unsigned SplatIdx) { |
347 | assert(SplatIdx < 7 && "SplatIdx out of encodable range" ); |
348 | return encodeFlag(V: SplatIdx + 1, MaskName: "SplatOperandMask" ); |
349 | } |
350 | |
351 | // Returns the SVETypeFlags value for the given SVEType. |
352 | uint64_t encodeTypeFlags(const SVEType &T); |
353 | |
354 | /// Emit arm_sve.h. |
355 | void createHeader(raw_ostream &o); |
356 | |
357 | // Emits core intrinsics in both arm_sme.h and arm_sve.h |
358 | void createCoreHeaderIntrinsics(raw_ostream &o, SVEEmitter &Emitter, |
359 | ACLEKind Kind); |
360 | |
361 | /// Emit all the __builtin prototypes and code needed by Sema. |
362 | void createBuiltins(raw_ostream &o); |
363 | |
364 | /// Emit all the information needed to map builtin -> LLVM IR intrinsic. |
365 | void createCodeGenMap(raw_ostream &o); |
366 | |
367 | /// Emit all the range checks for the immediates. |
368 | void createRangeChecks(raw_ostream &o); |
369 | |
370 | /// Create the SVETypeFlags used in CGBuiltins |
371 | void createTypeFlags(raw_ostream &o); |
372 | |
373 | /// Emit arm_sme.h. |
374 | void createSMEHeader(raw_ostream &o); |
375 | |
376 | /// Emit all the SME __builtin prototypes and code needed by Sema. |
377 | void createSMEBuiltins(raw_ostream &o); |
378 | |
379 | /// Emit all the information needed to map builtin -> LLVM IR intrinsic. |
380 | void createSMECodeGenMap(raw_ostream &o); |
381 | |
382 | /// Create a table for a builtin's requirement for PSTATE.SM. |
383 | void createStreamingAttrs(raw_ostream &o, ACLEKind Kind); |
384 | |
385 | /// Emit all the range checks for the immediates. |
386 | void createSMERangeChecks(raw_ostream &o); |
387 | |
388 | /// Create a table for a builtin's requirement for PSTATE.ZA. |
389 | void createBuiltinZAState(raw_ostream &OS); |
390 | |
391 | /// Create intrinsic and add it to \p Out |
392 | void createIntrinsic(Record *R, |
393 | SmallVectorImpl<std::unique_ptr<Intrinsic>> &Out); |
394 | }; |
395 | |
396 | const std::array<SVEEmitter::ReinterpretTypeInfo, 12> SVEEmitter::Reinterprets = |
397 | {._M_elems: {{.BaseType: SVEType("c" , 'd'), .Suffix: "s8" }, |
398 | {.BaseType: SVEType("Uc" , 'd'), .Suffix: "u8" }, |
399 | {.BaseType: SVEType("s" , 'd'), .Suffix: "s16" }, |
400 | {.BaseType: SVEType("Us" , 'd'), .Suffix: "u16" }, |
401 | {.BaseType: SVEType("i" , 'd'), .Suffix: "s32" }, |
402 | {.BaseType: SVEType("Ui" , 'd'), .Suffix: "u32" }, |
403 | {.BaseType: SVEType("l" , 'd'), .Suffix: "s64" }, |
404 | {.BaseType: SVEType("Ul" , 'd'), .Suffix: "u64" }, |
405 | {.BaseType: SVEType("h" , 'd'), .Suffix: "f16" }, |
406 | {.BaseType: SVEType("b" , 'd'), .Suffix: "bf16" }, |
407 | {.BaseType: SVEType("f" , 'd'), .Suffix: "f32" }, |
408 | {.BaseType: SVEType("d" , 'd'), .Suffix: "f64" }}}; |
409 | |
410 | } // end anonymous namespace |
411 | |
412 | |
413 | //===----------------------------------------------------------------------===// |
414 | // Type implementation |
415 | //===----------------------------------------------------------------------===// |
416 | |
417 | std::string SVEType::builtin_str() const { |
418 | std::string S; |
419 | if (isVoid()) |
420 | return "v" ; |
421 | |
422 | if (isScalarPredicate()) |
423 | return "b" ; |
424 | |
425 | if (isSvcount()) |
426 | return "Qa" ; |
427 | |
428 | if (isVoidPointer()) |
429 | S += "v" ; |
430 | else if (!isFloatingPoint()) |
431 | switch (ElementBitwidth) { |
432 | case 1: S += "b" ; break; |
433 | case 8: S += "c" ; break; |
434 | case 16: S += "s" ; break; |
435 | case 32: S += "i" ; break; |
436 | case 64: S += "Wi" ; break; |
437 | case 128: S += "LLLi" ; break; |
438 | default: llvm_unreachable("Unhandled case!" ); |
439 | } |
440 | else if (isFloat()) |
441 | switch (ElementBitwidth) { |
442 | case 16: S += "h" ; break; |
443 | case 32: S += "f" ; break; |
444 | case 64: S += "d" ; break; |
445 | default: llvm_unreachable("Unhandled case!" ); |
446 | } |
447 | else if (isBFloat()) { |
448 | assert(ElementBitwidth == 16 && "Not a valid BFloat." ); |
449 | S += "y" ; |
450 | } |
451 | |
452 | if (!isFloatingPoint()) { |
453 | if ((isChar() || isPointer()) && !isVoidPointer()) { |
454 | // Make chars and typed pointers explicitly signed. |
455 | if (Signed) |
456 | S = "S" + S; |
457 | else if (!Signed) |
458 | S = "U" + S; |
459 | } else if (!isVoidPointer() && !Signed) { |
460 | S = "U" + S; |
461 | } |
462 | } |
463 | |
464 | // Constant indices are "int", but have the "constant expression" modifier. |
465 | if (isImmediate()) { |
466 | assert(!isFloat() && "fp immediates are not supported" ); |
467 | S = "I" + S; |
468 | } |
469 | |
470 | if (isScalar()) { |
471 | if (Constant) S += "C" ; |
472 | if (Pointer) S += "*" ; |
473 | return S; |
474 | } |
475 | |
476 | if (isFixedLengthVector()) |
477 | return "V" + utostr(X: getNumElements() * NumVectors) + S; |
478 | return "q" + utostr(X: getNumElements() * NumVectors) + S; |
479 | } |
480 | |
481 | std::string SVEType::str() const { |
482 | if (isPredicatePattern()) |
483 | return "enum svpattern" ; |
484 | |
485 | if (isPrefetchOp()) |
486 | return "enum svprfop" ; |
487 | |
488 | std::string S; |
489 | if (Void) |
490 | S += "void" ; |
491 | else { |
492 | if (isScalableVector() || isSvcount()) |
493 | S += "sv" ; |
494 | if (!Signed && !isFloatingPoint()) |
495 | S += "u" ; |
496 | |
497 | if (Float) |
498 | S += "float" ; |
499 | else if (isSvcount()) |
500 | S += "count" ; |
501 | else if (isScalarPredicate() || isPredicateVector()) |
502 | S += "bool" ; |
503 | else if (isBFloat()) |
504 | S += "bfloat" ; |
505 | else |
506 | S += "int" ; |
507 | |
508 | if (!isScalarPredicate() && !isPredicateVector() && !isSvcount()) |
509 | S += utostr(X: ElementBitwidth); |
510 | if (isFixedLengthVector()) |
511 | S += "x" + utostr(X: getNumElements()); |
512 | if (NumVectors > 1) |
513 | S += "x" + utostr(X: NumVectors); |
514 | if (!isScalarPredicate()) |
515 | S += "_t" ; |
516 | } |
517 | |
518 | if (Constant) |
519 | S += " const" ; |
520 | if (Pointer) |
521 | S += " *" ; |
522 | |
523 | return S; |
524 | } |
525 | |
526 | void SVEType::applyTypespec(StringRef TS) { |
527 | for (char I : TS) { |
528 | switch (I) { |
529 | case 'Q': |
530 | Svcount = true; |
531 | break; |
532 | case 'P': |
533 | Predicate = true; |
534 | break; |
535 | case 'U': |
536 | Signed = false; |
537 | break; |
538 | case 'c': |
539 | ElementBitwidth = 8; |
540 | break; |
541 | case 's': |
542 | ElementBitwidth = 16; |
543 | break; |
544 | case 'i': |
545 | ElementBitwidth = 32; |
546 | break; |
547 | case 'l': |
548 | ElementBitwidth = 64; |
549 | break; |
550 | case 'q': |
551 | ElementBitwidth = 128; |
552 | break; |
553 | case 'h': |
554 | Float = true; |
555 | ElementBitwidth = 16; |
556 | break; |
557 | case 'f': |
558 | Float = true; |
559 | ElementBitwidth = 32; |
560 | break; |
561 | case 'd': |
562 | Float = true; |
563 | ElementBitwidth = 64; |
564 | break; |
565 | case 'b': |
566 | BFloat = true; |
567 | Float = false; |
568 | ElementBitwidth = 16; |
569 | break; |
570 | default: |
571 | llvm_unreachable("Unhandled type code!" ); |
572 | } |
573 | } |
574 | assert(ElementBitwidth != ~0U && "Bad element bitwidth!" ); |
575 | } |
576 | |
577 | void SVEType::applyModifier(char Mod) { |
578 | switch (Mod) { |
579 | case 'v': |
580 | Void = true; |
581 | break; |
582 | case 'd': |
583 | DefaultType = true; |
584 | break; |
585 | case 'c': |
586 | Constant = true; |
587 | [[fallthrough]]; |
588 | case 'p': |
589 | Pointer = true; |
590 | Bitwidth = ElementBitwidth; |
591 | NumVectors = 0; |
592 | break; |
593 | case 'e': |
594 | Signed = false; |
595 | ElementBitwidth /= 2; |
596 | break; |
597 | case 'h': |
598 | ElementBitwidth /= 2; |
599 | break; |
600 | case 'q': |
601 | ElementBitwidth /= 4; |
602 | break; |
603 | case 'b': |
604 | Signed = false; |
605 | Float = false; |
606 | BFloat = false; |
607 | ElementBitwidth /= 4; |
608 | break; |
609 | case 'o': |
610 | ElementBitwidth *= 4; |
611 | break; |
612 | case 'P': |
613 | Signed = true; |
614 | Float = false; |
615 | BFloat = false; |
616 | Predicate = true; |
617 | Svcount = false; |
618 | Bitwidth = 16; |
619 | ElementBitwidth = 1; |
620 | break; |
621 | case '{': |
622 | IsScalable = false; |
623 | Bitwidth = 128; |
624 | NumVectors = 1; |
625 | break; |
626 | case 's': |
627 | case 'a': |
628 | Bitwidth = ElementBitwidth; |
629 | NumVectors = 0; |
630 | break; |
631 | case 'R': |
632 | ElementBitwidth /= 2; |
633 | NumVectors = 0; |
634 | break; |
635 | case 'r': |
636 | ElementBitwidth /= 4; |
637 | NumVectors = 0; |
638 | break; |
639 | case '@': |
640 | Signed = false; |
641 | Float = false; |
642 | BFloat = false; |
643 | ElementBitwidth /= 4; |
644 | NumVectors = 0; |
645 | break; |
646 | case 'K': |
647 | Signed = true; |
648 | Float = false; |
649 | BFloat = false; |
650 | Bitwidth = ElementBitwidth; |
651 | NumVectors = 0; |
652 | break; |
653 | case 'L': |
654 | Signed = false; |
655 | Float = false; |
656 | BFloat = false; |
657 | Bitwidth = ElementBitwidth; |
658 | NumVectors = 0; |
659 | break; |
660 | case 'u': |
661 | Predicate = false; |
662 | Svcount = false; |
663 | Signed = false; |
664 | Float = false; |
665 | BFloat = false; |
666 | break; |
667 | case 'x': |
668 | Predicate = false; |
669 | Svcount = false; |
670 | Signed = true; |
671 | Float = false; |
672 | BFloat = false; |
673 | break; |
674 | case 'i': |
675 | Predicate = false; |
676 | Svcount = false; |
677 | Float = false; |
678 | BFloat = false; |
679 | ElementBitwidth = Bitwidth = 64; |
680 | NumVectors = 0; |
681 | Signed = false; |
682 | Immediate = true; |
683 | break; |
684 | case 'I': |
685 | Predicate = false; |
686 | Svcount = false; |
687 | Float = false; |
688 | BFloat = false; |
689 | ElementBitwidth = Bitwidth = 32; |
690 | NumVectors = 0; |
691 | Signed = true; |
692 | Immediate = true; |
693 | PredicatePattern = true; |
694 | break; |
695 | case 'J': |
696 | Predicate = false; |
697 | Svcount = false; |
698 | Float = false; |
699 | BFloat = false; |
700 | ElementBitwidth = Bitwidth = 32; |
701 | NumVectors = 0; |
702 | Signed = true; |
703 | Immediate = true; |
704 | PrefetchOp = true; |
705 | break; |
706 | case 'k': |
707 | Predicate = false; |
708 | Svcount = false; |
709 | Signed = true; |
710 | Float = false; |
711 | BFloat = false; |
712 | ElementBitwidth = Bitwidth = 32; |
713 | NumVectors = 0; |
714 | break; |
715 | case 'l': |
716 | Predicate = false; |
717 | Svcount = false; |
718 | Signed = true; |
719 | Float = false; |
720 | BFloat = false; |
721 | ElementBitwidth = Bitwidth = 64; |
722 | NumVectors = 0; |
723 | break; |
724 | case 'm': |
725 | Predicate = false; |
726 | Svcount = false; |
727 | Signed = false; |
728 | Float = false; |
729 | BFloat = false; |
730 | ElementBitwidth = Bitwidth = 32; |
731 | NumVectors = 0; |
732 | break; |
733 | case 'n': |
734 | Predicate = false; |
735 | Svcount = false; |
736 | Signed = false; |
737 | Float = false; |
738 | BFloat = false; |
739 | ElementBitwidth = Bitwidth = 64; |
740 | NumVectors = 0; |
741 | break; |
742 | case 'w': |
743 | ElementBitwidth = 64; |
744 | break; |
745 | case 'j': |
746 | ElementBitwidth = Bitwidth = 64; |
747 | NumVectors = 0; |
748 | break; |
749 | case 'f': |
750 | Signed = false; |
751 | ElementBitwidth = Bitwidth = 64; |
752 | NumVectors = 0; |
753 | break; |
754 | case 'g': |
755 | Signed = false; |
756 | Float = false; |
757 | BFloat = false; |
758 | ElementBitwidth = 64; |
759 | break; |
760 | case '[': |
761 | Signed = false; |
762 | Float = false; |
763 | BFloat = false; |
764 | ElementBitwidth = 8; |
765 | break; |
766 | case 't': |
767 | Signed = true; |
768 | Float = false; |
769 | BFloat = false; |
770 | ElementBitwidth = 32; |
771 | break; |
772 | case 'z': |
773 | Signed = false; |
774 | Float = false; |
775 | BFloat = false; |
776 | ElementBitwidth = 32; |
777 | break; |
778 | case 'O': |
779 | Predicate = false; |
780 | Svcount = false; |
781 | Float = true; |
782 | ElementBitwidth = 16; |
783 | break; |
784 | case 'M': |
785 | Predicate = false; |
786 | Svcount = false; |
787 | Float = true; |
788 | BFloat = false; |
789 | ElementBitwidth = 32; |
790 | break; |
791 | case 'N': |
792 | Predicate = false; |
793 | Svcount = false; |
794 | Float = true; |
795 | ElementBitwidth = 64; |
796 | break; |
797 | case 'Q': |
798 | Constant = true; |
799 | Pointer = true; |
800 | Void = true; |
801 | NumVectors = 0; |
802 | break; |
803 | case 'S': |
804 | Constant = true; |
805 | Pointer = true; |
806 | ElementBitwidth = Bitwidth = 8; |
807 | NumVectors = 0; |
808 | Signed = true; |
809 | break; |
810 | case 'W': |
811 | Constant = true; |
812 | Pointer = true; |
813 | ElementBitwidth = Bitwidth = 8; |
814 | NumVectors = 0; |
815 | Signed = false; |
816 | break; |
817 | case 'T': |
818 | Constant = true; |
819 | Pointer = true; |
820 | ElementBitwidth = Bitwidth = 16; |
821 | NumVectors = 0; |
822 | Signed = true; |
823 | break; |
824 | case 'X': |
825 | Constant = true; |
826 | Pointer = true; |
827 | ElementBitwidth = Bitwidth = 16; |
828 | NumVectors = 0; |
829 | Signed = false; |
830 | break; |
831 | case 'Y': |
832 | Constant = true; |
833 | Pointer = true; |
834 | ElementBitwidth = Bitwidth = 32; |
835 | NumVectors = 0; |
836 | Signed = false; |
837 | break; |
838 | case 'U': |
839 | Constant = true; |
840 | Pointer = true; |
841 | ElementBitwidth = Bitwidth = 32; |
842 | NumVectors = 0; |
843 | Signed = true; |
844 | break; |
845 | case '%': |
846 | Pointer = true; |
847 | Void = true; |
848 | NumVectors = 0; |
849 | break; |
850 | case 'A': |
851 | Pointer = true; |
852 | ElementBitwidth = Bitwidth = 8; |
853 | NumVectors = 0; |
854 | Signed = true; |
855 | break; |
856 | case 'B': |
857 | Pointer = true; |
858 | ElementBitwidth = Bitwidth = 16; |
859 | NumVectors = 0; |
860 | Signed = true; |
861 | break; |
862 | case 'C': |
863 | Pointer = true; |
864 | ElementBitwidth = Bitwidth = 32; |
865 | NumVectors = 0; |
866 | Signed = true; |
867 | break; |
868 | case 'D': |
869 | Pointer = true; |
870 | ElementBitwidth = Bitwidth = 64; |
871 | NumVectors = 0; |
872 | Signed = true; |
873 | break; |
874 | case 'E': |
875 | Pointer = true; |
876 | ElementBitwidth = Bitwidth = 8; |
877 | NumVectors = 0; |
878 | Signed = false; |
879 | break; |
880 | case 'F': |
881 | Pointer = true; |
882 | ElementBitwidth = Bitwidth = 16; |
883 | NumVectors = 0; |
884 | Signed = false; |
885 | break; |
886 | case 'G': |
887 | Pointer = true; |
888 | ElementBitwidth = Bitwidth = 32; |
889 | NumVectors = 0; |
890 | Signed = false; |
891 | break; |
892 | case '$': |
893 | Predicate = false; |
894 | Svcount = false; |
895 | Float = false; |
896 | BFloat = true; |
897 | ElementBitwidth = 16; |
898 | break; |
899 | case '}': |
900 | Predicate = false; |
901 | Signed = true; |
902 | Svcount = true; |
903 | NumVectors = 0; |
904 | Float = false; |
905 | BFloat = false; |
906 | break; |
907 | case '.': |
908 | llvm_unreachable(". is never a type in itself" ); |
909 | break; |
910 | default: |
911 | llvm_unreachable("Unhandled character!" ); |
912 | } |
913 | } |
914 | |
915 | /// Returns the modifier and number of vectors for the given operand \p Op. |
916 | std::pair<char, unsigned> getProtoModifier(StringRef Proto, unsigned Op) { |
917 | for (unsigned P = 0; !Proto.empty(); ++P) { |
918 | unsigned NumVectors = 1; |
919 | unsigned CharsToSkip = 1; |
920 | char Mod = Proto[0]; |
921 | if (Mod == '2' || Mod == '3' || Mod == '4') { |
922 | NumVectors = Mod - '0'; |
923 | Mod = 'd'; |
924 | if (Proto.size() > 1 && Proto[1] == '.') { |
925 | Mod = Proto[2]; |
926 | CharsToSkip = 3; |
927 | } |
928 | } |
929 | |
930 | if (P == Op) |
931 | return {Mod, NumVectors}; |
932 | |
933 | Proto = Proto.drop_front(N: CharsToSkip); |
934 | } |
935 | llvm_unreachable("Unexpected Op" ); |
936 | } |
937 | |
938 | //===----------------------------------------------------------------------===// |
939 | // Intrinsic implementation |
940 | //===----------------------------------------------------------------------===// |
941 | |
942 | Intrinsic::Intrinsic(StringRef Name, StringRef Proto, uint64_t MergeTy, |
943 | StringRef MergeSuffix, uint64_t MemoryElementTy, |
944 | StringRef LLVMName, uint64_t Flags, |
945 | ArrayRef<ImmCheck> Checks, TypeSpec BT, ClassKind Class, |
946 | SVEEmitter &Emitter, StringRef Guard) |
947 | : Name(Name.str()), LLVMName(LLVMName), Proto(Proto.str()), |
948 | BaseTypeSpec(BT), Class(Class), Guard(Guard.str()), |
949 | MergeSuffix(MergeSuffix.str()), BaseType(BT, 'd'), Flags(Flags), |
950 | ImmChecks(Checks.begin(), Checks.end()) { |
951 | // Types[0] is the return value. |
952 | for (unsigned I = 0; I < (getNumParams() + 1); ++I) { |
953 | char Mod; |
954 | unsigned NumVectors; |
955 | std::tie(args&: Mod, args&: NumVectors) = getProtoModifier(Proto, Op: I); |
956 | SVEType T(BaseTypeSpec, Mod, NumVectors); |
957 | Types.push_back(x: T); |
958 | |
959 | // Add range checks for immediates |
960 | if (I > 0) { |
961 | if (T.isPredicatePattern()) |
962 | ImmChecks.emplace_back( |
963 | Args: I - 1, Args: Emitter.getEnumValueForImmCheck(C: "ImmCheck0_31" )); |
964 | else if (T.isPrefetchOp()) |
965 | ImmChecks.emplace_back( |
966 | Args: I - 1, Args: Emitter.getEnumValueForImmCheck(C: "ImmCheck0_13" )); |
967 | } |
968 | } |
969 | |
970 | // Set flags based on properties |
971 | this->Flags |= Emitter.encodeTypeFlags(T: BaseType); |
972 | this->Flags |= Emitter.encodeMemoryElementType(MT: MemoryElementTy); |
973 | this->Flags |= Emitter.encodeMergeType(MT: MergeTy); |
974 | if (hasSplat()) |
975 | this->Flags |= Emitter.encodeSplatOperand(SplatIdx: getSplatIdx()); |
976 | } |
977 | |
978 | std::string Intrinsic::getBuiltinTypeStr() { |
979 | std::string S = getReturnType().builtin_str(); |
980 | for (unsigned I = 0; I < getNumParams(); ++I) |
981 | S += getParamType(I).builtin_str(); |
982 | |
983 | return S; |
984 | } |
985 | |
986 | std::string Intrinsic::replaceTemplatedArgs(std::string Name, TypeSpec TS, |
987 | std::string Proto) const { |
988 | std::string Ret = Name; |
989 | while (Ret.find(c: '{') != std::string::npos) { |
990 | size_t Pos = Ret.find(c: '{'); |
991 | size_t End = Ret.find(c: '}'); |
992 | unsigned NumChars = End - Pos + 1; |
993 | assert(NumChars == 3 && "Unexpected template argument" ); |
994 | |
995 | SVEType T; |
996 | char C = Ret[Pos+1]; |
997 | switch(C) { |
998 | default: |
999 | llvm_unreachable("Unknown predication specifier" ); |
1000 | case 'd': |
1001 | T = SVEType(TS, 'd'); |
1002 | break; |
1003 | case '0': |
1004 | case '1': |
1005 | case '2': |
1006 | case '3': |
1007 | T = SVEType(TS, Proto[C - '0']); |
1008 | break; |
1009 | } |
1010 | |
1011 | // Replace templated arg with the right suffix (e.g. u32) |
1012 | std::string TypeCode; |
1013 | if (T.isInteger()) |
1014 | TypeCode = T.isSigned() ? 's' : 'u'; |
1015 | else if (T.isSvcount()) |
1016 | TypeCode = 'c'; |
1017 | else if (T.isPredicateVector()) |
1018 | TypeCode = 'b'; |
1019 | else if (T.isBFloat()) |
1020 | TypeCode = "bf" ; |
1021 | else |
1022 | TypeCode = 'f'; |
1023 | Ret.replace(pos: Pos, n: NumChars, str: TypeCode + utostr(X: T.getElementSizeInBits())); |
1024 | } |
1025 | |
1026 | return Ret; |
1027 | } |
1028 | |
1029 | std::string Intrinsic::mangleLLVMName() const { |
1030 | std::string S = getLLVMName(); |
1031 | |
1032 | // Replace all {d} like expressions with e.g. 'u32' |
1033 | return replaceTemplatedArgs(Name: S, TS: getBaseTypeSpec(), Proto: getProto()); |
1034 | } |
1035 | |
1036 | std::string Intrinsic::mangleName(ClassKind LocalCK) const { |
1037 | std::string S = getName(); |
1038 | |
1039 | if (LocalCK == ClassG) { |
1040 | // Remove the square brackets and everything in between. |
1041 | while (S.find(c: '[') != std::string::npos) { |
1042 | auto Start = S.find(c: '['); |
1043 | auto End = S.find(c: ']'); |
1044 | S.erase(pos: Start, n: (End-Start)+1); |
1045 | } |
1046 | } else { |
1047 | // Remove the square brackets. |
1048 | while (S.find(c: '[') != std::string::npos) { |
1049 | auto BrPos = S.find(c: '['); |
1050 | if (BrPos != std::string::npos) |
1051 | S.erase(pos: BrPos, n: 1); |
1052 | BrPos = S.find(c: ']'); |
1053 | if (BrPos != std::string::npos) |
1054 | S.erase(pos: BrPos, n: 1); |
1055 | } |
1056 | } |
1057 | |
1058 | // Replace all {d} like expressions with e.g. 'u32' |
1059 | return replaceTemplatedArgs(Name: S, TS: getBaseTypeSpec(), Proto: getProto()) + |
1060 | getMergeSuffix(); |
1061 | } |
1062 | |
1063 | void Intrinsic::emitIntrinsic(raw_ostream &OS, SVEEmitter &Emitter, |
1064 | ACLEKind Kind) const { |
1065 | bool IsOverloaded = getClassKind() == ClassG && getProto().size() > 1; |
1066 | |
1067 | std::string FullName = mangleName(LocalCK: ClassS); |
1068 | std::string ProtoName = mangleName(LocalCK: getClassKind()); |
1069 | OS << (IsOverloaded ? "__aio " : "__ai " ) |
1070 | << "__attribute__((__clang_arm_builtin_alias(" ; |
1071 | |
1072 | switch (Kind) { |
1073 | case ACLEKind::SME: |
1074 | OS << "__builtin_sme_" << FullName << ")" ; |
1075 | break; |
1076 | case ACLEKind::SVE: |
1077 | OS << "__builtin_sve_" << FullName << ")" ; |
1078 | break; |
1079 | } |
1080 | |
1081 | OS << "))\n" ; |
1082 | |
1083 | OS << getTypes()[0].str() << " " << ProtoName << "(" ; |
1084 | for (unsigned I = 0; I < getTypes().size() - 1; ++I) { |
1085 | if (I != 0) |
1086 | OS << ", " ; |
1087 | OS << getTypes()[I + 1].str(); |
1088 | } |
1089 | OS << ");\n" ; |
1090 | } |
1091 | |
1092 | //===----------------------------------------------------------------------===// |
1093 | // SVEEmitter implementation |
1094 | //===----------------------------------------------------------------------===// |
1095 | uint64_t SVEEmitter::encodeTypeFlags(const SVEType &T) { |
1096 | if (T.isFloat()) { |
1097 | switch (T.getElementSizeInBits()) { |
1098 | case 16: |
1099 | return encodeEltType(EltName: "EltTyFloat16" ); |
1100 | case 32: |
1101 | return encodeEltType(EltName: "EltTyFloat32" ); |
1102 | case 64: |
1103 | return encodeEltType(EltName: "EltTyFloat64" ); |
1104 | default: |
1105 | llvm_unreachable("Unhandled float element bitwidth!" ); |
1106 | } |
1107 | } |
1108 | |
1109 | if (T.isBFloat()) { |
1110 | assert(T.getElementSizeInBits() == 16 && "Not a valid BFloat." ); |
1111 | return encodeEltType(EltName: "EltTyBFloat16" ); |
1112 | } |
1113 | |
1114 | if (T.isPredicateVector() || T.isSvcount()) { |
1115 | switch (T.getElementSizeInBits()) { |
1116 | case 8: |
1117 | return encodeEltType(EltName: "EltTyBool8" ); |
1118 | case 16: |
1119 | return encodeEltType(EltName: "EltTyBool16" ); |
1120 | case 32: |
1121 | return encodeEltType(EltName: "EltTyBool32" ); |
1122 | case 64: |
1123 | return encodeEltType(EltName: "EltTyBool64" ); |
1124 | default: |
1125 | llvm_unreachable("Unhandled predicate element bitwidth!" ); |
1126 | } |
1127 | } |
1128 | |
1129 | switch (T.getElementSizeInBits()) { |
1130 | case 8: |
1131 | return encodeEltType(EltName: "EltTyInt8" ); |
1132 | case 16: |
1133 | return encodeEltType(EltName: "EltTyInt16" ); |
1134 | case 32: |
1135 | return encodeEltType(EltName: "EltTyInt32" ); |
1136 | case 64: |
1137 | return encodeEltType(EltName: "EltTyInt64" ); |
1138 | case 128: |
1139 | return encodeEltType(EltName: "EltTyInt128" ); |
1140 | default: |
1141 | llvm_unreachable("Unhandled integer element bitwidth!" ); |
1142 | } |
1143 | } |
1144 | |
1145 | void SVEEmitter::createIntrinsic( |
1146 | Record *R, SmallVectorImpl<std::unique_ptr<Intrinsic>> &Out) { |
1147 | StringRef Name = R->getValueAsString(FieldName: "Name" ); |
1148 | StringRef Proto = R->getValueAsString(FieldName: "Prototype" ); |
1149 | StringRef Types = R->getValueAsString(FieldName: "Types" ); |
1150 | StringRef Guard = R->getValueAsString(FieldName: "TargetGuard" ); |
1151 | StringRef LLVMName = R->getValueAsString(FieldName: "LLVMIntrinsic" ); |
1152 | uint64_t Merge = R->getValueAsInt(FieldName: "Merge" ); |
1153 | StringRef MergeSuffix = R->getValueAsString(FieldName: "MergeSuffix" ); |
1154 | uint64_t MemEltType = R->getValueAsInt(FieldName: "MemEltType" ); |
1155 | std::vector<Record*> FlagsList = R->getValueAsListOfDefs(FieldName: "Flags" ); |
1156 | std::vector<Record*> ImmCheckList = R->getValueAsListOfDefs(FieldName: "ImmChecks" ); |
1157 | |
1158 | int64_t Flags = 0; |
1159 | for (auto FlagRec : FlagsList) |
1160 | Flags |= FlagRec->getValueAsInt(FieldName: "Value" ); |
1161 | |
1162 | // Create a dummy TypeSpec for non-overloaded builtins. |
1163 | if (Types.empty()) { |
1164 | assert((Flags & getEnumValueForFlag("IsOverloadNone" )) && |
1165 | "Expect TypeSpec for overloaded builtin!" ); |
1166 | Types = "i" ; |
1167 | } |
1168 | |
1169 | // Extract type specs from string |
1170 | SmallVector<TypeSpec, 8> TypeSpecs; |
1171 | TypeSpec Acc; |
1172 | for (char I : Types) { |
1173 | Acc.push_back(c: I); |
1174 | if (islower(I)) { |
1175 | TypeSpecs.push_back(Elt: TypeSpec(Acc)); |
1176 | Acc.clear(); |
1177 | } |
1178 | } |
1179 | |
1180 | // Remove duplicate type specs. |
1181 | llvm::sort(C&: TypeSpecs); |
1182 | TypeSpecs.erase(CS: std::unique(first: TypeSpecs.begin(), last: TypeSpecs.end()), |
1183 | CE: TypeSpecs.end()); |
1184 | |
1185 | // Create an Intrinsic for each type spec. |
1186 | for (auto TS : TypeSpecs) { |
1187 | // Collate a list of range/option checks for the immediates. |
1188 | SmallVector<ImmCheck, 2> ImmChecks; |
1189 | for (auto *R : ImmCheckList) { |
1190 | int64_t Arg = R->getValueAsInt(FieldName: "Arg" ); |
1191 | int64_t EltSizeArg = R->getValueAsInt(FieldName: "EltSizeArg" ); |
1192 | int64_t Kind = R->getValueAsDef(FieldName: "Kind" )->getValueAsInt(FieldName: "Value" ); |
1193 | assert(Arg >= 0 && Kind >= 0 && "Arg and Kind must be nonnegative" ); |
1194 | |
1195 | unsigned ElementSizeInBits = 0; |
1196 | char Mod; |
1197 | unsigned NumVectors; |
1198 | std::tie(args&: Mod, args&: NumVectors) = getProtoModifier(Proto, Op: EltSizeArg + 1); |
1199 | if (EltSizeArg >= 0) |
1200 | ElementSizeInBits = SVEType(TS, Mod, NumVectors).getElementSizeInBits(); |
1201 | ImmChecks.push_back(Elt: ImmCheck(Arg, Kind, ElementSizeInBits)); |
1202 | } |
1203 | |
1204 | Out.push_back(Elt: std::make_unique<Intrinsic>( |
1205 | args&: Name, args&: Proto, args&: Merge, args&: MergeSuffix, args&: MemEltType, args&: LLVMName, args&: Flags, args&: ImmChecks, |
1206 | args&: TS, args: ClassS, args&: *this, args&: Guard)); |
1207 | |
1208 | // Also generate the short-form (e.g. svadd_m) for the given type-spec. |
1209 | if (Intrinsic::isOverloadedIntrinsic(Name)) |
1210 | Out.push_back(Elt: std::make_unique<Intrinsic>( |
1211 | args&: Name, args&: Proto, args&: Merge, args&: MergeSuffix, args&: MemEltType, args&: LLVMName, args&: Flags, |
1212 | args&: ImmChecks, args&: TS, args: ClassG, args&: *this, args&: Guard)); |
1213 | } |
1214 | } |
1215 | |
1216 | void SVEEmitter::(raw_ostream &OS, |
1217 | SVEEmitter &Emitter, |
1218 | ACLEKind Kind) { |
1219 | SmallVector<std::unique_ptr<Intrinsic>, 128> Defs; |
1220 | std::vector<Record *> RV = Records.getAllDerivedDefinitions(ClassName: "Inst" ); |
1221 | for (auto *R : RV) |
1222 | createIntrinsic(R, Out&: Defs); |
1223 | |
1224 | // Sort intrinsics in header file by following order/priority: |
1225 | // - Architectural guard (i.e. does it require SVE2 or SVE2_AES) |
1226 | // - Class (is intrinsic overloaded or not) |
1227 | // - Intrinsic name |
1228 | std::stable_sort(first: Defs.begin(), last: Defs.end(), |
1229 | comp: [](const std::unique_ptr<Intrinsic> &A, |
1230 | const std::unique_ptr<Intrinsic> &B) { |
1231 | auto ToTuple = [](const std::unique_ptr<Intrinsic> &I) { |
1232 | return std::make_tuple(args: I->getGuard(), |
1233 | args: (unsigned)I->getClassKind(), |
1234 | args: I->getName()); |
1235 | }; |
1236 | return ToTuple(A) < ToTuple(B); |
1237 | }); |
1238 | |
1239 | // Actually emit the intrinsic declarations. |
1240 | for (auto &I : Defs) |
1241 | I->emitIntrinsic(OS, Emitter, Kind); |
1242 | } |
1243 | |
1244 | void SVEEmitter::(raw_ostream &OS) { |
1245 | OS << "/*===---- arm_sve.h - ARM SVE intrinsics " |
1246 | "-----------------------------------===\n" |
1247 | " *\n" |
1248 | " *\n" |
1249 | " * Part of the LLVM Project, under the Apache License v2.0 with LLVM " |
1250 | "Exceptions.\n" |
1251 | " * See https://llvm.org/LICENSE.txt for license information.\n" |
1252 | " * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n" |
1253 | " *\n" |
1254 | " *===-----------------------------------------------------------------" |
1255 | "------===\n" |
1256 | " */\n\n" ; |
1257 | |
1258 | OS << "#ifndef __ARM_SVE_H\n" ; |
1259 | OS << "#define __ARM_SVE_H\n\n" ; |
1260 | |
1261 | OS << "#if !defined(__LITTLE_ENDIAN__)\n" ; |
1262 | OS << "#error \"Big endian is currently not supported for arm_sve.h\"\n" ; |
1263 | OS << "#endif\n" ; |
1264 | |
1265 | OS << "#include <stdint.h>\n\n" ; |
1266 | OS << "#ifdef __cplusplus\n" ; |
1267 | OS << "extern \"C\" {\n" ; |
1268 | OS << "#else\n" ; |
1269 | OS << "#include <stdbool.h>\n" ; |
1270 | OS << "#endif\n\n" ; |
1271 | |
1272 | OS << "typedef __fp16 float16_t;\n" ; |
1273 | OS << "typedef float float32_t;\n" ; |
1274 | OS << "typedef double float64_t;\n" ; |
1275 | |
1276 | OS << "typedef __SVInt8_t svint8_t;\n" ; |
1277 | OS << "typedef __SVInt16_t svint16_t;\n" ; |
1278 | OS << "typedef __SVInt32_t svint32_t;\n" ; |
1279 | OS << "typedef __SVInt64_t svint64_t;\n" ; |
1280 | OS << "typedef __SVUint8_t svuint8_t;\n" ; |
1281 | OS << "typedef __SVUint16_t svuint16_t;\n" ; |
1282 | OS << "typedef __SVUint32_t svuint32_t;\n" ; |
1283 | OS << "typedef __SVUint64_t svuint64_t;\n" ; |
1284 | OS << "typedef __SVFloat16_t svfloat16_t;\n\n" ; |
1285 | |
1286 | OS << "typedef __SVBfloat16_t svbfloat16_t;\n" ; |
1287 | |
1288 | OS << "#include <arm_bf16.h>\n" ; |
1289 | OS << "#include <arm_vector_types.h>\n" ; |
1290 | |
1291 | OS << "typedef __SVFloat32_t svfloat32_t;\n" ; |
1292 | OS << "typedef __SVFloat64_t svfloat64_t;\n" ; |
1293 | OS << "typedef __clang_svint8x2_t svint8x2_t;\n" ; |
1294 | OS << "typedef __clang_svint16x2_t svint16x2_t;\n" ; |
1295 | OS << "typedef __clang_svint32x2_t svint32x2_t;\n" ; |
1296 | OS << "typedef __clang_svint64x2_t svint64x2_t;\n" ; |
1297 | OS << "typedef __clang_svuint8x2_t svuint8x2_t;\n" ; |
1298 | OS << "typedef __clang_svuint16x2_t svuint16x2_t;\n" ; |
1299 | OS << "typedef __clang_svuint32x2_t svuint32x2_t;\n" ; |
1300 | OS << "typedef __clang_svuint64x2_t svuint64x2_t;\n" ; |
1301 | OS << "typedef __clang_svfloat16x2_t svfloat16x2_t;\n" ; |
1302 | OS << "typedef __clang_svfloat32x2_t svfloat32x2_t;\n" ; |
1303 | OS << "typedef __clang_svfloat64x2_t svfloat64x2_t;\n" ; |
1304 | OS << "typedef __clang_svint8x3_t svint8x3_t;\n" ; |
1305 | OS << "typedef __clang_svint16x3_t svint16x3_t;\n" ; |
1306 | OS << "typedef __clang_svint32x3_t svint32x3_t;\n" ; |
1307 | OS << "typedef __clang_svint64x3_t svint64x3_t;\n" ; |
1308 | OS << "typedef __clang_svuint8x3_t svuint8x3_t;\n" ; |
1309 | OS << "typedef __clang_svuint16x3_t svuint16x3_t;\n" ; |
1310 | OS << "typedef __clang_svuint32x3_t svuint32x3_t;\n" ; |
1311 | OS << "typedef __clang_svuint64x3_t svuint64x3_t;\n" ; |
1312 | OS << "typedef __clang_svfloat16x3_t svfloat16x3_t;\n" ; |
1313 | OS << "typedef __clang_svfloat32x3_t svfloat32x3_t;\n" ; |
1314 | OS << "typedef __clang_svfloat64x3_t svfloat64x3_t;\n" ; |
1315 | OS << "typedef __clang_svint8x4_t svint8x4_t;\n" ; |
1316 | OS << "typedef __clang_svint16x4_t svint16x4_t;\n" ; |
1317 | OS << "typedef __clang_svint32x4_t svint32x4_t;\n" ; |
1318 | OS << "typedef __clang_svint64x4_t svint64x4_t;\n" ; |
1319 | OS << "typedef __clang_svuint8x4_t svuint8x4_t;\n" ; |
1320 | OS << "typedef __clang_svuint16x4_t svuint16x4_t;\n" ; |
1321 | OS << "typedef __clang_svuint32x4_t svuint32x4_t;\n" ; |
1322 | OS << "typedef __clang_svuint64x4_t svuint64x4_t;\n" ; |
1323 | OS << "typedef __clang_svfloat16x4_t svfloat16x4_t;\n" ; |
1324 | OS << "typedef __clang_svfloat32x4_t svfloat32x4_t;\n" ; |
1325 | OS << "typedef __clang_svfloat64x4_t svfloat64x4_t;\n" ; |
1326 | OS << "typedef __SVBool_t svbool_t;\n" ; |
1327 | OS << "typedef __clang_svboolx2_t svboolx2_t;\n" ; |
1328 | OS << "typedef __clang_svboolx4_t svboolx4_t;\n\n" ; |
1329 | |
1330 | OS << "typedef __clang_svbfloat16x2_t svbfloat16x2_t;\n" ; |
1331 | OS << "typedef __clang_svbfloat16x3_t svbfloat16x3_t;\n" ; |
1332 | OS << "typedef __clang_svbfloat16x4_t svbfloat16x4_t;\n" ; |
1333 | |
1334 | OS << "typedef __SVCount_t svcount_t;\n\n" ; |
1335 | |
1336 | OS << "enum svpattern\n" ; |
1337 | OS << "{\n" ; |
1338 | OS << " SV_POW2 = 0,\n" ; |
1339 | OS << " SV_VL1 = 1,\n" ; |
1340 | OS << " SV_VL2 = 2,\n" ; |
1341 | OS << " SV_VL3 = 3,\n" ; |
1342 | OS << " SV_VL4 = 4,\n" ; |
1343 | OS << " SV_VL5 = 5,\n" ; |
1344 | OS << " SV_VL6 = 6,\n" ; |
1345 | OS << " SV_VL7 = 7,\n" ; |
1346 | OS << " SV_VL8 = 8,\n" ; |
1347 | OS << " SV_VL16 = 9,\n" ; |
1348 | OS << " SV_VL32 = 10,\n" ; |
1349 | OS << " SV_VL64 = 11,\n" ; |
1350 | OS << " SV_VL128 = 12,\n" ; |
1351 | OS << " SV_VL256 = 13,\n" ; |
1352 | OS << " SV_MUL4 = 29,\n" ; |
1353 | OS << " SV_MUL3 = 30,\n" ; |
1354 | OS << " SV_ALL = 31\n" ; |
1355 | OS << "};\n\n" ; |
1356 | |
1357 | OS << "enum svprfop\n" ; |
1358 | OS << "{\n" ; |
1359 | OS << " SV_PLDL1KEEP = 0,\n" ; |
1360 | OS << " SV_PLDL1STRM = 1,\n" ; |
1361 | OS << " SV_PLDL2KEEP = 2,\n" ; |
1362 | OS << " SV_PLDL2STRM = 3,\n" ; |
1363 | OS << " SV_PLDL3KEEP = 4,\n" ; |
1364 | OS << " SV_PLDL3STRM = 5,\n" ; |
1365 | OS << " SV_PSTL1KEEP = 8,\n" ; |
1366 | OS << " SV_PSTL1STRM = 9,\n" ; |
1367 | OS << " SV_PSTL2KEEP = 10,\n" ; |
1368 | OS << " SV_PSTL2STRM = 11,\n" ; |
1369 | OS << " SV_PSTL3KEEP = 12,\n" ; |
1370 | OS << " SV_PSTL3STRM = 13\n" ; |
1371 | OS << "};\n\n" ; |
1372 | |
1373 | OS << "/* Function attributes */\n" ; |
1374 | OS << "#define __ai static __inline__ __attribute__((__always_inline__, " |
1375 | "__nodebug__))\n\n" ; |
1376 | OS << "#define __aio static __inline__ __attribute__((__always_inline__, " |
1377 | "__nodebug__, __overloadable__))\n\n" ; |
1378 | |
1379 | // Add reinterpret functions. |
1380 | for (auto [N, Suffix] : |
1381 | std::initializer_list<std::pair<unsigned, const char *>>{ |
1382 | {1, "" }, {2, "_x2" }, {3, "_x3" }, {4, "_x4" }}) { |
1383 | for (auto ShortForm : {false, true}) |
1384 | for (const ReinterpretTypeInfo &To : Reinterprets) { |
1385 | SVEType ToV(To.BaseType, N); |
1386 | for (const ReinterpretTypeInfo &From : Reinterprets) { |
1387 | SVEType FromV(From.BaseType, N); |
1388 | if (ShortForm) { |
1389 | OS << "__aio __attribute__((target(\"sve\"))) " << ToV.str() |
1390 | << " svreinterpret_" << To.Suffix; |
1391 | OS << "(" << FromV.str() << " op) __arm_streaming_compatible {\n" ; |
1392 | OS << " return __builtin_sve_reinterpret_" << To.Suffix << "_" |
1393 | << From.Suffix << Suffix << "(op);\n" ; |
1394 | OS << "}\n\n" ; |
1395 | } else |
1396 | OS << "#define svreinterpret_" << To.Suffix << "_" << From.Suffix |
1397 | << Suffix << "(...) __builtin_sve_reinterpret_" << To.Suffix |
1398 | << "_" << From.Suffix << Suffix << "(__VA_ARGS__)\n" ; |
1399 | } |
1400 | } |
1401 | } |
1402 | |
1403 | createCoreHeaderIntrinsics(OS, Emitter&: *this, Kind: ACLEKind::SVE); |
1404 | |
1405 | OS << "#define svcvtnt_bf16_x svcvtnt_bf16_m\n" ; |
1406 | OS << "#define svcvtnt_bf16_f32_x svcvtnt_bf16_f32_m\n" ; |
1407 | |
1408 | OS << "#define svcvtnt_f16_x svcvtnt_f16_m\n" ; |
1409 | OS << "#define svcvtnt_f16_f32_x svcvtnt_f16_f32_m\n" ; |
1410 | OS << "#define svcvtnt_f32_x svcvtnt_f32_m\n" ; |
1411 | OS << "#define svcvtnt_f32_f64_x svcvtnt_f32_f64_m\n\n" ; |
1412 | |
1413 | OS << "#define svcvtxnt_f32_x svcvtxnt_f32_m\n" ; |
1414 | OS << "#define svcvtxnt_f32_f64_x svcvtxnt_f32_f64_m\n\n" ; |
1415 | |
1416 | OS << "#ifdef __cplusplus\n" ; |
1417 | OS << "} // extern \"C\"\n" ; |
1418 | OS << "#endif\n\n" ; |
1419 | OS << "#undef __ai\n\n" ; |
1420 | OS << "#undef __aio\n\n" ; |
1421 | OS << "#endif /* __ARM_SVE_H */\n" ; |
1422 | } |
1423 | |
1424 | void SVEEmitter::createBuiltins(raw_ostream &OS) { |
1425 | std::vector<Record *> RV = Records.getAllDerivedDefinitions(ClassName: "Inst" ); |
1426 | SmallVector<std::unique_ptr<Intrinsic>, 128> Defs; |
1427 | for (auto *R : RV) |
1428 | createIntrinsic(R, Out&: Defs); |
1429 | |
1430 | // The mappings must be sorted based on BuiltinID. |
1431 | llvm::sort(C&: Defs, Comp: [](const std::unique_ptr<Intrinsic> &A, |
1432 | const std::unique_ptr<Intrinsic> &B) { |
1433 | return A->getMangledName() < B->getMangledName(); |
1434 | }); |
1435 | |
1436 | OS << "#ifdef GET_SVE_BUILTINS\n" ; |
1437 | for (auto &Def : Defs) { |
1438 | // Only create BUILTINs for non-overloaded intrinsics, as overloaded |
1439 | // declarations only live in the header file. |
1440 | if (Def->getClassKind() != ClassG) |
1441 | OS << "TARGET_BUILTIN(__builtin_sve_" << Def->getMangledName() << ", \"" |
1442 | << Def->getBuiltinTypeStr() << "\", \"n\", \"" << Def->getGuard() |
1443 | << "\")\n" ; |
1444 | } |
1445 | |
1446 | // Add reinterpret functions. |
1447 | for (auto [N, Suffix] : |
1448 | std::initializer_list<std::pair<unsigned, const char *>>{ |
1449 | {1, "" }, {2, "_x2" }, {3, "_x3" }, {4, "_x4" }}) { |
1450 | for (const ReinterpretTypeInfo &To : Reinterprets) { |
1451 | SVEType ToV(To.BaseType, N); |
1452 | for (const ReinterpretTypeInfo &From : Reinterprets) { |
1453 | SVEType FromV(From.BaseType, N); |
1454 | OS << "TARGET_BUILTIN(__builtin_sve_reinterpret_" << To.Suffix << "_" |
1455 | << From.Suffix << Suffix << +", \"" << ToV.builtin_str() |
1456 | << FromV.builtin_str() << "\", \"n\", \"sve\")\n" ; |
1457 | } |
1458 | } |
1459 | } |
1460 | |
1461 | OS << "#endif\n\n" ; |
1462 | } |
1463 | |
1464 | void SVEEmitter::createCodeGenMap(raw_ostream &OS) { |
1465 | std::vector<Record *> RV = Records.getAllDerivedDefinitions(ClassName: "Inst" ); |
1466 | SmallVector<std::unique_ptr<Intrinsic>, 128> Defs; |
1467 | for (auto *R : RV) |
1468 | createIntrinsic(R, Out&: Defs); |
1469 | |
1470 | // The mappings must be sorted based on BuiltinID. |
1471 | llvm::sort(C&: Defs, Comp: [](const std::unique_ptr<Intrinsic> &A, |
1472 | const std::unique_ptr<Intrinsic> &B) { |
1473 | return A->getMangledName() < B->getMangledName(); |
1474 | }); |
1475 | |
1476 | OS << "#ifdef GET_SVE_LLVM_INTRINSIC_MAP\n" ; |
1477 | for (auto &Def : Defs) { |
1478 | // Builtins only exist for non-overloaded intrinsics, overloaded |
1479 | // declarations only live in the header file. |
1480 | if (Def->getClassKind() == ClassG) |
1481 | continue; |
1482 | |
1483 | uint64_t Flags = Def->getFlags(); |
1484 | auto FlagString = std::to_string(val: Flags); |
1485 | |
1486 | std::string LLVMName = Def->getMangledLLVMName(); |
1487 | std::string Builtin = Def->getMangledName(); |
1488 | if (!LLVMName.empty()) |
1489 | OS << "SVEMAP1(" << Builtin << ", " << LLVMName << ", " << FlagString |
1490 | << "),\n" ; |
1491 | else |
1492 | OS << "SVEMAP2(" << Builtin << ", " << FlagString << "),\n" ; |
1493 | } |
1494 | OS << "#endif\n\n" ; |
1495 | } |
1496 | |
1497 | void SVEEmitter::createRangeChecks(raw_ostream &OS) { |
1498 | std::vector<Record *> RV = Records.getAllDerivedDefinitions(ClassName: "Inst" ); |
1499 | SmallVector<std::unique_ptr<Intrinsic>, 128> Defs; |
1500 | for (auto *R : RV) |
1501 | createIntrinsic(R, Out&: Defs); |
1502 | |
1503 | // The mappings must be sorted based on BuiltinID. |
1504 | llvm::sort(C&: Defs, Comp: [](const std::unique_ptr<Intrinsic> &A, |
1505 | const std::unique_ptr<Intrinsic> &B) { |
1506 | return A->getMangledName() < B->getMangledName(); |
1507 | }); |
1508 | |
1509 | |
1510 | OS << "#ifdef GET_SVE_IMMEDIATE_CHECK\n" ; |
1511 | |
1512 | // Ensure these are only emitted once. |
1513 | std::set<std::string> Emitted; |
1514 | |
1515 | for (auto &Def : Defs) { |
1516 | if (Emitted.find(x: Def->getMangledName()) != Emitted.end() || |
1517 | Def->getImmChecks().empty()) |
1518 | continue; |
1519 | |
1520 | OS << "case SVE::BI__builtin_sve_" << Def->getMangledName() << ":\n" ; |
1521 | for (auto &Check : Def->getImmChecks()) |
1522 | OS << "ImmChecks.push_back(std::make_tuple(" << Check.getArg() << ", " |
1523 | << Check.getKind() << ", " << Check.getElementSizeInBits() << "));\n" ; |
1524 | OS << " break;\n" ; |
1525 | |
1526 | Emitted.insert(x: Def->getMangledName()); |
1527 | } |
1528 | |
1529 | OS << "#endif\n\n" ; |
1530 | } |
1531 | |
1532 | /// Create the SVETypeFlags used in CGBuiltins |
1533 | void SVEEmitter::createTypeFlags(raw_ostream &OS) { |
1534 | OS << "#ifdef LLVM_GET_SVE_TYPEFLAGS\n" ; |
1535 | for (auto &KV : FlagTypes) |
1536 | OS << "const uint64_t " << KV.getKey() << " = " << KV.getValue() << ";\n" ; |
1537 | OS << "#endif\n\n" ; |
1538 | |
1539 | OS << "#ifdef LLVM_GET_SVE_ELTTYPES\n" ; |
1540 | for (auto &KV : EltTypes) |
1541 | OS << " " << KV.getKey() << " = " << KV.getValue() << ",\n" ; |
1542 | OS << "#endif\n\n" ; |
1543 | |
1544 | OS << "#ifdef LLVM_GET_SVE_MEMELTTYPES\n" ; |
1545 | for (auto &KV : MemEltTypes) |
1546 | OS << " " << KV.getKey() << " = " << KV.getValue() << ",\n" ; |
1547 | OS << "#endif\n\n" ; |
1548 | |
1549 | OS << "#ifdef LLVM_GET_SVE_MERGETYPES\n" ; |
1550 | for (auto &KV : MergeTypes) |
1551 | OS << " " << KV.getKey() << " = " << KV.getValue() << ",\n" ; |
1552 | OS << "#endif\n\n" ; |
1553 | |
1554 | OS << "#ifdef LLVM_GET_SVE_IMMCHECKTYPES\n" ; |
1555 | for (auto &KV : ImmCheckTypes) |
1556 | OS << " " << KV.getKey() << " = " << KV.getValue() << ",\n" ; |
1557 | OS << "#endif\n\n" ; |
1558 | } |
1559 | |
1560 | void SVEEmitter::(raw_ostream &OS) { |
1561 | OS << "/*===---- arm_sme.h - ARM SME intrinsics " |
1562 | "------===\n" |
1563 | " *\n" |
1564 | " *\n" |
1565 | " * Part of the LLVM Project, under the Apache License v2.0 with LLVM " |
1566 | "Exceptions.\n" |
1567 | " * See https://llvm.org/LICENSE.txt for license information.\n" |
1568 | " * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n" |
1569 | " *\n" |
1570 | " *===-----------------------------------------------------------------" |
1571 | "------===\n" |
1572 | " */\n\n" ; |
1573 | |
1574 | OS << "#ifndef __ARM_SME_H\n" ; |
1575 | OS << "#define __ARM_SME_H\n\n" ; |
1576 | |
1577 | OS << "#if !defined(__LITTLE_ENDIAN__)\n" ; |
1578 | OS << "#error \"Big endian is currently not supported for arm_sme.h\"\n" ; |
1579 | OS << "#endif\n" ; |
1580 | |
1581 | OS << "#include <arm_sve.h>\n\n" ; |
1582 | OS << "#include <stddef.h>\n\n" ; |
1583 | |
1584 | OS << "/* Function attributes */\n" ; |
1585 | OS << "#define __ai static __inline__ __attribute__((__always_inline__, " |
1586 | "__nodebug__))\n\n" ; |
1587 | OS << "#define __aio static __inline__ __attribute__((__always_inline__, " |
1588 | "__nodebug__, __overloadable__))\n\n" ; |
1589 | |
1590 | OS << "#ifdef __cplusplus\n" ; |
1591 | OS << "extern \"C\" {\n" ; |
1592 | OS << "#endif\n\n" ; |
1593 | |
1594 | OS << "void __arm_za_disable(void) __arm_streaming_compatible;\n\n" ; |
1595 | |
1596 | OS << "__ai bool __arm_has_sme(void) __arm_streaming_compatible {\n" ; |
1597 | OS << " uint64_t x0, x1;\n" ; |
1598 | OS << " __builtin_arm_get_sme_state(&x0, &x1);\n" ; |
1599 | OS << " return x0 & (1ULL << 63);\n" ; |
1600 | OS << "}\n\n" ; |
1601 | |
1602 | OS << "__ai bool __arm_in_streaming_mode(void) __arm_streaming_compatible " |
1603 | "{\n" ; |
1604 | OS << " uint64_t x0, x1;\n" ; |
1605 | OS << " __builtin_arm_get_sme_state(&x0, &x1);\n" ; |
1606 | OS << " return x0 & 1;\n" ; |
1607 | OS << "}\n\n" ; |
1608 | |
1609 | OS << "void *__arm_sc_memcpy(void *dest, const void *src, size_t n) __arm_streaming_compatible;\n" ; |
1610 | OS << "void *__arm_sc_memmove(void *dest, const void *src, size_t n) __arm_streaming_compatible;\n" ; |
1611 | OS << "void *__arm_sc_memset(void *s, int c, size_t n) __arm_streaming_compatible;\n" ; |
1612 | OS << "void *__arm_sc_memchr(void *s, int c, size_t n) __arm_streaming_compatible;\n\n" ; |
1613 | |
1614 | OS << "__ai __attribute__((target(\"sme\"))) void svundef_za(void) " |
1615 | "__arm_streaming_compatible __arm_out(\"za\") " |
1616 | "{ }\n\n" ; |
1617 | |
1618 | createCoreHeaderIntrinsics(OS, Emitter&: *this, Kind: ACLEKind::SME); |
1619 | |
1620 | OS << "#ifdef __cplusplus\n" ; |
1621 | OS << "} // extern \"C\"\n" ; |
1622 | OS << "#endif\n\n" ; |
1623 | OS << "#undef __ai\n\n" ; |
1624 | OS << "#endif /* __ARM_SME_H */\n" ; |
1625 | } |
1626 | |
1627 | void SVEEmitter::createSMEBuiltins(raw_ostream &OS) { |
1628 | std::vector<Record *> RV = Records.getAllDerivedDefinitions(ClassName: "Inst" ); |
1629 | SmallVector<std::unique_ptr<Intrinsic>, 128> Defs; |
1630 | for (auto *R : RV) { |
1631 | createIntrinsic(R, Out&: Defs); |
1632 | } |
1633 | |
1634 | // The mappings must be sorted based on BuiltinID. |
1635 | llvm::sort(C&: Defs, Comp: [](const std::unique_ptr<Intrinsic> &A, |
1636 | const std::unique_ptr<Intrinsic> &B) { |
1637 | return A->getMangledName() < B->getMangledName(); |
1638 | }); |
1639 | |
1640 | OS << "#ifdef GET_SME_BUILTINS\n" ; |
1641 | for (auto &Def : Defs) { |
1642 | // Only create BUILTINs for non-overloaded intrinsics, as overloaded |
1643 | // declarations only live in the header file. |
1644 | if (Def->getClassKind() != ClassG) |
1645 | OS << "TARGET_BUILTIN(__builtin_sme_" << Def->getMangledName() << ", \"" |
1646 | << Def->getBuiltinTypeStr() << "\", \"n\", \"" << Def->getGuard() |
1647 | << "\")\n" ; |
1648 | } |
1649 | |
1650 | OS << "#endif\n\n" ; |
1651 | } |
1652 | |
1653 | void SVEEmitter::createSMECodeGenMap(raw_ostream &OS) { |
1654 | std::vector<Record *> RV = Records.getAllDerivedDefinitions(ClassName: "Inst" ); |
1655 | SmallVector<std::unique_ptr<Intrinsic>, 128> Defs; |
1656 | for (auto *R : RV) { |
1657 | createIntrinsic(R, Out&: Defs); |
1658 | } |
1659 | |
1660 | // The mappings must be sorted based on BuiltinID. |
1661 | llvm::sort(C&: Defs, Comp: [](const std::unique_ptr<Intrinsic> &A, |
1662 | const std::unique_ptr<Intrinsic> &B) { |
1663 | return A->getMangledName() < B->getMangledName(); |
1664 | }); |
1665 | |
1666 | OS << "#ifdef GET_SME_LLVM_INTRINSIC_MAP\n" ; |
1667 | for (auto &Def : Defs) { |
1668 | // Builtins only exist for non-overloaded intrinsics, overloaded |
1669 | // declarations only live in the header file. |
1670 | if (Def->getClassKind() == ClassG) |
1671 | continue; |
1672 | |
1673 | uint64_t Flags = Def->getFlags(); |
1674 | auto FlagString = std::to_string(val: Flags); |
1675 | |
1676 | std::string LLVMName = Def->getLLVMName(); |
1677 | std::string Builtin = Def->getMangledName(); |
1678 | if (!LLVMName.empty()) |
1679 | OS << "SMEMAP1(" << Builtin << ", " << LLVMName << ", " << FlagString |
1680 | << "),\n" ; |
1681 | else |
1682 | OS << "SMEMAP2(" << Builtin << ", " << FlagString << "),\n" ; |
1683 | } |
1684 | OS << "#endif\n\n" ; |
1685 | } |
1686 | |
1687 | void SVEEmitter::createSMERangeChecks(raw_ostream &OS) { |
1688 | std::vector<Record *> RV = Records.getAllDerivedDefinitions(ClassName: "Inst" ); |
1689 | SmallVector<std::unique_ptr<Intrinsic>, 128> Defs; |
1690 | for (auto *R : RV) { |
1691 | createIntrinsic(R, Out&: Defs); |
1692 | } |
1693 | |
1694 | // The mappings must be sorted based on BuiltinID. |
1695 | llvm::sort(C&: Defs, Comp: [](const std::unique_ptr<Intrinsic> &A, |
1696 | const std::unique_ptr<Intrinsic> &B) { |
1697 | return A->getMangledName() < B->getMangledName(); |
1698 | }); |
1699 | |
1700 | |
1701 | OS << "#ifdef GET_SME_IMMEDIATE_CHECK\n" ; |
1702 | |
1703 | // Ensure these are only emitted once. |
1704 | std::set<std::string> Emitted; |
1705 | |
1706 | for (auto &Def : Defs) { |
1707 | if (Emitted.find(x: Def->getMangledName()) != Emitted.end() || |
1708 | Def->getImmChecks().empty()) |
1709 | continue; |
1710 | |
1711 | OS << "case SME::BI__builtin_sme_" << Def->getMangledName() << ":\n" ; |
1712 | for (auto &Check : Def->getImmChecks()) |
1713 | OS << "ImmChecks.push_back(std::make_tuple(" << Check.getArg() << ", " |
1714 | << Check.getKind() << ", " << Check.getElementSizeInBits() << "));\n" ; |
1715 | OS << " break;\n" ; |
1716 | |
1717 | Emitted.insert(x: Def->getMangledName()); |
1718 | } |
1719 | |
1720 | OS << "#endif\n\n" ; |
1721 | } |
1722 | |
1723 | void SVEEmitter::createBuiltinZAState(raw_ostream &OS) { |
1724 | std::vector<Record *> RV = Records.getAllDerivedDefinitions(ClassName: "Inst" ); |
1725 | SmallVector<std::unique_ptr<Intrinsic>, 128> Defs; |
1726 | for (auto *R : RV) |
1727 | createIntrinsic(R, Out&: Defs); |
1728 | |
1729 | std::map<std::string, std::set<std::string>> IntrinsicsPerState; |
1730 | for (auto &Def : Defs) { |
1731 | std::string Key; |
1732 | auto AddToKey = [&Key](const std::string &S) -> void { |
1733 | Key = Key.empty() ? S : (Key + " | " + S); |
1734 | }; |
1735 | |
1736 | if (Def->isFlagSet(Flag: getEnumValueForFlag(C: "IsInZA" ))) |
1737 | AddToKey("ArmInZA" ); |
1738 | else if (Def->isFlagSet(Flag: getEnumValueForFlag(C: "IsOutZA" ))) |
1739 | AddToKey("ArmOutZA" ); |
1740 | else if (Def->isFlagSet(Flag: getEnumValueForFlag(C: "IsInOutZA" ))) |
1741 | AddToKey("ArmInOutZA" ); |
1742 | |
1743 | if (Def->isFlagSet(Flag: getEnumValueForFlag(C: "IsInZT0" ))) |
1744 | AddToKey("ArmInZT0" ); |
1745 | else if (Def->isFlagSet(Flag: getEnumValueForFlag(C: "IsOutZT0" ))) |
1746 | AddToKey("ArmOutZT0" ); |
1747 | else if (Def->isFlagSet(Flag: getEnumValueForFlag(C: "IsInOutZT0" ))) |
1748 | AddToKey("ArmInOutZT0" ); |
1749 | |
1750 | if (!Key.empty()) |
1751 | IntrinsicsPerState[Key].insert(x: Def->getMangledName()); |
1752 | } |
1753 | |
1754 | OS << "#ifdef GET_SME_BUILTIN_GET_STATE\n" ; |
1755 | for (auto &KV : IntrinsicsPerState) { |
1756 | for (StringRef Name : KV.second) |
1757 | OS << "case SME::BI__builtin_sme_" << Name << ":\n" ; |
1758 | OS << " return " << KV.first << ";\n" ; |
1759 | } |
1760 | OS << "#endif\n\n" ; |
1761 | } |
1762 | |
1763 | void SVEEmitter::createStreamingAttrs(raw_ostream &OS, ACLEKind Kind) { |
1764 | std::vector<Record *> RV = Records.getAllDerivedDefinitions(ClassName: "Inst" ); |
1765 | SmallVector<std::unique_ptr<Intrinsic>, 128> Defs; |
1766 | for (auto *R : RV) |
1767 | createIntrinsic(R, Out&: Defs); |
1768 | |
1769 | StringRef ExtensionKind; |
1770 | switch (Kind) { |
1771 | case ACLEKind::SME: |
1772 | ExtensionKind = "SME" ; |
1773 | break; |
1774 | case ACLEKind::SVE: |
1775 | ExtensionKind = "SVE" ; |
1776 | break; |
1777 | } |
1778 | |
1779 | OS << "#ifdef GET_" << ExtensionKind << "_STREAMING_ATTRS\n" ; |
1780 | |
1781 | llvm::StringMap<std::set<std::string>> StreamingMap; |
1782 | |
1783 | uint64_t IsStreamingFlag = getEnumValueForFlag(C: "IsStreaming" ); |
1784 | uint64_t IsStreamingOrSVE2p1Flag = getEnumValueForFlag(C: "IsStreamingOrSVE2p1" ); |
1785 | uint64_t IsStreamingCompatibleFlag = |
1786 | getEnumValueForFlag(C: "IsStreamingCompatible" ); |
1787 | for (auto &Def : Defs) { |
1788 | if (Def->isFlagSet(Flag: IsStreamingFlag)) |
1789 | StreamingMap["ArmStreaming" ].insert(x: Def->getMangledName()); |
1790 | else if (Def->isFlagSet(Flag: IsStreamingOrSVE2p1Flag)) |
1791 | StreamingMap["ArmStreamingOrSVE2p1" ].insert(x: Def->getMangledName()); |
1792 | else if (Def->isFlagSet(Flag: IsStreamingCompatibleFlag)) |
1793 | StreamingMap["ArmStreamingCompatible" ].insert(x: Def->getMangledName()); |
1794 | else |
1795 | StreamingMap["ArmNonStreaming" ].insert(x: Def->getMangledName()); |
1796 | } |
1797 | |
1798 | for (auto BuiltinType : StreamingMap.keys()) { |
1799 | for (auto Name : StreamingMap[BuiltinType]) { |
1800 | OS << "case " << ExtensionKind << "::BI__builtin_" |
1801 | << ExtensionKind.lower() << "_" ; |
1802 | OS << Name << ":\n" ; |
1803 | } |
1804 | OS << " BuiltinType = " << BuiltinType << ";\n" ; |
1805 | OS << " break;\n" ; |
1806 | } |
1807 | |
1808 | OS << "#endif\n\n" ; |
1809 | } |
1810 | |
1811 | namespace clang { |
1812 | void (RecordKeeper &Records, raw_ostream &OS) { |
1813 | SVEEmitter(Records).createHeader(OS); |
1814 | } |
1815 | |
1816 | void EmitSveBuiltins(RecordKeeper &Records, raw_ostream &OS) { |
1817 | SVEEmitter(Records).createBuiltins(OS); |
1818 | } |
1819 | |
1820 | void EmitSveBuiltinCG(RecordKeeper &Records, raw_ostream &OS) { |
1821 | SVEEmitter(Records).createCodeGenMap(OS); |
1822 | } |
1823 | |
1824 | void EmitSveRangeChecks(RecordKeeper &Records, raw_ostream &OS) { |
1825 | SVEEmitter(Records).createRangeChecks(OS); |
1826 | } |
1827 | |
1828 | void EmitSveTypeFlags(RecordKeeper &Records, raw_ostream &OS) { |
1829 | SVEEmitter(Records).createTypeFlags(OS); |
1830 | } |
1831 | |
1832 | void EmitSveStreamingAttrs(RecordKeeper &Records, raw_ostream &OS) { |
1833 | SVEEmitter(Records).createStreamingAttrs(OS, Kind: ACLEKind::SVE); |
1834 | } |
1835 | |
1836 | void (RecordKeeper &Records, raw_ostream &OS) { |
1837 | SVEEmitter(Records).createSMEHeader(OS); |
1838 | } |
1839 | |
1840 | void EmitSmeBuiltins(RecordKeeper &Records, raw_ostream &OS) { |
1841 | SVEEmitter(Records).createSMEBuiltins(OS); |
1842 | } |
1843 | |
1844 | void EmitSmeBuiltinCG(RecordKeeper &Records, raw_ostream &OS) { |
1845 | SVEEmitter(Records).createSMECodeGenMap(OS); |
1846 | } |
1847 | |
1848 | void EmitSmeRangeChecks(RecordKeeper &Records, raw_ostream &OS) { |
1849 | SVEEmitter(Records).createSMERangeChecks(OS); |
1850 | } |
1851 | |
1852 | void EmitSmeStreamingAttrs(RecordKeeper &Records, raw_ostream &OS) { |
1853 | SVEEmitter(Records).createStreamingAttrs(OS, Kind: ACLEKind::SME); |
1854 | } |
1855 | |
1856 | void EmitSmeBuiltinZAState(RecordKeeper &Records, raw_ostream &OS) { |
1857 | SVEEmitter(Records).createBuiltinZAState(OS); |
1858 | } |
1859 | } // End namespace clang |
1860 | |