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/SmallSet.h" |
20 | #include "llvm/ADT/StringExtras.h" |
21 | #include "llvm/ADT/StringMap.h" |
22 | #include "llvm/ADT/StringSet.h" |
23 | #include "llvm/ADT/StringSwitch.h" |
24 | #include "llvm/ADT/Twine.h" |
25 | #include "llvm/TableGen/Error.h" |
26 | #include "llvm/TableGen/Record.h" |
27 | #include <numeric> |
28 | #include <optional> |
29 | |
30 | using namespace llvm; |
31 | using namespace clang::RISCV; |
32 | |
33 | namespace { |
34 | struct SemaRecord { |
35 | // Intrinsic name, e.g. vadd_vv |
36 | std::string Name; |
37 | |
38 | // Overloaded intrinsic name, could be empty if can be computed from Name |
39 | // e.g. vadd |
40 | std::string OverloadedName; |
41 | |
42 | // Supported type, mask of BasicType. |
43 | unsigned TypeRangeMask; |
44 | |
45 | // Supported LMUL. |
46 | unsigned Log2LMULMask; |
47 | |
48 | // Required extensions for this intrinsic. |
49 | uint32_t RequiredExtensions; |
50 | |
51 | // Prototype for this intrinsic. |
52 | SmallVector<PrototypeDescriptor> Prototype; |
53 | |
54 | // Suffix of intrinsic name. |
55 | SmallVector<PrototypeDescriptor> Suffix; |
56 | |
57 | // Suffix of overloaded intrinsic name. |
58 | SmallVector<PrototypeDescriptor> OverloadedSuffix; |
59 | |
60 | // Number of field, large than 1 if it's segment load/store. |
61 | unsigned NF; |
62 | |
63 | bool HasMasked :1; |
64 | bool HasVL :1; |
65 | bool HasMaskedOffOperand :1; |
66 | bool HasTailPolicy : 1; |
67 | bool HasMaskPolicy : 1; |
68 | bool HasFRMRoundModeOp : 1; |
69 | bool IsTuple : 1; |
70 | LLVM_PREFERRED_TYPE(PolicyScheme) |
71 | uint8_t UnMaskedPolicyScheme : 2; |
72 | LLVM_PREFERRED_TYPE(PolicyScheme) |
73 | uint8_t MaskedPolicyScheme : 2; |
74 | }; |
75 | |
76 | // Compressed function signature table. |
77 | class SemaSignatureTable { |
78 | private: |
79 | std::vector<PrototypeDescriptor> SignatureTable; |
80 | |
81 | void insert(ArrayRef<PrototypeDescriptor> Signature); |
82 | |
83 | public: |
84 | static constexpr unsigned INVALID_INDEX = ~0U; |
85 | |
86 | // Create compressed signature table from SemaRecords. |
87 | void init(ArrayRef<SemaRecord> SemaRecords); |
88 | |
89 | // Query the Signature, return INVALID_INDEX if not found. |
90 | unsigned getIndex(ArrayRef<PrototypeDescriptor> Signature); |
91 | |
92 | /// Print signature table in RVVHeader Record to \p OS |
93 | void print(raw_ostream &OS); |
94 | }; |
95 | |
96 | class RVVEmitter { |
97 | private: |
98 | RecordKeeper &Records; |
99 | RVVTypeCache TypeCache; |
100 | |
101 | public: |
102 | RVVEmitter(RecordKeeper &R) : Records(R) {} |
103 | |
104 | /// Emit riscv_vector.h |
105 | void createHeader(raw_ostream &o); |
106 | |
107 | /// Emit all the __builtin prototypes and code needed by Sema. |
108 | void createBuiltins(raw_ostream &o); |
109 | |
110 | /// Emit all the information needed to map builtin -> LLVM IR intrinsic. |
111 | void createCodeGen(raw_ostream &o); |
112 | |
113 | /// Emit all the information needed by SemaRISCVVectorLookup.cpp. |
114 | /// We've large number of intrinsic function for RVV, creating a customized |
115 | /// could speed up the compilation time. |
116 | void createSema(raw_ostream &o); |
117 | |
118 | private: |
119 | /// Create all intrinsics and add them to \p Out and SemaRecords. |
120 | void createRVVIntrinsics(std::vector<std::unique_ptr<RVVIntrinsic>> &Out, |
121 | std::vector<SemaRecord> *SemaRecords = nullptr); |
122 | /// Create all intrinsic records and SemaSignatureTable from SemaRecords. |
123 | void createRVVIntrinsicRecords(std::vector<RVVIntrinsicRecord> &Out, |
124 | SemaSignatureTable &SST, |
125 | ArrayRef<SemaRecord> SemaRecords); |
126 | |
127 | /// Print HeaderCode in RVVHeader Record to \p Out |
128 | void printHeaderCode(raw_ostream &OS); |
129 | }; |
130 | |
131 | } // namespace |
132 | |
133 | static BasicType ParseBasicType(char c) { |
134 | switch (c) { |
135 | case 'c': |
136 | return BasicType::Int8; |
137 | break; |
138 | case 's': |
139 | return BasicType::Int16; |
140 | break; |
141 | case 'i': |
142 | return BasicType::Int32; |
143 | break; |
144 | case 'l': |
145 | return BasicType::Int64; |
146 | break; |
147 | case 'x': |
148 | return BasicType::Float16; |
149 | break; |
150 | case 'f': |
151 | return BasicType::Float32; |
152 | break; |
153 | case 'd': |
154 | return BasicType::Float64; |
155 | break; |
156 | case 'y': |
157 | return BasicType::BFloat16; |
158 | break; |
159 | default: |
160 | return BasicType::Unknown; |
161 | } |
162 | } |
163 | |
164 | static VectorTypeModifier getTupleVTM(unsigned NF) { |
165 | assert(2 <= NF && NF <= 8 && "2 <= NF <= 8" ); |
166 | return static_cast<VectorTypeModifier>( |
167 | static_cast<uint8_t>(VectorTypeModifier::Tuple2) + (NF - 2)); |
168 | } |
169 | |
170 | void emitCodeGenSwitchBody(const RVVIntrinsic *RVVI, raw_ostream &OS) { |
171 | if (!RVVI->getIRName().empty()) |
172 | OS << " ID = Intrinsic::riscv_" + RVVI->getIRName() + ";\n" ; |
173 | if (RVVI->getNF() >= 2) |
174 | OS << " NF = " + utostr(X: RVVI->getNF()) + ";\n" ; |
175 | |
176 | OS << " PolicyAttrs = " << RVVI->getPolicyAttrsBits() << ";\n" ; |
177 | |
178 | if (RVVI->hasManualCodegen()) { |
179 | OS << "IsMasked = " << (RVVI->isMasked() ? "true" : "false" ) << ";\n" ; |
180 | OS << RVVI->getManualCodegen(); |
181 | OS << "break;\n" ; |
182 | return; |
183 | } |
184 | |
185 | for (const auto &I : enumerate(First: RVVI->getInputTypes())) { |
186 | if (I.value()->isPointer()) { |
187 | assert(RVVI->getIntrinsicTypes().front() == -1 && |
188 | "RVVI should be vector load intrinsic." ); |
189 | } |
190 | } |
191 | |
192 | if (RVVI->isMasked()) { |
193 | if (RVVI->hasVL()) { |
194 | OS << " std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end() - 1);\n" ; |
195 | if (RVVI->hasPolicyOperand()) |
196 | OS << " Ops.push_back(ConstantInt::get(Ops.back()->getType()," |
197 | " PolicyAttrs));\n" ; |
198 | if (RVVI->hasMaskedOffOperand() && RVVI->getPolicyAttrs().isTAMAPolicy()) |
199 | OS << " Ops.insert(Ops.begin(), " |
200 | "llvm::PoisonValue::get(ResultType));\n" ; |
201 | // Masked reduction cases. |
202 | if (!RVVI->hasMaskedOffOperand() && RVVI->hasPassthruOperand() && |
203 | RVVI->getPolicyAttrs().isTAMAPolicy()) |
204 | OS << " Ops.insert(Ops.begin(), " |
205 | "llvm::PoisonValue::get(ResultType));\n" ; |
206 | } else { |
207 | OS << " std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end());\n" ; |
208 | } |
209 | } else { |
210 | if (RVVI->hasPolicyOperand()) |
211 | OS << " Ops.push_back(ConstantInt::get(Ops.back()->getType(), " |
212 | "PolicyAttrs));\n" ; |
213 | else if (RVVI->hasPassthruOperand() && RVVI->getPolicyAttrs().isTAPolicy()) |
214 | OS << " Ops.insert(Ops.begin(), llvm::PoisonValue::get(ResultType));\n" ; |
215 | } |
216 | |
217 | OS << " IntrinsicTypes = {" ; |
218 | ListSeparator LS; |
219 | for (const auto &Idx : RVVI->getIntrinsicTypes()) { |
220 | if (Idx == -1) |
221 | OS << LS << "ResultType" ; |
222 | else |
223 | OS << LS << "Ops[" << Idx << "]->getType()" ; |
224 | } |
225 | |
226 | // VL could be i64 or i32, need to encode it in IntrinsicTypes. VL is |
227 | // always last operand. |
228 | if (RVVI->hasVL()) |
229 | OS << ", Ops.back()->getType()" ; |
230 | OS << "};\n" ; |
231 | OS << " break;\n" ; |
232 | } |
233 | |
234 | //===----------------------------------------------------------------------===// |
235 | // SemaSignatureTable implementation |
236 | //===----------------------------------------------------------------------===// |
237 | void SemaSignatureTable::init(ArrayRef<SemaRecord> SemaRecords) { |
238 | // Sort signature entries by length, let longer signature insert first, to |
239 | // make it more possible to reuse table entries, that can reduce ~10% table |
240 | // size. |
241 | struct Compare { |
242 | bool operator()(const SmallVector<PrototypeDescriptor> &A, |
243 | const SmallVector<PrototypeDescriptor> &B) const { |
244 | if (A.size() != B.size()) |
245 | return A.size() > B.size(); |
246 | |
247 | size_t Len = A.size(); |
248 | for (size_t i = 0; i < Len; ++i) { |
249 | if (A[i] != B[i]) |
250 | return A[i] < B[i]; |
251 | } |
252 | |
253 | return false; |
254 | } |
255 | }; |
256 | |
257 | std::set<SmallVector<PrototypeDescriptor>, Compare> Signatures; |
258 | auto InsertToSignatureSet = |
259 | [&](const SmallVector<PrototypeDescriptor> &Signature) { |
260 | if (Signature.empty()) |
261 | return; |
262 | |
263 | Signatures.insert(x: Signature); |
264 | }; |
265 | |
266 | assert(!SemaRecords.empty()); |
267 | |
268 | for (const SemaRecord &SR : SemaRecords) { |
269 | InsertToSignatureSet(SR.Prototype); |
270 | InsertToSignatureSet(SR.Suffix); |
271 | InsertToSignatureSet(SR.OverloadedSuffix); |
272 | } |
273 | |
274 | for (auto &Sig : Signatures) |
275 | insert(Signature: Sig); |
276 | } |
277 | |
278 | void SemaSignatureTable::insert(ArrayRef<PrototypeDescriptor> Signature) { |
279 | if (getIndex(Signature) != INVALID_INDEX) |
280 | return; |
281 | |
282 | // Insert Signature into SignatureTable if not found in the table. |
283 | SignatureTable.insert(position: SignatureTable.begin(), first: Signature.begin(), |
284 | last: Signature.end()); |
285 | } |
286 | |
287 | unsigned SemaSignatureTable::getIndex(ArrayRef<PrototypeDescriptor> Signature) { |
288 | // Empty signature could be point into any index since there is length |
289 | // field when we use, so just always point it to 0. |
290 | if (Signature.empty()) |
291 | return 0; |
292 | |
293 | // Checking Signature already in table or not. |
294 | if (Signature.size() <= SignatureTable.size()) { |
295 | size_t Bound = SignatureTable.size() - Signature.size() + 1; |
296 | for (size_t Index = 0; Index < Bound; ++Index) { |
297 | if (equal(Signature.begin(), Signature.end(), |
298 | SignatureTable.begin() + Index)) |
299 | return Index; |
300 | } |
301 | } |
302 | |
303 | return INVALID_INDEX; |
304 | } |
305 | |
306 | void SemaSignatureTable::print(raw_ostream &OS) { |
307 | for (const auto &Sig : SignatureTable) |
308 | OS << "PrototypeDescriptor(" << static_cast<int>(Sig.PT) << ", " |
309 | << static_cast<int>(Sig.VTM) << ", " << static_cast<int>(Sig.TM) |
310 | << "),\n" ; |
311 | } |
312 | |
313 | //===----------------------------------------------------------------------===// |
314 | // RVVEmitter implementation |
315 | //===----------------------------------------------------------------------===// |
316 | void RVVEmitter::(raw_ostream &OS) { |
317 | |
318 | OS << "/*===---- riscv_vector.h - RISC-V V-extension RVVIntrinsics " |
319 | "-------------------===\n" |
320 | " *\n" |
321 | " *\n" |
322 | " * Part of the LLVM Project, under the Apache License v2.0 with LLVM " |
323 | "Exceptions.\n" |
324 | " * See https://llvm.org/LICENSE.txt for license information.\n" |
325 | " * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n" |
326 | " *\n" |
327 | " *===-----------------------------------------------------------------" |
328 | "------===\n" |
329 | " */\n\n" ; |
330 | |
331 | OS << "#ifndef __RISCV_VECTOR_H\n" ; |
332 | OS << "#define __RISCV_VECTOR_H\n\n" ; |
333 | |
334 | OS << "#include <stdint.h>\n" ; |
335 | OS << "#include <stddef.h>\n\n" ; |
336 | |
337 | OS << "#ifdef __cplusplus\n" ; |
338 | OS << "extern \"C\" {\n" ; |
339 | OS << "#endif\n\n" ; |
340 | |
341 | OS << "#pragma clang riscv intrinsic vector\n\n" ; |
342 | |
343 | printHeaderCode(OS); |
344 | |
345 | auto printType = [&](auto T) { |
346 | OS << "typedef " << T->getClangBuiltinStr() << " " << T->getTypeStr() |
347 | << ";\n" ; |
348 | }; |
349 | |
350 | constexpr int Log2LMULs[] = {-3, -2, -1, 0, 1, 2, 3}; |
351 | // Print RVV boolean types. |
352 | for (int Log2LMUL : Log2LMULs) { |
353 | auto T = TypeCache.computeType(BT: BasicType::Int8, Log2LMUL, |
354 | Proto: PrototypeDescriptor::Mask); |
355 | if (T) |
356 | printType(*T); |
357 | } |
358 | // Print RVV int/float types. |
359 | for (char I : StringRef("csil" )) { |
360 | BasicType BT = ParseBasicType(c: I); |
361 | for (int Log2LMUL : Log2LMULs) { |
362 | auto T = TypeCache.computeType(BT, Log2LMUL, Proto: PrototypeDescriptor::Vector); |
363 | if (T) { |
364 | printType(*T); |
365 | auto UT = TypeCache.computeType( |
366 | BT, Log2LMUL, |
367 | Proto: PrototypeDescriptor(BaseTypeModifier::Vector, |
368 | VectorTypeModifier::NoModifier, |
369 | TypeModifier::UnsignedInteger)); |
370 | printType(*UT); |
371 | } |
372 | for (int NF = 2; NF <= 8; ++NF) { |
373 | auto TupleT = TypeCache.computeType( |
374 | BT, Log2LMUL, |
375 | Proto: PrototypeDescriptor(BaseTypeModifier::Vector, getTupleVTM(NF), |
376 | TypeModifier::SignedInteger)); |
377 | auto TupleUT = TypeCache.computeType( |
378 | BT, Log2LMUL, |
379 | Proto: PrototypeDescriptor(BaseTypeModifier::Vector, getTupleVTM(NF), |
380 | TypeModifier::UnsignedInteger)); |
381 | if (TupleT) |
382 | printType(*TupleT); |
383 | if (TupleUT) |
384 | printType(*TupleUT); |
385 | } |
386 | } |
387 | } |
388 | |
389 | for (BasicType BT : {BasicType::Float16, BasicType::Float32, |
390 | BasicType::Float64, BasicType::BFloat16}) { |
391 | for (int Log2LMUL : Log2LMULs) { |
392 | auto T = TypeCache.computeType(BT, Log2LMUL, Proto: PrototypeDescriptor::Vector); |
393 | if (T) |
394 | printType(*T); |
395 | for (int NF = 2; NF <= 8; ++NF) { |
396 | auto TupleT = TypeCache.computeType( |
397 | BT, Log2LMUL, |
398 | Proto: PrototypeDescriptor(BaseTypeModifier::Vector, getTupleVTM(NF), |
399 | (BT == BasicType::BFloat16 |
400 | ? TypeModifier::BFloat |
401 | : TypeModifier::Float))); |
402 | if (TupleT) |
403 | printType(*TupleT); |
404 | } |
405 | } |
406 | } |
407 | |
408 | OS << "#define __riscv_v_intrinsic_overloading 1\n" ; |
409 | |
410 | OS << "\n#ifdef __cplusplus\n" ; |
411 | OS << "}\n" ; |
412 | OS << "#endif // __cplusplus\n" ; |
413 | OS << "#endif // __RISCV_VECTOR_H\n" ; |
414 | } |
415 | |
416 | void RVVEmitter::createBuiltins(raw_ostream &OS) { |
417 | std::vector<std::unique_ptr<RVVIntrinsic>> Defs; |
418 | createRVVIntrinsics(Out&: Defs); |
419 | |
420 | // Map to keep track of which builtin names have already been emitted. |
421 | StringMap<RVVIntrinsic *> BuiltinMap; |
422 | |
423 | OS << "#if defined(TARGET_BUILTIN) && !defined(RISCVV_BUILTIN)\n" ; |
424 | OS << "#define RISCVV_BUILTIN(ID, TYPE, ATTRS) TARGET_BUILTIN(ID, TYPE, " |
425 | "ATTRS, \"zve32x\")\n" ; |
426 | OS << "#endif\n" ; |
427 | for (auto &Def : Defs) { |
428 | auto P = |
429 | BuiltinMap.insert(KV: std::make_pair(x: Def->getBuiltinName(), y: Def.get())); |
430 | if (!P.second) { |
431 | // Verf that this would have produced the same builtin definition. |
432 | if (P.first->second->hasBuiltinAlias() != Def->hasBuiltinAlias()) |
433 | PrintFatalError(Msg: "Builtin with same name has different hasAutoDef" ); |
434 | else if (!Def->hasBuiltinAlias() && |
435 | P.first->second->getBuiltinTypeStr() != Def->getBuiltinTypeStr()) |
436 | PrintFatalError(Msg: "Builtin with same name has different type string" ); |
437 | continue; |
438 | } |
439 | OS << "RISCVV_BUILTIN(__builtin_rvv_" << Def->getBuiltinName() << ",\"" ; |
440 | if (!Def->hasBuiltinAlias()) |
441 | OS << Def->getBuiltinTypeStr(); |
442 | OS << "\", \"n\")\n" ; |
443 | } |
444 | OS << "#undef RISCVV_BUILTIN\n" ; |
445 | } |
446 | |
447 | void RVVEmitter::createCodeGen(raw_ostream &OS) { |
448 | std::vector<std::unique_ptr<RVVIntrinsic>> Defs; |
449 | createRVVIntrinsics(Out&: Defs); |
450 | // IR name could be empty, use the stable sort preserves the relative order. |
451 | llvm::stable_sort(Range&: Defs, C: [](const std::unique_ptr<RVVIntrinsic> &A, |
452 | const std::unique_ptr<RVVIntrinsic> &B) { |
453 | if (A->getIRName() == B->getIRName()) |
454 | return (A->getPolicyAttrs() < B->getPolicyAttrs()); |
455 | return (A->getIRName() < B->getIRName()); |
456 | }); |
457 | |
458 | // Map to keep track of which builtin names have already been emitted. |
459 | StringMap<RVVIntrinsic *> BuiltinMap; |
460 | |
461 | // Print switch body when the ir name, ManualCodegen or policy changes from |
462 | // previous iteration. |
463 | RVVIntrinsic *PrevDef = Defs.begin()->get(); |
464 | for (auto &Def : Defs) { |
465 | StringRef CurIRName = Def->getIRName(); |
466 | if (CurIRName != PrevDef->getIRName() || |
467 | (Def->getManualCodegen() != PrevDef->getManualCodegen()) || |
468 | (Def->getPolicyAttrs() != PrevDef->getPolicyAttrs())) { |
469 | emitCodeGenSwitchBody(RVVI: PrevDef, OS); |
470 | } |
471 | PrevDef = Def.get(); |
472 | |
473 | auto P = |
474 | BuiltinMap.insert(KV: std::make_pair(x: Def->getBuiltinName(), y: Def.get())); |
475 | if (P.second) { |
476 | OS << "case RISCVVector::BI__builtin_rvv_" << Def->getBuiltinName() |
477 | << ":\n" ; |
478 | continue; |
479 | } |
480 | |
481 | if (P.first->second->getIRName() != Def->getIRName()) |
482 | PrintFatalError(Msg: "Builtin with same name has different IRName" ); |
483 | else if (P.first->second->getManualCodegen() != Def->getManualCodegen()) |
484 | PrintFatalError(Msg: "Builtin with same name has different ManualCodegen" ); |
485 | else if (P.first->second->isMasked() != Def->isMasked()) |
486 | PrintFatalError(Msg: "Builtin with same name has different isMasked" ); |
487 | else if (P.first->second->hasVL() != Def->hasVL()) |
488 | PrintFatalError(Msg: "Builtin with same name has different hasVL" ); |
489 | else if (P.first->second->getPolicyScheme() != Def->getPolicyScheme()) |
490 | PrintFatalError(Msg: "Builtin with same name has different getPolicyScheme" ); |
491 | else if (P.first->second->getIntrinsicTypes() != Def->getIntrinsicTypes()) |
492 | PrintFatalError(Msg: "Builtin with same name has different IntrinsicTypes" ); |
493 | } |
494 | emitCodeGenSwitchBody(RVVI: Defs.back().get(), OS); |
495 | OS << "\n" ; |
496 | } |
497 | |
498 | void RVVEmitter::createRVVIntrinsics( |
499 | std::vector<std::unique_ptr<RVVIntrinsic>> &Out, |
500 | std::vector<SemaRecord> *SemaRecords) { |
501 | std::vector<Record *> RV = Records.getAllDerivedDefinitions(ClassName: "RVVBuiltin" ); |
502 | for (auto *R : RV) { |
503 | StringRef Name = R->getValueAsString(FieldName: "Name" ); |
504 | StringRef SuffixProto = R->getValueAsString(FieldName: "Suffix" ); |
505 | StringRef OverloadedName = R->getValueAsString(FieldName: "OverloadedName" ); |
506 | StringRef OverloadedSuffixProto = R->getValueAsString(FieldName: "OverloadedSuffix" ); |
507 | StringRef Prototypes = R->getValueAsString(FieldName: "Prototype" ); |
508 | StringRef TypeRange = R->getValueAsString(FieldName: "TypeRange" ); |
509 | bool HasMasked = R->getValueAsBit(FieldName: "HasMasked" ); |
510 | bool HasMaskedOffOperand = R->getValueAsBit(FieldName: "HasMaskedOffOperand" ); |
511 | bool HasVL = R->getValueAsBit(FieldName: "HasVL" ); |
512 | Record *MPSRecord = R->getValueAsDef(FieldName: "MaskedPolicyScheme" ); |
513 | auto MaskedPolicyScheme = |
514 | static_cast<PolicyScheme>(MPSRecord->getValueAsInt(FieldName: "Value" )); |
515 | Record *UMPSRecord = R->getValueAsDef(FieldName: "UnMaskedPolicyScheme" ); |
516 | auto UnMaskedPolicyScheme = |
517 | static_cast<PolicyScheme>(UMPSRecord->getValueAsInt(FieldName: "Value" )); |
518 | std::vector<int64_t> Log2LMULList = R->getValueAsListOfInts(FieldName: "Log2LMUL" ); |
519 | bool HasTailPolicy = R->getValueAsBit(FieldName: "HasTailPolicy" ); |
520 | bool HasMaskPolicy = R->getValueAsBit(FieldName: "HasMaskPolicy" ); |
521 | bool SupportOverloading = R->getValueAsBit(FieldName: "SupportOverloading" ); |
522 | bool HasBuiltinAlias = R->getValueAsBit(FieldName: "HasBuiltinAlias" ); |
523 | StringRef ManualCodegen = R->getValueAsString(FieldName: "ManualCodegen" ); |
524 | std::vector<int64_t> IntrinsicTypes = |
525 | R->getValueAsListOfInts(FieldName: "IntrinsicTypes" ); |
526 | std::vector<StringRef> RequiredFeatures = |
527 | R->getValueAsListOfStrings(FieldName: "RequiredFeatures" ); |
528 | StringRef IRName = R->getValueAsString(FieldName: "IRName" ); |
529 | StringRef MaskedIRName = R->getValueAsString(FieldName: "MaskedIRName" ); |
530 | unsigned NF = R->getValueAsInt(FieldName: "NF" ); |
531 | bool IsTuple = R->getValueAsBit(FieldName: "IsTuple" ); |
532 | bool HasFRMRoundModeOp = R->getValueAsBit(FieldName: "HasFRMRoundModeOp" ); |
533 | |
534 | const Policy DefaultPolicy; |
535 | SmallVector<Policy> SupportedUnMaskedPolicies = |
536 | RVVIntrinsic::getSupportedUnMaskedPolicies(); |
537 | SmallVector<Policy> SupportedMaskedPolicies = |
538 | RVVIntrinsic::getSupportedMaskedPolicies(HasTailPolicy, HasMaskPolicy); |
539 | |
540 | // Parse prototype and create a list of primitive type with transformers |
541 | // (operand) in Prototype. Prototype[0] is output operand. |
542 | SmallVector<PrototypeDescriptor> BasicPrototype = |
543 | parsePrototypes(Prototypes); |
544 | |
545 | SmallVector<PrototypeDescriptor> SuffixDesc = parsePrototypes(Prototypes: SuffixProto); |
546 | SmallVector<PrototypeDescriptor> OverloadedSuffixDesc = |
547 | parsePrototypes(Prototypes: OverloadedSuffixProto); |
548 | |
549 | // Compute Builtin types |
550 | auto Prototype = RVVIntrinsic::computeBuiltinTypes( |
551 | Prototype: BasicPrototype, /*IsMasked=*/false, |
552 | /*HasMaskedOffOperand=*/false, HasVL, NF, DefaultScheme: UnMaskedPolicyScheme, |
553 | PolicyAttrs: DefaultPolicy, IsTuple); |
554 | llvm::SmallVector<PrototypeDescriptor> MaskedPrototype; |
555 | if (HasMasked) |
556 | MaskedPrototype = RVVIntrinsic::computeBuiltinTypes( |
557 | Prototype: BasicPrototype, /*IsMasked=*/true, HasMaskedOffOperand, HasVL, NF, |
558 | DefaultScheme: MaskedPolicyScheme, PolicyAttrs: DefaultPolicy, IsTuple); |
559 | |
560 | // Create Intrinsics for each type and LMUL. |
561 | for (char I : TypeRange) { |
562 | for (int Log2LMUL : Log2LMULList) { |
563 | BasicType BT = ParseBasicType(c: I); |
564 | std::optional<RVVTypes> Types = |
565 | TypeCache.computeTypes(BT, Log2LMUL, NF, Prototype); |
566 | // Ignored to create new intrinsic if there are any illegal types. |
567 | if (!Types) |
568 | continue; |
569 | |
570 | auto SuffixStr = |
571 | RVVIntrinsic::getSuffixStr(TypeCache, Type: BT, Log2LMUL, PrototypeDescriptors: SuffixDesc); |
572 | auto OverloadedSuffixStr = RVVIntrinsic::getSuffixStr( |
573 | TypeCache, Type: BT, Log2LMUL, PrototypeDescriptors: OverloadedSuffixDesc); |
574 | // Create a unmasked intrinsic |
575 | Out.push_back(x: std::make_unique<RVVIntrinsic>( |
576 | args&: Name, args&: SuffixStr, args&: OverloadedName, args&: OverloadedSuffixStr, args&: IRName, |
577 | /*IsMasked=*/args: false, /*HasMaskedOffOperand=*/args: false, args&: HasVL, |
578 | args&: UnMaskedPolicyScheme, args&: SupportOverloading, args&: HasBuiltinAlias, |
579 | args&: ManualCodegen, args&: *Types, args&: IntrinsicTypes, args&: NF, args: DefaultPolicy, |
580 | args&: HasFRMRoundModeOp)); |
581 | if (UnMaskedPolicyScheme != PolicyScheme::SchemeNone) |
582 | for (auto P : SupportedUnMaskedPolicies) { |
583 | SmallVector<PrototypeDescriptor> PolicyPrototype = |
584 | RVVIntrinsic::computeBuiltinTypes( |
585 | Prototype: BasicPrototype, /*IsMasked=*/false, |
586 | /*HasMaskedOffOperand=*/false, HasVL, NF, |
587 | DefaultScheme: UnMaskedPolicyScheme, PolicyAttrs: P, IsTuple); |
588 | std::optional<RVVTypes> PolicyTypes = |
589 | TypeCache.computeTypes(BT, Log2LMUL, NF, Prototype: PolicyPrototype); |
590 | Out.push_back(x: std::make_unique<RVVIntrinsic>( |
591 | args&: Name, args&: SuffixStr, args&: OverloadedName, args&: OverloadedSuffixStr, args&: IRName, |
592 | /*IsMask=*/args: false, /*HasMaskedOffOperand=*/args: false, args&: HasVL, |
593 | args&: UnMaskedPolicyScheme, args&: SupportOverloading, args&: HasBuiltinAlias, |
594 | args&: ManualCodegen, args&: *PolicyTypes, args&: IntrinsicTypes, args&: NF, args&: P, |
595 | args&: HasFRMRoundModeOp)); |
596 | } |
597 | if (!HasMasked) |
598 | continue; |
599 | // Create a masked intrinsic |
600 | std::optional<RVVTypes> MaskTypes = |
601 | TypeCache.computeTypes(BT, Log2LMUL, NF, Prototype: MaskedPrototype); |
602 | Out.push_back(x: std::make_unique<RVVIntrinsic>( |
603 | args&: Name, args&: SuffixStr, args&: OverloadedName, args&: OverloadedSuffixStr, args&: MaskedIRName, |
604 | /*IsMasked=*/args: true, args&: HasMaskedOffOperand, args&: HasVL, args&: MaskedPolicyScheme, |
605 | args&: SupportOverloading, args&: HasBuiltinAlias, args&: ManualCodegen, args&: *MaskTypes, |
606 | args&: IntrinsicTypes, args&: NF, args: DefaultPolicy, args&: HasFRMRoundModeOp)); |
607 | if (MaskedPolicyScheme == PolicyScheme::SchemeNone) |
608 | continue; |
609 | for (auto P : SupportedMaskedPolicies) { |
610 | SmallVector<PrototypeDescriptor> PolicyPrototype = |
611 | RVVIntrinsic::computeBuiltinTypes( |
612 | Prototype: BasicPrototype, /*IsMasked=*/true, HasMaskedOffOperand, HasVL, |
613 | NF, DefaultScheme: MaskedPolicyScheme, PolicyAttrs: P, IsTuple); |
614 | std::optional<RVVTypes> PolicyTypes = |
615 | TypeCache.computeTypes(BT, Log2LMUL, NF, Prototype: PolicyPrototype); |
616 | Out.push_back(x: std::make_unique<RVVIntrinsic>( |
617 | args&: Name, args&: SuffixStr, args&: OverloadedName, args&: OverloadedSuffixStr, |
618 | args&: MaskedIRName, /*IsMasked=*/args: true, args&: HasMaskedOffOperand, args&: HasVL, |
619 | args&: MaskedPolicyScheme, args&: SupportOverloading, args&: HasBuiltinAlias, |
620 | args&: ManualCodegen, args&: *PolicyTypes, args&: IntrinsicTypes, args&: NF, args&: P, |
621 | args&: HasFRMRoundModeOp)); |
622 | } |
623 | } // End for Log2LMULList |
624 | } // End for TypeRange |
625 | |
626 | // We don't emit vsetvli and vsetvlimax for SemaRecord. |
627 | // They are written in riscv_vector.td and will emit those marco define in |
628 | // riscv_vector.h |
629 | if (Name == "vsetvli" || Name == "vsetvlimax" ) |
630 | continue; |
631 | |
632 | if (!SemaRecords) |
633 | continue; |
634 | |
635 | // Create SemaRecord |
636 | SemaRecord SR; |
637 | SR.Name = Name.str(); |
638 | SR.OverloadedName = OverloadedName.str(); |
639 | BasicType TypeRangeMask = BasicType::Unknown; |
640 | for (char I : TypeRange) |
641 | TypeRangeMask |= ParseBasicType(c: I); |
642 | |
643 | SR.TypeRangeMask = static_cast<unsigned>(TypeRangeMask); |
644 | |
645 | unsigned Log2LMULMask = 0; |
646 | for (int Log2LMUL : Log2LMULList) |
647 | Log2LMULMask |= 1 << (Log2LMUL + 3); |
648 | |
649 | SR.Log2LMULMask = Log2LMULMask; |
650 | |
651 | SR.RequiredExtensions = 0; |
652 | for (auto RequiredFeature : RequiredFeatures) { |
653 | RVVRequire RequireExt = |
654 | StringSwitch<RVVRequire>(RequiredFeature) |
655 | .Case(S: "RV64" , Value: RVV_REQ_RV64) |
656 | .Case(S: "Zvfhmin" , Value: RVV_REQ_Zvfhmin) |
657 | .Case(S: "Xsfvcp" , Value: RVV_REQ_Xsfvcp) |
658 | .Case(S: "Xsfvfnrclipxfqf" , Value: RVV_REQ_Xsfvfnrclipxfqf) |
659 | .Case(S: "Xsfvfwmaccqqq" , Value: RVV_REQ_Xsfvfwmaccqqq) |
660 | .Case(S: "Xsfvqmaccdod" , Value: RVV_REQ_Xsfvqmaccdod) |
661 | .Case(S: "Xsfvqmaccqoq" , Value: RVV_REQ_Xsfvqmaccqoq) |
662 | .Case(S: "Zvbb" , Value: RVV_REQ_Zvbb) |
663 | .Case(S: "Zvbc" , Value: RVV_REQ_Zvbc) |
664 | .Case(S: "Zvkb" , Value: RVV_REQ_Zvkb) |
665 | .Case(S: "Zvkg" , Value: RVV_REQ_Zvkg) |
666 | .Case(S: "Zvkned" , Value: RVV_REQ_Zvkned) |
667 | .Case(S: "Zvknha" , Value: RVV_REQ_Zvknha) |
668 | .Case(S: "Zvknhb" , Value: RVV_REQ_Zvknhb) |
669 | .Case(S: "Zvksed" , Value: RVV_REQ_Zvksed) |
670 | .Case(S: "Zvksh" , Value: RVV_REQ_Zvksh) |
671 | .Case(S: "Zvfbfwma" , Value: RVV_REQ_Zvfbfwma) |
672 | .Case(S: "Zvfbfmin" , Value: RVV_REQ_Zvfbfmin) |
673 | .Case(S: "Experimental" , Value: RVV_REQ_Experimental) |
674 | .Default(Value: RVV_REQ_None); |
675 | assert(RequireExt != RVV_REQ_None && "Unrecognized required feature?" ); |
676 | SR.RequiredExtensions |= RequireExt; |
677 | } |
678 | |
679 | SR.NF = NF; |
680 | SR.HasMasked = HasMasked; |
681 | SR.HasVL = HasVL; |
682 | SR.HasMaskedOffOperand = HasMaskedOffOperand; |
683 | SR.HasTailPolicy = HasTailPolicy; |
684 | SR.HasMaskPolicy = HasMaskPolicy; |
685 | SR.UnMaskedPolicyScheme = static_cast<uint8_t>(UnMaskedPolicyScheme); |
686 | SR.MaskedPolicyScheme = static_cast<uint8_t>(MaskedPolicyScheme); |
687 | SR.Prototype = std::move(BasicPrototype); |
688 | SR.Suffix = parsePrototypes(Prototypes: SuffixProto); |
689 | SR.OverloadedSuffix = parsePrototypes(Prototypes: OverloadedSuffixProto); |
690 | SR.IsTuple = IsTuple; |
691 | SR.HasFRMRoundModeOp = HasFRMRoundModeOp; |
692 | |
693 | SemaRecords->push_back(x: SR); |
694 | } |
695 | } |
696 | |
697 | void RVVEmitter::(raw_ostream &OS) { |
698 | std::vector<Record *> = |
699 | Records.getAllDerivedDefinitions(ClassName: "RVVHeader" ); |
700 | for (auto *R : RVVHeaders) { |
701 | StringRef = R->getValueAsString(FieldName: "HeaderCode" ); |
702 | OS << HeaderCodeStr.str(); |
703 | } |
704 | } |
705 | |
706 | void RVVEmitter::createRVVIntrinsicRecords(std::vector<RVVIntrinsicRecord> &Out, |
707 | SemaSignatureTable &SST, |
708 | ArrayRef<SemaRecord> SemaRecords) { |
709 | SST.init(SemaRecords); |
710 | |
711 | for (const auto &SR : SemaRecords) { |
712 | Out.emplace_back(args: RVVIntrinsicRecord()); |
713 | RVVIntrinsicRecord &R = Out.back(); |
714 | R.Name = SR.Name.c_str(); |
715 | R.OverloadedName = SR.OverloadedName.c_str(); |
716 | R.PrototypeIndex = SST.getIndex(Signature: SR.Prototype); |
717 | R.SuffixIndex = SST.getIndex(Signature: SR.Suffix); |
718 | R.OverloadedSuffixIndex = SST.getIndex(Signature: SR.OverloadedSuffix); |
719 | R.PrototypeLength = SR.Prototype.size(); |
720 | R.SuffixLength = SR.Suffix.size(); |
721 | R.OverloadedSuffixSize = SR.OverloadedSuffix.size(); |
722 | R.RequiredExtensions = SR.RequiredExtensions; |
723 | R.TypeRangeMask = SR.TypeRangeMask; |
724 | R.Log2LMULMask = SR.Log2LMULMask; |
725 | R.NF = SR.NF; |
726 | R.HasMasked = SR.HasMasked; |
727 | R.HasVL = SR.HasVL; |
728 | R.HasMaskedOffOperand = SR.HasMaskedOffOperand; |
729 | R.HasTailPolicy = SR.HasTailPolicy; |
730 | R.HasMaskPolicy = SR.HasMaskPolicy; |
731 | R.UnMaskedPolicyScheme = SR.UnMaskedPolicyScheme; |
732 | R.MaskedPolicyScheme = SR.MaskedPolicyScheme; |
733 | R.IsTuple = SR.IsTuple; |
734 | R.HasFRMRoundModeOp = SR.HasFRMRoundModeOp; |
735 | |
736 | assert(R.PrototypeIndex != |
737 | static_cast<uint16_t>(SemaSignatureTable::INVALID_INDEX)); |
738 | assert(R.SuffixIndex != |
739 | static_cast<uint16_t>(SemaSignatureTable::INVALID_INDEX)); |
740 | assert(R.OverloadedSuffixIndex != |
741 | static_cast<uint16_t>(SemaSignatureTable::INVALID_INDEX)); |
742 | } |
743 | } |
744 | |
745 | void RVVEmitter::createSema(raw_ostream &OS) { |
746 | std::vector<std::unique_ptr<RVVIntrinsic>> Defs; |
747 | std::vector<RVVIntrinsicRecord> RVVIntrinsicRecords; |
748 | SemaSignatureTable SST; |
749 | std::vector<SemaRecord> SemaRecords; |
750 | |
751 | createRVVIntrinsics(Out&: Defs, SemaRecords: &SemaRecords); |
752 | |
753 | createRVVIntrinsicRecords(Out&: RVVIntrinsicRecords, SST, SemaRecords); |
754 | |
755 | // Emit signature table for SemaRISCVVectorLookup.cpp. |
756 | OS << "#ifdef DECL_SIGNATURE_TABLE\n" ; |
757 | SST.print(OS); |
758 | OS << "#endif\n" ; |
759 | |
760 | // Emit RVVIntrinsicRecords for SemaRISCVVectorLookup.cpp. |
761 | OS << "#ifdef DECL_INTRINSIC_RECORDS\n" ; |
762 | for (const RVVIntrinsicRecord &Record : RVVIntrinsicRecords) |
763 | OS << Record; |
764 | OS << "#endif\n" ; |
765 | } |
766 | |
767 | namespace clang { |
768 | void (RecordKeeper &Records, raw_ostream &OS) { |
769 | RVVEmitter(Records).createHeader(OS); |
770 | } |
771 | |
772 | void EmitRVVBuiltins(RecordKeeper &Records, raw_ostream &OS) { |
773 | RVVEmitter(Records).createBuiltins(OS); |
774 | } |
775 | |
776 | void EmitRVVBuiltinCG(RecordKeeper &Records, raw_ostream &OS) { |
777 | RVVEmitter(Records).createCodeGen(OS); |
778 | } |
779 | |
780 | void EmitRVVBuiltinSema(RecordKeeper &Records, raw_ostream &OS) { |
781 | RVVEmitter(Records).createSema(OS); |
782 | } |
783 | |
784 | } // End namespace clang |
785 | |