1//===-- RISCVVEmitter.cpp - Generate riscv_vector.h for use with clang ----===//
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 riscv_vector.h which
10// includes a declaration and definition of each intrinsic functions specified
11// in https://github.com/riscv/rvv-intrinsic-doc.
12//
13// See also the documentation in include/clang/Basic/riscv_vector.td.
14//
15//===----------------------------------------------------------------------===//
16
17#include "clang/Support/RISCVVIntrinsicUtils.h"
18#include "llvm/ADT/ArrayRef.h"
19#include "llvm/ADT/StringExtras.h"
20#include "llvm/ADT/StringMap.h"
21#include "llvm/ADT/StringRef.h"
22#include "llvm/ADT/StringSwitch.h"
23#include "llvm/ADT/Twine.h"
24#include "llvm/TableGen/Error.h"
25#include "llvm/TableGen/Record.h"
26#include "llvm/TableGen/StringToOffsetTable.h"
27#include <optional>
28
29using namespace llvm;
30using namespace clang::RISCV;
31
32namespace {
33struct SemaRecord {
34 // Intrinsic name, e.g. vadd_vv
35 std::string Name;
36
37 // Overloaded intrinsic name, could be empty if can be computed from Name
38 // e.g. vadd
39 std::string OverloadedName;
40
41 // Supported type, mask of BasicType.
42 unsigned TypeRangeMask;
43
44 // Supported LMUL.
45 unsigned Log2LMULMask;
46
47 // Required extensions for this intrinsic.
48 std::string RequiredExtensions;
49
50 // Prototype for this intrinsic.
51 SmallVector<PrototypeDescriptor> Prototype;
52
53 // Suffix of intrinsic name.
54 SmallVector<PrototypeDescriptor> Suffix;
55
56 // Suffix of overloaded intrinsic name.
57 SmallVector<PrototypeDescriptor> OverloadedSuffix;
58
59 // Number of field, large than 1 if it's segment load/store.
60 unsigned NF;
61
62 bool HasMasked : 1;
63 bool HasVL : 1;
64 bool HasMaskedOffOperand : 1;
65 bool HasTailPolicy : 1;
66 bool HasMaskPolicy : 1;
67 bool HasFRMRoundModeOp : 1;
68 bool MaskedPrototypeHasResultMask : 1;
69 bool AltFmt : 1;
70 bool IsTuple : 1;
71 LLVM_PREFERRED_TYPE(PolicyScheme)
72 uint8_t UnMaskedPolicyScheme : 2;
73 LLVM_PREFERRED_TYPE(PolicyScheme)
74 uint8_t MaskedPolicyScheme : 2;
75};
76
77// Compressed function signature table.
78class SemaSignatureTable {
79private:
80 std::vector<PrototypeDescriptor> SignatureTable;
81
82 void insert(ArrayRef<PrototypeDescriptor> Signature);
83
84public:
85 static constexpr unsigned INVALID_INDEX = ~0U;
86
87 // Create compressed signature table from SemaRecords.
88 void init(ArrayRef<SemaRecord> SemaRecords);
89
90 // Query the Signature, return INVALID_INDEX if not found.
91 unsigned getIndex(ArrayRef<PrototypeDescriptor> Signature);
92
93 /// Print signature table in RVVHeader Record to \p OS
94 void print(raw_ostream &OS);
95};
96
97class RVVEmitter {
98private:
99 const RecordKeeper &Records;
100 RVVTypeCache TypeCache;
101
102public:
103 RVVEmitter(const RecordKeeper &R) : Records(R) {}
104
105 /// Emit riscv_vector.h
106 void createHeader(raw_ostream &o);
107
108 /// Emit all the __builtin prototypes and code needed by Sema.
109 void createBuiltins(raw_ostream &o);
110
111 /// Emit all the information needed to map builtin -> LLVM IR intrinsic.
112 void createCodeGen(raw_ostream &o);
113
114 /// Emit all the information needed by SemaRISCVVectorLookup.cpp.
115 /// We've large number of intrinsic function for RVV, creating a customized
116 /// could speed up the compilation time.
117 void createSema(raw_ostream &o);
118
119private:
120 /// Create all intrinsics and add them to \p Out and SemaRecords.
121 void createRVVIntrinsics(std::vector<std::unique_ptr<RVVIntrinsic>> &Out,
122 std::vector<SemaRecord> *SemaRecords = nullptr);
123 /// Create all intrinsic records and SemaSignatureTable from SemaRecords.
124 void createRVVIntrinsicRecords(std::vector<RVVIntrinsicRecord> &Out,
125 SemaSignatureTable &SST,
126 ArrayRef<SemaRecord> SemaRecords);
127
128 /// Print HeaderCode in RVVHeader Record to \p Out
129 void printHeaderCode(raw_ostream &OS);
130};
131
132} // namespace
133
134static BasicType ParseBasicType(char c) {
135 switch (c) {
136 case 'c':
137 return BasicType::Int8;
138 case 's':
139 return BasicType::Int16;
140 case 'i':
141 return BasicType::Int32;
142 case 'l':
143 return BasicType::Int64;
144 case 'x':
145 return BasicType::Float16;
146 case 'f':
147 return BasicType::Float32;
148 case 'd':
149 return BasicType::Float64;
150 case 'y':
151 return BasicType::BFloat16;
152 case 'a':
153 return BasicType::F8E4M3;
154 case 'b':
155 return BasicType::F8E5M2;
156 default:
157 return BasicType::Unknown;
158 }
159}
160
161static VectorTypeModifier getTupleVTM(unsigned NF) {
162 assert(2 <= NF && NF <= 8 && "2 <= NF <= 8");
163 return static_cast<VectorTypeModifier>(
164 static_cast<uint8_t>(VectorTypeModifier::Tuple2) + (NF - 2));
165}
166
167static const unsigned UnknownIndex = (unsigned)-1;
168
169static unsigned getIndexedLoadStorePtrIdx(const RVVIntrinsic *RVVI) {
170 // We need a special rule for segment load/store since the data width is not
171 // encoded in the intrinsic name itself.
172 const StringRef IRName = RVVI->getIRName();
173 constexpr unsigned RVV_VTA = 0x1;
174 constexpr unsigned RVV_VMA = 0x2;
175
176 if (IRName.starts_with(Prefix: "vloxseg") || IRName.starts_with(Prefix: "vluxseg")) {
177 bool NoPassthru =
178 (RVVI->isMasked() && (RVVI->getPolicyAttrsBits() & RVV_VTA) &&
179 (RVVI->getPolicyAttrsBits() & RVV_VMA)) ||
180 (!RVVI->isMasked() && (RVVI->getPolicyAttrsBits() & RVV_VTA));
181 return RVVI->isMasked() ? NoPassthru ? 1 : 2 : NoPassthru ? 0 : 1;
182 }
183 if (IRName.starts_with(Prefix: "vsoxseg") || IRName.starts_with(Prefix: "vsuxseg"))
184 return RVVI->isMasked() ? 1 : 0;
185
186 return UnknownIndex;
187}
188
189// This function is used to get the log2SEW of each segment load/store, this
190// prevent to add a member to RVVIntrinsic.
191static unsigned getSegInstLog2SEW(StringRef InstName) {
192 // clang-format off
193 // We need a special rule for indexed segment load/store since the data width
194 // is not encoded in the intrinsic name itself.
195 if (InstName.starts_with(Prefix: "vloxseg") || InstName.starts_with(Prefix: "vluxseg") ||
196 InstName.starts_with(Prefix: "vsoxseg") || InstName.starts_with(Prefix: "vsuxseg"))
197 return (unsigned)-1;
198
199#define KEY_VAL(KEY, VAL) {#KEY, VAL}
200#define KEY_VAL_ALL_W_POLICY(KEY, VAL) \
201 KEY_VAL(KEY, VAL), \
202 KEY_VAL(KEY ## _tu, VAL), \
203 KEY_VAL(KEY ## _tum, VAL), \
204 KEY_VAL(KEY ## _tumu, VAL), \
205 KEY_VAL(KEY ## _mu, VAL)
206
207#define KEY_VAL_ALL_NF_BASE(MACRO_NAME, NAME, SEW, LOG2SEW, FF) \
208 MACRO_NAME(NAME ## 2e ## SEW ## FF, LOG2SEW), \
209 MACRO_NAME(NAME ## 3e ## SEW ## FF, LOG2SEW), \
210 MACRO_NAME(NAME ## 4e ## SEW ## FF, LOG2SEW), \
211 MACRO_NAME(NAME ## 5e ## SEW ## FF, LOG2SEW), \
212 MACRO_NAME(NAME ## 6e ## SEW ## FF, LOG2SEW), \
213 MACRO_NAME(NAME ## 7e ## SEW ## FF, LOG2SEW), \
214 MACRO_NAME(NAME ## 8e ## SEW ## FF, LOG2SEW)
215
216#define KEY_VAL_ALL_NF(NAME, SEW, LOG2SEW) \
217 KEY_VAL_ALL_NF_BASE(KEY_VAL_ALL_W_POLICY, NAME, SEW, LOG2SEW,)
218
219#define KEY_VAL_FF_ALL_NF(NAME, SEW, LOG2SEW) \
220 KEY_VAL_ALL_NF_BASE(KEY_VAL_ALL_W_POLICY, NAME, SEW, LOG2SEW, ff)
221
222#define KEY_VAL_ALL_NF_SEW_BASE(MACRO_NAME, NAME) \
223 MACRO_NAME(NAME, 8, 3), \
224 MACRO_NAME(NAME, 16, 4), \
225 MACRO_NAME(NAME, 32, 5), \
226 MACRO_NAME(NAME, 64, 6)
227
228#define KEY_VAL_ALL_NF_SEW(NAME) \
229 KEY_VAL_ALL_NF_SEW_BASE(KEY_VAL_ALL_NF, NAME)
230
231#define KEY_VAL_FF_ALL_NF_SEW(NAME) \
232 KEY_VAL_ALL_NF_SEW_BASE(KEY_VAL_FF_ALL_NF, NAME)
233 // clang-format on
234
235 static StringMap<unsigned> SegInsts = {
236 KEY_VAL_ALL_NF_SEW(vlseg), KEY_VAL_FF_ALL_NF_SEW(vlseg),
237 KEY_VAL_ALL_NF_SEW(vlsseg), KEY_VAL_ALL_NF_SEW(vsseg),
238 KEY_VAL_ALL_NF_SEW(vssseg)};
239
240#undef KEY_VAL_ALL_NF_SEW
241#undef KEY_VAL_ALL_NF
242#undef KEY_VAL
243
244 return SegInsts.lookup(Key: InstName);
245}
246
247void emitCodeGenSwitchBody(const RVVIntrinsic *RVVI, raw_ostream &OS) {
248 if (!RVVI->getIRName().empty())
249 OS << " ID = Intrinsic::riscv_" + RVVI->getIRName() + ";\n";
250 if (RVVI->getTWiden() > 0)
251 OS << " TWiden = " << RVVI->getTWiden() << ";\n";
252
253 OS << " PolicyAttrs = " << RVVI->getPolicyAttrsBits() << ";\n";
254 if (RVVI->hasSegInstSEW()) {
255 unsigned IndexedLoadStorePtrIdx = getIndexedLoadStorePtrIdx(RVVI);
256 if (IndexedLoadStorePtrIdx != UnknownIndex) {
257 OS << " {\n";
258 OS << " auto PointeeType = E->getArg(" << IndexedLoadStorePtrIdx
259 << ")->getType()->getPointeeType();\n";
260 OS << " SegInstSEW = "
261 "llvm::Log2_64(getContext().getTypeSize(PointeeType));\n";
262 OS << " }\n";
263 } else {
264 OS << " SegInstSEW = " << getSegInstLog2SEW(InstName: RVVI->getOverloadedName())
265 << ";\n";
266 }
267 }
268
269 if (RVVI->hasManualCodegen()) {
270 OS << "IsMasked = " << (RVVI->isMasked() ? "true" : "false") << ";\n";
271 OS << RVVI->getManualCodegen();
272 OS << "break;\n";
273 return;
274 }
275
276 for (const auto &I : enumerate(First: RVVI->getInputTypes())) {
277 if (I.value()->isPointer()) {
278 assert(RVVI->getIntrinsicTypes().front() == -1 &&
279 "RVVI should be vector load intrinsic.");
280 }
281 }
282
283 if (RVVI->isMasked()) {
284 if (RVVI->hasVL()) {
285 OS << " std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end() - 1);\n";
286 if (RVVI->hasPolicyOperand())
287 OS << " Ops.push_back(ConstantInt::get(Ops.back()->getType(),"
288 " PolicyAttrs));\n";
289 if (RVVI->hasMaskedOffOperand() && RVVI->getPolicyAttrs().isTAMAPolicy())
290 OS << " Ops.insert(Ops.begin(), "
291 "llvm::PoisonValue::get(ResultType));\n";
292 // Masked reduction cases.
293 if (!RVVI->hasMaskedOffOperand() && RVVI->hasPassthruOperand() &&
294 RVVI->getPolicyAttrs().isTAMAPolicy())
295 OS << " Ops.insert(Ops.begin(), "
296 "llvm::PoisonValue::get(ResultType));\n";
297 } else {
298 OS << " std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end());\n";
299 }
300 } else {
301 if (RVVI->hasPolicyOperand())
302 OS << " Ops.push_back(ConstantInt::get(Ops.back()->getType(), "
303 "PolicyAttrs));\n";
304 else if (RVVI->hasPassthruOperand() && RVVI->getPolicyAttrs().isTAPolicy())
305 OS << " Ops.insert(Ops.begin(), llvm::PoisonValue::get(ResultType));\n";
306 }
307
308 if (RVVI->getTWiden() > 0)
309 OS << " Ops.push_back(ConstantInt::get(Ops.back()->getType(), TWiden));\n";
310
311 OS << " IntrinsicTypes = {";
312 ListSeparator LS;
313 for (const auto &Idx : RVVI->getIntrinsicTypes()) {
314 if (Idx == -1)
315 OS << LS << "ResultType";
316 else
317 OS << LS << "Ops[" << Idx << "]->getType()";
318 }
319
320 // VL could be i64 or i32, need to encode it in IntrinsicTypes. VL is
321 // always last operand.
322 if (RVVI->hasVL())
323 OS << ", Ops.back()->getType()";
324 OS << "};\n";
325 OS << " break;\n";
326}
327
328//===----------------------------------------------------------------------===//
329// SemaSignatureTable implementation
330//===----------------------------------------------------------------------===//
331void SemaSignatureTable::init(ArrayRef<SemaRecord> SemaRecords) {
332 // Sort signature entries by length, let longer signature insert first, to
333 // make it more possible to reuse table entries, that can reduce ~10% table
334 // size.
335 struct Compare {
336 bool operator()(const SmallVector<PrototypeDescriptor> &A,
337 const SmallVector<PrototypeDescriptor> &B) const {
338 if (A.size() != B.size())
339 return A.size() > B.size();
340
341 size_t Len = A.size();
342 for (size_t i = 0; i < Len; ++i) {
343 if (A[i] != B[i])
344 return A[i] < B[i];
345 }
346
347 return false;
348 }
349 };
350
351 std::set<SmallVector<PrototypeDescriptor>, Compare> Signatures;
352 auto InsertToSignatureSet =
353 [&](const SmallVector<PrototypeDescriptor> &Signature) {
354 if (Signature.empty())
355 return;
356
357 Signatures.insert(x: Signature);
358 };
359
360 assert(!SemaRecords.empty());
361
362 for (const SemaRecord &SR : SemaRecords) {
363 InsertToSignatureSet(SR.Prototype);
364 InsertToSignatureSet(SR.Suffix);
365 InsertToSignatureSet(SR.OverloadedSuffix);
366 }
367
368 for (auto &Sig : Signatures)
369 insert(Signature: Sig);
370}
371
372void SemaSignatureTable::insert(ArrayRef<PrototypeDescriptor> Signature) {
373 if (getIndex(Signature) != INVALID_INDEX)
374 return;
375
376 // Insert Signature into SignatureTable if not found in the table.
377 SignatureTable.insert(position: SignatureTable.begin(), first: Signature.begin(),
378 last: Signature.end());
379}
380
381unsigned SemaSignatureTable::getIndex(ArrayRef<PrototypeDescriptor> Signature) {
382 // Empty signature could be point into any index since there is length
383 // field when we use, so just always point it to 0.
384 if (Signature.empty())
385 return 0;
386
387 // Checking Signature already in table or not.
388 if (Signature.size() <= SignatureTable.size()) {
389 size_t Bound = SignatureTable.size() - Signature.size() + 1;
390 for (size_t Index = 0; Index < Bound; ++Index) {
391 if (equal(first1: Signature.begin(), last1: Signature.end(),
392 first2: SignatureTable.begin() + Index))
393 return Index;
394 }
395 }
396
397 return INVALID_INDEX;
398}
399
400void SemaSignatureTable::print(raw_ostream &OS) {
401 for (const auto &Sig : SignatureTable)
402 OS << "PrototypeDescriptor(" << static_cast<int>(Sig.PT) << ", "
403 << static_cast<int>(Sig.VTM) << ", " << static_cast<int>(Sig.TM)
404 << "),\n";
405}
406
407//===----------------------------------------------------------------------===//
408// RVVEmitter implementation
409//===----------------------------------------------------------------------===//
410void RVVEmitter::createHeader(raw_ostream &OS) {
411
412 OS << "/*===---- riscv_vector.h - RISC-V V-extension RVVIntrinsics "
413 "-------------------===\n"
414 " *\n"
415 " *\n"
416 " * Part of the LLVM Project, under the Apache License v2.0 with LLVM "
417 "Exceptions.\n"
418 " * See https://llvm.org/LICENSE.txt for license information.\n"
419 " * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n"
420 " *\n"
421 " *===-----------------------------------------------------------------"
422 "------===\n"
423 " */\n\n";
424
425 OS << "#ifndef __RISCV_VECTOR_H\n";
426 OS << "#define __RISCV_VECTOR_H\n\n";
427
428 OS << "#include <stdint.h>\n";
429 OS << "#include <stddef.h>\n\n";
430
431 OS << "#ifdef __cplusplus\n";
432 OS << "extern \"C\" {\n";
433 OS << "#endif\n\n";
434
435 OS << "#pragma clang riscv intrinsic vector\n\n";
436
437 // This array includes all extensions that have intrinsics implemented. We
438 // need to update the list when any new intrinsic are defined.
439 static const char *const Exts[] = {
440 "v", "zvabd", "zvbb", "zvbc", "zvdot4a8i", "zve32f",
441 "zve32x", "zve64d", "zve64f", "zve64x", "zvfbfa", "zvfbfmin",
442 "zvfbfwma", "zvfh", "zvfhmin", "zvfofp8min", "zvkb", "zvkg",
443 "zvkn", "zvknc", "zvkned", "zvkng", "zvknha", "zvknhb",
444 "zvks", "zvksc", "zvksed", "zvksg", "zvksh"};
445 for (const char *Ext : Exts)
446 OS << "#define __riscv_intrinsic_" << Ext << " 1\n";
447
448 printHeaderCode(OS);
449
450 auto printType = [&](auto T) {
451 OS << "typedef " << T->getClangBuiltinStr() << " " << T->getTypeStr()
452 << ";\n";
453 };
454
455 constexpr int Log2LMULs[] = {-3, -2, -1, 0, 1, 2, 3};
456 // Print RVV boolean types.
457 for (int Log2LMUL : Log2LMULs) {
458 auto T = TypeCache.computeType(BT: BasicType::Int8, Log2LMUL,
459 Proto: PrototypeDescriptor::Mask);
460 if (T)
461 printType(*T);
462 }
463 // Print RVV int/float types.
464 for (char I : StringRef("csil")) {
465 BasicType BT = ParseBasicType(c: I);
466 for (int Log2LMUL : Log2LMULs) {
467 auto T = TypeCache.computeType(BT, Log2LMUL, Proto: PrototypeDescriptor::Vector);
468 if (T) {
469 printType(*T);
470 auto UT = TypeCache.computeType(
471 BT, Log2LMUL,
472 Proto: PrototypeDescriptor(BaseTypeModifier::Vector,
473 VectorTypeModifier::NoModifier,
474 TypeModifier::UnsignedInteger));
475 printType(*UT);
476 }
477 for (int NF = 2; NF <= 8; ++NF) {
478 auto TupleT = TypeCache.computeType(
479 BT, Log2LMUL,
480 Proto: PrototypeDescriptor(BaseTypeModifier::Vector, getTupleVTM(NF),
481 TypeModifier::SignedInteger));
482 auto TupleUT = TypeCache.computeType(
483 BT, Log2LMUL,
484 Proto: PrototypeDescriptor(BaseTypeModifier::Vector, getTupleVTM(NF),
485 TypeModifier::UnsignedInteger));
486 if (TupleT)
487 printType(*TupleT);
488 if (TupleUT)
489 printType(*TupleUT);
490 }
491 }
492 }
493
494 for (BasicType BT : {BasicType::Float16, BasicType::Float32,
495 BasicType::Float64, BasicType::BFloat16}) {
496 for (int Log2LMUL : Log2LMULs) {
497 auto T = TypeCache.computeType(BT, Log2LMUL, Proto: PrototypeDescriptor::Vector);
498 if (T)
499 printType(*T);
500 for (int NF = 2; NF <= 8; ++NF) {
501 auto TupleT = TypeCache.computeType(
502 BT, Log2LMUL,
503 Proto: PrototypeDescriptor(BaseTypeModifier::Vector, getTupleVTM(NF),
504 (BT == BasicType::BFloat16
505 ? TypeModifier::BFloat
506 : TypeModifier::Float)));
507 if (TupleT)
508 printType(*TupleT);
509 }
510 }
511 }
512
513 // TODO: Support tuple types for ofp8?
514 for (BasicType BT : {BasicType::F8E4M3, BasicType::F8E5M2}) {
515 for (int Log2LMUL : Log2LMULs) {
516 auto T = TypeCache.computeType(BT, Log2LMUL, Proto: PrototypeDescriptor::Vector);
517 if (T)
518 printType(*T);
519 }
520 }
521
522 OS << "\n#ifdef __cplusplus\n";
523 OS << "}\n";
524 OS << "#endif // __cplusplus\n";
525 OS << "#endif // __RISCV_VECTOR_H\n";
526}
527
528void RVVEmitter::createBuiltins(raw_ostream &OS) {
529 std::vector<std::unique_ptr<RVVIntrinsic>> Defs;
530 createRVVIntrinsics(Out&: Defs);
531
532 llvm::StringToOffsetTable Table;
533 // Ensure offset zero is the empty string.
534 Table.GetOrAddStringOffset(Str: "");
535 // Hard coded strings used in the builtin structures.
536 Table.GetOrAddStringOffset(Str: "n");
537 Table.GetOrAddStringOffset(Str: "zve32x");
538
539 // Map to unique the builtin names.
540 StringMap<RVVIntrinsic *> BuiltinMap;
541 std::vector<RVVIntrinsic *> UniqueDefs;
542 for (auto &Def : Defs) {
543 auto P = BuiltinMap.insert(KV: {Def->getBuiltinName(), Def.get()});
544 if (P.second) {
545 Table.GetOrAddStringOffset(Str: Def->getBuiltinName());
546 if (!Def->hasBuiltinAlias())
547 Table.GetOrAddStringOffset(Str: Def->getBuiltinTypeStr());
548 UniqueDefs.push_back(x: Def.get());
549 continue;
550 }
551
552 // Verf that this would have produced the same builtin definition.
553 if (P.first->second->hasBuiltinAlias() != Def->hasBuiltinAlias())
554 PrintFatalError(Msg: "Builtin with same name has different hasAutoDef");
555 else if (!Def->hasBuiltinAlias() &&
556 P.first->second->getBuiltinTypeStr() != Def->getBuiltinTypeStr())
557 PrintFatalError(Msg: "Builtin with same name has different type string");
558 }
559
560 // Emit the enumerators of RVV builtins. Note that these are emitted without
561 // any outer context to enable concatenating them.
562 OS << "// RISCV Vector builtin enumerators\n";
563 OS << "#ifdef GET_RISCVV_BUILTIN_ENUMERATORS\n";
564 for (RVVIntrinsic *Def : UniqueDefs)
565 OS << " BI__builtin_rvv_" << Def->getBuiltinName() << ",\n";
566 OS << "#endif // GET_RISCVV_BUILTIN_ENUMERATORS\n\n";
567
568 // Emit the string table for the RVV builtins.
569 OS << "// RISCV Vector builtin enumerators\n";
570 OS << "#ifdef GET_RISCVV_BUILTIN_STR_TABLE\n";
571 Table.EmitStringTableDef(OS, Name: "BuiltinStrings");
572 OS << "#endif // GET_RISCVV_BUILTIN_STR_TABLE\n\n";
573
574 // Emit the info structs of RVV builtins. Note that these are emitted without
575 // any outer context to enable concatenating them.
576 OS << "// RISCV Vector builtin infos\n";
577 OS << "#ifdef GET_RISCVV_BUILTIN_INFOS\n";
578 for (RVVIntrinsic *Def : UniqueDefs) {
579 OS << " Builtin::Info{Builtin::Info::StrOffsets{"
580 << Table.GetStringOffset(Str: Def->getBuiltinName()) << " /* "
581 << Def->getBuiltinName() << " */, ";
582 if (Def->hasBuiltinAlias()) {
583 OS << "0, ";
584 } else {
585 OS << Table.GetStringOffset(Str: Def->getBuiltinTypeStr()) << " /* "
586 << Def->getBuiltinTypeStr() << " */, ";
587 }
588 OS << Table.GetStringOffset(Str: "n") << " /* n */, ";
589 OS << Table.GetStringOffset(Str: "zve32x") << " /* zve32x */}, ";
590
591 OS << "HeaderDesc::NO_HEADER, ALL_LANGUAGES},\n";
592 }
593 OS << "#endif // GET_RISCVV_BUILTIN_INFOS\n\n";
594}
595
596void RVVEmitter::createCodeGen(raw_ostream &OS) {
597 std::vector<std::unique_ptr<RVVIntrinsic>> Defs;
598 createRVVIntrinsics(Out&: Defs);
599 // IR name could be empty, use the stable sort preserves the relative order.
600 stable_sort(Range&: Defs, C: [](const std::unique_ptr<RVVIntrinsic> &A,
601 const std::unique_ptr<RVVIntrinsic> &B) {
602 if (A->getIRName() == B->getIRName())
603 return (A->getPolicyAttrs() < B->getPolicyAttrs());
604 return (A->getIRName() < B->getIRName());
605 });
606
607 // Map to keep track of which builtin names have already been emitted.
608 StringMap<RVVIntrinsic *> BuiltinMap;
609
610 // Print switch body when the ir name, ManualCodegen, policy or log2sew
611 // changes from previous iteration.
612 RVVIntrinsic *PrevDef = Defs.begin()->get();
613 for (auto &Def : Defs) {
614 StringRef CurIRName = Def->getIRName();
615 if (CurIRName != PrevDef->getIRName() ||
616 (Def->getManualCodegen() != PrevDef->getManualCodegen()) ||
617 (Def->getPolicyAttrs() != PrevDef->getPolicyAttrs()) ||
618 (getSegInstLog2SEW(InstName: Def->getOverloadedName()) !=
619 getSegInstLog2SEW(InstName: PrevDef->getOverloadedName())) ||
620 (Def->getTWiden() != PrevDef->getTWiden())) {
621 emitCodeGenSwitchBody(RVVI: PrevDef, OS);
622 }
623 PrevDef = Def.get();
624
625 auto P =
626 BuiltinMap.insert(KV: std::make_pair(x: Def->getBuiltinName(), y: Def.get()));
627 if (P.second) {
628 OS << "case RISCVVector::BI__builtin_rvv_" << Def->getBuiltinName()
629 << ":\n";
630 continue;
631 }
632
633 if (P.first->second->getIRName() != Def->getIRName())
634 PrintFatalError(Msg: "Builtin with same name has different IRName");
635 else if (P.first->second->getManualCodegen() != Def->getManualCodegen())
636 PrintFatalError(Msg: "Builtin with same name has different ManualCodegen");
637 else if (P.first->second->isMasked() != Def->isMasked())
638 PrintFatalError(Msg: "Builtin with same name has different isMasked");
639 else if (P.first->second->hasVL() != Def->hasVL())
640 PrintFatalError(Msg: "Builtin with same name has different hasVL");
641 else if (P.first->second->getPolicyScheme() != Def->getPolicyScheme())
642 PrintFatalError(Msg: "Builtin with same name has different getPolicyScheme");
643 else if (P.first->second->getIntrinsicTypes() != Def->getIntrinsicTypes())
644 PrintFatalError(Msg: "Builtin with same name has different IntrinsicTypes");
645 }
646 emitCodeGenSwitchBody(RVVI: Defs.back().get(), OS);
647 OS << "\n";
648}
649
650void RVVEmitter::createRVVIntrinsics(
651 std::vector<std::unique_ptr<RVVIntrinsic>> &Out,
652 std::vector<SemaRecord> *SemaRecords) {
653 for (const Record *R : Records.getAllDerivedDefinitions(ClassName: "RVVBuiltin")) {
654 StringRef Name = R->getValueAsString(FieldName: "Name");
655 StringRef SuffixProto = R->getValueAsString(FieldName: "Suffix");
656 StringRef OverloadedName = R->getValueAsString(FieldName: "OverloadedName");
657 StringRef OverloadedSuffixProto = R->getValueAsString(FieldName: "OverloadedSuffix");
658 StringRef Prototypes = R->getValueAsString(FieldName: "Prototype");
659 StringRef TypeRange = R->getValueAsString(FieldName: "TypeRange");
660 bool HasMasked = R->getValueAsBit(FieldName: "HasMasked");
661 bool HasMaskedOffOperand = R->getValueAsBit(FieldName: "HasMaskedOffOperand");
662 bool HasVL = R->getValueAsBit(FieldName: "HasVL");
663 const Record *MPSRecord = R->getValueAsDef(FieldName: "MaskedPolicyScheme");
664 auto MaskedPolicyScheme =
665 static_cast<PolicyScheme>(MPSRecord->getValueAsInt(FieldName: "Value"));
666 const Record *UMPSRecord = R->getValueAsDef(FieldName: "UnMaskedPolicyScheme");
667 auto UnMaskedPolicyScheme =
668 static_cast<PolicyScheme>(UMPSRecord->getValueAsInt(FieldName: "Value"));
669 std::vector<int64_t> Log2LMULList = R->getValueAsListOfInts(FieldName: "Log2LMUL");
670 bool HasTailPolicy = R->getValueAsBit(FieldName: "HasTailPolicy");
671 bool HasMaskPolicy = R->getValueAsBit(FieldName: "HasMaskPolicy");
672 bool MaskedPrototypeHasResultMask =
673 R->getValueAsBit(FieldName: "MaskedPrototypeHasResultMask");
674 bool AltFmt = R->getValueAsBit(FieldName: "AltFmt");
675 bool SupportOverloading = R->getValueAsBit(FieldName: "SupportOverloading");
676 bool HasBuiltinAlias = R->getValueAsBit(FieldName: "HasBuiltinAlias");
677 StringRef ManualCodegen = R->getValueAsString(FieldName: "ManualCodegen");
678 std::vector<int64_t> IntrinsicTypes =
679 R->getValueAsListOfInts(FieldName: "IntrinsicTypes");
680 std::vector<StringRef> RequiredFeatures =
681 R->getValueAsListOfStrings(FieldName: "RequiredFeatures");
682 StringRef IRName = R->getValueAsString(FieldName: "IRName");
683 StringRef MaskedIRName = R->getValueAsString(FieldName: "MaskedIRName");
684 unsigned NF = R->getValueAsInt(FieldName: "NF");
685 bool HasSegInstSEW = R->getValueAsBit(FieldName: "HasSegInstSEW");
686 unsigned TWiden = R->getValueAsInt(FieldName: "TWiden");
687 bool IsTuple = R->getValueAsBit(FieldName: "IsTuple");
688 bool HasFRMRoundModeOp = R->getValueAsBit(FieldName: "HasFRMRoundModeOp");
689
690 const Policy DefaultPolicy;
691 SmallVector<Policy> SupportedUnMaskedPolicies =
692 RVVIntrinsic::getSupportedUnMaskedPolicies();
693 SmallVector<Policy> SupportedMaskedPolicies =
694 RVVIntrinsic::getSupportedMaskedPolicies(HasTailPolicy, HasMaskPolicy);
695
696 // Parse prototype and create a list of primitive type with transformers
697 // (operand) in Prototype. Prototype[0] is output operand.
698 SmallVector<PrototypeDescriptor> BasicPrototype =
699 parsePrototypes(Prototypes);
700
701 SmallVector<PrototypeDescriptor> SuffixDesc = parsePrototypes(Prototypes: SuffixProto);
702 SmallVector<PrototypeDescriptor> OverloadedSuffixDesc =
703 parsePrototypes(Prototypes: OverloadedSuffixProto);
704
705 // Compute Builtin types
706 auto Prototype = RVVIntrinsic::computeBuiltinTypes(
707 Prototype: BasicPrototype, /*IsMasked=*/false,
708 /*HasMaskedOffOperand=*/false,
709 /*MaskedPrototypeHasResultMask=*/false, HasVL, NF, DefaultScheme: UnMaskedPolicyScheme,
710 PolicyAttrs: DefaultPolicy, IsTuple);
711 SmallVector<PrototypeDescriptor> MaskedPrototype;
712 if (HasMasked)
713 MaskedPrototype = RVVIntrinsic::computeBuiltinTypes(
714 Prototype: BasicPrototype, /*IsMasked=*/true, HasMaskedOffOperand,
715 MaskedPrototypeHasResultMask, HasVL, NF, DefaultScheme: MaskedPolicyScheme,
716 PolicyAttrs: DefaultPolicy, IsTuple);
717
718 // Create Intrinsics for each type and LMUL.
719 for (char I : TypeRange) {
720 for (int Log2LMUL : Log2LMULList) {
721 BasicType BT = ParseBasicType(c: I);
722 std::optional<RVVTypes> Types =
723 TypeCache.computeTypes(BT, Log2LMUL, NF, Prototype);
724 // Ignored to create new intrinsic if there are any illegal types.
725 if (!Types)
726 continue;
727
728 auto SuffixStr =
729 RVVIntrinsic::getSuffixStr(TypeCache, Type: BT, Log2LMUL, PrototypeDescriptors: SuffixDesc);
730 auto OverloadedSuffixStr = RVVIntrinsic::getSuffixStr(
731 TypeCache, Type: BT, Log2LMUL, PrototypeDescriptors: OverloadedSuffixDesc);
732 // Create a unmasked intrinsic
733 Out.push_back(x: std::make_unique<RVVIntrinsic>(
734 args&: Name, args&: SuffixStr, args&: OverloadedName, args&: OverloadedSuffixStr, args&: IRName,
735 /*IsMasked=*/args: false, /*HasMaskedOffOperand=*/args: false, args&: HasVL,
736 args&: UnMaskedPolicyScheme, args&: SupportOverloading, args&: HasBuiltinAlias,
737 args&: ManualCodegen, args&: *Types, args&: IntrinsicTypes, args&: NF, args&: HasSegInstSEW,
738 args: DefaultPolicy, args&: HasFRMRoundModeOp, args&: TWiden, args&: AltFmt));
739 if (UnMaskedPolicyScheme != PolicyScheme::SchemeNone)
740 for (auto P : SupportedUnMaskedPolicies) {
741 SmallVector<PrototypeDescriptor> PolicyPrototype =
742 RVVIntrinsic::computeBuiltinTypes(
743 Prototype: BasicPrototype, /*IsMasked=*/false,
744 /*HasMaskedOffOperand=*/false,
745 /*MaskedPrototypeHasResultMask=*/false, HasVL, NF,
746 DefaultScheme: UnMaskedPolicyScheme, PolicyAttrs: P, IsTuple);
747 std::optional<RVVTypes> PolicyTypes =
748 TypeCache.computeTypes(BT, Log2LMUL, NF, Prototype: PolicyPrototype);
749 Out.push_back(x: std::make_unique<RVVIntrinsic>(
750 args&: Name, args&: SuffixStr, args&: OverloadedName, args&: OverloadedSuffixStr, args&: IRName,
751 /*IsMask=*/args: false, /*HasMaskedOffOperand=*/args: false, args&: HasVL,
752 args&: UnMaskedPolicyScheme, args&: SupportOverloading, args&: HasBuiltinAlias,
753 args&: ManualCodegen, args&: *PolicyTypes, args&: IntrinsicTypes, args&: NF, args&: HasSegInstSEW,
754 args&: P, args&: HasFRMRoundModeOp, args&: TWiden, args&: AltFmt));
755 }
756 if (!HasMasked)
757 continue;
758 // Create a masked intrinsic
759 std::optional<RVVTypes> MaskTypes =
760 TypeCache.computeTypes(BT, Log2LMUL, NF, Prototype: MaskedPrototype);
761 Out.push_back(x: std::make_unique<RVVIntrinsic>(
762 args&: Name, args&: SuffixStr, args&: OverloadedName, args&: OverloadedSuffixStr, args&: MaskedIRName,
763 /*IsMasked=*/args: true, args&: HasMaskedOffOperand, args&: HasVL, args&: MaskedPolicyScheme,
764 args&: SupportOverloading, args&: HasBuiltinAlias, args&: ManualCodegen, args&: *MaskTypes,
765 args&: IntrinsicTypes, args&: NF, args&: HasSegInstSEW, args: DefaultPolicy, args&: HasFRMRoundModeOp,
766 args&: TWiden, args&: AltFmt));
767 if (MaskedPolicyScheme == PolicyScheme::SchemeNone)
768 continue;
769 for (auto P : SupportedMaskedPolicies) {
770 SmallVector<PrototypeDescriptor> PolicyPrototype =
771 RVVIntrinsic::computeBuiltinTypes(
772 Prototype: BasicPrototype, /*IsMasked=*/true, HasMaskedOffOperand,
773 MaskedPrototypeHasResultMask, HasVL, NF, DefaultScheme: MaskedPolicyScheme,
774 PolicyAttrs: P, IsTuple);
775 std::optional<RVVTypes> PolicyTypes =
776 TypeCache.computeTypes(BT, Log2LMUL, NF, Prototype: PolicyPrototype);
777 Out.push_back(x: std::make_unique<RVVIntrinsic>(
778 args&: Name, args&: SuffixStr, args&: OverloadedName, args&: OverloadedSuffixStr,
779 args&: MaskedIRName, /*IsMasked=*/args: true, args&: HasMaskedOffOperand, args&: HasVL,
780 args&: MaskedPolicyScheme, args&: SupportOverloading, args&: HasBuiltinAlias,
781 args&: ManualCodegen, args&: *PolicyTypes, args&: IntrinsicTypes, args&: NF, args&: HasSegInstSEW, args&: P,
782 args&: HasFRMRoundModeOp, args&: TWiden, args&: AltFmt));
783 }
784 } // End for Log2LMULList
785 } // End for TypeRange
786
787 // We don't emit vsetvli and vsetvlimax for SemaRecord.
788 // They are written in riscv_vector.td and will emit those marco define in
789 // riscv_vector.h
790 if (Name == "vsetvli" || Name == "vsetvlimax")
791 continue;
792
793 if (!SemaRecords)
794 continue;
795
796 // Create SemaRecord
797 SemaRecord SR;
798 SR.Name = Name.str();
799 SR.OverloadedName = OverloadedName.str();
800 BasicType TypeRangeMask = BasicType::Unknown;
801 for (char I : TypeRange)
802 TypeRangeMask |= ParseBasicType(c: I);
803
804 SR.TypeRangeMask = static_cast<unsigned>(TypeRangeMask);
805
806 unsigned Log2LMULMask = 0;
807 for (int Log2LMUL : Log2LMULList)
808 Log2LMULMask |= 1 << (Log2LMUL + 3);
809
810 SR.Log2LMULMask = Log2LMULMask;
811 std::string RFs =
812 join(Begin: RequiredFeatures.begin(), End: RequiredFeatures.end(), Separator: ",");
813 SR.RequiredExtensions = RFs;
814 SR.NF = NF;
815 SR.HasMasked = HasMasked;
816 SR.HasVL = HasVL;
817 SR.HasMaskedOffOperand = HasMaskedOffOperand;
818 SR.HasTailPolicy = HasTailPolicy;
819 SR.HasMaskPolicy = HasMaskPolicy;
820 SR.MaskedPrototypeHasResultMask = MaskedPrototypeHasResultMask;
821 SR.AltFmt = AltFmt;
822 SR.UnMaskedPolicyScheme = static_cast<uint8_t>(UnMaskedPolicyScheme);
823 SR.MaskedPolicyScheme = static_cast<uint8_t>(MaskedPolicyScheme);
824 SR.Prototype = std::move(BasicPrototype);
825 SR.Suffix = parsePrototypes(Prototypes: SuffixProto);
826 SR.OverloadedSuffix = parsePrototypes(Prototypes: OverloadedSuffixProto);
827 SR.IsTuple = IsTuple;
828 SR.HasFRMRoundModeOp = HasFRMRoundModeOp;
829
830 SemaRecords->push_back(x: SR);
831 }
832}
833
834void RVVEmitter::printHeaderCode(raw_ostream &OS) {
835 for (const Record *R : Records.getAllDerivedDefinitions(ClassName: "RVVHeader")) {
836 StringRef HeaderCodeStr = R->getValueAsString(FieldName: "HeaderCode");
837 OS << HeaderCodeStr.str();
838 }
839}
840
841void RVVEmitter::createRVVIntrinsicRecords(std::vector<RVVIntrinsicRecord> &Out,
842 SemaSignatureTable &SST,
843 ArrayRef<SemaRecord> SemaRecords) {
844 SST.init(SemaRecords);
845
846 for (const auto &SR : SemaRecords) {
847 Out.emplace_back(args: RVVIntrinsicRecord());
848 RVVIntrinsicRecord &R = Out.back();
849 R.Name = SR.Name.c_str();
850 R.OverloadedName = SR.OverloadedName.c_str();
851 R.PrototypeIndex = SST.getIndex(Signature: SR.Prototype);
852 R.SuffixIndex = SST.getIndex(Signature: SR.Suffix);
853 R.OverloadedSuffixIndex = SST.getIndex(Signature: SR.OverloadedSuffix);
854 R.PrototypeLength = SR.Prototype.size();
855 R.SuffixLength = SR.Suffix.size();
856 R.OverloadedSuffixSize = SR.OverloadedSuffix.size();
857 R.RequiredExtensions = SR.RequiredExtensions.c_str();
858 R.TypeRangeMask = SR.TypeRangeMask;
859 R.Log2LMULMask = SR.Log2LMULMask;
860 R.NF = SR.NF;
861 R.HasMasked = SR.HasMasked;
862 R.HasVL = SR.HasVL;
863 R.HasMaskedOffOperand = SR.HasMaskedOffOperand;
864 R.HasTailPolicy = SR.HasTailPolicy;
865 R.HasMaskPolicy = SR.HasMaskPolicy;
866 R.MaskedPrototypeHasResultMask = SR.MaskedPrototypeHasResultMask;
867 R.AltFmt = SR.AltFmt;
868 R.UnMaskedPolicyScheme = SR.UnMaskedPolicyScheme;
869 R.MaskedPolicyScheme = SR.MaskedPolicyScheme;
870 R.IsTuple = SR.IsTuple;
871 R.HasFRMRoundModeOp = SR.HasFRMRoundModeOp;
872
873 assert(R.PrototypeIndex !=
874 static_cast<uint16_t>(SemaSignatureTable::INVALID_INDEX));
875 assert(R.SuffixIndex !=
876 static_cast<uint16_t>(SemaSignatureTable::INVALID_INDEX));
877 assert(R.OverloadedSuffixIndex !=
878 static_cast<uint16_t>(SemaSignatureTable::INVALID_INDEX));
879 }
880}
881
882void RVVEmitter::createSema(raw_ostream &OS) {
883 std::vector<std::unique_ptr<RVVIntrinsic>> Defs;
884 std::vector<RVVIntrinsicRecord> RVVIntrinsicRecords;
885 SemaSignatureTable SST;
886 std::vector<SemaRecord> SemaRecords;
887
888 createRVVIntrinsics(Out&: Defs, SemaRecords: &SemaRecords);
889
890 createRVVIntrinsicRecords(Out&: RVVIntrinsicRecords, SST, SemaRecords);
891
892 // Emit signature table for SemaRISCVVectorLookup.cpp.
893 OS << "#ifdef DECL_SIGNATURE_TABLE\n";
894 SST.print(OS);
895 OS << "#endif\n";
896
897 // Emit RVVIntrinsicRecords for SemaRISCVVectorLookup.cpp.
898 OS << "#ifdef DECL_INTRINSIC_RECORDS\n";
899 for (const RVVIntrinsicRecord &Record : RVVIntrinsicRecords)
900 OS << Record;
901 OS << "#endif\n";
902}
903
904namespace clang {
905void EmitRVVHeader(const RecordKeeper &Records, raw_ostream &OS) {
906 RVVEmitter(Records).createHeader(OS);
907}
908
909void EmitRVVBuiltins(const RecordKeeper &Records, raw_ostream &OS) {
910 RVVEmitter(Records).createBuiltins(OS);
911}
912
913void EmitRVVBuiltinCG(const RecordKeeper &Records, raw_ostream &OS) {
914 RVVEmitter(Records).createCodeGen(OS);
915}
916
917void EmitRVVBuiltinSema(const RecordKeeper &Records, raw_ostream &OS) {
918 RVVEmitter(Records).createSema(OS);
919}
920
921} // End namespace clang
922