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 | |
29 | using namespace llvm; |
30 | using namespace clang::RISCV; |
31 | |
32 | namespace { |
33 | struct 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. |
76 | class SemaSignatureTable { |
77 | private: |
78 | std::vector<PrototypeDescriptor> SignatureTable; |
79 | |
80 | void insert(ArrayRef<PrototypeDescriptor> Signature); |
81 | |
82 | public: |
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 | |
95 | class RVVEmitter { |
96 | private: |
97 | const RecordKeeper &Records; |
98 | RVVTypeCache TypeCache; |
99 | |
100 | public: |
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 | |
117 | private: |
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 | |
132 | static 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 | |
163 | static 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 | |
169 | static 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. |
191 | static 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 | |
247 | void 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 | //===----------------------------------------------------------------------===// |
322 | void 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 | |
363 | void 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 | |
372 | unsigned 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 | |
391 | void 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 | //===----------------------------------------------------------------------===// |
401 | void 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 | |
499 | void 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 | |
567 | void 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 | |
620 | void 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 | |
792 | void 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 | |
799 | void 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 | |
838 | void 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 | |
860 | namespace clang { |
861 | void EmitRVVHeader(const RecordKeeper &Records, raw_ostream &OS) { |
862 | RVVEmitter(Records).createHeader(OS); |
863 | } |
864 | |
865 | void EmitRVVBuiltins(const RecordKeeper &Records, raw_ostream &OS) { |
866 | RVVEmitter(Records).createBuiltins(OS); |
867 | } |
868 | |
869 | void EmitRVVBuiltinCG(const RecordKeeper &Records, raw_ostream &OS) { |
870 | RVVEmitter(Records).createCodeGen(OS); |
871 | } |
872 | |
873 | void EmitRVVBuiltinSema(const RecordKeeper &Records, raw_ostream &OS) { |
874 | RVVEmitter(Records).createSema(OS); |
875 | } |
876 | |
877 | } // End namespace clang |
878 |
Definitions
- SemaRecord
- SemaSignatureTable
- INVALID_INDEX
- RVVEmitter
- RVVEmitter
- ParseBasicType
- getTupleVTM
- getIndexedLoadStorePtrIdx
- getSegInstLog2SEW
- emitCodeGenSwitchBody
- init
- insert
- getIndex
- createHeader
- createBuiltins
- createCodeGen
- createRVVIntrinsics
- printHeaderCode
- createRVVIntrinsicRecords
- createSema
- EmitRVVHeader
- EmitRVVBuiltins
- EmitRVVBuiltinCG
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more