clang 23.0.0git
RISCVVIntrinsicUtils.cpp
Go to the documentation of this file.
1//===- RISCVVIntrinsicUtils.cpp - RISC-V Vector Intrinsic Utils -*- 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
10#include "llvm/ADT/ArrayRef.h"
11#include "llvm/ADT/StringExtras.h"
12#include "llvm/ADT/Twine.h"
13#include "llvm/Support/ErrorHandling.h"
14#include "llvm/Support/raw_ostream.h"
15#include <optional>
16
17using namespace llvm;
18
19namespace clang {
20namespace RISCV {
21
28
29//===----------------------------------------------------------------------===//
30// Type implementation
31//===----------------------------------------------------------------------===//
32
33LMULType::LMULType(int NewLog2LMUL) {
34 // Check Log2LMUL is -3, -2, -1, 0, 1, 2, 3
35 assert(NewLog2LMUL <= 3 && NewLog2LMUL >= -3 && "Bad LMUL number!");
36 Log2LMUL = NewLog2LMUL;
37}
38
39std::string LMULType::str() const {
40 if (Log2LMUL < 0)
41 return "mf" + utostr(1ULL << (-Log2LMUL));
42 return "m" + utostr(1ULL << Log2LMUL);
43}
44
45VScaleVal LMULType::getScale(unsigned ElementBitwidth) const {
46 int Log2ScaleResult = 0;
47 switch (ElementBitwidth) {
48 default:
49 break;
50 case 8:
51 Log2ScaleResult = Log2LMUL + 3;
52 break;
53 case 16:
54 Log2ScaleResult = Log2LMUL + 2;
55 break;
56 case 32:
57 Log2ScaleResult = Log2LMUL + 1;
58 break;
59 case 64:
60 Log2ScaleResult = Log2LMUL;
61 break;
62 }
63 // Illegal vscale result would be less than 1
64 if (Log2ScaleResult < 0)
65 return std::nullopt;
66 return 1 << Log2ScaleResult;
67}
68
69void LMULType::MulLog2LMUL(int log2LMUL) { Log2LMUL += log2LMUL; }
70
71RVVType::RVVType(BasicType BT, int Log2LMUL,
72 const PrototypeDescriptor &prototype)
73 : BT(BT), LMUL(LMULType(Log2LMUL)) {
74 applyBasicType();
75 applyModifier(prototype);
76 Valid = verifyType();
77 if (Valid) {
78 initBuiltinStr();
79 initTypeStr();
80 if (isVector()) {
81 initClangBuiltinStr();
82 }
83 }
84}
85
86// clang-format off
87// boolean type are encoded the ratio of n (SEW/LMUL)
88// SEW/LMUL | 1 | 2 | 4 | 8 | 16 | 32 | 64
89// c type | vbool64_t | vbool32_t | vbool16_t | vbool8_t | vbool4_t | vbool2_t | vbool1_t
90// IR type | nxv1i1 | nxv2i1 | nxv4i1 | nxv8i1 | nxv16i1 | nxv32i1 | nxv64i1
91
92// type\lmul | 1/8 | 1/4 | 1/2 | 1 | 2 | 4 | 8
93// -------- |------ | -------- | ------- | ------- | -------- | -------- | --------
94// i64 | N/A | N/A | N/A | nxv1i64 | nxv2i64 | nxv4i64 | nxv8i64
95// i32 | N/A | N/A | nxv1i32 | nxv2i32 | nxv4i32 | nxv8i32 | nxv16i32
96// i16 | N/A | nxv1i16 | nxv2i16 | nxv4i16 | nxv8i16 | nxv16i16 | nxv32i16
97// i8 | nxv1i8 | nxv2i8 | nxv4i8 | nxv8i8 | nxv16i8 | nxv32i8 | nxv64i8
98// double | N/A | N/A | N/A | nxv1f64 | nxv2f64 | nxv4f64 | nxv8f64
99// float | N/A | N/A | nxv1f32 | nxv2f32 | nxv4f32 | nxv8f32 | nxv16f32
100// half | N/A | nxv1f16 | nxv2f16 | nxv4f16 | nxv8f16 | nxv16f16 | nxv32f16
101// bfloat16 | N/A | nxv1bf16 | nxv2bf16| nxv4bf16| nxv8bf16 | nxv16bf16| nxv32bf16
102// clang-format on
103
104bool RVVType::verifyType() const {
105 if (ScalarType == Invalid)
106 return false;
107 if (isScalar())
108 return true;
109 if (!Scale)
110 return false;
111 if (isFloat() && ElementBitwidth == 8)
112 return false;
113 if (isBFloat() && ElementBitwidth != 16)
114 return false;
115 if (IsTuple && (NF == 1 || NF > 8))
116 return false;
117 if (IsTuple && (1 << std::max(0, LMUL.Log2LMUL)) * NF > 8)
118 return false;
119 unsigned V = *Scale;
120 switch (ElementBitwidth) {
121 case 1:
122 case 8:
123 // Check Scale is 1,2,4,8,16,32,64
124 return (V <= 64 && isPowerOf2_32(V));
125 case 16:
126 // Check Scale is 1,2,4,8,16,32
127 return (V <= 32 && isPowerOf2_32(V));
128 case 32:
129 // Check Scale is 1,2,4,8,16
130 return (V <= 16 && isPowerOf2_32(V));
131 case 64:
132 // Check Scale is 1,2,4,8
133 return (V <= 8 && isPowerOf2_32(V));
134 }
135 return false;
136}
137
138void RVVType::initBuiltinStr() {
139 assert(isValid() && "RVVType is invalid");
140 switch (ScalarType) {
142 BuiltinStr = "v";
143 return;
145 BuiltinStr = "z";
146 if (IsImmediate)
147 BuiltinStr = "I" + BuiltinStr;
148 if (IsPointer)
149 BuiltinStr += "*";
150 return;
152 BuiltinStr = "Y";
153 return;
155 BuiltinStr = "ULi";
156 return;
158 BuiltinStr = "Li";
159 return;
161 assert(ElementBitwidth == 1);
162 BuiltinStr += "b";
163 break;
166 switch (ElementBitwidth) {
167 case 8:
168 BuiltinStr += "c";
169 break;
170 case 16:
171 BuiltinStr += "s";
172 break;
173 case 32:
174 BuiltinStr += "i";
175 break;
176 case 64:
177 BuiltinStr += "Wi";
178 break;
179 default:
180 llvm_unreachable("Unhandled ElementBitwidth!");
181 }
182 if (isSignedInteger())
183 BuiltinStr = "S" + BuiltinStr;
184 else
185 BuiltinStr = "U" + BuiltinStr;
186 break;
188 switch (ElementBitwidth) {
189 case 16:
190 BuiltinStr += "x";
191 break;
192 case 32:
193 BuiltinStr += "f";
194 break;
195 case 64:
196 BuiltinStr += "d";
197 break;
198 default:
199 llvm_unreachable("Unhandled ElementBitwidth!");
200 }
201 break;
203 BuiltinStr += "y";
204 break;
206 BuiltinStr += "a";
207 break;
209 BuiltinStr += "b";
210 break;
211 default:
212 llvm_unreachable("ScalarType is invalid!");
213 }
214 if (IsImmediate)
215 BuiltinStr = "I" + BuiltinStr;
216 if (isScalar()) {
217 if (IsConstant)
218 BuiltinStr += "C";
219 if (IsPointer)
220 BuiltinStr += "*";
221 return;
222 }
223 BuiltinStr = "q" + utostr(*Scale) + BuiltinStr;
224 // Pointer to vector types. Defined for segment load intrinsics.
225 // segment load intrinsics have pointer type arguments to store the loaded
226 // vector values.
227 if (IsPointer)
228 BuiltinStr += "*";
229
230 if (IsTuple)
231 BuiltinStr = "T" + utostr(NF) + BuiltinStr;
232}
233
234void RVVType::initClangBuiltinStr() {
235 assert(isValid() && "RVVType is invalid");
236 assert(isVector() && "Handle Vector type only");
237
238 ClangBuiltinStr = "__rvv_";
239 switch (ScalarType) {
241 ClangBuiltinStr += "bool" + utostr(64 / *Scale) + "_t";
242 return;
244 ClangBuiltinStr += "float";
245 break;
247 ClangBuiltinStr += "bfloat";
248 break;
250 ClangBuiltinStr += "int";
251 break;
253 ClangBuiltinStr += "uint";
254 break;
256 ClangBuiltinStr += "float8e4m3" + LMUL.str() + "_t";
257 return;
259 ClangBuiltinStr += "float8e5m2" + LMUL.str() + "_t";
260 return;
261 default:
262 llvm_unreachable("ScalarTypeKind is invalid");
263 }
264 ClangBuiltinStr += utostr(ElementBitwidth) + LMUL.str() +
265 (IsTuple ? "x" + utostr(NF) : "") + "_t";
266}
267
268void RVVType::initTypeStr() {
269 assert(isValid() && "RVVType is invalid");
270
271 if (IsConstant)
272 Str += "const ";
273
274 auto getTypeString = [&](StringRef TypeStr) {
275 if (isScalar())
276 return Twine(TypeStr + Twine(ElementBitwidth) + "_t").str();
277 return Twine("v" + TypeStr + Twine(ElementBitwidth) + LMUL.str() +
278 (IsTuple ? "x" + utostr(NF) : "") + "_t")
279 .str();
280 };
281
282 switch (ScalarType) {
284 Str = "void";
285 return;
287 Str = "size_t";
288 if (IsPointer)
289 Str += " *";
290 return;
292 Str = "ptrdiff_t";
293 return;
295 Str = "unsigned long";
296 return;
298 Str = "long";
299 return;
301 if (isScalar())
302 Str += "bool";
303 else
304 // Vector bool is special case, the formulate is
305 // `vbool<N>_t = MVT::nxv<64/N>i1` ex. vbool16_t = MVT::4i1
306 Str += "vbool" + utostr(64 / *Scale) + "_t";
307 break;
309 if (isScalar()) {
310 if (ElementBitwidth == 64)
311 Str += "double";
312 else if (ElementBitwidth == 32)
313 Str += "float";
314 else if (ElementBitwidth == 16)
315 Str += "_Float16";
316 else
317 llvm_unreachable("Unhandled floating type.");
318 } else
319 Str += getTypeString("float");
320 break;
322 if (isScalar()) {
323 if (ElementBitwidth == 16)
324 Str += "__bf16";
325 else
326 llvm_unreachable("Unhandled floating type.");
327 } else
328 Str += getTypeString("bfloat");
329 break;
331 Str += getTypeString("int");
332 break;
334 Str += getTypeString("uint");
335 break;
337 Str += "vfloat8e4m3" + LMUL.str() + "_t";
338 break;
340 Str += "vfloat8e5m2" + LMUL.str() + "_t";
341 break;
342 default:
343 llvm_unreachable("ScalarType is invalid!");
344 }
345 if (IsPointer)
346 Str += " *";
347}
348
349void RVVType::initShortStr() {
350 switch (ScalarType) {
352 assert(isVector());
353 ShortStr = "b" + utostr(64 / *Scale);
354 return;
356 ShortStr = "f" + utostr(ElementBitwidth);
357 break;
359 ShortStr = "bf" + utostr(ElementBitwidth);
360 break;
362 ShortStr = "i" + utostr(ElementBitwidth);
363 break;
365 ShortStr = "u" + utostr(ElementBitwidth);
366 break;
368 ShortStr = "f8e4m3";
369 break;
371 ShortStr = "f8e5m2";
372 break;
373 default:
374 llvm_unreachable("Unhandled case!");
375 }
376 if (isVector())
377 ShortStr += LMUL.str();
378 if (isTuple())
379 ShortStr += "x" + utostr(NF);
380}
381
382static VectorTypeModifier getTupleVTM(unsigned NF) {
383 assert(2 <= NF && NF <= 8 && "2 <= NF <= 8");
384 return static_cast<VectorTypeModifier>(
385 static_cast<uint8_t>(VectorTypeModifier::Tuple2) + (NF - 2));
386}
387
388void RVVType::applyBasicType() {
389 switch (BT) {
390 case BasicType::Int8:
391 ElementBitwidth = 8;
393 break;
394 case BasicType::Int16:
395 ElementBitwidth = 16;
397 break;
398 case BasicType::Int32:
399 ElementBitwidth = 32;
401 break;
402 case BasicType::Int64:
403 ElementBitwidth = 64;
405 break;
407 ElementBitwidth = 16;
408 ScalarType = ScalarTypeKind::Float;
409 break;
411 ElementBitwidth = 32;
412 ScalarType = ScalarTypeKind::Float;
413 break;
415 ElementBitwidth = 64;
416 ScalarType = ScalarTypeKind::Float;
417 break;
419 ElementBitwidth = 16;
420 ScalarType = ScalarTypeKind::BFloat;
421 break;
423 ElementBitwidth = 8;
424 ScalarType = ScalarTypeKind::FloatE4M3;
425 break;
427 ElementBitwidth = 8;
428 ScalarType = ScalarTypeKind::FloatE5M2;
429 break;
430 default:
431 llvm_unreachable("Unhandled type code!");
432 }
433 assert(ElementBitwidth != 0 && "Bad element bitwidth!");
434}
435
436std::optional<PrototypeDescriptor>
438 llvm::StringRef PrototypeDescriptorStr) {
442
443 if (PrototypeDescriptorStr.empty())
444 return PD;
445
446 // Handle base type modifier
447 auto PType = PrototypeDescriptorStr.back();
448 switch (PType) {
449 case 'e':
451 break;
452 case 'v':
454 break;
455 case 'w':
458 break;
459 case 'd':
462 break;
463 case 'q':
466 break;
467 case 'o':
470 break;
471 case 'm':
474 break;
475 case '0':
477 break;
478 case 'z':
480 break;
481 case 't':
483 break;
484 case 'u':
486 break;
487 case 'l':
489 break;
490 case 'f':
492 break;
493 default:
494 llvm_unreachable("Illegal primitive type transformers!");
495 }
496 PD.PT = PT;
497 PrototypeDescriptorStr = PrototypeDescriptorStr.drop_back();
498
499 // Compute the vector type transformers, it can only appear one time.
500 if (PrototypeDescriptorStr.starts_with("(")) {
502 "VectorTypeModifier should only have one modifier");
503 size_t Idx = PrototypeDescriptorStr.find(')');
504 assert(Idx != StringRef::npos);
505 StringRef ComplexType = PrototypeDescriptorStr.slice(1, Idx);
506 PrototypeDescriptorStr = PrototypeDescriptorStr.drop_front(Idx + 1);
507 assert(!PrototypeDescriptorStr.contains('(') &&
508 "Only allow one vector type modifier");
509
510 auto ComplexTT = ComplexType.split(":");
511 if (ComplexTT.first == "Log2EEW") {
512 uint32_t Log2EEW;
513 if (ComplexTT.second.getAsInteger(10, Log2EEW)) {
514 llvm_unreachable("Invalid Log2EEW value!");
515 return std::nullopt;
516 }
517 switch (Log2EEW) {
518 case 3:
520 break;
521 case 4:
523 break;
524 case 5:
526 break;
527 case 6:
529 break;
530 default:
531 llvm_unreachable("Invalid Log2EEW value, should be [3-6]");
532 return std::nullopt;
533 }
534 } else if (ComplexTT.first == "FixedSEW") {
535 uint32_t NewSEW;
536 if (ComplexTT.second.getAsInteger(10, NewSEW)) {
537 llvm_unreachable("Invalid FixedSEW value!");
538 return std::nullopt;
539 }
540 switch (NewSEW) {
541 case 8:
543 break;
544 case 16:
546 break;
547 case 32:
549 break;
550 case 64:
552 break;
553 default:
554 llvm_unreachable("Invalid FixedSEW value, should be 8, 16, 32 or 64");
555 return std::nullopt;
556 }
557 } else if (ComplexTT.first == "LFixedLog2LMUL") {
558 int32_t Log2LMUL;
559 if (ComplexTT.second.getAsInteger(10, Log2LMUL)) {
560 llvm_unreachable("Invalid LFixedLog2LMUL value!");
561 return std::nullopt;
562 }
563 switch (Log2LMUL) {
564 case -3:
566 break;
567 case -2:
569 break;
570 case -1:
572 break;
573 case 0:
575 break;
576 case 1:
578 break;
579 case 2:
581 break;
582 case 3:
584 break;
585 default:
586 llvm_unreachable("Invalid LFixedLog2LMUL value, should be [-3, 3]");
587 return std::nullopt;
588 }
589 } else if (ComplexTT.first == "SFixedLog2LMUL") {
590 int32_t Log2LMUL;
591 if (ComplexTT.second.getAsInteger(10, Log2LMUL)) {
592 llvm_unreachable("Invalid SFixedLog2LMUL value!");
593 return std::nullopt;
594 }
595 switch (Log2LMUL) {
596 case -3:
598 break;
599 case -2:
601 break;
602 case -1:
604 break;
605 case 0:
607 break;
608 case 1:
610 break;
611 case 2:
613 break;
614 case 3:
616 break;
617 default:
618 llvm_unreachable("Invalid LFixedLog2LMUL value, should be [-3, 3]");
619 return std::nullopt;
620 }
621
622 } else if (ComplexTT.first == "SEFixedLog2LMUL") {
623 int32_t Log2LMUL;
624 if (ComplexTT.second.getAsInteger(10, Log2LMUL)) {
625 llvm_unreachable("Invalid SEFixedLog2LMUL value!");
626 return std::nullopt;
627 }
628 switch (Log2LMUL) {
629 case -3:
631 break;
632 case -2:
634 break;
635 case -1:
637 break;
638 case 0:
640 break;
641 case 1:
643 break;
644 case 2:
646 break;
647 case 3:
649 break;
650 default:
651 llvm_unreachable("Invalid LFixedLog2LMUL value, should be [-3, 3]");
652 return std::nullopt;
653 }
654 } else if (ComplexTT.first == "Tuple") {
655 unsigned NF = 0;
656 if (ComplexTT.second.getAsInteger(10, NF)) {
657 llvm_unreachable("Invalid NF value!");
658 return std::nullopt;
659 }
660 VTM = getTupleVTM(NF);
661 } else {
662 llvm_unreachable("Illegal complex type transformers!");
663 }
664 }
665 PD.VTM = VTM;
666
667 // Compute the remain type transformers
669 for (char I : PrototypeDescriptorStr) {
670 switch (I) {
671 case 'P':
673 llvm_unreachable("'P' transformer cannot be used after 'C'");
675 llvm_unreachable("'P' transformer cannot be used twice");
677 break;
678 case 'C':
680 break;
681 case 'K':
683 break;
684 case 'U':
686 break;
687 case 'I':
689 break;
690 case 'F':
692 break;
693 case 'Y':
695 break;
696 case 'S':
698 break;
699 default:
700 llvm_unreachable("Illegal non-primitive type transformer!");
701 }
702 }
703 PD.TM = TM;
704
705 return PD;
706}
707
708void RVVType::applyModifier(const PrototypeDescriptor &Transformer) {
709 // Handle primitive type transformer
710 switch (static_cast<BaseTypeModifier>(Transformer.PT)) {
712 Scale = 0;
713 break;
715 Scale = LMUL.getScale(ElementBitwidth);
716 break;
718 ScalarType = ScalarTypeKind::Void;
719 break;
721 ScalarType = ScalarTypeKind::Size_t;
722 break;
724 ScalarType = ScalarTypeKind::Ptrdiff_t;
725 break;
727 ScalarType = ScalarTypeKind::UnsignedLong;
728 break;
730 ScalarType = ScalarTypeKind::SignedLong;
731 break;
733 ElementBitwidth = 32;
734 ScalarType = ScalarTypeKind::Float;
735 break;
737 ScalarType = ScalarTypeKind::Invalid;
738 return;
739 }
740
741 switch (static_cast<VectorTypeModifier>(Transformer.VTM)) {
743 ElementBitwidth *= 2;
744 LMUL.MulLog2LMUL(1);
745 Scale = LMUL.getScale(ElementBitwidth);
746 if (ScalarType == ScalarTypeKind::BFloat)
747 ScalarType = ScalarTypeKind::Float;
748 if (ScalarType == ScalarTypeKind::FloatE4M3 ||
749 ScalarType == ScalarTypeKind::FloatE5M2)
750 ScalarType = ScalarTypeKind::BFloat;
751 break;
753 LMUL.MulLog2LMUL(1);
754 Scale = LMUL.getScale(ElementBitwidth);
755 break;
757 ElementBitwidth *= 4;
758 LMUL.MulLog2LMUL(2);
759 Scale = LMUL.getScale(ElementBitwidth);
760 if (ScalarType == ScalarTypeKind::FloatE4M3 ||
761 ScalarType == ScalarTypeKind::FloatE5M2)
762 ScalarType = ScalarTypeKind::Float;
763 break;
765 ElementBitwidth *= 8;
766 LMUL.MulLog2LMUL(3);
767 Scale = LMUL.getScale(ElementBitwidth);
768 break;
770 ScalarType = ScalarTypeKind::Boolean;
771 Scale = LMUL.getScale(ElementBitwidth);
772 ElementBitwidth = 1;
773 break;
775 applyLog2EEW(3);
776 break;
778 applyLog2EEW(4);
779 break;
781 applyLog2EEW(5);
782 break;
784 applyLog2EEW(6);
785 break;
787 applyFixedSEW(8);
788 break;
790 applyFixedSEW(16);
791 break;
793 applyFixedSEW(32);
794 break;
796 applyFixedSEW(64);
797 break;
799 applyFixedLog2LMUL(-3, FixedLMULType::LargerThan);
800 break;
802 applyFixedLog2LMUL(-2, FixedLMULType::LargerThan);
803 break;
805 applyFixedLog2LMUL(-1, FixedLMULType::LargerThan);
806 break;
808 applyFixedLog2LMUL(0, FixedLMULType::LargerThan);
809 break;
811 applyFixedLog2LMUL(1, FixedLMULType::LargerThan);
812 break;
814 applyFixedLog2LMUL(2, FixedLMULType::LargerThan);
815 break;
817 applyFixedLog2LMUL(3, FixedLMULType::LargerThan);
818 break;
820 applyFixedLog2LMUL(-3, FixedLMULType::SmallerThan);
821 break;
823 applyFixedLog2LMUL(-2, FixedLMULType::SmallerThan);
824 break;
826 applyFixedLog2LMUL(-1, FixedLMULType::SmallerThan);
827 break;
829 applyFixedLog2LMUL(0, FixedLMULType::SmallerThan);
830 break;
832 applyFixedLog2LMUL(1, FixedLMULType::SmallerThan);
833 break;
835 applyFixedLog2LMUL(2, FixedLMULType::SmallerThan);
836 break;
838 applyFixedLog2LMUL(3, FixedLMULType::SmallerThan);
839 break;
841 applyFixedLog2LMUL(-3, FixedLMULType::SmallerOrEqual);
842 break;
844 applyFixedLog2LMUL(-2, FixedLMULType::SmallerOrEqual);
845 break;
847 applyFixedLog2LMUL(-1, FixedLMULType::SmallerOrEqual);
848 break;
850 applyFixedLog2LMUL(0, FixedLMULType::SmallerOrEqual);
851 break;
853 applyFixedLog2LMUL(1, FixedLMULType::SmallerOrEqual);
854 break;
856 applyFixedLog2LMUL(2, FixedLMULType::SmallerOrEqual);
857 break;
859 applyFixedLog2LMUL(3, FixedLMULType::SmallerOrEqual);
860 break;
868 IsTuple = true;
869 NF = 2 + static_cast<uint8_t>(Transformer.VTM) -
870 static_cast<uint8_t>(VectorTypeModifier::Tuple2);
871 break;
872 }
874 break;
875 }
876
877 // Early return if the current type modifier is already invalid.
878 if (ScalarType == Invalid)
879 return;
880
881 for (unsigned TypeModifierMaskShift = 0;
882 TypeModifierMaskShift <= static_cast<unsigned>(TypeModifier::MaxOffset);
883 ++TypeModifierMaskShift) {
884 unsigned TypeModifierMask = 1 << TypeModifierMaskShift;
885 if ((static_cast<unsigned>(Transformer.TM) & TypeModifierMask) !=
886 TypeModifierMask)
887 continue;
888 switch (static_cast<TypeModifier>(TypeModifierMask)) {
890 IsPointer = true;
891 break;
893 IsConstant = true;
894 break;
896 IsImmediate = true;
897 IsConstant = true;
898 break;
901 break;
904 break;
906 ScalarType = ScalarTypeKind::Float;
907 break;
909 ScalarType = ScalarTypeKind::BFloat;
910 break;
912 LMUL = LMULType(0);
913 // Update ElementBitwidth need to update Scale too.
914 Scale = LMUL.getScale(ElementBitwidth);
915 break;
916 default:
917 llvm_unreachable("Unknown type modifier mask!");
918 }
919 }
920}
921
922void RVVType::applyLog2EEW(unsigned Log2EEW) {
923 // update new elmul = (eew/sew) * lmul
924 LMUL.MulLog2LMUL(Log2EEW - Log2_32(ElementBitwidth));
925 // update new eew
926 ElementBitwidth = 1 << Log2EEW;
928 Scale = LMUL.getScale(ElementBitwidth);
929}
930
931void RVVType::applyFixedSEW(unsigned NewSEW) {
932 // Set invalid type if src and dst SEW are same.
933 if (ElementBitwidth == NewSEW) {
934 ScalarType = ScalarTypeKind::Invalid;
935 return;
936 }
937 // Update new SEW
938 ElementBitwidth = NewSEW;
939 Scale = LMUL.getScale(ElementBitwidth);
940}
941
942void RVVType::applyFixedLog2LMUL(int Log2LMUL, enum FixedLMULType Type) {
943 switch (Type) {
944 case FixedLMULType::LargerThan:
945 if (Log2LMUL <= LMUL.Log2LMUL) {
946 ScalarType = ScalarTypeKind::Invalid;
947 return;
948 }
949 break;
950 case FixedLMULType::SmallerThan:
951 if (Log2LMUL >= LMUL.Log2LMUL) {
952 ScalarType = ScalarTypeKind::Invalid;
953 return;
954 }
955 break;
956 case FixedLMULType::SmallerOrEqual:
957 if (Log2LMUL > LMUL.Log2LMUL) {
958 ScalarType = ScalarTypeKind::Invalid;
959 return;
960 }
961 break;
962 }
963
964 // Update new LMUL
965 LMUL = LMULType(Log2LMUL);
966 Scale = LMUL.getScale(ElementBitwidth);
967}
968
969std::optional<RVVTypes>
970RVVTypeCache::computeTypes(BasicType BT, int Log2LMUL, unsigned NF,
972 RVVTypes Types;
973 for (const PrototypeDescriptor &Proto : Prototype) {
974 auto T = computeType(BT, Log2LMUL, Proto);
975 if (!T)
976 return std::nullopt;
977 // Record legal type index
978 Types.push_back(*T);
979 }
980 return Types;
981}
982
983// Compute the hash value of RVVType, used for cache the result of computeType.
984static uint64_t computeRVVTypeHashValue(BasicType BT, int Log2LMUL,
985 PrototypeDescriptor Proto) {
986 // Layout of hash value:
987 // 0 8 24 32 40 48
988 // | Log2LMUL + 3 | BT | Proto.PT | Proto.TM | Proto.VTM |
989 assert(Log2LMUL >= -3 && Log2LMUL <= 3);
990 return (Log2LMUL + 3) | (static_cast<uint64_t>(BT) & 0xffff) << 8 |
991 (static_cast<uint64_t>(Proto.PT) << 24) |
992 (static_cast<uint64_t>(Proto.TM) << 32) |
993 (static_cast<uint64_t>(Proto.VTM) << 40);
994}
995
996std::optional<RVVTypePtr> RVVTypeCache::computeType(BasicType BT, int Log2LMUL,
997 PrototypeDescriptor Proto) {
998 uint64_t Idx = computeRVVTypeHashValue(BT, Log2LMUL, Proto);
999 // Search first
1000 auto It = LegalTypes.find(Idx);
1001 if (It != LegalTypes.end())
1002 return &(It->second);
1003
1004 if (IllegalTypes.count(Idx))
1005 return std::nullopt;
1006
1007 // Compute type and record the result.
1008 RVVType T(BT, Log2LMUL, Proto);
1009 if (T.isValid()) {
1010 // Record legal type index and value.
1011 std::pair<std::unordered_map<uint64_t, RVVType>::iterator, bool>
1012 InsertResult = LegalTypes.insert({Idx, T});
1013 return &(InsertResult.first->second);
1014 }
1015 // Record illegal type index.
1016 IllegalTypes.insert(Idx);
1017 return std::nullopt;
1018}
1019
1020//===----------------------------------------------------------------------===//
1021// RVVIntrinsic implementation
1022//===----------------------------------------------------------------------===//
1024 StringRef NewName, StringRef Suffix, StringRef NewOverloadedName,
1025 StringRef OverloadedSuffix, StringRef IRName, bool IsMasked,
1026 bool HasMaskedOffOperand, bool HasVL, PolicyScheme Scheme,
1027 bool SupportOverloading, bool HasBuiltinAlias, StringRef ManualCodegen,
1028 const RVVTypes &OutInTypes, const std::vector<int64_t> &NewIntrinsicTypes,
1029 unsigned NF, bool HasSegInstSEW, Policy NewPolicyAttrs,
1030 bool HasFRMRoundModeOp, unsigned TWiden, bool AltFmt)
1031 : IRName(IRName), IsMasked(IsMasked),
1032 HasMaskedOffOperand(HasMaskedOffOperand), HasVL(HasVL), Scheme(Scheme),
1033 SupportOverloading(SupportOverloading), HasBuiltinAlias(HasBuiltinAlias),
1034 ManualCodegen(ManualCodegen.str()), NF(NF), HasSegInstSEW(HasSegInstSEW),
1035 PolicyAttrs(NewPolicyAttrs), TWiden(TWiden) {
1036
1037 // Init BuiltinName, Name and OverloadedName
1038 BuiltinName = NewName.str();
1039 Name = BuiltinName;
1040 if (NewOverloadedName.empty())
1041 OverloadedName = NewName.split("_").first.str();
1042 else
1043 OverloadedName = NewOverloadedName.str();
1044 if (!Suffix.empty())
1045 Name += "_" + Suffix.str();
1046 if (!OverloadedSuffix.empty())
1047 OverloadedName += "_" + OverloadedSuffix.str();
1048
1049 updateNamesAndPolicy(IsMasked, hasPolicy(), Name, BuiltinName, OverloadedName,
1050 PolicyAttrs, HasFRMRoundModeOp, AltFmt);
1051
1052 // Init OutputType and InputTypes
1053 OutputType = OutInTypes[0];
1054 InputTypes.assign(OutInTypes.begin() + 1, OutInTypes.end());
1055
1056 // IntrinsicTypes is unmasked TA version index. Need to update it
1057 // if there is merge operand (It is always in first operand).
1058 IntrinsicTypes = NewIntrinsicTypes;
1059 if ((IsMasked && hasMaskedOffOperand()) ||
1060 (!IsMasked && hasPassthruOperand())) {
1061 for (auto &I : IntrinsicTypes) {
1062 if (I >= 0)
1063 I += 1;
1064 }
1065 }
1066}
1067
1069 std::string S;
1070 S += OutputType->getBuiltinStr();
1071 for (const auto &T : InputTypes) {
1072 S += T->getBuiltinStr();
1073 }
1074 return S;
1075}
1076
1078 RVVTypeCache &TypeCache, BasicType Type, int Log2LMUL,
1079 llvm::ArrayRef<PrototypeDescriptor> PrototypeDescriptors) {
1080 SmallVector<std::string> SuffixStrs;
1081 for (auto PD : PrototypeDescriptors) {
1082 auto T = TypeCache.computeType(Type, Log2LMUL, PD);
1083 SuffixStrs.push_back((*T)->getShortStr());
1084 }
1085 return join(SuffixStrs, "_");
1086}
1087
1090 bool HasMaskedOffOperand, bool HasVL, unsigned NF,
1091 PolicyScheme DefaultScheme, Policy PolicyAttrs, bool IsTuple) {
1093 bool HasPassthruOp = DefaultScheme == PolicyScheme::HasPassthruOperand;
1094 if (IsMasked) {
1095 // If HasMaskedOffOperand, insert result type as first input operand if
1096 // need.
1097 if (HasMaskedOffOperand && !PolicyAttrs.isTAMAPolicy()) {
1098 if (NF == 1) {
1099 NewPrototype.insert(NewPrototype.begin() + 1, NewPrototype[0]);
1100 } else if (NF > 1) {
1101 if (IsTuple) {
1102 PrototypeDescriptor BasePtrOperand = Prototype[1];
1103 PrototypeDescriptor MaskoffType =
1105 BasePtrOperand.TM & ~TypeModifier::Pointer);
1106 NewPrototype.insert(NewPrototype.begin() + 1, MaskoffType);
1107 } else {
1108 // Convert
1109 // (void, op0 address, op1 address, ...)
1110 // to
1111 // (void, op0 address, op1 address, ..., maskedoff0, maskedoff1, ...)
1112 PrototypeDescriptor MaskoffType = NewPrototype[1];
1113 MaskoffType.TM &= ~TypeModifier::Pointer;
1114 NewPrototype.insert(NewPrototype.begin() + NF + 1, NF, MaskoffType);
1115 }
1116 }
1117 }
1118 if (HasMaskedOffOperand && NF > 1) {
1119 // Convert
1120 // (void, op0 address, op1 address, ..., maskedoff0, maskedoff1, ...)
1121 // to
1122 // (void, op0 address, op1 address, ..., mask, maskedoff0, maskedoff1,
1123 // ...)
1124 if (IsTuple)
1125 NewPrototype.insert(NewPrototype.begin() + 1,
1127 else
1128 NewPrototype.insert(NewPrototype.begin() + NF + 1,
1130 } else {
1131 // If IsMasked, insert PrototypeDescriptor:Mask as first input operand.
1132 NewPrototype.insert(NewPrototype.begin() + 1, PrototypeDescriptor::Mask);
1133 }
1134 } else {
1135 if (NF == 1) {
1136 if (PolicyAttrs.isTUPolicy() && HasPassthruOp)
1137 NewPrototype.insert(NewPrototype.begin(), NewPrototype[0]);
1138 } else if (PolicyAttrs.isTUPolicy() && HasPassthruOp) {
1139 if (IsTuple) {
1140 PrototypeDescriptor BasePtrOperand = Prototype[0];
1141 PrototypeDescriptor MaskoffType =
1143 BasePtrOperand.TM & ~TypeModifier::Pointer);
1144 NewPrototype.insert(NewPrototype.begin(), MaskoffType);
1145 } else {
1146 // NF > 1 cases for segment load operations.
1147 // Convert
1148 // (void, op0 address, op1 address, ...)
1149 // to
1150 // (void, op0 address, op1 address, maskedoff0, maskedoff1, ...)
1151 PrototypeDescriptor MaskoffType = Prototype[1];
1152 MaskoffType.TM &= ~TypeModifier::Pointer;
1153 NewPrototype.insert(NewPrototype.begin() + NF + 1, NF, MaskoffType);
1154 }
1155 }
1156 }
1157
1158 // If HasVL, append PrototypeDescriptor:VL to last operand
1159 if (HasVL)
1160 NewPrototype.push_back(PrototypeDescriptor::VL);
1161
1162 return NewPrototype;
1163}
1164
1168
1171 bool HasMaskPolicy) {
1172 if (HasTailPolicy && HasMaskPolicy)
1179 if (HasTailPolicy && !HasMaskPolicy)
1182 if (!HasTailPolicy && HasMaskPolicy)
1185 llvm_unreachable("An RVV instruction should not be without both tail policy "
1186 "and mask policy");
1187}
1188
1189void RVVIntrinsic::updateNamesAndPolicy(bool IsMasked, bool HasPolicy,
1190 std::string &Name,
1191 std::string &BuiltinName,
1192 std::string &OverloadedName,
1193 Policy &PolicyAttrs,
1194 bool HasFRMRoundModeOp, bool AltFmt) {
1195
1196 auto appendPolicySuffix = [&](const std::string &suffix) {
1197 Name += suffix;
1198 BuiltinName += suffix;
1199 OverloadedName += suffix;
1200 };
1201
1202 if (HasFRMRoundModeOp) {
1203 Name += "_rm";
1204 BuiltinName += "_rm";
1205 }
1206
1207 if (AltFmt)
1208 BuiltinName += "_alt";
1209
1210 if (IsMasked) {
1211 if (PolicyAttrs.isTUMUPolicy())
1212 appendPolicySuffix("_tumu");
1213 else if (PolicyAttrs.isTUMAPolicy())
1214 appendPolicySuffix("_tum");
1215 else if (PolicyAttrs.isTAMUPolicy())
1216 appendPolicySuffix("_mu");
1217 else if (PolicyAttrs.isTAMAPolicy()) {
1218 Name += "_m";
1219 BuiltinName += "_m";
1220 } else
1221 llvm_unreachable("Unhandled policy condition");
1222 } else {
1223 if (PolicyAttrs.isTUPolicy())
1224 appendPolicySuffix("_tu");
1225 else if (PolicyAttrs.isTAPolicy()) // no suffix needed
1226 return;
1227 else
1228 llvm_unreachable("Unhandled policy condition");
1229 }
1230}
1231
1233 SmallVector<PrototypeDescriptor> PrototypeDescriptors;
1234 const StringRef Primaries("evwdqom0ztulf");
1235 while (!Prototypes.empty()) {
1236 size_t Idx = 0;
1237 // Skip over complex prototype because it could contain primitive type
1238 // character.
1239 if (Prototypes[0] == '(')
1240 Idx = Prototypes.find_first_of(')');
1241 Idx = Prototypes.find_first_of(Primaries, Idx);
1242 assert(Idx != StringRef::npos);
1244 Prototypes.slice(0, Idx + 1));
1245 if (!PD)
1246 llvm_unreachable("Error during parsing prototype.");
1247 PrototypeDescriptors.push_back(*PD);
1248 Prototypes = Prototypes.drop_front(Idx + 1);
1249 }
1250 return PrototypeDescriptors;
1251}
1252
1253#define STRINGIFY(NAME) \
1254 case NAME: \
1255 OS << #NAME; \
1256 break;
1257
1258llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, enum PolicyScheme PS) {
1259 switch (PS) {
1263 }
1264 return OS;
1265}
1266
1267#undef STRINGIFY
1268
1269raw_ostream &operator<<(raw_ostream &OS, const RVVIntrinsicRecord &Record) {
1270 OS << "{";
1271 OS << "/*Name=*/\"" << Record.Name << "\", ";
1272 if (Record.OverloadedName == nullptr ||
1273 StringRef(Record.OverloadedName).empty())
1274 OS << "/*OverloadedName=*/nullptr, ";
1275 else
1276 OS << "/*OverloadedName=*/\"" << Record.OverloadedName << "\", ";
1277 OS << "/*RequiredExtensions=*/\"" << Record.RequiredExtensions << "\", ";
1278 OS << "/*PrototypeIndex=*/" << Record.PrototypeIndex << ", ";
1279 OS << "/*SuffixIndex=*/" << Record.SuffixIndex << ", ";
1280 OS << "/*OverloadedSuffixIndex=*/" << Record.OverloadedSuffixIndex << ", ";
1281 OS << "/*PrototypeLength=*/" << (int)Record.PrototypeLength << ", ";
1282 OS << "/*SuffixLength=*/" << (int)Record.SuffixLength << ", ";
1283 OS << "/*OverloadedSuffixSize=*/" << (int)Record.OverloadedSuffixSize << ", ";
1284 OS << "/*TypeRangeMask=*/" << (int)Record.TypeRangeMask << ", ";
1285 OS << "/*Log2LMULMask=*/" << (int)Record.Log2LMULMask << ", ";
1286 OS << "/*NF=*/" << (int)Record.NF << ", ";
1287 OS << "/*HasMasked=*/" << (int)Record.HasMasked << ", ";
1288 OS << "/*HasVL=*/" << (int)Record.HasVL << ", ";
1289 OS << "/*HasMaskedOffOperand=*/" << (int)Record.HasMaskedOffOperand << ", ";
1290 OS << "/*HasTailPolicy=*/" << (int)Record.HasTailPolicy << ", ";
1291 OS << "/*HasMaskPolicy=*/" << (int)Record.HasMaskPolicy << ", ";
1292 OS << "/*HasFRMRoundModeOp=*/" << (int)Record.HasFRMRoundModeOp << ", ";
1293 OS << "/*AltFmt=*/" << (int)Record.AltFmt << ",";
1294 OS << "/*IsTuple=*/" << (int)Record.IsTuple << ", ";
1295 OS << "/*UnMaskedPolicyScheme=*/" << (PolicyScheme)Record.UnMaskedPolicyScheme
1296 << ", ";
1297 OS << "/*MaskedPolicyScheme=*/" << (PolicyScheme)Record.MaskedPolicyScheme
1298 << ", ";
1299 OS << "},\n";
1300 return OS;
1301}
1302
1303} // end namespace RISCV
1304} // end namespace clang
#define V(N, I)
static bool getTypeString(SmallStringEnc &Enc, const Decl *D, const CodeGen::CodeGenModule &CGM, TypeStringCache &TSC)
The XCore ABI includes a type information section that communicates symbol type information to the li...
Definition XCore.cpp:630
llvm::MachO::Record Record
Definition MachO.h:31
#define STRINGIFY(NAME)
static bool isVector(QualType QT, QualType ElementType)
This helper function returns true if QT is a vector type that has element type ElementType.
Complex values, per C99 6.2.5p11.
Definition TypeBase.h:3337
RVVIntrinsic(llvm::StringRef Name, llvm::StringRef Suffix, llvm::StringRef OverloadedName, llvm::StringRef OverloadedSuffix, llvm::StringRef IRName, bool IsMasked, bool HasMaskedOffOperand, bool HasVL, PolicyScheme Scheme, bool SupportOverloading, bool HasBuiltinAlias, llvm::StringRef ManualCodegen, const RVVTypes &Types, const std::vector< int64_t > &IntrinsicTypes, unsigned NF, bool HasSegInstSEW, Policy PolicyAttrs, bool HasFRMRoundModeOp, unsigned TWiden, bool AltFmt)
static llvm::SmallVector< Policy > getSupportedMaskedPolicies(bool HasTailPolicy, bool HasMaskPolicy)
static llvm::SmallVector< PrototypeDescriptor > computeBuiltinTypes(llvm::ArrayRef< PrototypeDescriptor > Prototype, bool IsMasked, bool HasMaskedOffOperand, bool HasVL, unsigned NF, PolicyScheme DefaultScheme, Policy PolicyAttrs, bool IsTuple)
static std::string getSuffixStr(RVVTypeCache &TypeCache, BasicType Type, int Log2LMUL, llvm::ArrayRef< PrototypeDescriptor > PrototypeDescriptors)
static void updateNamesAndPolicy(bool IsMasked, bool HasPolicy, std::string &Name, std::string &BuiltinName, std::string &OverloadedName, Policy &PolicyAttrs, bool HasFRMRoundModeOp, bool AltFmt)
static llvm::SmallVector< Policy > getSupportedUnMaskedPolicies()
std::optional< RVVTypePtr > computeType(BasicType BT, int Log2LMUL, PrototypeDescriptor Proto)
std::optional< RVVTypes > computeTypes(BasicType BT, int Log2LMUL, unsigned NF, llvm::ArrayRef< PrototypeDescriptor > Prototype)
Compute output and input types by applying different config (basic type and LMUL with type transforme...
The base class of the type hierarchy.
Definition TypeBase.h:1875
RISCV builtins.
std::optional< unsigned > VScaleVal
llvm::raw_ostream & operator<<(llvm::raw_ostream &OS, enum PolicyScheme PS)
llvm::SmallVector< PrototypeDescriptor > parsePrototypes(llvm::StringRef Prototypes)
std::vector< RVVTypePtr > RVVTypes
static uint64_t computeRVVTypeHashValue(BasicType BT, int Log2LMUL, PrototypeDescriptor Proto)
static VectorTypeModifier getTupleVTM(unsigned NF)
The JSON file list parser is used to communicate input to InstallAPI.
if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))
@ Type
The name was classified as a type.
Definition Sema.h:564
Diagnostic wrappers for TextAPI types for error reporting.
Definition Dominators.h:30
std::optional< unsigned > getScale(unsigned ElementBitwidth) const
static std::optional< PrototypeDescriptor > parsePrototypeDescriptor(llvm::StringRef PrototypeStr)
static const PrototypeDescriptor VL
constexpr PrototypeDescriptor()=default
static const PrototypeDescriptor Mask
static const PrototypeDescriptor Vector