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 IsTuple : 1;
69 LLVM_PREFERRED_TYPE(PolicyScheme)
70 uint8_t UnMaskedPolicyScheme : 2;
71 LLVM_PREFERRED_TYPE(PolicyScheme)
72 uint8_t MaskedPolicyScheme : 2;
73};
74
75// Compressed function signature table.
76class SemaSignatureTable {
77private:
78 std::vector<PrototypeDescriptor> SignatureTable;
79
80 void insert(ArrayRef<PrototypeDescriptor> Signature);
81
82public:
83 static constexpr unsigned INVALID_INDEX = ~0U;
84
85 // Create compressed signature table from SemaRecords.
86 void init(ArrayRef<SemaRecord> SemaRecords);
87
88 // Query the Signature, return INVALID_INDEX if not found.
89 unsigned getIndex(ArrayRef<PrototypeDescriptor> Signature);
90
91 /// Print signature table in RVVHeader Record to \p OS
92 void print(raw_ostream &OS);
93};
94
95class RVVEmitter {
96private:
97 const RecordKeeper &Records;
98 RVVTypeCache TypeCache;
99
100public:
101 RVVEmitter(const RecordKeeper &R) : Records(R) {}
102
103 /// Emit riscv_vector.h
104 void createHeader(raw_ostream &o);
105
106 /// Emit all the __builtin prototypes and code needed by Sema.
107 void createBuiltins(raw_ostream &o);
108
109 /// Emit all the information needed to map builtin -> LLVM IR intrinsic.
110 void createCodeGen(raw_ostream &o);
111
112 /// Emit all the information needed by SemaRISCVVectorLookup.cpp.
113 /// We've large number of intrinsic function for RVV, creating a customized
114 /// could speed up the compilation time.
115 void createSema(raw_ostream &o);
116
117private:
118 /// Create all intrinsics and add them to \p Out and SemaRecords.
119 void createRVVIntrinsics(std::vector<std::unique_ptr<RVVIntrinsic>> &Out,
120 std::vector<SemaRecord> *SemaRecords = nullptr);
121 /// Create all intrinsic records and SemaSignatureTable from SemaRecords.
122 void createRVVIntrinsicRecords(std::vector<RVVIntrinsicRecord> &Out,
123 SemaSignatureTable &SST,
124 ArrayRef<SemaRecord> SemaRecords);
125
126 /// Print HeaderCode in RVVHeader Record to \p Out
127 void printHeaderCode(raw_ostream &OS);
128};
129
130} // namespace
131
132static BasicType ParseBasicType(char c) {
133 switch (c) {
134 case 'c':
135 return BasicType::Int8;
136 break;
137 case 's':
138 return BasicType::Int16;
139 break;
140 case 'i':
141 return BasicType::Int32;
142 break;
143 case 'l':
144 return BasicType::Int64;
145 break;
146 case 'x':
147 return BasicType::Float16;
148 break;
149 case 'f':
150 return BasicType::Float32;
151 break;
152 case 'd':
153 return BasicType::Float64;
154 break;
155 case 'y':
156 return BasicType::BFloat16;
157 break;
158 default:
159 return BasicType::Unknown;
160 }
161}
162
163static VectorTypeModifier getTupleVTM(unsigned NF) {
164 assert(2 <= NF && NF <= 8 && "2 <= NF <= 8");
165 return static_cast<VectorTypeModifier>(
166 static_cast<uint8_t>(VectorTypeModifier::Tuple2) + (NF - 2));
167}
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 (unsigned)-1;
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
251 OS << " PolicyAttrs = " << RVVI->getPolicyAttrsBits() << ";\n";
252 OS << " SegInstSEW = " << getSegInstLog2SEW(InstName: RVVI->getOverloadedName())
253 << ";\n";
254
255 if (RVVI->hasManualCodegen()) {
256 OS << "IsMasked = " << (RVVI->isMasked() ? "true" : "false") << ";\n";
257
258 // Skip the non-indexed load/store and compatible header load/store.
259 OS << "if (SegInstSEW == (unsigned)-1) {\n";
260 OS << " auto PointeeType = E->getArg(" << getIndexedLoadStorePtrIdx(RVVI)
261 << " )->getType()->getPointeeType();\n";
262 OS << " SegInstSEW = "
263 " llvm::Log2_64(getContext().getTypeSize(PointeeType));\n}\n";
264
265 OS << RVVI->getManualCodegen();
266 OS << "break;\n";
267 return;
268 }
269
270 for (const auto &I : enumerate(First: RVVI->getInputTypes())) {
271 if (I.value()->isPointer()) {
272 assert(RVVI->getIntrinsicTypes().front() == -1 &&
273 "RVVI should be vector load intrinsic.");
274 }
275 }
276
277 if (RVVI->isMasked()) {
278 if (RVVI->hasVL()) {
279 OS << " std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end() - 1);\n";
280 if (RVVI->hasPolicyOperand())
281 OS << " Ops.push_back(ConstantInt::get(Ops.back()->getType(),"
282 " PolicyAttrs));\n";
283 if (RVVI->hasMaskedOffOperand() && RVVI->getPolicyAttrs().isTAMAPolicy())
284 OS << " Ops.insert(Ops.begin(), "
285 "llvm::PoisonValue::get(ResultType));\n";
286 // Masked reduction cases.
287 if (!RVVI->hasMaskedOffOperand() && RVVI->hasPassthruOperand() &&
288 RVVI->getPolicyAttrs().isTAMAPolicy())
289 OS << " Ops.insert(Ops.begin(), "
290 "llvm::PoisonValue::get(ResultType));\n";
291 } else {
292 OS << " std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end());\n";
293 }
294 } else {
295 if (RVVI->hasPolicyOperand())
296 OS << " Ops.push_back(ConstantInt::get(Ops.back()->getType(), "
297 "PolicyAttrs));\n";
298 else if (RVVI->hasPassthruOperand() && RVVI->getPolicyAttrs().isTAPolicy())
299 OS << " Ops.insert(Ops.begin(), llvm::PoisonValue::get(ResultType));\n";
300 }
301
302 OS << " IntrinsicTypes = {";
303 ListSeparator LS;
304 for (const auto &Idx : RVVI->getIntrinsicTypes()) {
305 if (Idx == -1)
306 OS << LS << "ResultType";
307 else
308 OS << LS << "Ops[" << Idx << "]->getType()";
309 }
310
311 // VL could be i64 or i32, need to encode it in IntrinsicTypes. VL is
312 // always last operand.
313 if (RVVI->hasVL())
314 OS << ", Ops.back()->getType()";
315 OS << "};\n";
316 OS << " break;\n";
317}
318
319//===----------------------------------------------------------------------===//
320// SemaSignatureTable implementation
321//===----------------------------------------------------------------------===//
322void SemaSignatureTable::init(ArrayRef<SemaRecord> SemaRecords) {
323 // Sort signature entries by length, let longer signature insert first, to
324 // make it more possible to reuse table entries, that can reduce ~10% table
325 // size.
326 struct Compare {
327 bool operator()(const SmallVector<PrototypeDescriptor> &A,
328 const SmallVector<PrototypeDescriptor> &B) const {
329 if (A.size() != B.size())
330 return A.size() > B.size();
331
332 size_t Len = A.size();
333 for (size_t i = 0; i < Len; ++i) {
334 if (A[i] != B[i])
335 return A[i] < B[i];
336 }
337
338 return false;
339 }
340 };
341
342 std::set<SmallVector<PrototypeDescriptor>, Compare> Signatures;
343 auto InsertToSignatureSet =
344 [&](const SmallVector<PrototypeDescriptor> &Signature) {
345 if (Signature.empty())
346 return;
347
348 Signatures.insert(x: Signature);
349 };
350
351 assert(!SemaRecords.empty());
352
353 for (const SemaRecord &SR : SemaRecords) {
354 InsertToSignatureSet(SR.Prototype);
355 InsertToSignatureSet(SR.Suffix);
356 InsertToSignatureSet(SR.OverloadedSuffix);
357 }
358
359 for (auto &Sig : Signatures)
360 insert(Signature: Sig);
361}
362
363void SemaSignatureTable::insert(ArrayRef<PrototypeDescriptor> Signature) {
364 if (getIndex(Signature) != INVALID_INDEX)
365 return;
366
367 // Insert Signature into SignatureTable if not found in the table.
368 SignatureTable.insert(position: SignatureTable.begin(), first: Signature.begin(),
369 last: Signature.end());
370}
371
372unsigned SemaSignatureTable::getIndex(ArrayRef<PrototypeDescriptor> Signature) {
373 // Empty signature could be point into any index since there is length
374 // field when we use, so just always point it to 0.
375 if (Signature.empty())
376 return 0;
377
378 // Checking Signature already in table or not.
379 if (Signature.size() <= SignatureTable.size()) {
380 size_t Bound = SignatureTable.size() - Signature.size() + 1;
381 for (size_t Index = 0; Index < Bound; ++Index) {
382 if (equal(first1: Signature.begin(), last1: Signature.end(),
383 first2: SignatureTable.begin() + Index))
384 return Index;
385 }
386 }
387
388 return INVALID_INDEX;
389}
390
391void SemaSignatureTable::print(raw_ostream &OS) {
392 for (const auto &Sig : SignatureTable)
393 OS << "PrototypeDescriptor(" << static_cast<int>(Sig.PT) << ", "
394 << static_cast<int>(Sig.VTM) << ", " << static_cast<int>(Sig.TM)
395 << "),\n";
396}
397
398//===----------------------------------------------------------------------===//
399// RVVEmitter implementation
400//===----------------------------------------------------------------------===//
401void RVVEmitter::createHeader(raw_ostream &OS) {
402
403 OS << "/*===---- riscv_vector.h - RISC-V V-extension RVVIntrinsics "
404 "-------------------===\n"
405 " *\n"
406 " *\n"
407 " * Part of the LLVM Project, under the Apache License v2.0 with LLVM "
408 "Exceptions.\n"
409 " * See https://llvm.org/LICENSE.txt for license information.\n"
410 " * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n"
411 " *\n"
412 " *===-----------------------------------------------------------------"
413 "------===\n"
414 " */\n\n";
415
416 OS << "#ifndef __RISCV_VECTOR_H\n";
417 OS << "#define __RISCV_VECTOR_H\n\n";
418
419 OS << "#include <stdint.h>\n";
420 OS << "#include <stddef.h>\n\n";
421
422 OS << "#ifdef __cplusplus\n";
423 OS << "extern \"C\" {\n";
424 OS << "#endif\n\n";
425
426 OS << "#pragma clang riscv intrinsic vector\n\n";
427
428 printHeaderCode(OS);
429
430 auto printType = [&](auto T) {
431 OS << "typedef " << T->getClangBuiltinStr() << " " << T->getTypeStr()
432 << ";\n";
433 };
434
435 constexpr int Log2LMULs[] = {-3, -2, -1, 0, 1, 2, 3};
436 // Print RVV boolean types.
437 for (int Log2LMUL : Log2LMULs) {
438 auto T = TypeCache.computeType(BT: BasicType::Int8, Log2LMUL,
439 Proto: PrototypeDescriptor::Mask);
440 if (T)
441 printType(*T);
442 }
443 // Print RVV int/float types.
444 for (char I : StringRef("csil")) {
445 BasicType BT = ParseBasicType(c: I);
446 for (int Log2LMUL : Log2LMULs) {
447 auto T = TypeCache.computeType(BT, Log2LMUL, Proto: PrototypeDescriptor::Vector);
448 if (T) {
449 printType(*T);
450 auto UT = TypeCache.computeType(
451 BT, Log2LMUL,
452 Proto: PrototypeDescriptor(BaseTypeModifier::Vector,
453 VectorTypeModifier::NoModifier,
454 TypeModifier::UnsignedInteger));
455 printType(*UT);
456 }
457 for (int NF = 2; NF <= 8; ++NF) {
458 auto TupleT = TypeCache.computeType(
459 BT, Log2LMUL,
460 Proto: PrototypeDescriptor(BaseTypeModifier::Vector, getTupleVTM(NF),
461 TypeModifier::SignedInteger));
462 auto TupleUT = TypeCache.computeType(
463 BT, Log2LMUL,
464 Proto: PrototypeDescriptor(BaseTypeModifier::Vector, getTupleVTM(NF),
465 TypeModifier::UnsignedInteger));
466 if (TupleT)
467 printType(*TupleT);
468 if (TupleUT)
469 printType(*TupleUT);
470 }
471 }
472 }
473
474 for (BasicType BT : {BasicType::Float16, BasicType::Float32,
475 BasicType::Float64, BasicType::BFloat16}) {
476 for (int Log2LMUL : Log2LMULs) {
477 auto T = TypeCache.computeType(BT, Log2LMUL, Proto: PrototypeDescriptor::Vector);
478 if (T)
479 printType(*T);
480 for (int NF = 2; NF <= 8; ++NF) {
481 auto TupleT = TypeCache.computeType(
482 BT, Log2LMUL,
483 Proto: PrototypeDescriptor(BaseTypeModifier::Vector, getTupleVTM(NF),
484 (BT == BasicType::BFloat16
485 ? TypeModifier::BFloat
486 : TypeModifier::Float)));
487 if (TupleT)
488 printType(*TupleT);
489 }
490 }
491 }
492
493 OS << "\n#ifdef __cplusplus\n";
494 OS << "}\n";
495 OS << "#endif // __cplusplus\n";
496 OS << "#endif // __RISCV_VECTOR_H\n";
497}
498
499void RVVEmitter::createBuiltins(raw_ostream &OS) {
500 std::vector<std::unique_ptr<RVVIntrinsic>> Defs;
501 createRVVIntrinsics(Out&: Defs);
502
503 llvm::StringToOffsetTable Table;
504 // Ensure offset zero is the empty string.
505 Table.GetOrAddStringOffset(Str: "");
506 // Hard coded strings used in the builtin structures.
507 Table.GetOrAddStringOffset(Str: "n");
508 Table.GetOrAddStringOffset(Str: "zve32x");
509
510 // Map to unique the builtin names.
511 StringMap<RVVIntrinsic *> BuiltinMap;
512 std::vector<RVVIntrinsic *> UniqueDefs;
513 for (auto &Def : Defs) {
514 auto P = BuiltinMap.insert(KV: {Def->getBuiltinName(), Def.get()});
515 if (P.second) {
516 Table.GetOrAddStringOffset(Str: Def->getBuiltinName());
517 if (!Def->hasBuiltinAlias())
518 Table.GetOrAddStringOffset(Str: Def->getBuiltinTypeStr());
519 UniqueDefs.push_back(x: Def.get());
520 continue;
521 }
522
523 // Verf that this would have produced the same builtin definition.
524 if (P.first->second->hasBuiltinAlias() != Def->hasBuiltinAlias())
525 PrintFatalError(Msg: "Builtin with same name has different hasAutoDef");
526 else if (!Def->hasBuiltinAlias() &&
527 P.first->second->getBuiltinTypeStr() != Def->getBuiltinTypeStr())
528 PrintFatalError(Msg: "Builtin with same name has different type string");
529 }
530
531 // Emit the enumerators of RVV builtins. Note that these are emitted without
532 // any outer context to enable concatenating them.
533 OS << "// RISCV Vector builtin enumerators\n";
534 OS << "#ifdef GET_RISCVV_BUILTIN_ENUMERATORS\n";
535 for (RVVIntrinsic *Def : UniqueDefs)
536 OS << " BI__builtin_rvv_" << Def->getBuiltinName() << ",\n";
537 OS << "#endif // GET_RISCVV_BUILTIN_ENUMERATORS\n\n";
538
539 // Emit the string table for the RVV builtins.
540 OS << "// RISCV Vector builtin enumerators\n";
541 OS << "#ifdef GET_RISCVV_BUILTIN_STR_TABLE\n";
542 Table.EmitStringTableDef(OS, Name: "BuiltinStrings");
543 OS << "#endif // GET_RISCVV_BUILTIN_STR_TABLE\n\n";
544
545 // Emit the info structs of RVV builtins. Note that these are emitted without
546 // any outer context to enable concatenating them.
547 OS << "// RISCV Vector builtin infos\n";
548 OS << "#ifdef GET_RISCVV_BUILTIN_INFOS\n";
549 for (RVVIntrinsic *Def : UniqueDefs) {
550 OS << " Builtin::Info{Builtin::Info::StrOffsets{"
551 << Table.GetStringOffset(Str: Def->getBuiltinName()) << " /* "
552 << Def->getBuiltinName() << " */, ";
553 if (Def->hasBuiltinAlias()) {
554 OS << "0, ";
555 } else {
556 OS << Table.GetStringOffset(Str: Def->getBuiltinTypeStr()) << " /* "
557 << Def->getBuiltinTypeStr() << " */, ";
558 }
559 OS << Table.GetStringOffset(Str: "n") << " /* n */, ";
560 OS << Table.GetStringOffset(Str: "zve32x") << " /* zve32x */}, ";
561
562 OS << "HeaderDesc::NO_HEADER, ALL_LANGUAGES},\n";
563 }
564 OS << "#endif // GET_RISCVV_BUILTIN_INFOS\n\n";
565}
566
567void RVVEmitter::createCodeGen(raw_ostream &OS) {
568 std::vector<std::unique_ptr<RVVIntrinsic>> Defs;
569 createRVVIntrinsics(Out&: Defs);
570 // IR name could be empty, use the stable sort preserves the relative order.
571 stable_sort(Range&: Defs, C: [](const std::unique_ptr<RVVIntrinsic> &A,
572 const std::unique_ptr<RVVIntrinsic> &B) {
573 if (A->getIRName() == B->getIRName())
574 return (A->getPolicyAttrs() < B->getPolicyAttrs());
575 return (A->getIRName() < B->getIRName());
576 });
577
578 // Map to keep track of which builtin names have already been emitted.
579 StringMap<RVVIntrinsic *> BuiltinMap;
580
581 // Print switch body when the ir name, ManualCodegen, policy or log2sew
582 // changes from previous iteration.
583 RVVIntrinsic *PrevDef = Defs.begin()->get();
584 for (auto &Def : Defs) {
585 StringRef CurIRName = Def->getIRName();
586 if (CurIRName != PrevDef->getIRName() ||
587 (Def->getManualCodegen() != PrevDef->getManualCodegen()) ||
588 (Def->getPolicyAttrs() != PrevDef->getPolicyAttrs()) ||
589 (getSegInstLog2SEW(InstName: Def->getOverloadedName()) !=
590 getSegInstLog2SEW(InstName: PrevDef->getOverloadedName()))) {
591 emitCodeGenSwitchBody(RVVI: PrevDef, OS);
592 }
593 PrevDef = Def.get();
594
595 auto P =
596 BuiltinMap.insert(KV: std::make_pair(x: Def->getBuiltinName(), y: Def.get()));
597 if (P.second) {
598 OS << "case RISCVVector::BI__builtin_rvv_" << Def->getBuiltinName()
599 << ":\n";
600 continue;
601 }
602
603 if (P.first->second->getIRName() != Def->getIRName())
604 PrintFatalError(Msg: "Builtin with same name has different IRName");
605 else if (P.first->second->getManualCodegen() != Def->getManualCodegen())
606 PrintFatalError(Msg: "Builtin with same name has different ManualCodegen");
607 else if (P.first->second->isMasked() != Def->isMasked())
608 PrintFatalError(Msg: "Builtin with same name has different isMasked");
609 else if (P.first->second->hasVL() != Def->hasVL())
610 PrintFatalError(Msg: "Builtin with same name has different hasVL");
611 else if (P.first->second->getPolicyScheme() != Def->getPolicyScheme())
612 PrintFatalError(Msg: "Builtin with same name has different getPolicyScheme");
613 else if (P.first->second->getIntrinsicTypes() != Def->getIntrinsicTypes())
614 PrintFatalError(Msg: "Builtin with same name has different IntrinsicTypes");
615 }
616 emitCodeGenSwitchBody(RVVI: Defs.back().get(), OS);
617 OS << "\n";
618}
619
620void RVVEmitter::createRVVIntrinsics(
621 std::vector<std::unique_ptr<RVVIntrinsic>> &Out,
622 std::vector<SemaRecord> *SemaRecords) {
623 for (const Record *R : Records.getAllDerivedDefinitions(ClassName: "RVVBuiltin")) {
624 StringRef Name = R->getValueAsString(FieldName: "Name");
625 StringRef SuffixProto = R->getValueAsString(FieldName: "Suffix");
626 StringRef OverloadedName = R->getValueAsString(FieldName: "OverloadedName");
627 StringRef OverloadedSuffixProto = R->getValueAsString(FieldName: "OverloadedSuffix");
628 StringRef Prototypes = R->getValueAsString(FieldName: "Prototype");
629 StringRef TypeRange = R->getValueAsString(FieldName: "TypeRange");
630 bool HasMasked = R->getValueAsBit(FieldName: "HasMasked");
631 bool HasMaskedOffOperand = R->getValueAsBit(FieldName: "HasMaskedOffOperand");
632 bool HasVL = R->getValueAsBit(FieldName: "HasVL");
633 const Record *MPSRecord = R->getValueAsDef(FieldName: "MaskedPolicyScheme");
634 auto MaskedPolicyScheme =
635 static_cast<PolicyScheme>(MPSRecord->getValueAsInt(FieldName: "Value"));
636 const Record *UMPSRecord = R->getValueAsDef(FieldName: "UnMaskedPolicyScheme");
637 auto UnMaskedPolicyScheme =
638 static_cast<PolicyScheme>(UMPSRecord->getValueAsInt(FieldName: "Value"));
639 std::vector<int64_t> Log2LMULList = R->getValueAsListOfInts(FieldName: "Log2LMUL");
640 bool HasTailPolicy = R->getValueAsBit(FieldName: "HasTailPolicy");
641 bool HasMaskPolicy = R->getValueAsBit(FieldName: "HasMaskPolicy");
642 bool SupportOverloading = R->getValueAsBit(FieldName: "SupportOverloading");
643 bool HasBuiltinAlias = R->getValueAsBit(FieldName: "HasBuiltinAlias");
644 StringRef ManualCodegen = R->getValueAsString(FieldName: "ManualCodegen");
645 std::vector<int64_t> IntrinsicTypes =
646 R->getValueAsListOfInts(FieldName: "IntrinsicTypes");
647 std::vector<StringRef> RequiredFeatures =
648 R->getValueAsListOfStrings(FieldName: "RequiredFeatures");
649 StringRef IRName = R->getValueAsString(FieldName: "IRName");
650 StringRef MaskedIRName = R->getValueAsString(FieldName: "MaskedIRName");
651 unsigned NF = R->getValueAsInt(FieldName: "NF");
652 bool IsTuple = R->getValueAsBit(FieldName: "IsTuple");
653 bool HasFRMRoundModeOp = R->getValueAsBit(FieldName: "HasFRMRoundModeOp");
654
655 const Policy DefaultPolicy;
656 SmallVector<Policy> SupportedUnMaskedPolicies =
657 RVVIntrinsic::getSupportedUnMaskedPolicies();
658 SmallVector<Policy> SupportedMaskedPolicies =
659 RVVIntrinsic::getSupportedMaskedPolicies(HasTailPolicy, HasMaskPolicy);
660
661 // Parse prototype and create a list of primitive type with transformers
662 // (operand) in Prototype. Prototype[0] is output operand.
663 SmallVector<PrototypeDescriptor> BasicPrototype =
664 parsePrototypes(Prototypes);
665
666 SmallVector<PrototypeDescriptor> SuffixDesc = parsePrototypes(Prototypes: SuffixProto);
667 SmallVector<PrototypeDescriptor> OverloadedSuffixDesc =
668 parsePrototypes(Prototypes: OverloadedSuffixProto);
669
670 // Compute Builtin types
671 auto Prototype = RVVIntrinsic::computeBuiltinTypes(
672 Prototype: BasicPrototype, /*IsMasked=*/false,
673 /*HasMaskedOffOperand=*/false, HasVL, NF, DefaultScheme: UnMaskedPolicyScheme,
674 PolicyAttrs: DefaultPolicy, IsTuple);
675 SmallVector<PrototypeDescriptor> MaskedPrototype;
676 if (HasMasked)
677 MaskedPrototype = RVVIntrinsic::computeBuiltinTypes(
678 Prototype: BasicPrototype, /*IsMasked=*/true, HasMaskedOffOperand, HasVL, NF,
679 DefaultScheme: MaskedPolicyScheme, PolicyAttrs: DefaultPolicy, IsTuple);
680
681 // Create Intrinsics for each type and LMUL.
682 for (char I : TypeRange) {
683 for (int Log2LMUL : Log2LMULList) {
684 BasicType BT = ParseBasicType(c: I);
685 std::optional<RVVTypes> Types =
686 TypeCache.computeTypes(BT, Log2LMUL, NF, Prototype);
687 // Ignored to create new intrinsic if there are any illegal types.
688 if (!Types)
689 continue;
690
691 auto SuffixStr =
692 RVVIntrinsic::getSuffixStr(TypeCache, Type: BT, Log2LMUL, PrototypeDescriptors: SuffixDesc);
693 auto OverloadedSuffixStr = RVVIntrinsic::getSuffixStr(
694 TypeCache, Type: BT, Log2LMUL, PrototypeDescriptors: OverloadedSuffixDesc);
695 // Create a unmasked intrinsic
696 Out.push_back(x: std::make_unique<RVVIntrinsic>(
697 args&: Name, args&: SuffixStr, args&: OverloadedName, args&: OverloadedSuffixStr, args&: IRName,
698 /*IsMasked=*/args: false, /*HasMaskedOffOperand=*/args: false, args&: HasVL,
699 args&: UnMaskedPolicyScheme, args&: SupportOverloading, args&: HasBuiltinAlias,
700 args&: ManualCodegen, args&: *Types, args&: IntrinsicTypes, args&: NF, args: DefaultPolicy,
701 args&: HasFRMRoundModeOp));
702 if (UnMaskedPolicyScheme != PolicyScheme::SchemeNone)
703 for (auto P : SupportedUnMaskedPolicies) {
704 SmallVector<PrototypeDescriptor> PolicyPrototype =
705 RVVIntrinsic::computeBuiltinTypes(
706 Prototype: BasicPrototype, /*IsMasked=*/false,
707 /*HasMaskedOffOperand=*/false, HasVL, NF,
708 DefaultScheme: UnMaskedPolicyScheme, PolicyAttrs: P, IsTuple);
709 std::optional<RVVTypes> PolicyTypes =
710 TypeCache.computeTypes(BT, Log2LMUL, NF, Prototype: PolicyPrototype);
711 Out.push_back(x: std::make_unique<RVVIntrinsic>(
712 args&: Name, args&: SuffixStr, args&: OverloadedName, args&: OverloadedSuffixStr, args&: IRName,
713 /*IsMask=*/args: false, /*HasMaskedOffOperand=*/args: false, args&: HasVL,
714 args&: UnMaskedPolicyScheme, args&: SupportOverloading, args&: HasBuiltinAlias,
715 args&: ManualCodegen, args&: *PolicyTypes, args&: IntrinsicTypes, args&: NF, args&: P,
716 args&: HasFRMRoundModeOp));
717 }
718 if (!HasMasked)
719 continue;
720 // Create a masked intrinsic
721 std::optional<RVVTypes> MaskTypes =
722 TypeCache.computeTypes(BT, Log2LMUL, NF, Prototype: MaskedPrototype);
723 Out.push_back(x: std::make_unique<RVVIntrinsic>(
724 args&: Name, args&: SuffixStr, args&: OverloadedName, args&: OverloadedSuffixStr, args&: MaskedIRName,
725 /*IsMasked=*/args: true, args&: HasMaskedOffOperand, args&: HasVL, args&: MaskedPolicyScheme,
726 args&: SupportOverloading, args&: HasBuiltinAlias, args&: ManualCodegen, args&: *MaskTypes,
727 args&: IntrinsicTypes, args&: NF, args: DefaultPolicy, args&: HasFRMRoundModeOp));
728 if (MaskedPolicyScheme == PolicyScheme::SchemeNone)
729 continue;
730 for (auto P : SupportedMaskedPolicies) {
731 SmallVector<PrototypeDescriptor> PolicyPrototype =
732 RVVIntrinsic::computeBuiltinTypes(
733 Prototype: BasicPrototype, /*IsMasked=*/true, HasMaskedOffOperand, HasVL,
734 NF, DefaultScheme: MaskedPolicyScheme, PolicyAttrs: P, IsTuple);
735 std::optional<RVVTypes> PolicyTypes =
736 TypeCache.computeTypes(BT, Log2LMUL, NF, Prototype: PolicyPrototype);
737 Out.push_back(x: std::make_unique<RVVIntrinsic>(
738 args&: Name, args&: SuffixStr, args&: OverloadedName, args&: OverloadedSuffixStr,
739 args&: MaskedIRName, /*IsMasked=*/args: true, args&: HasMaskedOffOperand, args&: HasVL,
740 args&: MaskedPolicyScheme, args&: SupportOverloading, args&: HasBuiltinAlias,
741 args&: ManualCodegen, args&: *PolicyTypes, args&: IntrinsicTypes, args&: NF, args&: P,
742 args&: HasFRMRoundModeOp));
743 }
744 } // End for Log2LMULList
745 } // End for TypeRange
746
747 // We don't emit vsetvli and vsetvlimax for SemaRecord.
748 // They are written in riscv_vector.td and will emit those marco define in
749 // riscv_vector.h
750 if (Name == "vsetvli" || Name == "vsetvlimax")
751 continue;
752
753 if (!SemaRecords)
754 continue;
755
756 // Create SemaRecord
757 SemaRecord SR;
758 SR.Name = Name.str();
759 SR.OverloadedName = OverloadedName.str();
760 BasicType TypeRangeMask = BasicType::Unknown;
761 for (char I : TypeRange)
762 TypeRangeMask |= ParseBasicType(c: I);
763
764 SR.TypeRangeMask = static_cast<unsigned>(TypeRangeMask);
765
766 unsigned Log2LMULMask = 0;
767 for (int Log2LMUL : Log2LMULList)
768 Log2LMULMask |= 1 << (Log2LMUL + 3);
769
770 SR.Log2LMULMask = Log2LMULMask;
771 std::string RFs =
772 join(Begin: RequiredFeatures.begin(), End: RequiredFeatures.end(), Separator: ",");
773 SR.RequiredExtensions = RFs;
774 SR.NF = NF;
775 SR.HasMasked = HasMasked;
776 SR.HasVL = HasVL;
777 SR.HasMaskedOffOperand = HasMaskedOffOperand;
778 SR.HasTailPolicy = HasTailPolicy;
779 SR.HasMaskPolicy = HasMaskPolicy;
780 SR.UnMaskedPolicyScheme = static_cast<uint8_t>(UnMaskedPolicyScheme);
781 SR.MaskedPolicyScheme = static_cast<uint8_t>(MaskedPolicyScheme);
782 SR.Prototype = std::move(BasicPrototype);
783 SR.Suffix = parsePrototypes(Prototypes: SuffixProto);
784 SR.OverloadedSuffix = parsePrototypes(Prototypes: OverloadedSuffixProto);
785 SR.IsTuple = IsTuple;
786 SR.HasFRMRoundModeOp = HasFRMRoundModeOp;
787
788 SemaRecords->push_back(x: SR);
789 }
790}
791
792void RVVEmitter::printHeaderCode(raw_ostream &OS) {
793 for (const Record *R : Records.getAllDerivedDefinitions(ClassName: "RVVHeader")) {
794 StringRef HeaderCodeStr = R->getValueAsString(FieldName: "HeaderCode");
795 OS << HeaderCodeStr.str();
796 }
797}
798
799void RVVEmitter::createRVVIntrinsicRecords(std::vector<RVVIntrinsicRecord> &Out,
800 SemaSignatureTable &SST,
801 ArrayRef<SemaRecord> SemaRecords) {
802 SST.init(SemaRecords);
803
804 for (const auto &SR : SemaRecords) {
805 Out.emplace_back(args: RVVIntrinsicRecord());
806 RVVIntrinsicRecord &R = Out.back();
807 R.Name = SR.Name.c_str();
808 R.OverloadedName = SR.OverloadedName.c_str();
809 R.PrototypeIndex = SST.getIndex(Signature: SR.Prototype);
810 R.SuffixIndex = SST.getIndex(Signature: SR.Suffix);
811 R.OverloadedSuffixIndex = SST.getIndex(Signature: SR.OverloadedSuffix);
812 R.PrototypeLength = SR.Prototype.size();
813 R.SuffixLength = SR.Suffix.size();
814 R.OverloadedSuffixSize = SR.OverloadedSuffix.size();
815 R.RequiredExtensions = SR.RequiredExtensions.c_str();
816 R.TypeRangeMask = SR.TypeRangeMask;
817 R.Log2LMULMask = SR.Log2LMULMask;
818 R.NF = SR.NF;
819 R.HasMasked = SR.HasMasked;
820 R.HasVL = SR.HasVL;
821 R.HasMaskedOffOperand = SR.HasMaskedOffOperand;
822 R.HasTailPolicy = SR.HasTailPolicy;
823 R.HasMaskPolicy = SR.HasMaskPolicy;
824 R.UnMaskedPolicyScheme = SR.UnMaskedPolicyScheme;
825 R.MaskedPolicyScheme = SR.MaskedPolicyScheme;
826 R.IsTuple = SR.IsTuple;
827 R.HasFRMRoundModeOp = SR.HasFRMRoundModeOp;
828
829 assert(R.PrototypeIndex !=
830 static_cast<uint16_t>(SemaSignatureTable::INVALID_INDEX));
831 assert(R.SuffixIndex !=
832 static_cast<uint16_t>(SemaSignatureTable::INVALID_INDEX));
833 assert(R.OverloadedSuffixIndex !=
834 static_cast<uint16_t>(SemaSignatureTable::INVALID_INDEX));
835 }
836}
837
838void RVVEmitter::createSema(raw_ostream &OS) {
839 std::vector<std::unique_ptr<RVVIntrinsic>> Defs;
840 std::vector<RVVIntrinsicRecord> RVVIntrinsicRecords;
841 SemaSignatureTable SST;
842 std::vector<SemaRecord> SemaRecords;
843
844 createRVVIntrinsics(Out&: Defs, SemaRecords: &SemaRecords);
845
846 createRVVIntrinsicRecords(Out&: RVVIntrinsicRecords, SST, SemaRecords);
847
848 // Emit signature table for SemaRISCVVectorLookup.cpp.
849 OS << "#ifdef DECL_SIGNATURE_TABLE\n";
850 SST.print(OS);
851 OS << "#endif\n";
852
853 // Emit RVVIntrinsicRecords for SemaRISCVVectorLookup.cpp.
854 OS << "#ifdef DECL_INTRINSIC_RECORDS\n";
855 for (const RVVIntrinsicRecord &Record : RVVIntrinsicRecords)
856 OS << Record;
857 OS << "#endif\n";
858}
859
860namespace clang {
861void EmitRVVHeader(const RecordKeeper &Records, raw_ostream &OS) {
862 RVVEmitter(Records).createHeader(OS);
863}
864
865void EmitRVVBuiltins(const RecordKeeper &Records, raw_ostream &OS) {
866 RVVEmitter(Records).createBuiltins(OS);
867}
868
869void EmitRVVBuiltinCG(const RecordKeeper &Records, raw_ostream &OS) {
870 RVVEmitter(Records).createCodeGen(OS);
871}
872
873void EmitRVVBuiltinSema(const RecordKeeper &Records, raw_ostream &OS) {
874 RVVEmitter(Records).createSema(OS);
875}
876
877} // End namespace clang
878

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

source code of clang/utils/TableGen/RISCVVEmitter.cpp