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