| 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::(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::(raw_ostream &OS) { |
| 793 | for (const Record *R : Records.getAllDerivedDefinitions(ClassName: "RVVHeader" )) { |
| 794 | StringRef = 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 (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 | |